<commit_before>package com.czbix.v2ex.network

import android.annotation.SuppressLint
import android.os.Build
import android.util.Log
import com.crashlytics.android.Crashlytics
import com.czbix.v2ex.AppCtx
import com.czbix.v2ex.BuildConfig
import com.czbix.v2ex.common.DeviceStatus
import com.czbix.v2ex.common.UserState
import com.czbix.v2ex.common.exception.*
import com.czbix.v2ex.model.*
import com.czbix.v2ex.parser.*
import com.czbix.v2ex.parser.Parser.PageType
import com.czbix.v2ex.ui.loader.TopicListLoader
import com.czbix.v2ex.util.*
import com.franmontiel.persistentcookiejar.PersistentCookieJar
import com.franmontiel.persistentcookiejar.cache.SetCookieCache
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor
import com.google.common.base.Preconditions
import com.google.common.base.Stopwatch
import com.google.common.base.Strings
import com.google.common.net.HttpHeaders
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.SingleSubject
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.jsoup.nodes.Document
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit

object RequestHelper {
    const val BASE_URL = "https://www.v2ex.com"
    private val USER_AGENT = "V2EX+/" + BuildConfig.VERSION_NAME
    private val USER_AGENT_ANDROID = String.format("%s (Android %s)", USER_AGENT, Build.VERSION.RELEASE)

    private val DEBUG_NETWORK_FAIL_PROBABILITY = 95

    private val TAG = RequestHelper::class.java.simpleName
    private val API_GET_ALL_NODES = BASE_URL + "/api/nodes/all.json"
    private val URL_SIGN_IN = BASE_URL + "/signin"
    private val URL_MISSION_DAILY = BASE_URL + "/mission/daily"
    private val URL_ONCE_TOKEN = URL_SIGN_IN
    private val URL_NOTIFICATIONS = BASE_URL + "/notifications"
    private val URL_FAVORITE_NODES = BASE_URL + "/my/nodes"
    private val URL_UNREAD_NOTIFICATIONS = BASE_URL + "/mission"
    private val URL_TWO_FACTOR_AUTH = BASE_URL + "/2fa"
    private val URL_NEW_TOPIC = BASE_URL + "/new/%s"
    private val URL_CAPTCHA = BASE_URL + "/_captcha"
    private const val URL_SELECT_LANG = "$BASE_URL/select/language/%s"

    val client: OkHttpClient
    private val isChinese: Boolean

    private var cookieJar: PersistentCookieJar

    init {
        cookieJar = PersistentCookieJar(SetCookieCache(),
                SharedPrefsCookiePersistor(AppCtx.instance))

        client = OkHttpClient.Builder().apply {
            AppCtx.instance.debugHelpers.addStethoInterceptor(this)
            cache(buildCache())
            connectTimeout(10, TimeUnit.SECONDS)
            writeTimeout(10, TimeUnit.SECONDS)
            readTimeout(30, TimeUnit.SECONDS)
            followRedirects(false)
            cookieJar(cookieJar)
        }.build()

        isChinese = MiscUtils.isChineseLang(AppCtx.instance.resources)
    }

    private fun buildCache(): Cache {
        val cacheDir = IoUtils.webCachePath
        val cacheSize = 16 * 1024 * 1024

        return Cache(cacheDir, cacheSize.toLong())
    }

    internal fun newRequest(useMobile: Boolean = false): Request.Builder {
        val ua = if (useMobile) {
            USER_AGENT_ANDROID
        } else {
            USER_AGENT
        }

        return Request.Builder().apply {
            header(HttpHeaders.USER_AGENT, ua)
        }
    }

    @SuppressLint("CheckResult")
    fun setLang() {
        val lang = if (isChinese) "zhcn" else "enus"

        val baseUrl = BASE_URL.toHttpUrl()
        val cookies = cookieJar.loadForRequest(baseUrl)
        val currentLang = cookies.find { cookie ->
            cookie.name == "V2EX_LANG"
        }?.value

        if (currentLang == lang) {
            return
        }

        val request = newRequest()
                .url(URL_SELECT_LANG.format(lang))
                .build()

        sendRequest(request) {
            Unit
        }.onErrorReturn {
            checkIsRedirectException(it)
        }.subscribe()
    }<commit_msg>Fix crash on start when network not connected<commit_after>package com.czbix.v2ex.network

import android.annotation.SuppressLint
import android.os.Build
import android.util.Log
import com.crashlytics.android.Crashlytics
import com.czbix.v2ex.AppCtx
import com.czbix.v2ex.BuildConfig
import com.czbix.v2ex.common.DeviceStatus
import com.czbix.v2ex.common.UserState
import com.czbix.v2ex.common.exception.*
import com.czbix.v2ex.model.*
import com.czbix.v2ex.parser.*
import com.czbix.v2ex.parser.Parser.PageType
import com.czbix.v2ex.ui.loader.TopicListLoader
import com.czbix.v2ex.util.*
import com.franmontiel.persistentcookiejar.PersistentCookieJar
import com.franmontiel.persistentcookiejar.cache.SetCookieCache
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor
import com.google.common.base.Preconditions
import com.google.common.base.Stopwatch
import com.google.common.base.Strings
import com.google.common.net.HttpHeaders
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.SingleSubject
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.jsoup.nodes.Document
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit

object RequestHelper {
    const val BASE_URL = "https://www.v2ex.com"
    private val USER_AGENT = "V2EX+/" + BuildConfig.VERSION_NAME
    private val USER_AGENT_ANDROID = String.format("%s (Android %s)", USER_AGENT, Build.VERSION.RELEASE)

    private val DEBUG_NETWORK_FAIL_PROBABILITY = 95

    private val TAG = RequestHelper::class.java.simpleName
    private val API_GET_ALL_NODES = BASE_URL + "/api/nodes/all.json"
    private val URL_SIGN_IN = BASE_URL + "/signin"
    private val URL_MISSION_DAILY = BASE_URL + "/mission/daily"
    private val URL_ONCE_TOKEN = URL_SIGN_IN
    private val URL_NOTIFICATIONS = BASE_URL + "/notifications"
    private val URL_FAVORITE_NODES = BASE_URL + "/my/nodes"
    private val URL_UNREAD_NOTIFICATIONS = BASE_URL + "/mission"
    private val URL_TWO_FACTOR_AUTH = BASE_URL + "/2fa"
    private val URL_NEW_TOPIC = BASE_URL + "/new/%s"
    private val URL_CAPTCHA = BASE_URL + "/_captcha"
    private const val URL_SELECT_LANG = "$BASE_URL/select/language/%s"

    val client: OkHttpClient
    private val isChinese: Boolean

    private var cookieJar: PersistentCookieJar

    init {
        cookieJar = PersistentCookieJar(SetCookieCache(),
                SharedPrefsCookiePersistor(AppCtx.instance))

        client = OkHttpClient.Builder().apply {
            AppCtx.instance.debugHelpers.addStethoInterceptor(this)
            cache(buildCache())
            connectTimeout(10, TimeUnit.SECONDS)
            writeTimeout(10, TimeUnit.SECONDS)
            readTimeout(30, TimeUnit.SECONDS)
            followRedirects(false)
            cookieJar(cookieJar)
        }.build()

        isChinese = MiscUtils.isChineseLang(AppCtx.instance.resources)
    }

    private fun buildCache(): Cache {
        val cacheDir = IoUtils.webCachePath
        val cacheSize = 16 * 1024 * 1024

        return Cache(cacheDir, cacheSize.toLong())
    }

    internal fun newRequest(useMobile: Boolean = false): Request.Builder {
        val ua = if (useMobile) {
            USER_AGENT_ANDROID
        } else {
            USER_AGENT
        }

        return Request.Builder().apply {
            header(HttpHeaders.USER_AGENT, ua)
        }
    }

    @SuppressLint("CheckResult")
    fun setLang() {
        val lang = if (isChinese) "zhcn" else "enus"

        val baseUrl = BASE_URL.toHttpUrl()
        val cookies = cookieJar.loadForRequest(baseUrl)
        val currentLang = cookies.find { cookie ->
            cookie.name == "V2EX_LANG"
        }?.value

        if (currentLang == lang) {
            return
        }

        val request = newRequest()
                .url(URL_SELECT_LANG.format(lang))
                .build()

        sendRequest(request) {
            Unit
        }.onErrorReturn {
            checkIsRedirectException(it)
        }.onErrorReturn { t ->
            if (t !is ConnectionException && t !is RemoteException) {
                Crashlytics.logException(t)
            }

            // Ignore all exception
            Unit
        }.subscribe()
    }
{'fraction_non_alphanumeric': 0.09144507279509084, 'fraction_numerical': 0.008422572494284682, 'mean_word_length': 2.96186844613918, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 6, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6813906', 'n_tokens_mistral': 2703, 'n_tokens_neox': 2601, 'n_words': 505}