kunfei 4 лет назад
Родитель
Сommit
fa48eb682c
72 измененных файлов с 334 добавлено и 267 удалено
  1. 4 1
      app/build.gradle
  2. 0 4
      app/src/main/java/io/legado/app/App.kt
  3. 7 0
      app/src/main/java/io/legado/app/constant/AppLog.kt
  4. 3 3
      app/src/main/java/io/legado/app/data/entities/BaseSource.kt
  5. 1 2
      app/src/main/java/io/legado/app/data/entities/BookSource.kt
  6. 1 2
      app/src/main/java/io/legado/app/help/BookHelp.kt
  7. 8 13
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  8. 5 5
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  9. 2 2
      app/src/main/java/io/legado/app/help/SourceAnalyzer.kt
  10. 2 2
      app/src/main/java/io/legado/app/help/ThemeConfig.kt
  11. 3 2
      app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
  12. 6 5
      app/src/main/java/io/legado/app/help/http/SSLHelper.kt
  13. 3 3
      app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt
  14. 3 2
      app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt
  15. 29 27
      app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt
  16. 6 5
      app/src/main/java/io/legado/app/help/http/cronet/CronetRequestCallback.kt
  17. 2 2
      app/src/main/java/io/legado/app/help/storage/OldBook.kt
  18. 7 4
      app/src/main/java/io/legado/app/help/storage/Preferences.kt
  19. 6 6
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  20. 4 3
      app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
  21. 1 2
      app/src/main/java/io/legado/app/model/CacheBook.kt
  22. 2 2
      app/src/main/java/io/legado/app/model/ReadBook.kt
  23. 5 4
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeByJSonPath.kt
  24. 2 2
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  25. 4 7
      app/src/main/java/io/legado/app/model/localBook/EpubFile.kt
  26. 2 2
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  27. 4 6
      app/src/main/java/io/legado/app/model/localBook/UmdFile.kt
  28. 7 6
      app/src/main/java/io/legado/app/model/webBook/BookInfo.kt
  29. 4 5
      app/src/main/java/io/legado/app/service/AudioPlayService.kt
  30. 4 4
      app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
  31. 2 2
      app/src/main/java/io/legado/app/service/WebService.kt
  32. 2 2
      app/src/main/java/io/legado/app/service/WebTileService.kt
  33. 3 6
      app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
  34. 3 2
      app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt
  35. 3 2
      app/src/main/java/io/legado/app/ui/association/FileAssociationViewModel.kt
  36. 2 2
      app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt
  37. 3 3
      app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt
  38. 4 3
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt
  39. 4 3
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceViewModel.kt
  40. 3 2
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt
  41. 2 2
      app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
  42. 4 4
      app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
  43. 3 3
      app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditViewModel.kt
  44. 2 2
      app/src/main/java/io/legado/app/ui/browser/WebViewModel.kt
  45. 3 7
      app/src/main/java/io/legado/app/ui/document/HandleFileViewModel.kt
  46. 2 2
      app/src/main/java/io/legado/app/ui/font/FontAdapter.kt
  47. 2 2
      app/src/main/java/io/legado/app/ui/login/SourceLoginDialog.kt
  48. 1 2
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
  49. 5 3
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
  50. 3 2
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
  51. 2 2
      app/src/main/java/io/legado/app/ui/widget/SearchView.kt
  52. 4 3
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/ExplosionView.kt
  53. 3 2
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/Utils.kt
  54. 3 6
      app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt
  55. 6 2
      app/src/main/java/io/legado/app/ui/widget/recycler/DividerNoLast.kt
  56. 5 4
      app/src/main/java/io/legado/app/ui/widget/recycler/DragSelectTouchHelper.kt
  57. 16 16
      app/src/main/java/io/legado/app/utils/ACache.kt
  58. 3 3
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  59. 54 0
      app/src/main/java/io/legado/app/utils/DebugLog.kt
  60. 3 3
      app/src/main/java/io/legado/app/utils/FileUtils.kt
  61. 5 3
      app/src/main/java/io/legado/app/utils/GsonExtensions.kt
  62. 7 0
      app/src/main/java/io/legado/app/utils/LogUtils.kt
  63. 2 2
      app/src/main/java/io/legado/app/utils/MD5Utils.kt
  64. 5 4
      app/src/main/java/io/legado/app/utils/NetworkUtils.kt
  65. 4 4
      app/src/main/java/io/legado/app/utils/RealPathUtil.kt
  66. 2 2
      app/src/main/java/io/legado/app/utils/StringUtils.kt
  67. 3 3
      app/src/main/java/io/legado/app/utils/UriExtensions.kt
  68. 2 2
      app/src/main/java/io/legado/app/utils/ViewExtensions.kt
  69. 5 5
      app/src/main/java/io/legado/app/utils/ZipUtils.kt
  70. 3 6
      app/src/main/java/io/legado/app/web/socket/BookSourceDebugWebSocket.kt
  71. 3 6
      app/src/main/java/io/legado/app/web/socket/RssSourceDebugWebSocket.kt
  72. 1 0
      build.gradle

+ 4 - 1
app/build.gradle

@@ -3,6 +3,7 @@ apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-parcelize'
 apply plugin: 'kotlin-parcelize'
 apply plugin: 'kotlin-kapt'
 apply plugin: 'kotlin-kapt'
 apply plugin: 'de.timfreiheit.resourceplaceholders'
 apply plugin: 'de.timfreiheit.resourceplaceholders'
+apply plugin: 'com.google.gms.google-services'
 apply from: 'download.gradle'
 apply from: 'download.gradle'
 
 
 static def releaseTime() {
 static def releaseTime() {
@@ -150,7 +151,9 @@ dependencies {
     implementation('com.google.code.gson:gson:2.8.9')
     implementation('com.google.code.gson:gson:2.8.9')
     implementation('androidx.webkit:webkit:1.4.0')
     implementation('androidx.webkit:webkit:1.4.0')
 
 
-    implementation('com.jakewharton.timber:timber:5.0.1')
+    //firebase
+    implementation platform('com.google.firebase:firebase-bom:29.1.0')
+    implementation 'com.google.firebase:firebase-analytics-ktx'
 
 
     //media
     //media
     implementation("androidx.media:media:1.5.0")
     implementation("androidx.media:media:1.5.0")

+ 0 - 4
app/src/main/java/io/legado/app/App.kt

@@ -18,16 +18,12 @@ import io.legado.app.help.ThemeConfig.applyDayNight
 import io.legado.app.help.http.cronet.CronetLoader
 import io.legado.app.help.http.cronet.CronetLoader
 import io.legado.app.utils.defaultSharedPreferences
 import io.legado.app.utils.defaultSharedPreferences
 import splitties.systemservices.notificationManager
 import splitties.systemservices.notificationManager
-import timber.log.Timber
 
 
 class App : MultiDexApplication() {
 class App : MultiDexApplication() {
 
 
     override fun onCreate() {
     override fun onCreate() {
         super.onCreate()
         super.onCreate()
         CrashHandler(this)
         CrashHandler(this)
-        if (BuildConfig.DEBUG) {
-            Timber.plant(Timber.DebugTree())
-        }
         //预下载Cronet so
         //预下载Cronet so
         CronetLoader.preDownload()
         CronetLoader.preDownload()
         createNotificationChannels()
         createNotificationChannels()

+ 7 - 0
app/src/main/java/io/legado/app/constant/AppLog.kt

@@ -1,5 +1,7 @@
 package io.legado.app.constant
 package io.legado.app.constant
 
 
+import android.util.Log
+import io.legado.app.BuildConfig
 import io.legado.app.help.AppConfig
 import io.legado.app.help.AppConfig
 
 
 object AppLog {
 object AppLog {
@@ -15,6 +17,11 @@ object AppLog {
             mLogs.removeLastOrNull()
             mLogs.removeLastOrNull()
         }
         }
         mLogs.add(0, Triple(System.currentTimeMillis(), message, throwable))
         mLogs.add(0, Triple(System.currentTimeMillis(), message, throwable))
+        if (throwable != null) {
+            if (BuildConfig.DEBUG) {
+                Log.e(message, throwable.stackTraceToString())
+            }
+        }
     }
     }
 
 
     @Synchronized
     @Synchronized

+ 3 - 3
app/src/main/java/io/legado/app/data/entities/BaseSource.kt

@@ -2,6 +2,7 @@ package io.legado.app.data.entities
 
 
 import android.util.Base64
 import android.util.Base64
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst
+import io.legado.app.constant.AppLog
 import io.legado.app.data.entities.rule.RowUi
 import io.legado.app.data.entities.rule.RowUi
 import io.legado.app.help.AppConfig
 import io.legado.app.help.AppConfig
 import io.legado.app.help.CacheManager
 import io.legado.app.help.CacheManager
@@ -11,7 +12,6 @@ import io.legado.app.utils.EncoderUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
 import io.legado.app.utils.fromJsonArray
 import io.legado.app.utils.fromJsonArray
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.fromJsonObject
-import timber.log.Timber
 import javax.script.SimpleBindings
 import javax.script.SimpleBindings
 
 
 /**
 /**
@@ -111,7 +111,7 @@ interface BaseSource : JsExtensions {
                 ?: return null
                 ?: return null
             return String(decodeBytes)
             return String(decodeBytes)
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            AppLog.put("获取登陆信息出错", e)
             return null
             return null
         }
         }
     }
     }
@@ -131,7 +131,7 @@ interface BaseSource : JsExtensions {
             CacheManager.put("userInfo_${getKey()}", encodeStr)
             CacheManager.put("userInfo_${getKey()}", encodeStr)
             true
             true
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            AppLog.put("保存登陆信息出错", e)
             false
             false
         }
         }
     }
     }

+ 1 - 2
app/src/main/java/io/legado/app/data/entities/BookSource.kt

@@ -11,7 +11,6 @@ import io.legado.app.utils.*
 import kotlinx.parcelize.IgnoredOnParcel
 import kotlinx.parcelize.IgnoredOnParcel
 import kotlinx.parcelize.Parcelize
 import kotlinx.parcelize.Parcelize
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
 
 
 @Parcelize
 @Parcelize
 @TypeConverters(BookSource.Converters::class)
 @TypeConverters(BookSource.Converters::class)
@@ -120,7 +119,7 @@ data class BookSource(
                 }
                 }
             }.onFailure {
             }.onFailure {
                 kinds.add(ExploreKind("ERROR:${it.localizedMessage}", it.stackTraceToString()))
                 kinds.add(ExploreKind("ERROR:${it.localizedMessage}", it.stackTraceToString()))
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
         }
         }
         return@lazy kinds
         return@lazy kinds

+ 1 - 2
app/src/main/java/io/legado/app/help/BookHelp.kt

@@ -16,7 +16,6 @@ import kotlinx.coroutines.async
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import org.apache.commons.text.similarity.JaccardSimilarity
 import org.apache.commons.text.similarity.JaccardSimilarity
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
 import java.io.File
 import java.io.File
 import java.util.concurrent.CopyOnWriteArraySet
 import java.util.concurrent.CopyOnWriteArraySet
 import java.util.regex.Pattern
 import java.util.regex.Pattern
@@ -131,7 +130,7 @@ object BookHelp {
                 ).writeBytes(it)
                 ).writeBytes(it)
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         } finally {
         } finally {
             downloadImages.remove(src)
             downloadImages.remove(src)
         }
         }

+ 8 - 13
app/src/main/java/io/legado/app/help/JsExtensions.kt

@@ -6,7 +6,6 @@ import androidx.annotation.Keep
 import cn.hutool.crypto.digest.DigestUtil
 import cn.hutool.crypto.digest.DigestUtil
 import cn.hutool.crypto.symmetric.AES
 import cn.hutool.crypto.symmetric.AES
 import cn.hutool.crypto.symmetric.DESede
 import cn.hutool.crypto.symmetric.DESede
-import io.legado.app.BuildConfig
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst.dateFormat
 import io.legado.app.constant.AppConst.dateFormat
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
@@ -22,7 +21,6 @@ import kotlinx.coroutines.runBlocking
 import org.jsoup.Connection
 import org.jsoup.Connection
 import org.jsoup.Jsoup
 import org.jsoup.Jsoup
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 import java.io.ByteArrayOutputStream
 import java.io.File
 import java.io.File
@@ -54,7 +52,7 @@ interface JsExtensions {
                 analyzeUrl.getStrResponseAwait().body
                 analyzeUrl.getStrResponseAwait().body
             }.onFailure {
             }.onFailure {
                 log("ajax(${urlStr}) error\n${it.stackTraceToString()}")
                 log("ajax(${urlStr}) error\n${it.stackTraceToString()}")
-                Timber.e(it)
+                it.printOnDebug()
             }.getOrElse {
             }.getOrElse {
                 it.msg
                 it.msg
             }
             }
@@ -90,7 +88,7 @@ interface JsExtensions {
                 analyzeUrl.getStrResponseAwait()
                 analyzeUrl.getStrResponseAwait()
             }.onFailure {
             }.onFailure {
                 log("connect(${urlStr}) error\n${it.stackTraceToString()}")
                 log("connect(${urlStr}) error\n${it.stackTraceToString()}")
-                Timber.e(it)
+                it.printOnDebug()
             }.getOrElse {
             }.getOrElse {
                 StrResponse(analyzeUrl.url, it.localizedMessage)
                 StrResponse(analyzeUrl.url, it.localizedMessage)
             }
             }
@@ -105,7 +103,7 @@ interface JsExtensions {
                 analyzeUrl.getStrResponseAwait()
                 analyzeUrl.getStrResponseAwait()
             }.onFailure {
             }.onFailure {
                 log("ajax($urlStr,$header) error\n${it.stackTraceToString()}")
                 log("ajax($urlStr,$header) error\n${it.stackTraceToString()}")
-                Timber.e(it)
+                it.printOnDebug()
             }.getOrElse {
             }.getOrElse {
                 StrResponse(analyzeUrl.url, it.localizedMessage)
                 StrResponse(analyzeUrl.url, it.localizedMessage)
             }
             }
@@ -481,7 +479,7 @@ interface JsExtensions {
             CacheManager.put(key, qTTF)
             CacheManager.put(key, qTTF)
             return qTTF
             return qTTF
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e, "获取字体处理类出错")
+            AppLog.put("获取字体处理类出错", e)
             throw e
             throw e
         }
         }
     }
     }
@@ -518,9 +516,6 @@ interface JsExtensions {
         getSource()?.let {
         getSource()?.let {
             Debug.log(it.getKey(), msg.toString())
             Debug.log(it.getKey(), msg.toString())
         } ?: Debug.log(msg.toString())
         } ?: Debug.log(msg.toString())
-        if (BuildConfig.DEBUG) {
-            Timber.d(msg.toString())
-        }
         AppLog.putDebug(msg.toString())
         AppLog.putDebug(msg.toString())
         return msg
         return msg
     }
     }
@@ -561,7 +556,7 @@ interface JsExtensions {
                 iv.encodeToByteArray()
                 iv.encodeToByteArray()
             )
             )
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
             log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
             log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
             null
             null
         }
         }
@@ -600,7 +595,7 @@ interface JsExtensions {
                 iv.encodeToByteArray()
                 iv.encodeToByteArray()
             )
             )
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
             log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
             log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
             null
             null
         }
         }
@@ -638,7 +633,7 @@ interface JsExtensions {
                 iv.encodeToByteArray()
                 iv.encodeToByteArray()
             )
             )
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
             log(e.localizedMessage ?: "aesEncodeToByteArrayERROR")
             log(e.localizedMessage ?: "aesEncodeToByteArrayERROR")
             null
             null
         }
         }
@@ -675,7 +670,7 @@ interface JsExtensions {
                 iv.encodeToByteArray()
                 iv.encodeToByteArray()
             )
             )
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
             log(e.localizedMessage ?: "aesEncodeToBase64ByteArrayERROR")
             log(e.localizedMessage ?: "aesEncodeToBase64ByteArrayERROR")
             null
             null
         }
         }

+ 5 - 5
app/src/main/java/io/legado/app/help/ReadBookConfig.kt

@@ -13,7 +13,7 @@ import io.legado.app.utils.*
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 
 
 /**
 /**
@@ -62,7 +62,7 @@ object ReadBookConfig {
                 val json = configFile.readText()
                 val json = configFile.readText()
                 configs = GSON.fromJsonArray(json)
                 configs = GSON.fromJsonArray(json)
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
         }
         }
         (configs ?: DefaultData.readConfigs).let {
         (configs ?: DefaultData.readConfigs).let {
@@ -79,7 +79,7 @@ object ReadBookConfig {
                 val json = configFile.readText()
                 val json = configFile.readText()
                 c = GSON.fromJsonObject(json)
                 c = GSON.fromJsonObject(json)
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
         }
         }
         shareConfig = c ?: configList.getOrNull(5) ?: Config()
         shareConfig = c ?: configList.getOrNull(5) ?: Config()
@@ -580,9 +580,9 @@ object ReadBookConfig {
                     )
                     )
                 }
                 }
             } catch (e: OutOfMemoryError) {
             } catch (e: OutOfMemoryError) {
-                Timber.e(e)
+                e.printOnDebug()
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
             return bgDrawable ?: ColorDrawable(appCtx.getCompatColor(R.color.background))
             return bgDrawable ?: ColorDrawable(appCtx.getCompatColor(R.color.background))
         }
         }

+ 2 - 2
app/src/main/java/io/legado/app/help/SourceAnalyzer.kt

@@ -7,7 +7,7 @@ import io.legado.app.constant.BookType
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.rule.*
 import io.legado.app.data.entities.rule.*
 import io.legado.app.utils.*
 import io.legado.app.utils.*
-import timber.log.Timber
+
 import java.util.regex.Pattern
 import java.util.regex.Pattern
 
 
 @Suppress("RegExpRedundantEscape")
 @Suppress("RegExpRedundantEscape")
@@ -159,7 +159,7 @@ object SourceAnalyzer {
                 }
                 }
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return source
         return source
     }
     }

+ 2 - 2
app/src/main/java/io/legado/app/help/ThemeConfig.kt

@@ -14,7 +14,7 @@ import io.legado.app.lib.theme.ThemeStore
 import io.legado.app.model.BookCover
 import io.legado.app.model.BookCover
 import io.legado.app.utils.*
 import io.legado.app.utils.*
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 
 
 object ThemeConfig {
 object ThemeConfig {
@@ -108,7 +108,7 @@ object ThemeConfig {
                 val json = configFile.readText()
                 val json = configFile.readText()
                 return GSON.fromJsonArray(json)
                 return GSON.fromJsonArray(json)
             }.onFailure {
             }.onFailure {
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
         }
         }
         return null
         return null

+ 3 - 2
app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt

@@ -1,7 +1,8 @@
 package io.legado.app.help.coroutine
 package io.legado.app.help.coroutine
 
 
+import io.legado.app.utils.printOnDebug
 import kotlinx.coroutines.*
 import kotlinx.coroutines.*
-import timber.log.Timber
+
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.CoroutineContext
 
 
 /**
 /**
@@ -144,7 +145,7 @@ class Coroutine<T>(
                 ensureActive()
                 ensureActive()
                 success?.let { dispatchCallback(this, value, it) }
                 success?.let { dispatchCallback(this, value, it) }
             } catch (e: Throwable) {
             } catch (e: Throwable) {
-                Timber.e(e)
+                e.printOnDebug()
                 if (e is CancellationException && e !is TimeoutCancellationException) {
                 if (e is CancellationException && e !is TimeoutCancellationException) {
                     return@launch
                     return@launch
                 }
                 }

+ 6 - 5
app/src/main/java/io/legado/app/help/http/SSLHelper.kt

@@ -1,8 +1,9 @@
 package io.legado.app.help.http
 package io.legado.app.help.http
 
 
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
+import io.legado.app.utils.printOnDebug
+
 
 
-import timber.log.Timber
 import java.io.IOException
 import java.io.IOException
 import java.io.InputStream
 import java.io.InputStream
 import java.security.KeyManagementException
 import java.security.KeyManagementException
@@ -127,9 +128,9 @@ object SSLHelper {
             sslParams.trustManager = manager
             sslParams.trustManager = manager
             return sslParams
             return sslParams
         } catch (e: NoSuchAlgorithmException) {
         } catch (e: NoSuchAlgorithmException) {
-            Timber.e(e)
+            e.printOnDebug()
         } catch (e: KeyManagementException) {
         } catch (e: KeyManagementException) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return null
         return null
     }
     }
@@ -143,7 +144,7 @@ object SSLHelper {
             kmf.init(clientKeyStore, password.toCharArray())
             kmf.init(clientKeyStore, password.toCharArray())
             return kmf.keyManagers
             return kmf.keyManagers
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return null
         return null
     }
     }
@@ -162,7 +163,7 @@ object SSLHelper {
             try {
             try {
                 certStream.close()
                 certStream.close()
             } catch (e: IOException) {
             } catch (e: IOException) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
         }
         }
         //我们创建一个默认类型的TrustManagerFactory
         //我们创建一个默认类型的TrustManagerFactory

+ 3 - 3
app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt

@@ -2,6 +2,7 @@ package io.legado.app.help.http.cronet
 
 
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
 import io.legado.app.help.AppConfig
 import io.legado.app.help.AppConfig
+import io.legado.app.utils.DebugLog
 import okhttp3.Headers
 import okhttp3.Headers
 import okhttp3.MediaType
 import okhttp3.MediaType
 import okhttp3.Request
 import okhttp3.Request
@@ -11,7 +12,7 @@ import org.chromium.net.ExperimentalCronetEngine
 import org.chromium.net.UploadDataProviders
 import org.chromium.net.UploadDataProviders
 import org.chromium.net.UrlRequest
 import org.chromium.net.UrlRequest
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
 import java.util.concurrent.Executors
 
 
@@ -35,11 +36,10 @@ val cronetEngine: ExperimentalCronetEngine? by lazy {
     }
     }
     try {
     try {
         val engine = builder.build()
         val engine = builder.build()
-        Timber.d("Cronet Version:" + engine.versionString)
+        DebugLog.d("Cronet Version:", engine.versionString)
         return@lazy engine
         return@lazy engine
     } catch (e: UnsatisfiedLinkError) {
     } catch (e: UnsatisfiedLinkError) {
         AppLog.put("初始化cronetEngine出错", e)
         AppLog.put("初始化cronetEngine出错", e)
-        Timber.e(e, "初始化cronetEngine出错")
         return@lazy null
         return@lazy null
     }
     }
 }
 }

+ 3 - 2
app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt

@@ -1,8 +1,9 @@
 package io.legado.app.help.http.cronet
 package io.legado.app.help.http.cronet
 
 
 import io.legado.app.help.http.CookieStore
 import io.legado.app.help.http.CookieStore
+import io.legado.app.utils.printOnDebug
 import okhttp3.*
 import okhttp3.*
-import timber.log.Timber
+
 
 
 class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
 class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
 
 
@@ -33,7 +34,7 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
             if (!e.message.toString().contains("ERR_CERT_", true)
             if (!e.message.toString().contains("ERR_CERT_", true)
                 && !e.message.toString().contains("ERR_SSL_", true)
                 && !e.message.toString().contains("ERR_SSL_", true)
             ) {
             ) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
             chain.proceed(original)
             chain.proceed(original)
         }
         }

+ 29 - 27
app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt

@@ -8,11 +8,13 @@ import android.text.TextUtils
 import io.legado.app.BuildConfig
 import io.legado.app.BuildConfig
 import io.legado.app.help.AppConfig
 import io.legado.app.help.AppConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
+import io.legado.app.utils.DebugLog
+import io.legado.app.utils.printOnDebug
 
 
 import org.chromium.net.CronetEngine
 import org.chromium.net.CronetEngine
 import org.json.JSONObject
 import org.json.JSONObject
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.*
 import java.io.*
 import java.math.BigInteger
 import java.math.BigInteger
 import java.net.HttpURLConnection
 import java.net.HttpURLConnection
@@ -36,16 +38,16 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
 
 
     init {
     init {
         soUrl = ("https://storage.googleapis.com/chromium-cronet/android/"
         soUrl = ("https://storage.googleapis.com/chromium-cronet/android/"
-                + soVersion + "/Release/cronet/libs/"
-                + getCpuAbi(appCtx) + "/" + soName)
+            + soVersion + "/Release/cronet/libs/"
+            + getCpuAbi(appCtx) + "/" + soName)
         md5 = getMd5(appCtx)
         md5 = getMd5(appCtx)
         val dir = appCtx.getDir("cronet", Context.MODE_PRIVATE)
         val dir = appCtx.getDir("cronet", Context.MODE_PRIVATE)
         soFile = File(dir.toString() + "/" + getCpuAbi(appCtx), soName)
         soFile = File(dir.toString() + "/" + getCpuAbi(appCtx), soName)
         downloadFile = File(appCtx.cacheDir.toString() + "/so_download", soName)
         downloadFile = File(appCtx.cacheDir.toString() + "/so_download", soName)
-        Timber.d("soName+:$soName")
-        Timber.d("destSuccessFile:$soFile")
-        Timber.d("tempFile:$downloadFile")
-        Timber.d("soUrl:$soUrl")
+        DebugLog.d(javaClass.simpleName, "soName+:$soName")
+        DebugLog.d(javaClass.simpleName, "destSuccessFile:$soFile")
+        DebugLog.d(javaClass.simpleName, "tempFile:$downloadFile")
+        DebugLog.d(javaClass.simpleName, "soUrl:$soUrl")
     }
     }
 
 
     /**
     /**
@@ -77,11 +79,11 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
         Coroutine.async {
         Coroutine.async {
             //md5 = getUrlMd5(md5Url)
             //md5 = getUrlMd5(md5Url)
             if (soFile.exists() && md5 == getFileMD5(soFile)) {
             if (soFile.exists() && md5 == getFileMD5(soFile)) {
-                Timber.d("So 库已存在")
+                DebugLog.d(javaClass.simpleName, "So 库已存在")
             } else {
             } else {
                 download(soUrl, md5, downloadFile, soFile)
                 download(soUrl, md5, downloadFile, soFile)
             }
             }
-            Timber.d(soName)
+            DebugLog.d(javaClass.simpleName, soName)
         }
         }
     }
     }
 
 
@@ -108,7 +110,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
 
 
     @SuppressLint("UnsafeDynamicallyLoadedCode")
     @SuppressLint("UnsafeDynamicallyLoadedCode")
     override fun loadLibrary(libName: String) {
     override fun loadLibrary(libName: String) {
-        Timber.d("libName:$libName")
+        DebugLog.d(javaClass.simpleName, "libName:$libName")
         val start = System.currentTimeMillis()
         val start = System.currentTimeMillis()
         @Suppress("SameParameterValue")
         @Suppress("SameParameterValue")
         try {
         try {
@@ -120,13 +122,13 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
             //以下逻辑为cronet加载,优先加载本地,否则从远程加载
             //以下逻辑为cronet加载,优先加载本地,否则从远程加载
             //首先调用系统行为进行加载
             //首先调用系统行为进行加载
             System.loadLibrary(libName)
             System.loadLibrary(libName)
-            Timber.d("load from system")
+            DebugLog.d(javaClass.simpleName, "load from system")
         } catch (e: Throwable) {
         } catch (e: Throwable) {
             //如果找不到,则从远程下载
             //如果找不到,则从远程下载
             //删除历史文件
             //删除历史文件
             deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile)
             deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile)
             //md5 = getUrlMd5(md5Url)
             //md5 = getUrlMd5(md5Url)
-            Timber.d("soMD5:$md5")
+            DebugLog.d(javaClass.simpleName, "soMD5:$md5")
             if (md5.length != 32 || soUrl.isEmpty()) {
             if (md5.length != 32 || soUrl.isEmpty()) {
                 //如果md5或下载的url为空,则调用系统行为进行加载
                 //如果md5或下载的url为空,则调用系统行为进行加载
                 System.loadLibrary(libName)
                 System.loadLibrary(libName)
@@ -145,7 +147,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
                 if (fileMD5 != null && fileMD5.equals(md5, ignoreCase = true)) {
                 if (fileMD5 != null && fileMD5.equals(md5, ignoreCase = true)) {
                     //md5值一样,则加载
                     //md5值一样,则加载
                     System.load(soFile.absolutePath)
                     System.load(soFile.absolutePath)
-                    Timber.d("load from:$soFile")
+                    DebugLog.d(javaClass.simpleName, "load from:$soFile")
                     return
                     return
                 }
                 }
                 //md5不一样则删除
                 //md5不一样则删除
@@ -156,7 +158,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
             //使用系统加载方法
             //使用系统加载方法
             System.loadLibrary(libName)
             System.loadLibrary(libName)
         } finally {
         } finally {
-            Timber.d("time:" + (System.currentTimeMillis() - start))
+            DebugLog.d(javaClass.simpleName, "time:" + (System.currentTimeMillis() - start))
         }
         }
     }
     }
 
 
@@ -172,7 +174,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
             abiField.isAccessible = true
             abiField.isAccessible = true
             cpuAbi = abiField.get(appInfo) as String?
             cpuAbi = abiField.get(appInfo) as String?
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         if (TextUtils.isEmpty(cpuAbi)) {
         if (TextUtils.isEmpty(cpuAbi)) {
             cpuAbi = Build.SUPPORTED_ABIS[0]
             cpuAbi = Build.SUPPORTED_ABIS[0]
@@ -191,7 +193,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
             for (f in files) {
             for (f in files) {
                 if (f.exists() && (currentFile == null || f.absolutePath != currentFile.absolutePath)) {
                 if (f.exists() && (currentFile == null || f.absolutePath != currentFile.absolutePath)) {
                     val delete = f.delete()
                     val delete = f.delete()
-                    Timber.d("delete file: $f result: $delete")
+                    DebugLog.d(javaClass.simpleName, "delete file: $f result: $delete")
                     if (!delete) {
                     if (!delete) {
                         f.deleteOnExit()
                         f.deleteOnExit()
                     }
                     }
@@ -223,7 +225,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
             }
             }
             return true
             return true
         } catch (e: Throwable) {
         } catch (e: Throwable) {
-            Timber.e(e)
+            e.printOnDebug()
             if (destFile.exists() && !destFile.delete()) {
             if (destFile.exists() && !destFile.delete()) {
                 destFile.deleteOnExit()
                 destFile.deleteOnExit()
             }
             }
@@ -232,14 +234,14 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
                 try {
                 try {
                     inputStream.close()
                     inputStream.close()
                 } catch (e: IOException) {
                 } catch (e: IOException) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
             if (outputStream != null) {
             if (outputStream != null) {
                 try {
                 try {
                     outputStream.close()
                     outputStream.close()
                 } catch (e: IOException) {
                 } catch (e: IOException) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
         }
         }
@@ -263,7 +265,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
         download = true
         download = true
         executor.execute {
         executor.execute {
             val result = downloadFileIfNotExist(url, downloadTempFile)
             val result = downloadFileIfNotExist(url, downloadTempFile)
-            Timber.d("download result:$result")
+            DebugLog.d(javaClass.simpleName, "download result:$result")
             //文件md5再次校验
             //文件md5再次校验
             val fileMD5 = getFileMD5(downloadTempFile)
             val fileMD5 = getFileMD5(downloadTempFile)
             if (md5 != null && !md5.equals(fileMD5, ignoreCase = true)) {
             if (md5 != null && !md5.equals(fileMD5, ignoreCase = true)) {
@@ -274,7 +276,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
                 download = false
                 download = false
                 return@execute
                 return@execute
             }
             }
-            Timber.d("download success, copy to $destSuccessFile")
+            DebugLog.d(javaClass.simpleName, "download success, copy to $destSuccessFile")
             //下载成功拷贝文件
             //下载成功拷贝文件
             copyFile(downloadTempFile, destSuccessFile)
             copyFile(downloadTempFile, destSuccessFile)
             cacheInstall = false
             cacheInstall = false
@@ -313,20 +315,20 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
             }
             }
             return true
             return true
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         } finally {
         } finally {
             if (fileInputStream != null) {
             if (fileInputStream != null) {
                 try {
                 try {
                     fileInputStream.close()
                     fileInputStream.close()
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
             if (os != null) {
             if (os != null) {
                 try {
                 try {
                     os.close()
                     os.close()
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
         }
         }
@@ -348,15 +350,15 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
             }
             }
             return String.format("%032x", BigInteger(1, md5.digest())).lowercase()
             return String.format("%032x", BigInteger(1, md5.digest())).lowercase()
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         } catch (e: OutOfMemoryError) {
         } catch (e: OutOfMemoryError) {
-            Timber.e(e)
+            e.printOnDebug()
         } finally {
         } finally {
             if (fileInputStream != null) {
             if (fileInputStream != null) {
                 try {
                 try {
                     fileInputStream.close()
                     fileInputStream.close()
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
         }
         }

+ 6 - 5
app/src/main/java/io/legado/app/help/http/cronet/CronetRequestCallback.kt

@@ -2,6 +2,7 @@ package io.legado.app.help.http.cronet
 
 
 import android.os.ConditionVariable
 import android.os.ConditionVariable
 import io.legado.app.help.http.okHttpClient
 import io.legado.app.help.http.okHttpClient
+import io.legado.app.utils.DebugLog
 import okhttp3.*
 import okhttp3.*
 import okhttp3.EventListener
 import okhttp3.EventListener
 import okhttp3.MediaType.Companion.toMediaTypeOrNull
 import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -10,7 +11,7 @@ import okio.Buffer
 import org.chromium.net.CronetException
 import org.chromium.net.CronetException
 import org.chromium.net.UrlRequest
 import org.chromium.net.UrlRequest
 import org.chromium.net.UrlResponseInfo
 import org.chromium.net.UrlResponseInfo
-import timber.log.Timber
+
 import java.io.IOException
 import java.io.IOException
 import java.nio.ByteBuffer
 import java.nio.ByteBuffer
 import java.util.*
 import java.util.*
@@ -86,7 +87,7 @@ class CronetRequestCallback @JvmOverloads internal constructor(
 //        }
 //        }
 //        Log.e("Cronet", sb.toString())
 //        Log.e("Cronet", sb.toString())
         //打印协议,用于调试
         //打印协议,用于调试
-        Timber.i(info.negotiatedProtocol)
+        DebugLog.i(javaClass.name, info.negotiatedProtocol)
         if (eventListener != null) {
         if (eventListener != null) {
             eventListener.responseHeadersEnd(mCall, this.mResponse)
             eventListener.responseHeadersEnd(mCall, this.mResponse)
             eventListener.responseBodyStart(mCall)
             eventListener.responseBodyStart(mCall)
@@ -107,7 +108,7 @@ class CronetRequestCallback @JvmOverloads internal constructor(
         try {
         try {
             mBuffer.write(byteBuffer)
             mBuffer.write(byteBuffer)
         } catch (e: IOException) {
         } catch (e: IOException) {
-            Timber.i(e, "IOException during ByteBuffer read. Details: ")
+            DebugLog.i(javaClass.name, "IOException during ByteBuffer read. Details: ", e)
             throw e
             throw e
         }
         }
         byteBuffer.clear()
         byteBuffer.clear()
@@ -135,7 +136,7 @@ class CronetRequestCallback @JvmOverloads internal constructor(
 
 
     //UrlResponseInfo可能为null
     //UrlResponseInfo可能为null
     override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
     override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
-        Timber.e(error.message.toString())
+        DebugLog.i(javaClass.name, error.message.toString())
         val msg = error.localizedMessage
         val msg = error.localizedMessage
         val e = IOException(msg?.substring(msg.indexOf("net::")), error)
         val e = IOException(msg?.substring(msg.indexOf("net::")), error)
         mException = e
         mException = e
@@ -192,7 +193,7 @@ class CronetRequestCallback @JvmOverloads internal constructor(
                         }
                         }
                         add(key, value)
                         add(key, value)
                     } catch (e: Exception) {
                     } catch (e: Exception) {
-                        Timber.w("Invalid HTTP header/value: $key$value")
+                        DebugLog.w(javaClass.name, "Invalid HTTP header/value: $key$value")
                         // Ignore that header
                         // Ignore that header
                     }
                     }
                 }
                 }

+ 2 - 2
app/src/main/java/io/legado/app/help/storage/OldBook.kt

@@ -3,7 +3,7 @@ package io.legado.app.help.storage
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
 import io.legado.app.utils.*
 import io.legado.app.utils.*
-import timber.log.Timber
+
 
 
 object OldBook {
 object OldBook {
 
 
@@ -18,7 +18,7 @@ object OldBook {
             if (book.bookUrl.isBlank()) continue
             if (book.bookUrl.isBlank()) continue
             book.name = jsonItem.readString("$.bookInfoBean.name") ?: ""
             book.name = jsonItem.readString("$.bookInfoBean.name") ?: ""
             if (book.bookUrl in existingBooks) {
             if (book.bookUrl in existingBooks) {
-                Timber.d("Found existing book: " + book.name)
+                DebugLog.d(javaClass.name, "Found existing book: " + book.name)
                 continue
                 continue
             }
             }
             book.origin = jsonItem.readString("$.tag") ?: ""
             book.origin = jsonItem.readString("$.tag") ?: ""

+ 7 - 4
app/src/main/java/io/legado/app/help/storage/Preferences.kt

@@ -1,11 +1,13 @@
 package io.legado.app.help.storage
 package io.legado.app.help.storage
 
 
+import android.annotation.SuppressLint
 import android.app.Activity
 import android.app.Activity
 import android.content.Context
 import android.content.Context
 import android.content.ContextWrapper
 import android.content.ContextWrapper
 import android.content.SharedPreferences
 import android.content.SharedPreferences
+import io.legado.app.utils.printOnDebug
+
 
 
-import timber.log.Timber
 import java.io.File
 import java.io.File
 
 
 object Preferences {
 object Preferences {
@@ -17,6 +19,7 @@ object Preferences {
      * @param fileName 文件名,不需要 '.xml' 后缀
      * @param fileName 文件名,不需要 '.xml' 后缀
      * @return
      * @return
      */
      */
+    @SuppressLint("DiscouragedPrivateApi")
     fun getSharedPreferences(
     fun getSharedPreferences(
         context: Context,
         context: Context,
         dir: String,
         dir: String,
@@ -38,11 +41,11 @@ object Preferences {
             // 返回修改路径以后的 SharedPreferences :%FILE_PATH%/%fileName%.xml
             // 返回修改路径以后的 SharedPreferences :%FILE_PATH%/%fileName%.xml
             return context.getSharedPreferences(fileName, Activity.MODE_PRIVATE)
             return context.getSharedPreferences(fileName, Activity.MODE_PRIVATE)
         } catch (e: NoSuchFieldException) {
         } catch (e: NoSuchFieldException) {
-            Timber.e(e)
+            e.printOnDebug()
         } catch (e: IllegalArgumentException) {
         } catch (e: IllegalArgumentException) {
-            Timber.e(e)
+            e.printOnDebug()
         } catch (e: IllegalAccessException) {
         } catch (e: IllegalAccessException) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return null
         return null
     }
     }

+ 6 - 6
app/src/main/java/io/legado/app/help/storage/Restore.kt

@@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers.Main
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 
 
 
 
@@ -53,7 +53,7 @@ object Restore : BackupRestore() {
                         }
                         }
                     }
                     }
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
         }
         }
@@ -129,7 +129,7 @@ object Restore : BackupRestore() {
                     ThemeConfig.upConfig()
                     ThemeConfig.upConfig()
                 }
                 }
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
             if (!ignoreReadConfig) {
             if (!ignoreReadConfig) {
                 //恢复阅读界面配置
                 //恢复阅读界面配置
@@ -142,7 +142,7 @@ object Restore : BackupRestore() {
                         ReadBookConfig.initConfigs()
                         ReadBookConfig.initConfigs()
                     }
                     }
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
                 try {
                 try {
                     val file =
                     val file =
@@ -153,7 +153,7 @@ object Restore : BackupRestore() {
                         ReadBookConfig.initShareConfig()
                         ReadBookConfig.initShareConfig()
                     }
                     }
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
             Preferences.getSharedPreferences(appCtx, path, "config")?.all?.let { map ->
             Preferences.getSharedPreferences(appCtx, path, "config")?.all?.let { map ->
@@ -195,7 +195,7 @@ object Restore : BackupRestore() {
             val json = file.readText()
             val json = file.readText()
             return GSON.fromJsonArray(json)
             return GSON.fromJsonArray(json)
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return null
         return null
     }
     }

+ 4 - 3
app/src/main/java/io/legado/app/lib/webdav/WebDav.kt

@@ -3,13 +3,14 @@ package io.legado.app.lib.webdav
 import io.legado.app.help.http.newCallResponseBody
 import io.legado.app.help.http.newCallResponseBody
 import io.legado.app.help.http.okHttpClient
 import io.legado.app.help.http.okHttpClient
 import io.legado.app.help.http.text
 import io.legado.app.help.http.text
+import io.legado.app.utils.printOnDebug
 import okhttp3.Credentials
 import okhttp3.Credentials
 import okhttp3.MediaType.Companion.toMediaType
 import okhttp3.MediaType.Companion.toMediaType
 import okhttp3.RequestBody.Companion.asRequestBody
 import okhttp3.RequestBody.Companion.asRequestBody
 import okhttp3.RequestBody.Companion.toRequestBody
 import okhttp3.RequestBody.Companion.toRequestBody
 import org.intellij.lang.annotations.Language
 import org.intellij.lang.annotations.Language
 import org.jsoup.Jsoup
 import org.jsoup.Jsoup
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.InputStream
 import java.io.InputStream
 import java.net.MalformedURLException
 import java.net.MalformedURLException
@@ -100,7 +101,7 @@ class WebDav(urlStr: String) {
                     method("PROPFIND", requestBody)
                     method("PROPFIND", requestBody)
                 }.text()
                 }.text()
             }.onFailure { e ->
             }.onFailure { e ->
-                Timber.e(e)
+                e.printOnDebug()
             }.getOrNull()
             }.getOrNull()
         }
         }
         return null
         return null
@@ -132,7 +133,7 @@ class WebDav(urlStr: String) {
                         }
                         }
                         list.add(webDavFile)
                         list.add(webDavFile)
                     } catch (e: MalformedURLException) {
                     } catch (e: MalformedURLException) {
-                        Timber.e(e)
+                        e.printOnDebug()
                     }
                     }
                 }
                 }
             }
             }

+ 1 - 2
app/src/main/java/io/legado/app/model/CacheBook.kt

@@ -16,7 +16,7 @@ import io.legado.app.utils.postEvent
 import io.legado.app.utils.startService
 import io.legado.app.utils.startService
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
-import timber.log.Timber
+
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.ConcurrentHashMap
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.CoroutineContext
 
 
@@ -180,7 +180,6 @@ object CacheBook {
                     "下载${book.name}-${chapterTitle}失败\n${error.localizedMessage}",
                     "下载${book.name}-${chapterTitle}失败\n${error.localizedMessage}",
                     error
                     error
                 )
                 )
-                Timber.e(error)
             }
             }
         }
         }
 
 

+ 2 - 2
app/src/main/java/io/legado/app/model/ReadBook.kt

@@ -23,7 +23,7 @@ import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 
 
 
 
 @Suppress("MemberVisibilityCanBePrivate")
 @Suppress("MemberVisibilityCanBePrivate")
@@ -378,7 +378,7 @@ object ReadBook : CoroutineScope by MainScope() {
                 }
                 }
             }
             }
         }.onError {
         }.onError {
-            Timber.e(it)
+            AppLog.put("ChapterProvider ERROR", it)
             appCtx.toastOnUi("ChapterProvider ERROR:\n${it.msg}")
             appCtx.toastOnUi("ChapterProvider ERROR:\n${it.msg}")
         }.onSuccess {
         }.onSuccess {
             success?.invoke()
             success?.invoke()

+ 5 - 4
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeByJSonPath.kt

@@ -3,7 +3,8 @@ package io.legado.app.model.analyzeRule
 import androidx.annotation.Keep
 import androidx.annotation.Keep
 import com.jayway.jsonpath.JsonPath
 import com.jayway.jsonpath.JsonPath
 import com.jayway.jsonpath.ReadContext
 import com.jayway.jsonpath.ReadContext
-import timber.log.Timber
+import io.legado.app.utils.printOnDebug
+
 
 
 @Suppress("RegExpRedundantEscape")
 @Suppress("RegExpRedundantEscape")
 @Keep
 @Keep
@@ -48,7 +49,7 @@ class AnalyzeByJSonPath(json: Any) {
                         ob.toString()
                         ob.toString()
                     }
                     }
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
             return result
             return result
@@ -85,7 +86,7 @@ class AnalyzeByJSonPath(json: Any) {
                         result.add(obj.toString())
                         result.add(obj.toString())
                     }
                     }
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             } else {
             } else {
                 result.add(st)
                 result.add(st)
@@ -135,7 +136,7 @@ class AnalyzeByJSonPath(json: Any) {
                 try {
                 try {
                     return it.read<ArrayList<Any>>(rules[0])
                     return it.read<ArrayList<Any>>(rules[0])
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
             }
             }
         } else {
         } else {

+ 2 - 2
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt

@@ -13,7 +13,7 @@ import io.legado.app.utils.*
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
 import org.jsoup.nodes.Entities
 import org.jsoup.nodes.Entities
 import org.mozilla.javascript.NativeObject
 import org.mozilla.javascript.NativeObject
-import timber.log.Timber
+
 import java.net.URL
 import java.net.URL
 import java.util.regex.Pattern
 import java.util.regex.Pattern
 import javax.script.SimpleBindings
 import javax.script.SimpleBindings
@@ -662,7 +662,7 @@ class AnalyzeRule(
                 analyzeUrl.getStrResponseAwait().body
                 analyzeUrl.getStrResponseAwait().body
             }.onFailure {
             }.onFailure {
                 log("ajax(${urlStr}) error\n${it.stackTraceToString()}")
                 log("ajax(${urlStr}) error\n${it.stackTraceToString()}")
-                Timber.e(it)
+                it.printOnDebug()
             }.getOrElse {
             }.getOrElse {
                 it.msg
                 it.msg
             }
             }

+ 4 - 7
app/src/main/java/io/legado/app/model/localBook/EpubFile.kt

@@ -5,10 +5,7 @@ import android.graphics.BitmapFactory
 import android.text.TextUtils
 import android.text.TextUtils
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookChapter
-import io.legado.app.utils.FileUtils
-import io.legado.app.utils.HtmlFormatter
-import io.legado.app.utils.MD5Utils
-import io.legado.app.utils.externalFiles
+import io.legado.app.utils.*
 import me.ag2s.epublib.domain.EpubBook
 import me.ag2s.epublib.domain.EpubBook
 import me.ag2s.epublib.domain.Resource
 import me.ag2s.epublib.domain.Resource
 import me.ag2s.epublib.domain.TOCReference
 import me.ag2s.epublib.domain.TOCReference
@@ -17,7 +14,7 @@ import org.jsoup.Jsoup
 import org.jsoup.nodes.Element
 import org.jsoup.nodes.Element
 import org.jsoup.select.Elements
 import org.jsoup.select.Elements
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.FileOutputStream
 import java.io.FileOutputStream
 import java.io.IOException
 import java.io.IOException
@@ -97,7 +94,7 @@ class EpubFile(var book: Book) {
                 }
                 }
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
     }
     }
 
 
@@ -108,7 +105,7 @@ class EpubFile(var book: Book) {
             //通过懒加载读取epub
             //通过懒加载读取epub
             return EpubReader().readEpub(bis, "utf-8")
             return EpubReader().readEpub(bis, "utf-8")
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return null
         return null
     }
     }

+ 2 - 2
app/src/main/java/io/legado/app/model/localBook/LocalBook.kt

@@ -12,7 +12,7 @@ import io.legado.app.help.BookHelp
 import io.legado.app.model.TocEmptyException
 import io.legado.app.model.TocEmptyException
 import io.legado.app.utils.*
 import io.legado.app.utils.*
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.FileInputStream
 import java.io.FileInputStream
 import java.io.FileNotFoundException
 import java.io.FileNotFoundException
@@ -69,7 +69,7 @@ object LocalBook {
                 }
                 }
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
             e.localizedMessage
             e.localizedMessage
         }
         }
     }
     }

+ 4 - 6
app/src/main/java/io/legado/app/model/localBook/UmdFile.kt

@@ -2,13 +2,11 @@ package io.legado.app.model.localBook
 
 
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookChapter
-import io.legado.app.utils.FileUtils
-import io.legado.app.utils.MD5Utils
-import io.legado.app.utils.externalFiles
+import io.legado.app.utils.*
 import me.ag2s.umdlib.domain.UmdBook
 import me.ag2s.umdlib.domain.UmdBook
 import me.ag2s.umdlib.umd.UmdReader
 import me.ag2s.umdlib.umd.UmdReader
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.InputStream
 import java.io.InputStream
 
 
@@ -77,7 +75,7 @@ class UmdFile(var book: Book) {
                 }
                 }
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
     }
     }
 
 
@@ -111,7 +109,7 @@ class UmdFile(var book: Book) {
             chapter.index = index
             chapter.index = index
             chapter.bookUrl = book.bookUrl
             chapter.bookUrl = book.bookUrl
             chapter.url = index.toString()
             chapter.url = index.toString()
-            Timber.d(chapter.url)
+            DebugLog.d(javaClass.name, chapter.url)
             chapterList.add(chapter)
             chapterList.add(chapter)
         }
         }
         book.latestChapterTitle = chapterList.lastOrNull()?.title
         book.latestChapterTitle = chapterList.lastOrNull()?.title

+ 7 - 6
app/src/main/java/io/legado/app/model/webBook/BookInfo.kt

@@ -7,13 +7,14 @@ import io.legado.app.help.BookHelp
 import io.legado.app.model.Debug
 import io.legado.app.model.Debug
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.analyzeRule.AnalyzeRule
 import io.legado.app.model.analyzeRule.AnalyzeRule
+import io.legado.app.utils.DebugLog
 import io.legado.app.utils.HtmlFormatter
 import io.legado.app.utils.HtmlFormatter
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.StringUtils.wordCountFormat
 import io.legado.app.utils.StringUtils.wordCountFormat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.ensureActive
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 
 
 /**
 /**
  * 获取详情
  * 获取详情
@@ -87,7 +88,7 @@ object BookInfo {
                 } ?: Debug.log(bookSource.bookSourceUrl, "└")
                 } ?: Debug.log(bookSource.bookSourceUrl, "└")
         } catch (e: Exception) {
         } catch (e: Exception) {
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
-            Timber.e(e, "获取分类出错")
+            DebugLog.e("获取分类出错", e)
         }
         }
         scope.ensureActive()
         scope.ensureActive()
         Debug.log(bookSource.bookSourceUrl, "┌获取字数")
         Debug.log(bookSource.bookSourceUrl, "┌获取字数")
@@ -98,7 +99,7 @@ object BookInfo {
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
-            Timber.e(e, "获取字数出错")
+            DebugLog.e("获取字数出错", e)
         }
         }
         scope.ensureActive()
         scope.ensureActive()
         Debug.log(bookSource.bookSourceUrl, "┌获取最新章节")
         Debug.log(bookSource.bookSourceUrl, "┌获取最新章节")
@@ -109,7 +110,7 @@ object BookInfo {
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
-            Timber.e(e, "获取最新章节出错")
+            DebugLog.e("获取最新章节出错", e)
         }
         }
         scope.ensureActive()
         scope.ensureActive()
         Debug.log(bookSource.bookSourceUrl, "┌获取简介")
         Debug.log(bookSource.bookSourceUrl, "┌获取简介")
@@ -120,7 +121,7 @@ object BookInfo {
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
-            Timber.e(e, "获取简介出错")
+            DebugLog.e("获取简介出错", e)
         }
         }
         scope.ensureActive()
         scope.ensureActive()
         Debug.log(bookSource.bookSourceUrl, "┌获取封面链接")
         Debug.log(bookSource.bookSourceUrl, "┌获取封面链接")
@@ -131,7 +132,7 @@ object BookInfo {
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
             Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
-            Timber.e(e, "获取封面出错")
+            DebugLog.e("获取封面出错", e)
         }
         }
         scope.ensureActive()
         scope.ensureActive()
         Debug.log(bookSource.bookSourceUrl, "┌获取目录链接")
         Debug.log(bookSource.bookSourceUrl, "┌获取目录链接")

+ 4 - 5
app/src/main/java/io/legado/app/service/AudioPlayService.kt

@@ -33,7 +33,7 @@ import io.legado.app.utils.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.Dispatchers.Main
 import kotlinx.coroutines.Dispatchers.Main
 import splitties.systemservices.audioManager
 import splitties.systemservices.audioManager
-import timber.log.Timber
+
 
 
 
 
 class AudioPlayService : BaseService(),
 class AudioPlayService : BaseService(),
@@ -138,7 +138,7 @@ class AudioPlayService : BaseService(),
                 exoPlayer.playWhenReady = true
                 exoPlayer.playWhenReady = true
                 exoPlayer.prepare()
                 exoPlayer.prepare()
             }.onFailure {
             }.onFailure {
-                Timber.e(it)
+                it.printOnDebug()
                 toastOnUi("$url ${it.localizedMessage}")
                 toastOnUi("$url ${it.localizedMessage}")
                 stopSelf()
                 stopSelf()
             }
             }
@@ -159,7 +159,7 @@ class AudioPlayService : BaseService(),
             postEvent(EventBus.AUDIO_STATE, Status.PAUSE)
             postEvent(EventBus.AUDIO_STATE, Status.PAUSE)
             upNotification()
             upNotification()
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
     }
     }
 
 
@@ -178,7 +178,7 @@ class AudioPlayService : BaseService(),
             postEvent(EventBus.AUDIO_STATE, Status.PLAY)
             postEvent(EventBus.AUDIO_STATE, Status.PLAY)
             upNotification()
             upNotification()
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
             stopSelf()
             stopSelf()
         }
         }
     }
     }
@@ -250,7 +250,6 @@ class AudioPlayService : BaseService(),
         val errorMsg = "音频播放出错\n${error.errorCodeName} ${error.errorCode}"
         val errorMsg = "音频播放出错\n${error.errorCodeName} ${error.errorCode}"
         AppLog.put(errorMsg, error)
         AppLog.put(errorMsg, error)
         toastOnUi(errorMsg)
         toastOnUi(errorMsg)
-        Timber.e(error)
     }
     }
 
 
     private fun setTimer(minute: Int) {
     private fun setTimer(minute: Int) {

+ 4 - 4
app/src/main/java/io/legado/app/service/HttpReadAloudService.kt

@@ -19,7 +19,7 @@ import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.sync.withLock
 import okhttp3.Response
 import okhttp3.Response
 import org.mozilla.javascript.WrappedException
 import org.mozilla.javascript.WrappedException
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.FileDescriptor
 import java.io.FileDescriptor
 import java.io.FileInputStream
 import java.io.FileInputStream
@@ -177,7 +177,7 @@ class HttpReadAloudService : BaseReadAloudService(),
                             is ScriptException, is WrappedException -> {
                             is ScriptException, is WrappedException -> {
                                 AppLog.put("js错误\n${it.localizedMessage}", it)
                                 AppLog.put("js错误\n${it.localizedMessage}", it)
                                 toastOnUi("js错误\n${it.localizedMessage}")
                                 toastOnUi("js错误\n${it.localizedMessage}")
-                                Timber.e(it)
+                                it.printOnDebug()
                                 cancel()
                                 cancel()
                                 pauseReadAloud(true)
                                 pauseReadAloud(true)
                             }
                             }
@@ -198,7 +198,7 @@ class HttpReadAloudService : BaseReadAloudService(),
                                 downloadErrorNo++
                                 downloadErrorNo++
                                 val msg = "tts下载错误\n${it.localizedMessage}"
                                 val msg = "tts下载错误\n${it.localizedMessage}"
                                 AppLog.put(msg, it)
                                 AppLog.put(msg, it)
-                                Timber.e(it)
+                                it.printOnDebug()
                                 if (downloadErrorNo > 5) {
                                 if (downloadErrorNo > 5) {
                                     pauseReadAloud(true)
                                     pauseReadAloud(true)
                                 } else {
                                 } else {
@@ -222,7 +222,7 @@ class HttpReadAloudService : BaseReadAloudService(),
                 playingIndex = nowSpeak
                 playingIndex = nowSpeak
                 postEvent(EventBus.TTS_PROGRESS, readAloudNumber + 1)
                 postEvent(EventBus.TTS_PROGRESS, readAloudNumber + 1)
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
         }
         }
     }
     }

+ 2 - 2
app/src/main/java/io/legado/app/service/WebService.kt

@@ -13,7 +13,7 @@ import io.legado.app.constant.PreferKey
 import io.legado.app.utils.*
 import io.legado.app.utils.*
 import io.legado.app.web.HttpServer
 import io.legado.app.web.HttpServer
 import io.legado.app.web.WebSocketServer
 import io.legado.app.web.WebSocketServer
-import timber.log.Timber
+
 import java.io.IOException
 import java.io.IOException
 
 
 class WebService : BaseService() {
 class WebService : BaseService() {
@@ -88,7 +88,7 @@ class WebService : BaseService() {
                 upNotification()
                 upNotification()
             } catch (e: IOException) {
             } catch (e: IOException) {
                 toastOnUi(e.localizedMessage ?: "")
                 toastOnUi(e.localizedMessage ?: "")
-                Timber.e(e)
+                e.printOnDebug()
                 stopSelf()
                 stopSelf()
             }
             }
         } else {
         } else {

+ 2 - 2
app/src/main/java/io/legado/app/service/WebTileService.kt

@@ -6,7 +6,7 @@ import android.service.quicksettings.Tile
 import android.service.quicksettings.TileService
 import android.service.quicksettings.TileService
 import androidx.annotation.RequiresApi
 import androidx.annotation.RequiresApi
 import io.legado.app.constant.IntentAction
 import io.legado.app.constant.IntentAction
-import timber.log.Timber
+import io.legado.app.utils.printOnDebug
 
 
 
 
 /**
 /**
@@ -28,7 +28,7 @@ class WebTileService : TileService() {
                 }
                 }
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return super.onStartCommand(intent, flags, startId)
         return super.onStartCommand(intent, flags, startId)
     }
     }

+ 3 - 6
app/src/main/java/io/legado/app/ui/about/DonateFragment.kt

@@ -7,11 +7,8 @@ import android.view.View
 import androidx.preference.Preference
 import androidx.preference.Preference
 import androidx.preference.PreferenceFragmentCompat
 import androidx.preference.PreferenceFragmentCompat
 import io.legado.app.R
 import io.legado.app.R
-import io.legado.app.utils.ACache
-import io.legado.app.utils.longToastOnUi
-import io.legado.app.utils.openUrl
-import io.legado.app.utils.sendToClip
-import timber.log.Timber
+import io.legado.app.utils.*
+
 
 
 class DonateFragment : PreferenceFragmentCompat() {
 class DonateFragment : PreferenceFragmentCompat() {
 
 
@@ -50,7 +47,7 @@ class DonateFragment : PreferenceFragmentCompat() {
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
             context.startActivity(intent)
             context.startActivity(intent)
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         } finally {
         } finally {
             ACache.get(requireContext(), cacheDir = false)
             ACache.get(requireContext(), cacheDir = false)
                 .put("proTime", System.currentTimeMillis())
                 .put("proTime", System.currentTimeMillis())

+ 3 - 2
app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt

@@ -6,6 +6,7 @@ import androidx.activity.viewModels
 import androidx.documentfile.provider.DocumentFile
 import androidx.documentfile.provider.DocumentFile
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.base.VMBaseActivity
 import io.legado.app.base.VMBaseActivity
+import io.legado.app.constant.AppLog
 import io.legado.app.databinding.ActivityTranslucenceBinding
 import io.legado.app.databinding.ActivityTranslucenceBinding
 import io.legado.app.help.AppConfig
 import io.legado.app.help.AppConfig
 import io.legado.app.lib.dialogs.alert
 import io.legado.app.lib.dialogs.alert
@@ -18,7 +19,7 @@ import io.legado.app.utils.viewbindingdelegate.viewBinding
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.FileOutputStream
 import java.io.FileOutputStream
 
 
@@ -145,7 +146,7 @@ class FileAssociationActivity :
                         mode = HandleFileContract.DIR_SYS
                         mode = HandleFileContract.DIR_SYS
                     }
                     }
                     else -> {
                     else -> {
-                        Timber.e(it, "导入书籍失败")
+                        AppLog.put("导入书籍失败", it)
                         toastOnUi(it.localizedMessage)
                         toastOnUi(it.localizedMessage)
                         finish()
                         finish()
                     }
                     }

+ 3 - 2
app/src/main/java/io/legado/app/ui/association/FileAssociationViewModel.kt

@@ -7,8 +7,9 @@ import androidx.lifecycle.MutableLiveData
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.utils.isJson
 import io.legado.app.utils.isJson
+import io.legado.app.utils.printOnDebug
 import io.legado.app.utils.readText
 import io.legado.app.utils.readText
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 
 
 class FileAssociationViewModel(application: Application) : BaseAssociationViewModel(application) {
 class FileAssociationViewModel(application: Application) : BaseAssociationViewModel(application) {
@@ -54,7 +55,7 @@ class FileAssociationViewModel(application: Application) : BaseAssociationViewMo
                 onLineImportLive.postValue(uri)
                 onLineImportLive.postValue(uri)
             }
             }
         }.onError {
         }.onError {
-            Timber.e(it)
+            it.printOnDebug()
             errorLiveData.postValue(it.localizedMessage)
             errorLiveData.postValue(it.localizedMessage)
         }
         }
     }
     }

+ 2 - 2
app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt

@@ -16,7 +16,7 @@ import io.legado.app.help.http.okHttpClient
 import io.legado.app.help.http.text
 import io.legado.app.help.http.text
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.utils.*
 import io.legado.app.utils.*
-import timber.log.Timber
+
 
 
 class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
 class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
     var isAddGroup = false
     var isAddGroup = false
@@ -113,7 +113,7 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
                 else -> throw NoStackTraceException(context.getString(R.string.wrong_format))
                 else -> throw NoStackTraceException(context.getString(R.string.wrong_format))
             }
             }
         }.onError {
         }.onError {
-            Timber.e(it)
+            it.printOnDebug()
             errorLiveData.postValue(it.localizedMessage ?: "")
             errorLiveData.postValue(it.localizedMessage ?: "")
         }.onSuccess {
         }.onSuccess {
             comparisonSource()
             comparisonSource()

+ 3 - 3
app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt

@@ -28,7 +28,7 @@ import me.ag2s.epublib.domain.*
 import me.ag2s.epublib.epub.EpubWriter
 import me.ag2s.epublib.epub.EpubWriter
 import me.ag2s.epublib.util.ResourceUtil
 import me.ag2s.epublib.util.ResourceUtil
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.ByteArrayOutputStream
 import java.io.ByteArrayOutputStream
 import java.io.File
 import java.io.File
 import java.io.FileOutputStream
 import java.io.FileOutputStream
@@ -71,7 +71,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
             exportProgress.remove(book.bookUrl)
             exportProgress.remove(book.bookUrl)
             exportMsg[book.bookUrl] = it.localizedMessage ?: "ERROR"
             exportMsg[book.bookUrl] = it.localizedMessage ?: "ERROR"
             upAdapterLiveData.postValue(book.bookUrl)
             upAdapterLiveData.postValue(book.bookUrl)
-            Timber.e(it)
+            it.printOnDebug()
         }.onSuccess {
         }.onSuccess {
             exportProgress.remove(book.bookUrl)
             exportProgress.remove(book.bookUrl)
             exportMsg[book.bookUrl] = context.getString(R.string.export_success)
             exportMsg[book.bookUrl] = context.getString(R.string.export_success)
@@ -206,7 +206,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
             exportProgress.remove(book.bookUrl)
             exportProgress.remove(book.bookUrl)
             exportMsg[book.bookUrl] = it.localizedMessage ?: "ERROR"
             exportMsg[book.bookUrl] = it.localizedMessage ?: "ERROR"
             upAdapterLiveData.postValue(book.bookUrl)
             upAdapterLiveData.postValue(book.bookUrl)
-            Timber.e(it)
+            it.printOnDebug()
         }.onSuccess {
         }.onSuccess {
             exportProgress.remove(book.bookUrl)
             exportProgress.remove(book.bookUrl)
             exportMsg[book.bookUrl] = context.getString(R.string.export_success)
             exportMsg[book.bookUrl] = context.getString(R.string.export_success)

+ 4 - 3
app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt

@@ -17,6 +17,7 @@ import io.legado.app.help.coroutine.CompositeCoroutine
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.utils.getPrefBoolean
 import io.legado.app.utils.getPrefBoolean
 import io.legado.app.utils.getPrefString
 import io.legado.app.utils.getPrefString
+import io.legado.app.utils.printOnDebug
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.ExecutorCoroutineDispatcher
 import kotlinx.coroutines.ExecutorCoroutineDispatcher
 import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.asCoroutineDispatcher
@@ -25,7 +26,7 @@ import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.map
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.util.*
 import java.util.*
 import java.util.concurrent.Executors
 import java.util.concurrent.Executors
 import kotlin.math.min
 import kotlin.math.min
@@ -194,7 +195,7 @@ class ChangeBookSourceViewModel(application: Application) : BaseViewModel(applic
                     searchCallback?.searchSuccess(searchBook)
                     searchCallback?.searchSuccess(searchBook)
                 }
                 }
             }.onError(IO) {
             }.onError(IO) {
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
     }
     }
 
 
@@ -205,7 +206,7 @@ class ChangeBookSourceViewModel(application: Application) : BaseViewModel(applic
                 val searchBook: SearchBook = book.toSearchBook()
                 val searchBook: SearchBook = book.toSearchBook()
                 searchCallback?.searchSuccess(searchBook)
                 searchCallback?.searchSuccess(searchBook)
             }.onError(IO) {
             }.onError(IO) {
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
     }
     }
 
 

+ 4 - 3
app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceViewModel.kt

@@ -19,6 +19,7 @@ import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.utils.getPrefBoolean
 import io.legado.app.utils.getPrefBoolean
 import io.legado.app.utils.getPrefString
 import io.legado.app.utils.getPrefString
+import io.legado.app.utils.printOnDebug
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.ExecutorCoroutineDispatcher
 import kotlinx.coroutines.ExecutorCoroutineDispatcher
 import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.asCoroutineDispatcher
@@ -27,7 +28,7 @@ import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.map
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.util.*
 import java.util.*
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.Executors
 import java.util.concurrent.Executors
@@ -204,7 +205,7 @@ class ChangeChapterSourceViewModel(application: Application) : BaseViewModel(app
                     searchCallback?.searchSuccess(searchBook)
                     searchCallback?.searchSuccess(searchBook)
                 }
                 }
             }.onError(IO) {
             }.onError(IO) {
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
     }
     }
 
 
@@ -216,7 +217,7 @@ class ChangeChapterSourceViewModel(application: Application) : BaseViewModel(app
                 val searchBook: SearchBook = book.toSearchBook()
                 val searchBook: SearchBook = book.toSearchBook()
                 searchCallback?.searchSuccess(searchBook)
                 searchCallback?.searchSuccess(searchBook)
             }.onError(IO) {
             }.onError(IO) {
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
     }
     }
 
 

+ 3 - 2
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt

@@ -10,9 +10,10 @@ import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.utils.msg
 import io.legado.app.utils.msg
+import io.legado.app.utils.printOnDebug
 
 
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
-import timber.log.Timber
+
 
 
 class ExploreShowViewModel(application: Application) : BaseViewModel(application) {
 class ExploreShowViewModel(application: Application) : BaseViewModel(application) {
 
 
@@ -44,7 +45,7 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
                     appDb.searchBookDao.insert(*searchBooks.toTypedArray())
                     appDb.searchBookDao.insert(*searchBooks.toTypedArray())
                     page++
                     page++
                 }.onError {
                 }.onError {
-                    Timber.e(it)
+                    it.printOnDebug()
                     errorLiveData.postValue(it.msg)
                     errorLiveData.postValue(it.msg)
                 }
                 }
         }
         }

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt

@@ -24,7 +24,7 @@ import io.legado.app.databinding.ItemTextBinding
 import io.legado.app.databinding.PopupActionMenuBinding
 import io.legado.app.databinding.PopupActionMenuBinding
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.utils.*
 import io.legado.app.utils.*
-import timber.log.Timber
+
 import java.util.*
 import java.util.*
 
 
 @SuppressLint("RestrictedApi")
 @SuppressLint("RestrictedApi")
@@ -210,7 +210,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
                     }
                     }
                     context.startActivity(intent)
                     context.startActivity(intent)
                 }.onFailure {
                 }.onFailure {
-                    Timber.e(it)
+                    it.printOnDebug()
                     context.toastOnUi(it.localizedMessage ?: "ERROR")
                     context.toastOnUi(it.localizedMessage ?: "ERROR")
                 }
                 }
             }
             }

+ 4 - 4
app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt

@@ -33,7 +33,7 @@ import io.legado.app.ui.document.HandleFileContract
 import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
 import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
 import io.legado.app.utils.*
 import io.legado.app.utils.*
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import io.legado.app.utils.viewbindingdelegate.viewBinding
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 
 
 class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
 class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
@@ -293,7 +293,7 @@ class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
         }.onSuccess {
         }.onSuccess {
             toastOnUi("导出成功, 文件名为 $exportFileName")
             toastOnUi("导出成功, 文件名为 $exportFileName")
         }.onError {
         }.onError {
-            Timber.e(it)
+            it.printOnDebug()
             longToast("导出失败:${it.localizedMessage}")
             longToast("导出失败:${it.localizedMessage}")
         }
         }
     }
     }
@@ -330,7 +330,7 @@ class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
             @Suppress("BlockingMethodInNonBlockingContext")
             @Suppress("BlockingMethodInNonBlockingContext")
             importConfig(uri.readBytes(requireContext()))
             importConfig(uri.readBytes(requireContext()))
         }.onError {
         }.onError {
-            Timber.e(it)
+            it.printOnDebug()
             longToast("导入失败:${it.localizedMessage}")
             longToast("导入失败:${it.localizedMessage}")
         }
         }
     }
     }
@@ -344,7 +344,7 @@ class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
             postEvent(EventBus.UP_CONFIG, true)
             postEvent(EventBus.UP_CONFIG, true)
             toastOnUi("导入成功")
             toastOnUi("导入成功")
         }.onError {
         }.onError {
-            Timber.e(it)
+            it.printOnDebug()
             longToast("导入失败:${it.localizedMessage}")
             longToast("导入失败:${it.localizedMessage}")
         }
         }
     }
     }

+ 3 - 3
app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditViewModel.kt

@@ -10,7 +10,7 @@ import io.legado.app.help.http.okHttpClient
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.utils.*
 import io.legado.app.utils.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
-import timber.log.Timber
+
 
 
 class BookSourceEditViewModel(application: Application) : BaseViewModel(application) {
 class BookSourceEditViewModel(application: Application) : BaseViewModel(application) {
 
 
@@ -48,7 +48,7 @@ class BookSourceEditViewModel(application: Application) : BaseViewModel(applicat
             success?.invoke()
             success?.invoke()
         }.onError {
         }.onError {
             context.toastOnUi(it.localizedMessage)
             context.toastOnUi(it.localizedMessage)
-            Timber.e(it)
+            it.printOnDebug()
         }
         }
     }
     }
 
 
@@ -62,7 +62,7 @@ class BookSourceEditViewModel(application: Application) : BaseViewModel(applicat
             }
             }
         }.onError {
         }.onError {
             context.toastOnUi(it.localizedMessage ?: "Error")
             context.toastOnUi(it.localizedMessage ?: "Error")
-            Timber.e(it)
+            it.printOnDebug()
         }
         }
     }
     }
 
 

+ 2 - 2
app/src/main/java/io/legado/app/ui/browser/WebViewModel.kt

@@ -14,7 +14,7 @@ import io.legado.app.help.http.okHttpClient
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.utils.*
 import io.legado.app.utils.*
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.util.*
 import java.util.*
 
 
@@ -41,7 +41,7 @@ class WebViewModel(application: Application) : BaseViewModel(application) {
             success.invoke()
             success.invoke()
         }.onError {
         }.onError {
             context.toastOnUi("error\n${it.localizedMessage}")
             context.toastOnUi("error\n${it.localizedMessage}")
-            Timber.e(it)
+            it.printOnDebug()
         }
         }
     }
     }
 
 

+ 3 - 7
app/src/main/java/io/legado/app/ui/document/HandleFileViewModel.kt

@@ -7,12 +7,8 @@ import androidx.lifecycle.MutableLiveData
 import io.legado.app.base.BaseViewModel
 import io.legado.app.base.BaseViewModel
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
 import io.legado.app.help.DirectLinkUpload
 import io.legado.app.help.DirectLinkUpload
-import io.legado.app.utils.FileUtils
-import io.legado.app.utils.GSON
-import io.legado.app.utils.isContentScheme
+import io.legado.app.utils.*
 
 
-import io.legado.app.utils.writeBytes
-import timber.log.Timber
 import java.io.File
 import java.io.File
 
 
 class HandleFileViewModel(application: Application) : BaseViewModel(application) {
 class HandleFileViewModel(application: Application) : BaseViewModel(application) {
@@ -31,7 +27,7 @@ class HandleFileViewModel(application: Application) : BaseViewModel(application)
             success.invoke(it)
             success.invoke(it)
         }.onError {
         }.onError {
             AppLog.put("上传文件失败\n${it.localizedMessage}", it)
             AppLog.put("上传文件失败\n${it.localizedMessage}", it)
-            Timber.e(it)
+            it.printOnDebug()
             errorLiveData.postValue(it.localizedMessage)
             errorLiveData.postValue(it.localizedMessage)
         }
         }
     }
     }
@@ -57,7 +53,7 @@ class HandleFileViewModel(application: Application) : BaseViewModel(application)
                 Uri.fromFile(newFile)
                 Uri.fromFile(newFile)
             }
             }
         }.onError {
         }.onError {
-            Timber.e(it)
+            it.printOnDebug()
             errorLiveData.postValue(it.localizedMessage)
             errorLiveData.postValue(it.localizedMessage)
         }.onSuccess {
         }.onSuccess {
             success.invoke(it)
             success.invoke(it)

+ 2 - 2
app/src/main/java/io/legado/app/ui/font/FontAdapter.kt

@@ -8,7 +8,7 @@ import io.legado.app.base.adapter.ItemViewHolder
 import io.legado.app.base.adapter.RecyclerAdapter
 import io.legado.app.base.adapter.RecyclerAdapter
 import io.legado.app.databinding.ItemFontBinding
 import io.legado.app.databinding.ItemFontBinding
 import io.legado.app.utils.*
 import io.legado.app.utils.*
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.net.URLDecoder
 import java.net.URLDecoder
 
 
@@ -45,7 +45,7 @@ class FontAdapter(context: Context, curFilePath: String, val callBack: CallBack)
                 }
                 }
                 tvFont.typeface = typeface
                 tvFont.typeface = typeface
             }.onFailure {
             }.onFailure {
-                Timber.e(it)
+                it.printOnDebug()
                 context.toastOnUi("Read ${item.name} Error: ${it.localizedMessage}")
                 context.toastOnUi("Read ${item.name} Error: ${it.localizedMessage}")
             }
             }
             tvFont.text = item.name
             tvFont.text = item.name

+ 2 - 2
app/src/main/java/io/legado/app/ui/login/SourceLoginDialog.kt

@@ -24,7 +24,7 @@ import kotlinx.coroutines.Dispatchers.Main
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
 import splitties.views.onClick
 import splitties.views.onClick
-import timber.log.Timber
+
 
 
 class SourceLoginDialog : BaseDialogFragment(R.layout.dialog_login) {
 class SourceLoginDialog : BaseDialogFragment(R.layout.dialog_login) {
 
 
@@ -124,7 +124,7 @@ class SourceLoginDialog : BaseDialogFragment(R.layout.dialog_login) {
                     } catch (e: Exception) {
                     } catch (e: Exception) {
                         AppLog.put("登录出错\n${e.localizedMessage}", e)
                         AppLog.put("登录出错\n${e.localizedMessage}", e)
                         context?.toastOnUi("登录出错\n${e.localizedMessage}")
                         context?.toastOnUi("登录出错\n${e.localizedMessage}")
-                        Timber.e(e)
+                        e.printOnDebug()
                     }
                     }
                 }
                 }
             }
             }

+ 1 - 2
app/src/main/java/io/legado/app/ui/main/MainViewModel.kt

@@ -18,7 +18,7 @@ import io.legado.app.model.webBook.WebBook
 import io.legado.app.service.CacheBookService
 import io.legado.app.service.CacheBookService
 import io.legado.app.utils.postEvent
 import io.legado.app.utils.postEvent
 import kotlinx.coroutines.*
 import kotlinx.coroutines.*
-import timber.log.Timber
+
 import java.util.concurrent.CopyOnWriteArraySet
 import java.util.concurrent.CopyOnWriteArraySet
 import java.util.concurrent.Executors
 import java.util.concurrent.Executors
 import kotlin.math.min
 import kotlin.math.min
@@ -126,7 +126,6 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
             addDownload(source, book)
             addDownload(source, book)
         }.onError(upTocPool) {
         }.onError(upTocPool) {
             AppLog.put("${book.name} 更新目录失败\n${it.localizedMessage}", it)
             AppLog.put("${book.name} 更新目录失败\n${it.localizedMessage}", it)
-            Timber.e(it, "${book.name} 更新目录失败")
         }.onCancel(upTocPool) {
         }.onCancel(upTocPool) {
             upTocCancel(book.bookUrl)
             upTocCancel(book.bookUrl)
         }.onFinally(upTocPool) {
         }.onFinally(upTocPool) {

+ 5 - 3
app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt

@@ -5,15 +5,17 @@ import android.os.Bundle
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.viewModelScope
 import androidx.lifecycle.viewModelScope
 import io.legado.app.base.BaseViewModel
 import io.legado.app.base.BaseViewModel
+import io.legado.app.constant.AppLog
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.RssArticle
 import io.legado.app.data.entities.RssArticle
 import io.legado.app.data.entities.RssSource
 import io.legado.app.data.entities.RssSource
 import io.legado.app.model.rss.Rss
 import io.legado.app.model.rss.Rss
+import io.legado.app.utils.printOnDebug
 
 
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
-import timber.log.Timber
+
 
 
 class RssArticlesViewModel(application: Application) : BaseViewModel(application) {
 class RssArticlesViewModel(application: Application) : BaseViewModel(application) {
     val loadFinally = MutableLiveData<Boolean>()
     val loadFinally = MutableLiveData<Boolean>()
@@ -54,7 +56,7 @@ class RssArticlesViewModel(application: Application) : BaseViewModel(application
                 }
                 }
             }.onError {
             }.onError {
                 loadFinally.postValue(false)
                 loadFinally.postValue(false)
-                Timber.e(it)
+                AppLog.put("rss获取内容失败", it)
                 context.toastOnUi(it.localizedMessage)
                 context.toastOnUi(it.localizedMessage)
             }
             }
     }
     }
@@ -70,7 +72,7 @@ class RssArticlesViewModel(application: Application) : BaseViewModel(application
                     loadMoreSuccess(it.first)
                     loadMoreSuccess(it.first)
                 }
                 }
                 .onError {
                 .onError {
-                    Timber.e(it)
+                    it.printOnDebug()
                     loadFinally.postValue(false)
                     loadFinally.postValue(false)
                 }
                 }
         } else {
         } else {

+ 3 - 2
app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt

@@ -7,10 +7,11 @@ import io.legado.app.data.appDb
 import io.legado.app.data.entities.RssSource
 import io.legado.app.data.entities.RssSource
 import io.legado.app.utils.getClipText
 import io.legado.app.utils.getClipText
 import io.legado.app.utils.msg
 import io.legado.app.utils.msg
+import io.legado.app.utils.printOnDebug
 
 
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
-import timber.log.Timber
+
 
 
 class RssSourceEditViewModel(application: Application) : BaseViewModel(application) {
 class RssSourceEditViewModel(application: Application) : BaseViewModel(application) {
 
 
@@ -42,7 +43,7 @@ class RssSourceEditViewModel(application: Application) : BaseViewModel(applicati
             success()
             success()
         }.onError {
         }.onError {
             context.toastOnUi(it.localizedMessage)
             context.toastOnUi(it.localizedMessage)
-            Timber.e(it)
+            it.printOnDebug()
         }
         }
     }
     }
 
 

+ 2 - 2
app/src/main/java/io/legado/app/ui/widget/SearchView.kt

@@ -15,7 +15,7 @@ import android.view.Gravity
 import android.widget.TextView
 import android.widget.TextView
 import androidx.appcompat.widget.SearchView
 import androidx.appcompat.widget.SearchView
 import io.legado.app.R
 import io.legado.app.R
-import timber.log.Timber
+import io.legado.app.utils.printOnDebug
 
 
 
 
 class SearchView @JvmOverloads constructor(
 class SearchView @JvmOverloads constructor(
@@ -44,7 +44,7 @@ class SearchView @JvmOverloads constructor(
             textView!!.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
             textView!!.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
             textView!!.gravity = Gravity.CENTER_VERTICAL
             textView!!.gravity = Gravity.CENTER_VERTICAL
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
     }
     }
 
 

+ 4 - 3
app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/ExplosionView.kt

@@ -25,7 +25,8 @@ import android.graphics.Rect
 import android.media.MediaPlayer
 import android.media.MediaPlayer
 import android.util.AttributeSet
 import android.util.AttributeSet
 import android.view.View
 import android.view.View
-import timber.log.Timber
+import io.legado.app.utils.DebugLog
+
 import java.util.*
 import java.util.*
 
 
 
 
@@ -125,11 +126,11 @@ class ExplosionView @JvmOverloads constructor(context: Context, attrs: Attribute
             }
             }
 
 
             override fun onAnimationCancel(animator: Animator) {
             override fun onAnimationCancel(animator: Animator) {
-                Timber.i("CANCEL")
+                DebugLog.i(javaClass.name, "CANCEL")
             }
             }
 
 
             override fun onAnimationRepeat(animator: Animator) {
             override fun onAnimationRepeat(animator: Animator) {
-                Timber.i("REPEAT")
+                DebugLog.i(javaClass.name, "REPEAT")
             }
             }
         })
         })
 
 

+ 3 - 2
app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/Utils.kt

@@ -22,7 +22,8 @@ import android.graphics.Canvas
 import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.BitmapDrawable
 import android.view.View
 import android.view.View
 import android.widget.ImageView
 import android.widget.ImageView
-import timber.log.Timber
+import io.legado.app.utils.printOnDebug
+
 
 
 import kotlin.math.roundToInt
 import kotlin.math.roundToInt
 
 
@@ -67,7 +68,7 @@ object Utils {
         try {
         try {
             return Bitmap.createBitmap(width, height, config)
             return Bitmap.createBitmap(width, height, config)
         } catch (e: OutOfMemoryError) {
         } catch (e: OutOfMemoryError) {
-            Timber.e(e)
+            e.printOnDebug()
             if (retryCount > 0) {
             if (retryCount > 0) {
                 System.gc()
                 System.gc()
                 return createBitmapSafely(width, height, config, retryCount - 1)
                 return createBitmapSafely(width, height, config, retryCount - 1)

+ 3 - 6
app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt

@@ -20,9 +20,10 @@ import androidx.annotation.RequiresApi
 import androidx.appcompat.widget.AppCompatImageView
 import androidx.appcompat.widget.AppCompatImageView
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.utils.getCompatColor
 import io.legado.app.utils.getCompatColor
+import io.legado.app.utils.printOnDebug
 
 
 import io.legado.app.utils.sp
 import io.legado.app.utils.sp
-import timber.log.Timber
+
 import kotlin.math.min
 import kotlin.math.min
 import kotlin.math.pow
 import kotlin.math.pow
 
 
@@ -151,10 +152,6 @@ class CircleImageView @JvmOverloads constructor(
 
 
         mReady = true
         mReady = true
 
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            outlineProvider = OutlineProvider()
-        }
-
         if (mSetupPending) {
         if (mSetupPending) {
             setup()
             setup()
             mSetupPending = false
             mSetupPending = false
@@ -318,7 +315,7 @@ class CircleImageView @JvmOverloads constructor(
             drawable.draw(canvas)
             drawable.draw(canvas)
             bitmap
             bitmap
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
             null
             null
         }
         }
 
 

+ 6 - 2
app/src/main/java/io/legado/app/ui/widget/recycler/DividerNoLast.kt

@@ -7,7 +7,8 @@ import android.graphics.drawable.Drawable
 import android.view.View
 import android.view.View
 import android.widget.LinearLayout
 import android.widget.LinearLayout
 import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView
-import timber.log.Timber
+import io.legado.app.utils.DebugLog
+
 import kotlin.math.roundToInt
 import kotlin.math.roundToInt
 
 
 
 
@@ -38,7 +39,10 @@ class DividerNoLast(context: Context, orientation: Int) :
         val a = context.obtainStyledAttributes(attrs)
         val a = context.obtainStyledAttributes(attrs)
         mDivider = a.getDrawable(0)
         mDivider = a.getDrawable(0)
         if (mDivider == null) {
         if (mDivider == null) {
-            Timber.w("@android:attr/listDivider was not set in the theme used for this DividerItemDecoration. Please set that attribute all call setDrawable()")
+            DebugLog.w(
+                javaClass.name,
+                "@android:attr/listDivider was not set in the theme used for this DividerItemDecoration. Please set that attribute all call setDrawable()"
+            )
         }
         }
         a.recycle()
         a.recycle()
         setOrientation(orientation)
         setOrientation(orientation)

+ 5 - 4
app/src/main/java/io/legado/app/ui/widget/recycler/DragSelectTouchHelper.kt

@@ -27,7 +27,8 @@ import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener
 import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener
 import io.legado.app.BuildConfig
 import io.legado.app.BuildConfig
 import io.legado.app.ui.widget.recycler.DragSelectTouchHelper.AdvanceCallback.Mode
 import io.legado.app.ui.widget.recycler.DragSelectTouchHelper.AdvanceCallback.Mode
-import timber.log.Timber
+import io.legado.app.utils.DebugLog
+
 import java.util.*
 import java.util.*
 import kotlin.math.max
 import kotlin.math.max
 import kotlin.math.min
 import kotlin.math.min
@@ -971,15 +972,15 @@ class DragSelectTouchHelper(
     private object Logger {
     private object Logger {
         private val DEBUG = BuildConfig.DEBUG
         private val DEBUG = BuildConfig.DEBUG
         fun d(msg: String) {
         fun d(msg: String) {
-            Timber.d(msg)
+            DebugLog.d(javaClass.name, msg)
         }
         }
 
 
         fun e(msg: String) {
         fun e(msg: String) {
-            Timber.e(msg)
+            DebugLog.e(javaClass.name, msg)
         }
         }
 
 
         fun i(msg: String) {
         fun i(msg: String) {
-            Timber.i(msg)
+            DebugLog.i(javaClass.name, msg)
         }
         }
 
 
         fun logSelectStateChange(before: Int, after: Int) {
         fun logSelectStateChange(before: Int, after: Int) {

+ 16 - 16
app/src/main/java/io/legado/app/utils/ACache.kt

@@ -11,7 +11,7 @@ import android.graphics.drawable.Drawable
 import org.json.JSONArray
 import org.json.JSONArray
 import org.json.JSONObject
 import org.json.JSONObject
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.*
 import java.io.*
 import java.util.*
 import java.util.*
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.atomic.AtomicInteger
@@ -70,11 +70,11 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
     init {
     init {
         try {
         try {
             if (!cacheDir.exists() && !cacheDir.mkdirs()) {
             if (!cacheDir.exists() && !cacheDir.mkdirs()) {
-                Timber.i("can't make dirs in %s" + cacheDir.absolutePath)
+                DebugLog.i(javaClass.name, "can't make dirs in %s" + cacheDir.absolutePath)
             }
             }
             mCache = ACacheManager(cacheDir, max_size, max_count)
             mCache = ACacheManager(cacheDir, max_size, max_count)
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
 
 
     }
     }
@@ -96,7 +96,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                 file.writeText(value)
                 file.writeText(value)
                 mCache.put(file)
                 mCache.put(file)
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
         }
         }
     }
     }
@@ -131,7 +131,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                     removeFile = true
                     removeFile = true
                 }
                 }
             } catch (e: IOException) {
             } catch (e: IOException) {
-                Timber.e(e)
+                e.printOnDebug()
             } finally {
             } finally {
                 if (removeFile)
                 if (removeFile)
                     remove(key)
                     remove(key)
@@ -269,7 +269,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                     null
                     null
                 }
                 }
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             } finally {
             } finally {
                 if (removeFile)
                 if (removeFile)
                     remove(key)
                     remove(key)
@@ -299,7 +299,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                 }
                 }
             }
             }
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
     }
     }
 
 
@@ -318,18 +318,18 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                 ois = ObjectInputStream(bis)
                 ois = ObjectInputStream(bis)
                 return ois.readObject()
                 return ois.readObject()
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             } finally {
             } finally {
                 try {
                 try {
                     bis?.close()
                     bis?.close()
                 } catch (e: IOException) {
                 } catch (e: IOException) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
 
 
                 try {
                 try {
                     ois?.close()
                     ois?.close()
                 } catch (e: IOException) {
                 } catch (e: IOException) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
 
 
             }
             }
@@ -427,7 +427,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                     return f
                     return f
                 }
                 }
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
         }
         }
         return null
         return null
@@ -488,7 +488,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                     }
                     }
                 }
                 }
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
 
 
             return false
             return false
@@ -662,7 +662,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                         cacheCount.set(count)
                         cacheCount.set(count)
                     }
                     }
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    Timber.e(e)
+                    e.printOnDebug()
                 }
                 }
 
 
 
 
@@ -693,7 +693,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                 file.setLastModified(currentTime)
                 file.setLastModified(currentTime)
                 lastUsageDates[file] = currentTime
                 lastUsageDates[file] = currentTime
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
 
 
         }
         }
@@ -727,7 +727,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                     }
                     }
                 }
                 }
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
             }
             }
 
 
         }
         }
@@ -767,7 +767,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
                 }
                 }
                 return fileSize
                 return fileSize
             } catch (e: Exception) {
             } catch (e: Exception) {
-                Timber.e(e)
+                e.printOnDebug()
                 return 0
                 return 0
             }
             }
 
 

+ 3 - 3
app/src/main/java/io/legado/app/utils/ContextExtensions.kt

@@ -29,7 +29,7 @@ import io.legado.app.R
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst
 import io.legado.app.help.IntentHelp
 import io.legado.app.help.IntentHelp
 import splitties.systemservices.clipboardManager
 import splitties.systemservices.clipboardManager
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.FileOutputStream
 import java.io.FileOutputStream
 
 
@@ -178,7 +178,7 @@ val Context.sysScreenOffTime: Int
         return kotlin.runCatching {
         return kotlin.runCatching {
             Settings.System.getInt(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
             Settings.System.getInt(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
         }.onFailure {
         }.onFailure {
-            Timber.e(it)
+            it.printOnDebug()
         }.getOrDefault(0)
         }.getOrDefault(0)
     }
     }
 
 
@@ -338,7 +338,7 @@ val Context.channel: String
             val appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
             val appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
             return appInfo.metaData.getString("channel") ?: ""
             return appInfo.metaData.getString("channel") ?: ""
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return ""
         return ""
     }
     }

+ 54 - 0
app/src/main/java/io/legado/app/utils/DebugLog.kt

@@ -0,0 +1,54 @@
+package io.legado.app.utils
+
+import android.util.Log
+import io.legado.app.BuildConfig
+
+object DebugLog {
+
+    fun e(tag: String, throwable: Throwable) {
+        if (BuildConfig.DEBUG) {
+            Log.e(tag, throwable.stackTraceToString())
+        }
+    }
+
+    fun e(tag: String, msg: String, throwable: Throwable? = null) {
+        if (BuildConfig.DEBUG) {
+            if (throwable == null) {
+                Log.e(tag, msg)
+            } else {
+                Log.e(tag, msg, throwable)
+            }
+        }
+    }
+
+    fun d(tag: String, msg: String, throwable: Throwable? = null) {
+        if (BuildConfig.DEBUG) {
+            if (throwable == null) {
+                Log.d(tag, msg)
+            } else {
+                Log.d(tag, msg, throwable)
+            }
+        }
+    }
+
+    fun i(tag: String, msg: String, throwable: Throwable? = null) {
+        if (BuildConfig.DEBUG) {
+            if (throwable == null) {
+                Log.i(tag, msg)
+            } else {
+                Log.i(tag, msg, throwable)
+            }
+        }
+    }
+
+    fun w(tag: String, msg: String, throwable: Throwable? = null) {
+        if (BuildConfig.DEBUG) {
+            if (throwable == null) {
+                Log.w(tag, msg)
+            } else {
+                Log.w(tag, msg, throwable)
+            }
+        }
+    }
+
+}

+ 3 - 3
app/src/main/java/io/legado/app/utils/FileUtils.kt

@@ -4,7 +4,7 @@ import android.os.Environment
 import android.webkit.MimeTypeMap
 import android.webkit.MimeTypeMap
 import androidx.annotation.IntDef
 import androidx.annotation.IntDef
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.*
 import java.io.*
 import java.nio.charset.Charset
 import java.nio.charset.Charset
 import java.text.SimpleDateFormat
 import java.text.SimpleDateFormat
@@ -46,7 +46,7 @@ object FileUtils {
                 file.createNewFile()
                 file.createNewFile()
             }
             }
         } catch (e: IOException) {
         } catch (e: IOException) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return file
         return file
     }
     }
@@ -117,7 +117,7 @@ object FileUtils {
         try {
         try {
             sdCardDirectory = File(sdCardDirectory).canonicalPath
             sdCardDirectory = File(sdCardDirectory).canonicalPath
         } catch (e: IOException) {
         } catch (e: IOException) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return sdCardDirectory
         return sdCardDirectory
     }
     }

+ 5 - 3
app/src/main/java/io/legado/app/utils/GsonExtensions.kt

@@ -4,7 +4,7 @@ import com.google.gson.*
 import com.google.gson.internal.LinkedTreeMap
 import com.google.gson.internal.LinkedTreeMap
 import com.google.gson.reflect.TypeToken
 import com.google.gson.reflect.TypeToken
 import com.google.gson.stream.JsonWriter
 import com.google.gson.stream.JsonWriter
-import timber.log.Timber
+
 import java.io.OutputStream
 import java.io.OutputStream
 import java.io.OutputStreamWriter
 import java.io.OutputStreamWriter
 import java.lang.reflect.ParameterizedType
 import java.lang.reflect.ParameterizedType
@@ -27,18 +27,20 @@ val GSON: Gson by lazy {
 inline fun <reified T> genericType(): Type = object : TypeToken<T>() {}.type
 inline fun <reified T> genericType(): Type = object : TypeToken<T>() {}.type
 
 
 inline fun <reified T> Gson.fromJsonObject(json: String?): T? {//可转成任意类型
 inline fun <reified T> Gson.fromJsonObject(json: String?): T? {//可转成任意类型
+    json ?: return null
     return kotlin.runCatching {
     return kotlin.runCatching {
         fromJson(json, genericType<T>()) as? T
         fromJson(json, genericType<T>()) as? T
     }.onFailure {
     }.onFailure {
-        Timber.e(it, json)
+        DebugLog.e("GSON解析出错", json, it)
     }.getOrNull()
     }.getOrNull()
 }
 }
 
 
 inline fun <reified T> Gson.fromJsonArray(json: String?): List<T>? {
 inline fun <reified T> Gson.fromJsonArray(json: String?): List<T>? {
+    json ?: return null
     return kotlin.runCatching {
     return kotlin.runCatching {
         fromJson(json, ParameterizedTypeImpl(T::class.java)) as? List<T>
         fromJson(json, ParameterizedTypeImpl(T::class.java)) as? List<T>
     }.onFailure {
     }.onFailure {
-        Timber.e(it, json)
+        DebugLog.e("GSON解析出错", json, it)
     }.getOrNull()
     }.getOrNull()
 }
 }
 
 

+ 7 - 0
app/src/main/java/io/legado/app/utils/LogUtils.kt

@@ -3,6 +3,7 @@
 package io.legado.app.utils
 package io.legado.app.utils
 
 
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
+import io.legado.app.BuildConfig
 import io.legado.app.help.AppConfig
 import io.legado.app.help.AppConfig
 import splitties.init.appCtx
 import splitties.init.appCtx
 import java.text.SimpleDateFormat
 import java.text.SimpleDateFormat
@@ -73,3 +74,9 @@ object LogUtils {
         return sdf.format(date)
         return sdf.format(date)
     }
     }
 }
 }
+
+fun Throwable.printOnDebug() {
+    if (BuildConfig.DEBUG) {
+        printStackTrace()
+    }
+}

+ 2 - 2
app/src/main/java/io/legado/app/utils/MD5Utils.kt

@@ -1,6 +1,6 @@
 package io.legado.app.utils
 package io.legado.app.utils
 
 
-import timber.log.Timber
+
 import java.security.MessageDigest
 import java.security.MessageDigest
 import java.security.NoSuchAlgorithmException
 import java.security.NoSuchAlgorithmException
 
 
@@ -26,7 +26,7 @@ object MD5Utils {
             }
             }
             reStr = stringBuffer.toString()
             reStr = stringBuffer.toString()
         } catch (e: NoSuchAlgorithmException) {
         } catch (e: NoSuchAlgorithmException) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
 
 
         return reStr
         return reStr

+ 5 - 4
app/src/main/java/io/legado/app/utils/NetworkUtils.kt

@@ -3,9 +3,10 @@ package io.legado.app.utils
 import android.net.ConnectivityManager
 import android.net.ConnectivityManager
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities
 import android.os.Build
 import android.os.Build
+import io.legado.app.constant.AppLog
 import okhttp3.internal.publicsuffix.PublicSuffixDatabase
 import okhttp3.internal.publicsuffix.PublicSuffixDatabase
 import splitties.systemservices.connectivityManager
 import splitties.systemservices.connectivityManager
-import timber.log.Timber
+
 import java.net.InetAddress
 import java.net.InetAddress
 import java.net.NetworkInterface
 import java.net.NetworkInterface
 import java.net.SocketException
 import java.net.SocketException
@@ -117,7 +118,7 @@ object NetworkUtils {
             relativeUrl = parseUrl.toString()
             relativeUrl = parseUrl.toString()
             return relativeUrl
             return relativeUrl
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
         return relativeUrl
         return relativeUrl
     }
     }
@@ -134,7 +135,7 @@ object NetworkUtils {
             relativeUrl = parseUrl.toString()
             relativeUrl = parseUrl.toString()
             return relativeUrl
             return relativeUrl
         } catch (e: Exception) {
         } catch (e: Exception) {
-            Timber.e("网址拼接出错\n${e.localizedMessage}", e)
+            AppLog.put("网址拼接出错\n${e.localizedMessage}", e)
         }
         }
         return relativeUrl
         return relativeUrl
     }
     }
@@ -179,7 +180,7 @@ object NetworkUtils {
         try {
         try {
             enumeration = NetworkInterface.getNetworkInterfaces()
             enumeration = NetworkInterface.getNetworkInterfaces()
         } catch (e: SocketException) {
         } catch (e: SocketException) {
-            Timber.e(e)
+            e.printOnDebug()
         }
         }
 
 
         if (enumeration != null) {
         if (enumeration != null) {

+ 4 - 4
app/src/main/java/io/legado/app/utils/RealPathUtil.kt

@@ -9,7 +9,7 @@ import android.os.Build
 import android.os.Environment
 import android.os.Environment
 import android.provider.DocumentsContract
 import android.provider.DocumentsContract
 import android.provider.MediaStore
 import android.provider.MediaStore
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 import java.io.FileInputStream
 import java.io.FileInputStream
 import java.io.FileOutputStream
 import java.io.FileOutputStream
@@ -112,7 +112,7 @@ object RealPathUtil {
                 return cursor.getString(index)
                 return cursor.getString(index)
             }
             }
         } catch (e: IllegalArgumentException) {
         } catch (e: IllegalArgumentException) {
-            Timber.e(e)
+            e.printOnDebug()
             val file = File(context.cacheDir, "tmp")
             val file = File(context.cacheDir, "tmp")
             val filePath = file.absolutePath
             val filePath = file.absolutePath
             var input: FileInputStream? = null
             var input: FileInputStream? = null
@@ -130,8 +130,8 @@ object RealPathUtil {
                     output.write(bytes, 0, read)
                     output.write(bytes, 0, read)
                 }
                 }
                 return File(filePath).absolutePath
                 return File(filePath).absolutePath
-            } catch (ignored: IOException) {
-                Timber.e(ignored)
+            } catch (e: IOException) {
+                e.printStackTrace()
             } finally {
             } finally {
                 input?.close()
                 input?.close()
                 output?.close()
                 output?.close()

+ 2 - 2
app/src/main/java/io/legado/app/utils/StringUtils.kt

@@ -2,7 +2,7 @@ package io.legado.app.utils
 
 
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
 import android.text.TextUtils.isEmpty
 import android.text.TextUtils.isEmpty
-import timber.log.Timber
+
 import java.text.DecimalFormat
 import java.text.DecimalFormat
 import java.text.SimpleDateFormat
 import java.text.SimpleDateFormat
 import java.util.*
 import java.util.*
@@ -83,7 +83,7 @@ object StringUtils {
                 }
                 }
             }
             }
         }.onFailure {
         }.onFailure {
-            Timber.e(it)
+            it.printOnDebug()
         }
         }
         return ""
         return ""
     }
     }

+ 3 - 3
app/src/main/java/io/legado/app/utils/UriExtensions.kt

@@ -9,7 +9,7 @@ import io.legado.app.R
 import io.legado.app.lib.permission.Permissions
 import io.legado.app.lib.permission.Permissions
 import io.legado.app.lib.permission.PermissionsCompat
 import io.legado.app.lib.permission.PermissionsCompat
 import io.legado.app.model.NoStackTraceException
 import io.legado.app.model.NoStackTraceException
-import timber.log.Timber
+
 import java.io.File
 import java.io.File
 
 
 fun Uri.isContentScheme() = this.scheme == "content"
 fun Uri.isContentScheme() = this.scheme == "content"
@@ -42,7 +42,7 @@ fun AppCompatActivity.readUri(uri: Uri?, success: (name: String, bytes: ByteArra
                 .request()
                 .request()
         }
         }
     } catch (e: Exception) {
     } catch (e: Exception) {
-        Timber.e(e)
+        e.printOnDebug()
         toastOnUi(e.localizedMessage ?: "read uri error")
         toastOnUi(e.localizedMessage ?: "read uri error")
     }
     }
 }
 }
@@ -75,7 +75,7 @@ fun Fragment.readUri(uri: Uri?, success: (name: String, bytes: ByteArray) -> Uni
                 .request()
                 .request()
         }
         }
     } catch (e: Exception) {
     } catch (e: Exception) {
-        Timber.e(e)
+        e.printOnDebug()
         toastOnUi(e.localizedMessage ?: "read uri error")
         toastOnUi(e.localizedMessage ?: "read uri error")
     }
     }
 }
 }

+ 2 - 2
app/src/main/java/io/legado/app/utils/ViewExtensions.kt

@@ -21,7 +21,7 @@ import androidx.viewpager.widget.ViewPager
 import io.legado.app.help.AppConfig
 import io.legado.app.help.AppConfig
 import io.legado.app.lib.theme.TintHelper
 import io.legado.app.lib.theme.TintHelper
 import splitties.systemservices.inputMethodManager
 import splitties.systemservices.inputMethodManager
-import timber.log.Timber
+
 import java.lang.reflect.Field
 import java.lang.reflect.Field
 
 
 
 
@@ -170,6 +170,6 @@ fun PopupMenu.show(x: Int, y: Int) {
         field.isAccessible = true
         field.isAccessible = true
         (field.get(this) as MenuPopupHelper).show(x, y)
         (field.get(this) as MenuPopupHelper).show(x, y)
     }.onFailure {
     }.onFailure {
-        Timber.e(it)
+        it.printOnDebug()
     }
     }
 }
 }

+ 5 - 5
app/src/main/java/io/legado/app/utils/ZipUtils.kt

@@ -2,7 +2,7 @@ package io.legado.app.utils
 
 
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
-import timber.log.Timber
+
 import java.io.*
 import java.io.*
 import java.util.zip.ZipEntry
 import java.util.zip.ZipEntry
 import java.util.zip.ZipFile
 import java.util.zip.ZipFile
@@ -241,7 +241,7 @@ object ZipUtils {
                     val entry = entries.nextElement() as ZipEntry
                     val entry = entries.nextElement() as ZipEntry
                     val entryName = entry.name
                     val entryName = entry.name
                     if (entryName.contains("../")) {
                     if (entryName.contains("../")) {
-                        Timber.e("entryName: $entryName is dangerous!")
+                        DebugLog.e(javaClass.name, "entryName: $entryName is dangerous!")
                         continue
                         continue
                     }
                     }
                     if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files
                     if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files
@@ -251,7 +251,7 @@ object ZipUtils {
                     val entry = entries.nextElement() as ZipEntry
                     val entry = entries.nextElement() as ZipEntry
                     val entryName = entry.name
                     val entryName = entry.name
                     if (entryName.contains("../")) {
                     if (entryName.contains("../")) {
-                        Timber.e("entryName: $entryName is dangerous!")
+                        DebugLog.e(javaClass.name, "entryName: $entryName is dangerous!")
                         continue
                         continue
                     }
                     }
                     if (entryName.contains(keyword!!)) {
                     if (entryName.contains(keyword!!)) {
@@ -314,7 +314,7 @@ object ZipUtils {
         while (entries.hasMoreElements()) {
         while (entries.hasMoreElements()) {
             val entryName = (entries.nextElement() as ZipEntry).name
             val entryName = (entries.nextElement() as ZipEntry).name
             if (entryName.contains("../")) {
             if (entryName.contains("../")) {
-                Timber.e("entryName: $entryName is dangerous!")
+                DebugLog.e(javaClass.name, "entryName: $entryName is dangerous!")
                 paths.add(entryName)
                 paths.add(entryName)
             } else {
             } else {
                 paths.add(entryName)
                 paths.add(entryName)
@@ -368,7 +368,7 @@ object ZipUtils {
         return try {
         return try {
             file.createNewFile()
             file.createNewFile()
         } catch (e: IOException) {
         } catch (e: IOException) {
-            Timber.e(e)
+            e.printOnDebug()
             false
             false
         }
         }
     }
     }

+ 3 - 6
app/src/main/java/io/legado/app/web/socket/BookSourceDebugWebSocket.kt

@@ -6,14 +6,11 @@ import fi.iki.elonen.NanoWSD
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
 import io.legado.app.model.Debug
 import io.legado.app.model.Debug
-import io.legado.app.utils.GSON
-import io.legado.app.utils.fromJsonObject
-import io.legado.app.utils.isJson
-import io.legado.app.utils.runOnIO
+import io.legado.app.utils.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.IOException
 import java.io.IOException
 
 
 
 
@@ -90,7 +87,7 @@ class BookSourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) :
                     close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
                     close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
                 }
                 }
             }.onFailure {
             }.onFailure {
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
         }
         }
     }
     }

+ 3 - 6
app/src/main/java/io/legado/app/web/socket/RssSourceDebugWebSocket.kt

@@ -6,14 +6,11 @@ import fi.iki.elonen.NanoWSD
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
 import io.legado.app.model.Debug
 import io.legado.app.model.Debug
-import io.legado.app.utils.GSON
-import io.legado.app.utils.fromJsonObject
-import io.legado.app.utils.isJson
-import io.legado.app.utils.runOnIO
+import io.legado.app.utils.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import splitties.init.appCtx
 import splitties.init.appCtx
-import timber.log.Timber
+
 import java.io.IOException
 import java.io.IOException
 
 
 
 
@@ -89,7 +86,7 @@ class RssSourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) :
                     close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
                     close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
                 }
                 }
             }.onFailure {
             }.onFailure {
-                Timber.e(it)
+                it.printOnDebug()
             }
             }
         }
         }
     }
     }

+ 1 - 0
build.gradle

@@ -17,6 +17,7 @@ buildscript {
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4'
         classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4'
         classpath 'de.undercouch:gradle-download-task:4.1.2'
         classpath 'de.undercouch:gradle-download-task:4.1.2'
+        classpath 'com.google.gms:google-services:4.3.10'
     }
     }
 }
 }