feat: logging to file

This commit is contained in:
rebelonion 2024-03-11 03:01:08 -05:00
parent 1028ac66cb
commit dbce7c5b29
67 changed files with 475 additions and 324 deletions

View file

@ -42,7 +42,7 @@ android {
buildTypes { buildTypes {
alpha { alpha {
applicationIdSuffix ".beta" // keep as beta by popular request applicationIdSuffix ".beta" // keep as beta by popular request
versionNameSuffix "-alpha02" versionNameSuffix "-alpha03"
manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_alpha", icon_placeholder_round: "@mipmap/ic_launcher_alpha_round"] manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_alpha", icon_placeholder_round: "@mipmap/ic_launcher_alpha_round"]
debuggable System.getenv("CI") == null debuggable System.getenv("CI") == null
isDefault true isDefault true

View file

@ -14,8 +14,18 @@ import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import ani.dantotsu.* import ani.dantotsu.BuildConfig
import ani.dantotsu.Mapper
import ani.dantotsu.R
import ani.dantotsu.client
import ani.dantotsu.currContext
import ani.dantotsu.logError
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.snackString
import ani.dantotsu.toast
import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -50,7 +60,7 @@ object AppUpdater {
res to res.substringAfter("# ").substringBefore("\n") res to res.substringAfter("# ").substringBefore("\n")
} }
logger("Git Version : $version") Logger.log("Git Version : $version")
val dontShow = PrefManager.getCustomVal("dont_ask_for_update_$version", false) val dontShow = PrefManager.getCustomVal("dont_ask_for_update_$version", false)
if (compareVersion(version) && !dontShow && !activity.isDestroyed) activity.runOnUiThread { if (compareVersion(version) && !dontShow && !activity.isDestroyed) activity.runOnUiThread {
CustomBottomDialog.newInstance().apply { CustomBottomDialog.newInstance().apply {

View file

@ -21,6 +21,8 @@ import ani.dantotsu.parsers.novel.NovelExtensionManager
import ani.dantotsu.settings.SettingsActivity import ani.dantotsu.settings.SettingsActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.FinalExceptionHandler
import ani.dantotsu.util.Logger
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
@ -83,7 +85,8 @@ class App : MultiDexApplication() {
} }
crashlytics.setCustomKey("device Info", SettingsActivity.getDeviceInfo()) crashlytics.setCustomKey("device Info", SettingsActivity.getDeviceInfo())
Logger.init(this)
Thread.setDefaultUncaughtExceptionHandler(FinalExceptionHandler())
initializeNetwork(baseContext) initializeNetwork(baseContext)
@ -99,19 +102,19 @@ class App : MultiDexApplication() {
val animeScope = CoroutineScope(Dispatchers.Default) val animeScope = CoroutineScope(Dispatchers.Default)
animeScope.launch { animeScope.launch {
animeExtensionManager.findAvailableExtensions() animeExtensionManager.findAvailableExtensions()
logger("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}") Logger.log("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}")
AnimeSources.init(animeExtensionManager.installedExtensionsFlow) AnimeSources.init(animeExtensionManager.installedExtensionsFlow)
} }
val mangaScope = CoroutineScope(Dispatchers.Default) val mangaScope = CoroutineScope(Dispatchers.Default)
mangaScope.launch { mangaScope.launch {
mangaExtensionManager.findAvailableExtensions() mangaExtensionManager.findAvailableExtensions()
logger("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}") Logger.log("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}")
MangaSources.init(mangaExtensionManager.installedExtensionsFlow) MangaSources.init(mangaExtensionManager.installedExtensionsFlow)
} }
val novelScope = CoroutineScope(Dispatchers.Default) val novelScope = CoroutineScope(Dispatchers.Default)
novelScope.launch { novelScope.launch {
novelExtensionManager.findAvailableExtensions() novelExtensionManager.findAvailableExtensions()
logger("Novel Extensions: ${novelExtensionManager.installedExtensionsFlow.first()}") Logger.log("Novel Extensions: ${novelExtensionManager.installedExtensionsFlow.first()}")
NovelSources.init(novelExtensionManager.installedExtensionsFlow) NovelSources.init(novelExtensionManager.installedExtensionsFlow)
} }
val commentsScope = CoroutineScope(Dispatchers.Default) val commentsScope = CoroutineScope(Dispatchers.Default)
@ -138,7 +141,8 @@ class App : MultiDexApplication() {
try { try {
Notifications.createChannels(this) Notifications.createChannels(this)
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to modify notification channels" } Logger.log("Failed to modify notification channels")
Logger.log(e)
} }
} }

View file

@ -28,7 +28,6 @@ import android.telephony.TelephonyManager
import android.text.InputFilter import android.text.InputFilter
import android.text.Spanned import android.text.Spanned
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.*
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
@ -60,6 +59,7 @@ import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.settings.saving.internal.PreferenceKeystore import ani.dantotsu.settings.saving.internal.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferenceKeystore.Companion.generateSalt import ani.dantotsu.settings.saving.internal.PreferenceKeystore.Companion.generateSalt
import ani.dantotsu.subcriptions.NotificationClickReceiver import ani.dantotsu.subcriptions.NotificationClickReceiver
import ani.dantotsu.util.Logger
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.RequestManager import com.bumptech.glide.RequestManager
@ -128,13 +128,6 @@ fun currActivity(): Activity? {
var loadMedia: Int? = null var loadMedia: Int? = null
var loadIsMAL = false var loadIsMAL = false
fun logger(e: Any?, print: Boolean = true) {
if (print)
//println(e)
Log.d("Logger", e.toString())
}
fun initActivity(a: Activity) { fun initActivity(a: Activity) {
val window = a.window val window = a.window
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
@ -306,7 +299,7 @@ class InputFilterMinMax(
val input = (dest.toString() + source.toString()).toDouble() val input = (dest.toString() + source.toString()).toDouble()
if (isInRange(min, max, input)) return null if (isInRange(min, max, input)) return null
} catch (nfe: NumberFormatException) { } catch (nfe: NumberFormatException) {
logger(nfe.stackTraceToString()) Logger.log(nfe)
} }
return "" return ""
} }
@ -761,7 +754,7 @@ fun saveImage(image: Bitmap, path: String, imageFileName: String): File? {
private fun scanFile(path: String, context: Context) { private fun scanFile(path: String, context: Context) {
MediaScannerConnection.scanFile(context, arrayOf(path), null) { p, _ -> MediaScannerConnection.scanFile(context, arrayOf(path), null) { p, _ ->
logger("Finished scanning $p") Logger.log("Finished scanning $p")
} }
} }
@ -903,7 +896,7 @@ class EmptyAdapter(private val count: Int) : RecyclerView.Adapter<RecyclerView.V
fun toast(string: String?) { fun toast(string: String?) {
if (string != null) { if (string != null) {
logger(string) Logger.log(string)
MainScope().launch { MainScope().launch {
Toast.makeText(currActivity()?.application ?: return@launch, string, Toast.LENGTH_SHORT) Toast.makeText(currActivity()?.application ?: return@launch, string, Toast.LENGTH_SHORT)
.show() .show()
@ -942,10 +935,10 @@ fun snackString(s: String?, activity: Activity? = null, clipboard: String? = nul
} }
return snackBar return snackBar
} }
logger(s) Logger.log(s)
} }
} catch (e: Exception) { } catch (e: Exception) {
logger(e.stackTraceToString()) Logger.log(e)
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)
} }
return null return null

View file

@ -49,6 +49,7 @@ import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.Logger
import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences

View file

@ -5,6 +5,7 @@ import android.os.Build
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import ani.dantotsu.others.webview.CloudFlare import ani.dantotsu.others.webview.CloudFlare
import ani.dantotsu.others.webview.WebViewBottomDialog import ani.dantotsu.others.webview.WebViewBottomDialog
import ani.dantotsu.util.Logger
import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ResponseParser import com.lagradost.nicehttp.ResponseParser
import com.lagradost.nicehttp.addGenericDns import com.lagradost.nicehttp.addGenericDns
@ -104,6 +105,7 @@ fun logError(e: Throwable, post: Boolean = true, snackbar: Boolean = true) {
toast(e.localizedMessage) toast(e.localizedMessage)
} }
e.printStackTrace() e.printStackTrace()
Logger.log(e)
} }
fun <T> tryWith(post: Boolean = false, snackbar: Boolean = true, call: () -> T): T? { fun <T> tryWith(post: Boolean = false, snackbar: Boolean = true, call: () -> T): T? {

View file

@ -14,6 +14,7 @@ import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.tryWithSuspend import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger
import java.util.Calendar import java.util.Calendar
object Anilist { object Anilist {
@ -150,7 +151,7 @@ object Anilist {
cacheTime = cache ?: 10 cacheTime = cache ?: 10
) )
val remaining = json.headers["X-RateLimit-Remaining"]?.toIntOrNull() ?: -1 val remaining = json.headers["X-RateLimit-Remaining"]?.toIntOrNull() ?: -1
Log.d("AnilistQuery", "Remaining requests: $remaining") Logger.log("Remaining requests: $remaining")
if (json.code == 429) { if (json.code == 429) {
val retry = json.headers["Retry-After"]?.toIntOrNull() ?: -1 val retry = json.headers["Retry-After"]?.toIntOrNull() ?: -1
val passedLimitReset = json.headers["X-RateLimit-Reset"]?.toLongOrNull() ?: 0 val passedLimitReset = json.headers["X-RateLimit-Reset"]?.toLongOrNull() ?: 0
@ -161,13 +162,13 @@ object Anilist {
toast("Rate limited. Try after $retry seconds") toast("Rate limited. Try after $retry seconds")
throw Exception("Rate limited after $retry seconds") throw Exception("Rate limited after $retry seconds")
} }
if (!json.text.startsWith("{")) throw Exception(currContext()?.getString(R.string.anilist_down)) if (!json.text.startsWith("{")) {throw Exception(currContext()?.getString(R.string.anilist_down))}
if (show) println("Response : ${json.text}") if (show) println("Response : ${json.text}")
json.parsed() json.parsed()
} else null } else null
} catch (e: Exception) { } catch (e: Exception) {
if (show) snackString("Error fetching Anilist data: ${e.message}") if (show) snackString("Error fetching Anilist data: ${e.message}")
Log.e("AnilistQuery", "Error: ${e.message}") Logger.log("Anilist Query Error: ${e.message}")
null null
} }
} }

View file

@ -15,6 +15,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.tryWithSuspend import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -99,6 +100,7 @@ class AnilistHomeViewModel : ViewModel() {
suspend fun initHomePage() { suspend fun initHomePage() {
val res = Anilist.query.initHomePage() val res = Anilist.query.initHomePage()
Logger.log("AnilistHomeViewModel : res=$res")
res["currentAnime"]?.let { animeContinue.postValue(it) } res["currentAnime"]?.let { animeContinue.postValue(it) }
res["favoriteAnime"]?.let { animeFav.postValue(it) } res["favoriteAnime"]?.let { animeFav.postValue(it) }
res["plannedAnime"]?.let { animePlanned.postValue(it) } res["plannedAnime"]?.let { animePlanned.postValue(it) }

View file

@ -4,7 +4,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import ani.dantotsu.logError import ani.dantotsu.logError
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.startMainActivity import ani.dantotsu.startMainActivity
@ -16,7 +16,7 @@ class Login : AppCompatActivity() {
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
val data: Uri? = intent?.data val data: Uri? = intent?.data
logger(data.toString()) Logger.log(data.toString())
try { try {
Anilist.token = Anilist.token =
Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value

View file

@ -1,17 +1,18 @@
package ani.dantotsu.connections.crashlytics package ani.dantotsu.connections.crashlytics
import android.content.Context import android.content.Context
import ani.dantotsu.util.Logger
class CrashlyticsStub : CrashlyticsInterface { class CrashlyticsStub : CrashlyticsInterface {
override fun initialize(context: Context) { override fun initialize(context: Context) {
//no-op //no-op
} }
override fun logException(e: Throwable) { override fun logException(e: Throwable) {
//no-op Logger.log(e)
} }
override fun log(message: String) { override fun log(message: String) {
//no-op Logger.log(message)
} }
override fun setUserId(id: String) { override fun setUserId(id: String) {

View file

@ -15,7 +15,6 @@ import android.os.Environment
import android.os.IBinder import android.os.IBinder
import android.os.PowerManager import android.os.PowerManager
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Log
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -26,6 +25,7 @@ import ani.dantotsu.connections.discord.serializers.User
import ani.dantotsu.isOnline import ani.dantotsu.isOnline
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
@ -274,7 +274,7 @@ class DiscordService : Service() {
return return
} }
} }
t.message?.let { Log.d("WebSocket", "onFailure() $it") } t.message?.let { Logger.log("onFailure() $it") }
log("WebSocket: Error, onFailure() reason: ${t.message}") log("WebSocket: Error, onFailure() reason: ${t.message}")
client = OkHttpClient() client = OkHttpClient()
client.newWebSocket( client.newWebSocket(
@ -289,7 +289,7 @@ class DiscordService : Service() {
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
super.onClosing(webSocket, code, reason) super.onClosing(webSocket, code, reason)
Log.d("WebSocket", "onClosing() $code $reason") Logger.log("onClosing() $code $reason")
if (::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) { if (::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) {
heartbeatThread.interrupt() heartbeatThread.interrupt()
} }
@ -297,7 +297,7 @@ class DiscordService : Service() {
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
super.onClosed(webSocket, code, reason) super.onClosed(webSocket, code, reason)
Log.d("WebSocket", "onClosed() $code $reason") Logger.log("onClosed() $code $reason")
if (code >= 4000) { if (code >= 4000) {
log("WebSocket: Error, code: $code reason: $reason") log("WebSocket: Error, code: $code reason: $reason")
client = OkHttpClient() client = OkHttpClient()
@ -382,52 +382,7 @@ class DiscordService : Service() {
} }
fun log(string: String) { fun log(string: String) {
Log.d("WebSocket_Discord", string) //Logger.log(string)
//log += "${SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().time)} $string\n"
}
fun saveLogToFile() {
val fileName = "log_${System.currentTimeMillis()}.txt"
// ContentValues to store file metadata
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.MediaColumns.RELATIVE_PATH, "Download/")
}
}
// Inserting the file in the MediaStore
val resolver = baseContext.contentResolver
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
} else {
val directory =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val file = File(directory, fileName)
// Make sure the Downloads directory exists
if (!directory.exists()) {
directory.mkdirs()
}
// Use FileProvider to get the URI for the file
val authority =
"${baseContext.packageName}.provider" // Adjust with your app's package name
Uri.fromFile(file)
}
// Writing to the file
uri?.let {
resolver.openOutputStream(it).use { outputStream ->
OutputStreamWriter(outputStream).use { writer ->
writer.write(log)
}
}
} ?: run {
log("Error saving log file")
}
} }
fun resume() { fun resume() {

View file

@ -27,7 +27,7 @@ import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.video.ExoplayerDownloadService import ani.dantotsu.download.video.ExoplayerDownloadService
import ani.dantotsu.download.video.Helper import ani.dantotsu.download.video.Helper
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.SubtitleDownloader import ani.dantotsu.media.SubtitleDownloader
import ani.dantotsu.media.anime.AnimeWatchFragment import ani.dantotsu.media.anime.AnimeWatchFragment
@ -249,7 +249,7 @@ class AnimeDownloaderService : Service() {
hasDownloadStarted(downloadManager, task, 30000) // 30 seconds timeout hasDownloadStarted(downloadManager, task, 30000) // 30 seconds timeout
if (!downloadStarted) { if (!downloadStarted) {
logger("Download failed to start") Logger.log("Download failed to start")
builder.setContentText("${task.title} - ${task.episode} Download failed to start") builder.setContentText("${task.title} - ${task.episode} Download failed to start")
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download failed to start") snackString("${task.title} - ${task.episode} Download failed to start")
@ -263,11 +263,11 @@ class AnimeDownloaderService : Service() {
val download = downloadManager.downloadIndex.getDownload(task.video.file.url) val download = downloadManager.downloadIndex.getDownload(task.video.file.url)
if (download != null) { if (download != null) {
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_FAILED) { if (download.state == androidx.media3.exoplayer.offline.Download.STATE_FAILED) {
logger("Download failed") Logger.log("Download failed")
builder.setContentText("${task.title} - ${task.episode} Download failed") builder.setContentText("${task.title} - ${task.episode} Download failed")
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download failed") snackString("${task.title} - ${task.episode} Download failed")
logger("Download failed: ${download.failureReason}") Logger.log("Download failed: ${download.failureReason}")
downloadsManager.removeDownload( downloadsManager.removeDownload(
DownloadedType( DownloadedType(
task.title, task.title,
@ -289,7 +289,7 @@ class AnimeDownloaderService : Service() {
break break
} }
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_COMPLETED) { if (download.state == androidx.media3.exoplayer.offline.Download.STATE_COMPLETED) {
logger("Download completed") Logger.log("Download completed")
builder.setContentText("${task.title} - ${task.episode} Download completed") builder.setContentText("${task.title} - ${task.episode} Download completed")
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download completed") snackString("${task.title} - ${task.episode} Download completed")
@ -309,7 +309,7 @@ class AnimeDownloaderService : Service() {
break break
} }
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_STOPPED) { if (download.state == androidx.media3.exoplayer.offline.Download.STATE_STOPPED) {
logger("Download stopped") Logger.log("Download stopped")
builder.setContentText("${task.title} - ${task.episode} Download stopped") builder.setContentText("${task.title} - ${task.episode} Download stopped")
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download stopped") snackString("${task.title} - ${task.episode} Download stopped")
@ -328,7 +328,7 @@ class AnimeDownloaderService : Service() {
} }
} catch (e: Exception) { } catch (e: Exception) {
if (e.message?.contains("Coroutine was cancelled") == false) { //wut if (e.message?.contains("Coroutine was cancelled") == false) { //wut
logger("Exception while downloading file: ${e.message}") Logger.log("Exception while downloading file: ${e.message}")
snackString("Exception while downloading file: ${e.message}") snackString("Exception while downloading file: ${e.message}")
e.printStackTrace() e.printStackTrace()
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)

View file

@ -33,7 +33,7 @@ import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
@ -318,8 +318,8 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
val mediaJson = media.readText() val mediaJson = media.readText()
gson.fromJson(mediaJson, Media::class.java) gson.fromJson(mediaJson, Media::class.java)
} catch (e: Exception) { } catch (e: Exception) {
logger("Error loading media.json: ${e.message}") Logger.log("Error loading media.json: ${e.message}")
logger(e.printStackTrace()) Logger.log(e)
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)
null null
} }
@ -374,8 +374,8 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
bannerUri bannerUri
) )
} catch (e: Exception) { } catch (e: Exception) {
logger("Error loading media.json: ${e.message}") Logger.log("Error loading media.json: ${e.message}")
logger(e.printStackTrace()) Logger.log(e)
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)
return OfflineAnimeModel( return OfflineAnimeModel(
"unknown", "unknown",

View file

@ -21,7 +21,7 @@ import ani.dantotsu.R
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.manga.ImageData import ani.dantotsu.media.manga.ImageData
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FAILED import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FAILED
@ -251,7 +251,7 @@ class MangaDownloaderService : Service() {
snackString("${task.title} - ${task.chapter} Download finished") snackString("${task.title} - ${task.chapter} Download finished")
} }
} catch (e: Exception) { } catch (e: Exception) {
logger("Exception while downloading file: ${e.message}") Logger.log("Exception while downloading file: ${e.message}")
snackString("Exception while downloading file: ${e.message}") snackString("Exception while downloading file: ${e.message}")
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)
broadcastDownloadFailed(task.chapter) broadcastDownloadFailed(task.chapter)

View file

@ -30,7 +30,7 @@ import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
@ -308,8 +308,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
val mediaJson = media.readText() val mediaJson = media.readText()
gson.fromJson(mediaJson, Media::class.java) gson.fromJson(mediaJson, Media::class.java)
} catch (e: Exception) { } catch (e: Exception) {
logger("Error loading media.json: ${e.message}") Logger.log("Error loading media.json: ${e.message}")
logger(e.printStackTrace()) Logger.log(e)
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)
null null
} }
@ -358,8 +358,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
bannerUri bannerUri
) )
} catch (e: Exception) { } catch (e: Exception) {
logger("Error loading media.json: ${e.message}") Logger.log("Error loading media.json: ${e.message}")
logger(e.printStackTrace()) Logger.log(e)
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)
return OfflineMangaModel( return OfflineMangaModel(
"unknown", "unknown",

View file

@ -20,7 +20,7 @@ import ani.dantotsu.R
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.novel.NovelReadFragment import ani.dantotsu.media.novel.NovelReadFragment
import ani.dantotsu.snackString import ani.dantotsu.snackString
@ -186,15 +186,15 @@ class NovelDownloaderService : Service() {
val contentType = response.header("Content-Type") val contentType = response.header("Content-Type")
val contentDisposition = response.header("Content-Disposition") val contentDisposition = response.header("Content-Disposition")
logger("Content-Type: $contentType") Logger.log("Content-Type: $contentType")
logger("Content-Disposition: $contentDisposition") Logger.log("Content-Disposition: $contentDisposition")
// Return true if the Content-Type or Content-Disposition indicates an EPUB file // Return true if the Content-Type or Content-Disposition indicates an EPUB file
contentType == "application/epub+zip" || contentType == "application/epub+zip" ||
(contentDisposition?.contains(".epub") == true) (contentDisposition?.contains(".epub") == true)
} }
} catch (e: Exception) { } catch (e: Exception) {
logger("Error checking file type: ${e.message}") Logger.log("Error checking file type: ${e.message}")
false false
} }
} }
@ -225,12 +225,12 @@ class NovelDownloaderService : Service() {
if (!isEpubFile(task.downloadLink)) { if (!isEpubFile(task.downloadLink)) {
if (isAlreadyDownloaded(task.originalLink)) { if (isAlreadyDownloaded(task.originalLink)) {
logger("Already downloaded") Logger.log("Already downloaded")
broadcastDownloadFinished(task.originalLink) broadcastDownloadFinished(task.originalLink)
snackString("Already downloaded") snackString("Already downloaded")
return@withContext return@withContext
} }
logger("Download link is not an .epub file") Logger.log("Download link is not an .epub file")
broadcastDownloadFailed(task.originalLink) broadcastDownloadFailed(task.originalLink)
snackString("Download link is not an .epub file") snackString("Download link is not an .epub file")
return@withContext return@withContext
@ -301,7 +301,7 @@ class NovelDownloaderService : Service() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val progress = val progress =
(downloadedBytes * 100 / totalBytes).toInt() (downloadedBytes * 100 / totalBytes).toInt()
logger("Download progress: $progress") Logger.log("Download progress: $progress")
broadcastDownloadProgress(task.originalLink, progress) broadcastDownloadProgress(task.originalLink, progress)
} }
lastBroadcastUpdate = downloadedBytes lastBroadcastUpdate = downloadedBytes
@ -316,7 +316,7 @@ class NovelDownloaderService : Service() {
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
logger("Exception while downloading .epub inside request: ${e.message}") Logger.log("Exception while downloading .epub inside request: ${e.message}")
throw e throw e
} }
} }
@ -340,7 +340,7 @@ class NovelDownloaderService : Service() {
snackString("${task.title} - ${task.chapter} Download finished") snackString("${task.title} - ${task.chapter} Download finished")
} }
} catch (e: Exception) { } catch (e: Exception) {
logger("Exception while downloading .epub: ${e.message}") Logger.log("Exception while downloading .epub: ${e.message}")
snackString("Exception while downloading .epub: ${e.message}") snackString("Exception while downloading .epub: ${e.message}")
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)
broadcastDownloadFailed(task.originalLink) broadcastDownloadFailed(task.originalLink)

View file

@ -43,6 +43,7 @@ import ani.dantotsu.parsers.SubtitleType
import ani.dantotsu.parsers.Video import ani.dantotsu.parsers.Video
import ani.dantotsu.parsers.VideoType import ani.dantotsu.parsers.VideoType
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -157,15 +158,15 @@ object Helper {
finalException: Exception? finalException: Exception?
) { ) {
if (download.state == Download.STATE_COMPLETED) { if (download.state == Download.STATE_COMPLETED) {
Log.e("Downloader", "Download Completed") Logger.log("Download Completed")
} else if (download.state == Download.STATE_FAILED) { } else if (download.state == Download.STATE_FAILED) {
Log.e("Downloader", "Download Failed") Logger.log("Download Failed")
} else if (download.state == Download.STATE_STOPPED) { } else if (download.state == Download.STATE_STOPPED) {
Log.e("Downloader", "Download Stopped") Logger.log("Download Stopped")
} else if (download.state == Download.STATE_QUEUED) { } else if (download.state == Download.STATE_QUEUED) {
Log.e("Downloader", "Download Queued") Logger.log("Download Queued")
} else if (download.state == Download.STATE_DOWNLOADING) { } else if (download.state == Download.STATE_DOWNLOADING) {
Log.e("Downloader", "Download Downloading") Logger.log("Download Downloading")
} }
} }
} }

View file

@ -9,7 +9,7 @@ import androidx.lifecycle.ViewModel
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.anime.Episode import ani.dantotsu.media.anime.Episode
import ani.dantotsu.media.anime.SelectorDialogFragment import ani.dantotsu.media.anime.SelectorDialogFragment
import ani.dantotsu.media.manga.MangaChapter import ani.dantotsu.media.manga.MangaChapter
@ -223,7 +223,7 @@ class MediaDetailsViewModel : ViewModel() {
} }
fun setEpisode(ep: Episode?, who: String) { fun setEpisode(ep: Episode?, who: String) {
logger("set episode ${ep?.number} - $who", false) Logger.log("set episode ${ep?.number} - $who")
episode.postValue(ep) episode.postValue(ep)
MainScope().launch(Dispatchers.Main) { MainScope().launch(Dispatchers.Main) {
episode.value = null episode.value = null
@ -270,7 +270,7 @@ class MediaDetailsViewModel : ViewModel() {
mangaChapters mangaChapters
suspend fun loadMangaChapters(media: Media, i: Int, invalidate: Boolean = false) { suspend fun loadMangaChapters(media: Media, i: Int, invalidate: Boolean = false) {
logger("Loading Manga Chapters : $mangaLoaded") Logger.log("Loading Manga Chapters : $mangaLoaded")
if (!mangaLoaded.containsKey(i) || invalidate) tryWithSuspend { if (!mangaLoaded.containsKey(i) || invalidate) tryWithSuspend {
mangaLoaded[i] = mangaLoaded[i] =
mangaReadSources?.loadChaptersFromMedia(i, media) ?: return@tryWithSuspend mangaReadSources?.loadChaptersFromMedia(i, media) ?: return@tryWithSuspend

View file

@ -86,6 +86,7 @@ import ani.dantotsu.settings.PlayerSettingsActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.Logger
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.CastContext import com.google.android.gms.cast.framework.CastContext
@ -1372,8 +1373,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
mediaItem = if (downloadedMediaItem == null) { mediaItem = if (downloadedMediaItem == null) {
val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType) val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
logger("url: ${video!!.file.url}") Logger.log("url: ${video!!.file.url}")
logger("mimeType: $mimeType") Logger.log("mimeType: $mimeType")
if (sub != null) { if (sub != null) {
val listofnotnullsubs = listOfNotNull(sub) val listofnotnullsubs = listOfNotNull(sub)

View file

@ -10,7 +10,7 @@ import android.os.Build
import android.os.Environment import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import android.util.LruCache import android.util.LruCache
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.snackString import ani.dantotsu.snackString
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
@ -32,8 +32,8 @@ data class ImageData(
try { try {
// Fetch the image // Fetch the image
val response = httpSource.getImage(page) val response = httpSource.getImage(page)
logger("Response: ${response.code}") Logger.log("Response: ${response.code}")
logger("Response: ${response.message}") Logger.log("Response: ${response.message}")
// Convert the Response to an InputStream // Convert the Response to an InputStream
val inputStream = response.body.byteStream() val inputStream = response.body.byteStream()
@ -47,7 +47,7 @@ data class ImageData(
return@withContext bitmap return@withContext bitmap
} catch (e: Exception) { } catch (e: Exception) {
// Handle any exceptions // Handle any exceptions
logger("An error occurred: ${e.message}") Logger.log("An error occurred: ${e.message}")
snackString("An error occurred: ${e.message}") snackString("An error occurred: ${e.message}")
return@withContext null return@withContext null
} }

View file

@ -31,6 +31,7 @@ import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.novel.novelreader.NovelReaderActivity import ani.dantotsu.media.novel.novelreader.NovelReaderActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.parsers.ShowResponse import ani.dantotsu.parsers.ShowResponse
import ani.dantotsu.util.Logger
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -59,7 +60,7 @@ class NovelReadFragment : Fragment(),
var loaded = false var loaded = false
override fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage) { override fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage) {
Log.e("downloadTrigger", novelDownloadPackage.link) Logger.log("novel link: ${novelDownloadPackage.link}")
val downloadTask = NovelDownloaderService.DownloadTask( val downloadTask = NovelDownloaderService.DownloadTask(
title = media.mainName(), title = media.mainName(),
chapter = novelDownloadPackage.novelName, chapter = novelDownloadPackage.novelName,

View file

@ -11,6 +11,7 @@ import ani.dantotsu.databinding.ItemNovelResponseBinding
import ani.dantotsu.parsers.ShowResponse import ani.dantotsu.parsers.ShowResponse
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
@ -181,7 +182,7 @@ class NovelResponseAdapter(
if (position != -1) { if (position != -1) {
list[position].extra?.remove("0") list[position].extra?.remove("0")
list[position].extra?.set("0", "Downloading: $progress%") list[position].extra?.set("0", "Downloading: $progress%")
Log.d("NovelResponseAdapter", "updateDownloadProgress: $progress, position: $position") Logger.log( "updateDownloadProgress: $progress, position: $position")
notifyItemChanged(position) notifyItemChanged(position)
} }
} }

View file

@ -1,6 +1,6 @@
package ani.dantotsu.others package ani.dantotsu.others
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.math.pow import kotlin.math.pow
@ -64,7 +64,7 @@ class JsUnpacker(packedJS: String?) {
return decoded.toString() return decoded.toString()
} }
} catch (e: Exception) { } catch (e: Exception) {
logger(e) Logger.log(e)
} }
return null return null
} }

View file

@ -2,7 +2,7 @@ package ani.dantotsu.others
import ani.dantotsu.FileUrl import ani.dantotsu.FileUrl
import ani.dantotsu.client import ani.dantotsu.client
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.anime.Episode import ani.dantotsu.media.anime.Episode
import ani.dantotsu.tryWithSuspend import ani.dantotsu.tryWithSuspend
@ -41,8 +41,7 @@ object Kitsu {
} }
suspend fun getKitsuEpisodesDetails(media: Media): Map<String, Episode>? { suspend fun getKitsuEpisodesDetails(media: Media): Map<String, Episode>? {
val print = false Logger.log("Kitsu : title=${media.mainName()}")
logger("Kitsu : title=${media.mainName()}", print)
val query = val query =
""" """
query { query {
@ -70,7 +69,7 @@ query {
val result = getKitsuData(query) ?: return null val result = getKitsuData(query) ?: return null
logger("Kitsu : result=$result", print) //Logger.log("Kitsu : result=$result")
media.idKitsu = result.data?.lookupMapping?.id media.idKitsu = result.data?.lookupMapping?.id
val a = (result.data?.lookupMapping?.episodes?.nodes ?: return null).mapNotNull { ep -> val a = (result.data?.lookupMapping?.episodes?.nodes ?: return null).mapNotNull { ep ->
val num = ep?.number?.toString() ?: return@mapNotNull null val num = ep?.number?.toString() ?: return@mapNotNull null
@ -81,7 +80,7 @@ query {
thumb = FileUrl[ep.thumbnail?.original?.url], thumb = FileUrl[ep.thumbnail?.original?.url],
) )
}.toMap() }.toMap()
logger("Kitsu : a=$a", print) //Logger.log("Kitsu : a=$a")
return a return a
} }

View file

@ -11,7 +11,7 @@ import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import ani.dantotsu.FileUrl import ani.dantotsu.FileUrl
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.anime.AnimeNameAdapter import ani.dantotsu.media.anime.AnimeNameAdapter
import ani.dantotsu.media.manga.ImageData import ani.dantotsu.media.manga.ImageData
import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.media.manga.MangaCache
@ -129,7 +129,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
?: return false ?: return false
currContext()?.let { context -> currContext()?.let { context ->
logger("isDubAvailableSeparately: ${configurableSource.getPreferenceKey()}") Logger.log("isDubAvailableSeparately: ${configurableSource.getPreferenceKey()}")
val sharedPreferences = val sharedPreferences =
context.getSharedPreferences( context.getSharedPreferences(
configurableSource.getPreferenceKey(), configurableSource.getPreferenceKey(),
@ -205,7 +205,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
} }
return sortedEpisodes.map { SEpisodeToEpisode(it) } return sortedEpisodes.map { SEpisodeToEpisode(it) }
} catch (e: Exception) { } catch (e: Exception) {
logger("Exception: $e") Logger.log("Exception: $e")
} }
return emptyList() return emptyList()
} }
@ -240,7 +240,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
val videos = source.getVideoList(sEpisode) val videos = source.getVideoList(sEpisode)
videos.map { VideoToVideoServer(it) } videos.map { VideoToVideoServer(it) }
} catch (e: Exception) { } catch (e: Exception) {
logger("Exception occurred: ${e.message}") Logger.log("Exception occurred: ${e.message}")
emptyList() emptyList()
} }
} }
@ -260,16 +260,16 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
?: return emptyList()) ?: return emptyList())
return try { return try {
val res = source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle() val res = source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle()
logger("query: $query") Logger.log("query: $query")
convertAnimesPageToShowResponse(res) convertAnimesPageToShowResponse(res)
} catch (e: CloudflareBypassException) { } catch (e: CloudflareBypassException) {
logger("Exception in search: $e") Logger.log("Exception in search: $e")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
snackString("Failed to bypass Cloudflare") snackString("Failed to bypass Cloudflare")
} }
emptyList() emptyList()
} catch (e: Exception) { } catch (e: Exception) {
logger("General exception in search: $e") Logger.log("General exception in search: $e")
emptyList() emptyList()
} }
} }
@ -358,12 +358,12 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
val res = source.getChapterList(sManga) val res = source.getChapterList(sManga)
val reversedRes = res.reversed() val reversedRes = res.reversed()
val chapterList = reversedRes.map { SChapterToMangaChapter(it) } val chapterList = reversedRes.map { SChapterToMangaChapter(it) }
logger("chapterList size: ${chapterList.size}") Logger.log("chapterList size: ${chapterList.size}")
logger("chapterList: ${chapterList[1].title}") Logger.log("chapterList: ${chapterList[1].title}")
logger("chapterList: ${chapterList[1].description}") Logger.log("chapterList: ${chapterList[1].description}")
chapterList chapterList
} catch (e: Exception) { } catch (e: Exception) {
logger("loadChapters Exception: $e") Logger.log("loadChapters Exception: $e")
emptyList() emptyList()
} }
} }
@ -379,7 +379,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
var imageDataList: List<ImageData> = listOf() var imageDataList: List<ImageData> = listOf()
val ret = coroutineScope { val ret = coroutineScope {
try { try {
logger("source.name " + source.name) Logger.log("source.name " + source.name)
val res = source.getPageList(sChapter) val res = source.getPageList(sChapter)
val reIndexedPages = val reIndexedPages =
res.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) } res.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) }
@ -388,7 +388,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
async(Dispatchers.IO) { async(Dispatchers.IO) {
mangaCache.put(page.imageUrl ?: "", ImageData(page, source)) mangaCache.put(page.imageUrl ?: "", ImageData(page, source))
imageDataList += ImageData(page, source) imageDataList += ImageData(page, source)
logger("put page: ${page.imageUrl}") Logger.log("put page: ${page.imageUrl}")
pageToMangaImage(page) pageToMangaImage(page)
} }
} }
@ -396,7 +396,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
deferreds.awaitAll() deferreds.awaitAll()
} catch (e: Exception) { } catch (e: Exception) {
logger("loadImages Exception: $e") Logger.log("loadImages Exception: $e")
snackString("Failed to load images: $e") snackString("Failed to load images: $e")
emptyList() emptyList()
} }
@ -414,7 +414,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
return coroutineScope { return coroutineScope {
try { try {
logger("source.name " + source.name) Logger.log("source.name " + source.name)
val res = source.getPageList(sChapter) val res = source.getPageList(sChapter)
val reIndexedPages = val reIndexedPages =
res.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) } res.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) }
@ -430,7 +430,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
deferreds.awaitAll() deferreds.awaitAll()
} catch (e: Exception) { } catch (e: Exception) {
logger("loadImages Exception: $e") Logger.log("loadImages Exception: $e")
snackString("Failed to load images: $e") snackString("Failed to load images: $e")
emptyList() emptyList()
} }
@ -446,8 +446,8 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
try { try {
// Fetch the image // Fetch the image
val response = httpSource.getImage(page) val response = httpSource.getImage(page)
logger("Response: ${response.code}") Logger.log("Response: ${response.code}")
logger("Response: ${response.message}") Logger.log("Response: ${response.message}")
// Convert the Response to an InputStream // Convert the Response to an InputStream
val inputStream = response.body.byteStream() val inputStream = response.body.byteStream()
@ -467,7 +467,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
return@withContext bitmap return@withContext bitmap
} catch (e: Exception) { } catch (e: Exception) {
// Handle any exceptions // Handle any exceptions
logger("An error occurred: ${e.message}") Logger.log("An error occurred: ${e.message}")
return@withContext null return@withContext null
} }
} }
@ -500,7 +500,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
inputStream.close() inputStream.close()
} catch (e: Exception) { } catch (e: Exception) {
// Handle any exceptions // Handle any exceptions
logger("An error occurred: ${e.message}") Logger.log("An error occurred: ${e.message}")
} }
} }
} }
@ -547,7 +547,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
} }
} catch (e: Exception) { } catch (e: Exception) {
// Handle exception here // Handle exception here
logger("Exception while saving image: ${e.message}") Logger.log("Exception while saving image: ${e.message}")
} }
} }
@ -562,16 +562,16 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
return try { return try {
val res = source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle() val res = source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
logger("res observable: $res") Logger.log("res observable: $res")
convertMangasPageToShowResponse(res) convertMangasPageToShowResponse(res)
} catch (e: CloudflareBypassException) { } catch (e: CloudflareBypassException) {
logger("Exception in search: $e") Logger.log("Exception in search: $e")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
snackString("Failed to bypass Cloudflare") snackString("Failed to bypass Cloudflare")
} }
emptyList() emptyList()
} catch (e: Exception) { } catch (e: Exception) {
logger("General exception in search: $e") Logger.log("General exception in search: $e")
emptyList() emptyList()
} }
} }
@ -714,7 +714,7 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
// If the format is still undetermined, log an error // If the format is still undetermined, log an error
if (format == null) { if (format == null) {
logger("Unknown video format: $videoUrl") Logger.log("Unknown video format: $videoUrl")
format = VideoType.CONTAINER format = VideoType.CONTAINER
} }
val headersMap: Map<String, String> = val headersMap: Map<String, String> =
@ -746,7 +746,7 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
private fun headRequest(fileName: String, networkHelper: NetworkHelper): VideoType? { private fun headRequest(fileName: String, networkHelper: NetworkHelper): VideoType? {
return try { return try {
logger("attempting head request for $fileName") Logger.log("attempting head request for $fileName")
val request = Request.Builder() val request = Request.Builder()
.url(fileName) .url(fileName)
.head() .head()
@ -771,13 +771,13 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
else -> null else -> null
} }
} else { } else {
logger("failed head request for $fileName") Logger.log("failed head request for $fileName")
null null
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
logger("Exception in headRequest: $e") Logger.log("Exception in headRequest: $e")
null null
} }

View file

@ -3,7 +3,7 @@ package ani.dantotsu.parsers
import ani.dantotsu.FileUrl import ani.dantotsu.FileUrl
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
@ -59,11 +59,11 @@ abstract class BaseParser {
saveShowResponse(mediaObj.id, response, true) saveShowResponse(mediaObj.id, response, true)
} else { } else {
setUserText("Searching : ${mediaObj.mainName()}") setUserText("Searching : ${mediaObj.mainName()}")
logger("Searching : ${mediaObj.mainName()}") Logger.log("Searching : ${mediaObj.mainName()}")
val results = search(mediaObj.mainName()) val results = search(mediaObj.mainName())
//log all results //log all results
results.forEach { results.forEach {
logger("Result: ${it.name}") Logger.log("Result: ${it.name}")
} }
val sortedResults = if (results.isNotEmpty()) { val sortedResults = if (results.isNotEmpty()) {
results.sortedByDescending { results.sortedByDescending {
@ -83,7 +83,7 @@ abstract class BaseParser {
) < 100 ) < 100
) { ) {
setUserText("Searching : ${mediaObj.nameRomaji}") setUserText("Searching : ${mediaObj.nameRomaji}")
logger("Searching : ${mediaObj.nameRomaji}") Logger.log("Searching : ${mediaObj.nameRomaji}")
val romajiResults = search(mediaObj.nameRomaji) val romajiResults = search(mediaObj.nameRomaji)
val sortedRomajiResults = if (romajiResults.isNotEmpty()) { val sortedRomajiResults = if (romajiResults.isNotEmpty()) {
romajiResults.sortedByDescending { romajiResults.sortedByDescending {
@ -96,10 +96,10 @@ abstract class BaseParser {
emptyList() emptyList()
} }
val closestRomaji = sortedRomajiResults.firstOrNull() val closestRomaji = sortedRomajiResults.firstOrNull()
logger("Closest match from RomajiResults: ${closestRomaji?.name ?: "None"}") Logger.log("Closest match from RomajiResults: ${closestRomaji?.name ?: "None"}")
response = if (response == null) { response = if (response == null) {
logger("No exact match found in results. Using closest match from RomajiResults.") Logger.log("No exact match found in results. Using closest match from RomajiResults.")
closestRomaji closestRomaji
} else { } else {
val romajiRatio = FuzzySearch.ratio( val romajiRatio = FuzzySearch.ratio(
@ -110,14 +110,14 @@ abstract class BaseParser {
response.name.lowercase(), response.name.lowercase(),
mediaObj.mainName().lowercase() mediaObj.mainName().lowercase()
) )
logger("Fuzzy ratio for closest match in results: $mainNameRatio for ${response.name.lowercase()}") Logger.log("Fuzzy ratio for closest match in results: $mainNameRatio for ${response.name.lowercase()}")
logger("Fuzzy ratio for closest match in RomajiResults: $romajiRatio for ${closestRomaji?.name?.lowercase() ?: "None"}") Logger.log("Fuzzy ratio for closest match in RomajiResults: $romajiRatio for ${closestRomaji?.name?.lowercase() ?: "None"}")
if (romajiRatio > mainNameRatio) { if (romajiRatio > mainNameRatio) {
logger("RomajiResults has a closer match. Replacing response.") Logger.log("RomajiResults has a closer match. Replacing response.")
closestRomaji closestRomaji
} else { } else {
logger("Results has a closer or equal match. Keeping existing response.") Logger.log("Results has a closer or equal match. Keeping existing response.")
response response
} }
} }

View file

@ -1,7 +1,7 @@
package ani.dantotsu.parsers package ani.dantotsu.parsers
import ani.dantotsu.Lazier import ani.dantotsu.Lazier
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.anime.Episode import ani.dantotsu.media.anime.Episode
import ani.dantotsu.media.manga.MangaChapter import ani.dantotsu.media.manga.MangaChapter
@ -96,7 +96,7 @@ abstract class MangaReadSources : BaseSources() {
} }
//must be downloaded //must be downloaded
if (show.sManga == null) { if (show.sManga == null) {
logger("sManga is null") Logger.log("sManga is null")
} }
if (parser is OfflineMangaParser && show.sManga == null) { if (parser is OfflineMangaParser && show.sManga == null) {
tryWithSuspend(true) { tryWithSuspend(true) {
@ -106,11 +106,11 @@ abstract class MangaReadSources : BaseSources() {
} }
} }
} else { } else {
logger("Parser is not an instance of OfflineMangaParser") Logger.log("Parser is not an instance of OfflineMangaParser")
} }
logger("map size ${map.size}") Logger.log("map size ${map.size}")
return map return map
} }
} }

View file

@ -6,6 +6,7 @@ import ani.dantotsu.parsers.novel.DynamicNovelParser
import ani.dantotsu.parsers.novel.NovelExtension import ani.dantotsu.parsers.novel.NovelExtension
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -47,8 +48,8 @@ object NovelSources : NovelReadSources() {
} }
private fun createParsersFromExtensions(extensions: List<NovelExtension.Installed>): List<Lazier<BaseParser>> { private fun createParsersFromExtensions(extensions: List<NovelExtension.Installed>): List<Lazier<BaseParser>> {
Log.d("NovelSources", "createParsersFromExtensions") Logger.log("createParsersFromExtensions")
Log.d("NovelSources", extensions.toString()) Logger.log(extensions.toString())
return extensions.map { extension -> return extensions.map { extension ->
val name = extension.name val name = extension.name
Lazier({ DynamicNovelParser(extension) }, name) Lazier({ DynamicNovelParser(extension) }, name)

View file

@ -3,7 +3,7 @@ package ani.dantotsu.parsers
import android.os.Environment import android.os.Environment
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.media.manga.MangaNameAdapter import ani.dantotsu.media.manga.MangaNameAdapter
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@ -68,7 +68,7 @@ class OfflineMangaParser : MangaParser() {
matchResult?.groups?.get(1)?.value?.toIntOrNull() ?: Int.MAX_VALUE matchResult?.groups?.get(1)?.value?.toIntOrNull() ?: Int.MAX_VALUE
} }
for (image in images) { for (image in images) {
logger("imageNumber: ${image.url.url}") Logger.log("imageNumber: ${image.url.url}")
} }
return images return images
} }

View file

@ -1,6 +1,6 @@
package ani.dantotsu.parsers package ani.dantotsu.parsers
import ani.dantotsu.logger import ani.dantotsu.util.Logger
class StringMatcher { class StringMatcher {
companion object { companion object {
@ -54,10 +54,10 @@ class StringMatcher {
val closestShowAndIndex = closestShow(target, shows) val closestShowAndIndex = closestShow(target, shows)
val closestIndex = closestShowAndIndex.second val closestIndex = closestShowAndIndex.second
if (closestIndex == -1) { if (closestIndex == -1) {
logger("No closest show found for $target") Logger.log("No closest show found for $target")
return shows // Return original list if no closest show found return shows // Return original list if no closest show found
} }
logger("Closest show found for $target is ${closestShowAndIndex.first.name}") Logger.log("Closest show found for $target is ${closestShowAndIndex.first.name}")
return listOf(shows[closestIndex]) + shows.subList(0, closestIndex) + shows.subList( return listOf(shows[closestIndex]) + shows.subList(0, closestIndex) + shows.subList(
closestIndex + 1, closestIndex + 1,
shows.size shows.size

View file

@ -2,7 +2,7 @@ package ani.dantotsu.parsers.novel
import android.content.Context import android.content.Context
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
@ -14,9 +14,7 @@ import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import logcat.LogPriority
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date import java.util.Date
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
@ -41,20 +39,20 @@ class NovelExtensionGithubApi {
.newCall(GET("${REPO_URL_PREFIX}index.min.json")) .newCall(GET("${REPO_URL_PREFIX}index.min.json"))
.awaitSuccess() .awaitSuccess()
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Failed to get extensions from GitHub" } Logger.log("Failed to get extensions from GitHub")
requiresFallbackSource = true requiresFallbackSource = true
null null
} }
} }
val response = githubResponse ?: run { val response = githubResponse ?: run {
logger("using fallback source") Logger.log("using fallback source")
networkService.client networkService.client
.newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json")) .newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json"))
.awaitSuccess() .awaitSuccess()
} }
logger("response: $response") Logger.log("response: $response")
val extensions = with(json) { val extensions = with(json) {
response response
@ -67,7 +65,7 @@ class NovelExtensionGithubApi {
/*if (extensions.size < 10) { //TODO: uncomment when more extensions are added /*if (extensions.size < 10) { //TODO: uncomment when more extensions are added
throw Exception() throw Exception()
}*/ }*/
logger("extensions: $extensions") Logger.log("extensions: $extensions")
extensions extensions
} }
} }

View file

@ -3,6 +3,7 @@ package ani.dantotsu.parsers.novel
import android.os.FileObserver import android.os.FileObserver
import android.util.Log import android.util.Log
import ani.dantotsu.parsers.novel.FileObserver.fileObserver import ani.dantotsu.parsers.novel.FileObserver.fileObserver
import ani.dantotsu.util.Logger
import java.io.File import java.io.File
@ -22,24 +23,24 @@ class NovelExtensionFileObserver(private val listener: Listener, private val pat
override fun onEvent(event: Int, file: String?) { override fun onEvent(event: Int, file: String?) {
Log.e("NovelExtensionFileObserver", "Event: $event") Logger.log("Event: $event")
if (file == null) return if (file == null) return
val fullPath = File(path, file) val fullPath = File(path, file)
when (event) { when (event) {
CREATE -> { CREATE -> {
Log.e("NovelExtensionFileObserver", "File created: $fullPath") Logger.log("File created: $fullPath")
listener.onExtensionFileCreated(fullPath) listener.onExtensionFileCreated(fullPath)
} }
DELETE -> { DELETE -> {
Log.e("NovelExtensionFileObserver", "File deleted: $fullPath") Logger.log("File deleted: $fullPath")
listener.onExtensionFileDeleted(fullPath) listener.onExtensionFileDeleted(fullPath)
} }
MODIFY -> { MODIFY -> {
Log.e("NovelExtensionFileObserver", "File modified: $fullPath") Logger.log("File modified: $fullPath")
listener.onExtensionFileModified(fullPath) listener.onExtensionFileModified(fullPath)
} }
} }

View file

@ -15,13 +15,12 @@ import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.net.toUri import androidx.core.net.toUri
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
import logcat.LogPriority
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import tachiyomi.core.util.system.logcat
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
@ -77,12 +76,12 @@ internal class NovelExtensionInstaller(private val context: Context) {
val fileToDelete = File("$sourcePath/${url.toUri().lastPathSegment}") val fileToDelete = File("$sourcePath/${url.toUri().lastPathSegment}")
if (fileToDelete.exists()) { if (fileToDelete.exists()) {
if (fileToDelete.delete()) { if (fileToDelete.delete()) {
Log.i("Install APK", "APK file deleted successfully.") Logger.log("APK file deleted successfully.")
} else { } else {
Log.e("Install APK", "Failed to delete APK file.") Logger.log("Failed to delete APK file.")
} }
} else { } else {
Log.e("Install APK", "APK file not found.") Logger.log("APK file not found.")
} }
// Register the receiver after removing (and unregistering) the previous download // Register the receiver after removing (and unregistering) the previous download
@ -161,7 +160,7 @@ internal class NovelExtensionInstaller(private val context: Context) {
// Check if source path is obtained correctly // Check if source path is obtained correctly
if (sourcePath == null) { if (sourcePath == null) {
Log.e("Install APK", "Source APK path not found.") Logger.log("Source APK path not found.")
downloadsRelay.call(downloadId to InstallStep.Error) downloadsRelay.call(downloadId to InstallStep.Error)
return InstallStep.Error return InstallStep.Error
} }
@ -172,14 +171,14 @@ internal class NovelExtensionInstaller(private val context: Context) {
destinationDir.mkdirs() destinationDir.mkdirs()
} }
if (destinationDir?.setWritable(true) == false) { if (destinationDir?.setWritable(true) == false) {
Log.e("Install APK", "Failed to set destinationDir to writable.") Logger.log("Failed to set destinationDir to writable.")
downloadsRelay.call(downloadId to InstallStep.Error) downloadsRelay.call(downloadId to InstallStep.Error)
return InstallStep.Error return InstallStep.Error
} }
// Copy the file to the new location // Copy the file to the new location
copyFileToInternalStorage(sourcePath, destinationPath) copyFileToInternalStorage(sourcePath, destinationPath)
Log.i("Install APK", "APK moved to $destinationPath") Logger.log("APK moved to $destinationPath")
downloadsRelay.call(downloadId to InstallStep.Installed) downloadsRelay.call(downloadId to InstallStep.Installed)
return InstallStep.Installed return InstallStep.Installed
} }
@ -198,9 +197,9 @@ internal class NovelExtensionInstaller(private val context: Context) {
val fileToDelete = File(apkPath) val fileToDelete = File(apkPath)
//give write permission to the file //give write permission to the file
if (fileToDelete.exists() && !fileToDelete.canWrite()) { if (fileToDelete.exists() && !fileToDelete.canWrite()) {
Log.i("Uninstall APK", "File is not writable. Giving write permission.") Logger.log("File is not writable. Giving write permission.")
val a = fileToDelete.setWritable(true) val a = fileToDelete.setWritable(true)
Log.i("Uninstall APK", "Success: $a") Logger.log("Success: $a")
} }
//set the directory to writable //set the directory to writable
val destinationDir = File(apkPath).parentFile val destinationDir = File(apkPath).parentFile
@ -208,27 +207,27 @@ internal class NovelExtensionInstaller(private val context: Context) {
destinationDir.mkdirs() destinationDir.mkdirs()
} }
val s = destinationDir?.setWritable(true) val s = destinationDir?.setWritable(true)
Log.i("Uninstall APK", "Success destinationDir: $s") Logger.log("Success destinationDir: $s")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try { try {
Files.delete(fileToDelete.toPath()) Files.delete(fileToDelete.toPath())
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Uninstall APK", "Failed to delete APK file.") Logger.log("Failed to delete APK file.")
Log.e("Uninstall APK", e.toString()) Logger.log(e)
snackString("Failed to delete APK file.") snackString("Failed to delete APK file.")
} }
} else { } else {
if (fileToDelete.exists()) { if (fileToDelete.exists()) {
if (fileToDelete.delete()) { if (fileToDelete.delete()) {
Log.i("Uninstall APK", "APK file deleted successfully.") Logger.log("APK file deleted successfully.")
snackString("APK file deleted successfully.") snackString("APK file deleted successfully.")
} else { } else {
Log.e("Uninstall APK", "Failed to delete APK file.") Logger.log("Failed to delete APK file.")
snackString("Failed to delete APK file.") snackString("Failed to delete APK file.")
} }
} else { } else {
Log.e("Uninstall APK", "APK file not found.") Logger.log("APK file not found.")
snackString("APK file not found.") snackString("APK file not found.")
} }
} }
@ -242,9 +241,9 @@ internal class NovelExtensionInstaller(private val context: Context) {
//delete the file if it already exists //delete the file if it already exists
if (destination.exists()) { if (destination.exists()) {
if (destination.delete()) { if (destination.delete()) {
Log.i("File Copy", "File deleted successfully.") Logger.log("File deleted successfully.")
} else { } else {
Log.e("File Copy", "Failed to delete file.") Logger.log("Failed to delete file.")
} }
} }
@ -262,7 +261,7 @@ internal class NovelExtensionInstaller(private val context: Context) {
outputChannel?.close() outputChannel?.close()
} }
Log.i("File Copy", "File copied to internal storage.") Logger.log("File copied to internal storage.")
} }
private fun getRealPathFromURI(context: Context, contentUri: Uri): String? { private fun getRealPathFromURI(context: Context, contentUri: Uri): String? {
@ -350,7 +349,7 @@ internal class NovelExtensionInstaller(private val context: Context) {
// Set next installation step // Set next installation step
if (uri == null) { if (uri == null) {
logcat(LogPriority.ERROR) { "Couldn't locate downloaded APK" } Logger.log("Couldn't locate downloaded APK")
downloadsRelay.call(id to InstallStep.Error) downloadsRelay.call(id to InstallStep.Error)
return return
} }
@ -371,7 +370,7 @@ internal class NovelExtensionInstaller(private val context: Context) {
val uri = Uri.parse(localUri) val uri = Uri.parse(localUri)
val path = uri.path val path = uri.path
val pkgName = path?.substring(path.lastIndexOf('/') + 1)?.removeSuffix(".apk") val pkgName = path?.substring(path.lastIndexOf('/') + 1)?.removeSuffix(".apk")
Log.i("Install APK", "Package name: $pkgName") Logger.log("Package name: $pkgName")
return pkgName ?: "" return pkgName ?: ""
} }
} }

View file

@ -7,7 +7,7 @@ import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.parsers.NovelInterface import ani.dantotsu.parsers.NovelInterface
import ani.dantotsu.snackString import ani.dantotsu.snackString
import dalvik.system.PathClassLoader import dalvik.system.PathClassLoader
@ -26,21 +26,20 @@ internal object NovelExtensionLoader {
val installDir = context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/" val installDir = context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/"
val results = mutableListOf<NovelLoadResult>() val results = mutableListOf<NovelLoadResult>()
//the number of files //the number of files
Log.e("NovelExtensionLoader", "Loading extensions from $installDir") Logger.log("Loading extensions from $installDir")
Log.e( Logger.log(
"NovelExtensionLoader",
"Loading extensions from ${File(installDir).listFiles()?.size}" "Loading extensions from ${File(installDir).listFiles()?.size}"
) )
File(installDir).setWritable(false) File(installDir).setWritable(false)
File(installDir).listFiles()?.forEach { File(installDir).listFiles()?.forEach {
//set the file to read only //set the file to read only
it.setWritable(false) it.setWritable(false)
Log.e("NovelExtensionLoader", "Loading extension ${it.name}") Logger.log("Loading extension ${it.name}")
val extension = loadExtension(context, it) val extension = loadExtension(context, it)
if (extension is NovelLoadResult.Success) { if (extension is NovelLoadResult.Success) {
results.add(extension) results.add(extension)
} else { } else {
logger("Failed to load extension ${it.name}") Logger.log("Failed to load extension ${it.name}")
} }
} }
return results return results
@ -62,7 +61,7 @@ internal object NovelExtensionLoader {
context.packageManager.getPackageArchiveInfo(path, 0) context.packageManager.getPackageArchiveInfo(path, 0)
} catch (error: Exception) { } catch (error: Exception) {
// Unlikely, but the package may have been uninstalled at this point // Unlikely, but the package may have been uninstalled at this point
logger("Failed to load extension $pkgName") Logger.log("Failed to load extension $pkgName")
return NovelLoadResult.Error(Exception("Failed to load extension")) return NovelLoadResult.Error(Exception("Failed to load extension"))
} }
return loadExtension(context, File(path)) return loadExtension(context, File(path))
@ -88,8 +87,8 @@ internal object NovelExtensionLoader {
val signatureHash = getSignatureHash(packageInfo) val signatureHash = getSignatureHash(packageInfo)
if ((signatureHash == null) || !signatureHash.contains(officialSignature)) { if ((signatureHash == null) || !signatureHash.contains(officialSignature)) {
logger("Package ${packageInfo.packageName} isn't signed") Logger.log("Package ${packageInfo.packageName} isn't signed")
logger("signatureHash: $signatureHash") Logger.log("signatureHash: $signatureHash")
snackString("Package ${packageInfo.packageName} isn't signed") snackString("Package ${packageInfo.packageName} isn't signed")
//return NovelLoadResult.Error(Exception("Extension not signed")) //return NovelLoadResult.Error(Exception("Extension not signed"))
} }
@ -128,12 +127,12 @@ internal object NovelExtensionLoader {
private fun loadSources(context: Context, file: File, className: String): List<NovelInterface> { private fun loadSources(context: Context, file: File, className: String): List<NovelInterface> {
return try { return try {
Log.e("NovelExtensionLoader", "isFileWritable: ${file.canWrite()}") Logger.log("isFileWritable: ${file.canWrite()}")
if (file.canWrite()) { if (file.canWrite()) {
val a = file.setWritable(false) val a = file.setWritable(false)
Log.e("NovelExtensionLoader", "success: $a") Logger.log("success: $a")
} }
Log.e("NovelExtensionLoader", "isFileWritable: ${file.canWrite()}") Logger.log("isFileWritable: ${file.canWrite()}")
val classLoader = PathClassLoader(file.absolutePath, null, context.classLoader) val classLoader = PathClassLoader(file.absolutePath, null, context.classLoader)
val className = val className =
"some.random.novelextensions.${className.lowercase(Locale.getDefault())}.$className" "some.random.novelextensions.${className.lowercase(Locale.getDefault())}.$className"

View file

@ -2,7 +2,7 @@ package ani.dantotsu.parsers.novel
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.snackString import ani.dantotsu.snackString
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -70,7 +70,7 @@ class NovelExtensionManager(private val context: Context) {
val extensions: List<NovelExtension.Available> = try { val extensions: List<NovelExtension.Available> = try {
api.findExtensions() api.findExtensions()
} catch (e: Exception) { } catch (e: Exception) {
logger("Error finding extensions: ${e.message}") Logger.log("Error finding extensions: ${e.message}")
withUIContext { snackString("Failed to get Novel extensions list") } withUIContext { snackString("Failed to get Novel extensions list") }
emptyList() emptyList()
} }

View file

@ -14,7 +14,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistQueries import ani.dantotsu.connections.anilist.AnilistQueries
import ani.dantotsu.connections.anilist.api.Activity import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.FragmentFeedBinding import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.snackString import ani.dantotsu.snackString
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter

View file

@ -25,7 +25,7 @@ import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
@ -146,7 +146,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
}, },
{ error -> { error ->
Injekt.get<CrashlyticsInterface>().logException(error) Injekt.get<CrashlyticsInterface>().logException(error)
Log.e("AnimeExtensionsAdapter", "Error: ", error) // Log the error Logger.log(error) // Log the error
val builder = NotificationCompat.Builder( val builder = NotificationCompat.Builder(
context, context,
Notifications.CHANNEL_DOWNLOADER_ERROR Notifications.CHANNEL_DOWNLOADER_ERROR

View file

@ -32,6 +32,7 @@ import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
@ -143,7 +144,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
}, },
{ error -> { error ->
Injekt.get<CrashlyticsInterface>().logException(error) Injekt.get<CrashlyticsInterface>().logException(error)
Log.e("MangaExtensionsAdapter", "Error: ", error) // Log the error Logger.log(error) // Log the error
val builder = NotificationCompat.Builder( val builder = NotificationCompat.Builder(
context, context,
Notifications.CHANNEL_DOWNLOADER_ERROR Notifications.CHANNEL_DOWNLOADER_ERROR

View file

@ -30,6 +30,7 @@ import ani.dantotsu.parsers.novel.NovelExtensionManager
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
@ -72,7 +73,7 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
}, },
{ error -> { error ->
Injekt.get<CrashlyticsInterface>().logException(error) Injekt.get<CrashlyticsInterface>().logException(error)
Log.e("NovelExtensionsAdapter", "Error: ", error) // Log the error Logger.log(error) // Log the error
val builder = NotificationCompat.Builder( val builder = NotificationCompat.Builder(
context, context,
Notifications.CHANNEL_DOWNLOADER_ERROR Notifications.CHANNEL_DOWNLOADER_ERROR
@ -216,7 +217,7 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context) val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_extension, parent, false) .inflate(R.layout.item_extension, parent, false)
Log.d("NovelExtensionsAdapter", "onCreateViewHolder: $view") Logger.log("onCreateViewHolder: $view")
return ViewHolder(view) return ViewHolder(view)
} }

View file

@ -59,7 +59,6 @@ class NovelExtensionsFragment : Fragment(),
lifecycleScope.launch { lifecycleScope.launch {
viewModel.pagerFlow.collectLatest { pagingData -> viewModel.pagerFlow.collectLatest { pagingData ->
Log.d("NovelExtensionsFragment", "collectLatest")
adapter.submitData(pagingData) adapter.submitData(pagingData)
} }
} }

View file

@ -45,7 +45,7 @@ import ani.dantotsu.download.video.ExoplayerDownloadService
import ani.dantotsu.downloadsPermission import ani.dantotsu.downloadsPermission
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.AppUpdater
@ -730,6 +730,16 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
binding.settingsShareUsername.isChecked = false binding.settingsShareUsername.isChecked = false
} }
binding.settingsLogToFile.isChecked = PrefManager.getVal(PrefName.LogToFile)
binding.settingsLogToFile.setOnCheckedChangeListener { _, isChecked ->
PrefManager.setVal(PrefName.LogToFile, isChecked)
restartApp()
}
binding.settingsShareLog.setOnClickListener {
Logger.shareLog(this)
}
binding.settingsAccountHelp.setOnClickListener { binding.settingsAccountHelp.setOnClickListener {
val title = getString(R.string.account_help) val title = getString(R.string.account_help)
val full = getString(R.string.full_account_help) val full = getString(R.string.full_account_help)
@ -884,7 +894,7 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
if (dialogTag == "colorPicker") { if (dialogTag == "colorPicker") {
val color = extras.getInt(SimpleColorDialog.COLOR) val color = extras.getInt(SimpleColorDialog.COLOR)
PrefManager.setVal(PrefName.CustomThemeInt, color) PrefManager.setVal(PrefName.CustomThemeInt, color)
logger("Custom Theme: $color") Logger.log("Custom Theme: $color")
} }
} }
return true return true

View file

@ -61,11 +61,7 @@ class AnimeSourcePreferencesFragment : PreferenceFragmentCompat() {
pref.isIconSpaceReserved = false pref.isIconSpaceReserved = false
if (pref is DialogPreference) { if (pref is DialogPreference) {
pref.dialogTitle = pref.title pref.dialogTitle = pref.title
//println("pref.dialogTitle: ${pref.dialogTitle}") //TODO: could be useful for dub/sub selection
} }
/*for (entry in sharedPreferences.all.entries) {
Log.d("Preferences", "Key: ${entry.key}, Value: ${entry.value}")
}*/
// Apply incognito IME for EditTextPreference // Apply incognito IME for EditTextPreference
if (pref is EditTextPreference) { if (pref is EditTextPreference) {

View file

@ -159,6 +159,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
FirstComment(Pref(Location.Irrelevant, Boolean::class, true)), FirstComment(Pref(Location.Irrelevant, Boolean::class, true)),
CommentAuthResponse(Pref(Location.Irrelevant, AuthResponse::class, "")), CommentAuthResponse(Pref(Location.Irrelevant, AuthResponse::class, "")),
CommentTokenExpiry(Pref(Location.Irrelevant, Long::class, 0L)), CommentTokenExpiry(Pref(Location.Irrelevant, Long::class, 0L)),
LogToFile(Pref(Location.Irrelevant, Boolean::class, false)),
//Protected //Protected
DiscordToken(Pref(Location.Protected, String::class, "")), DiscordToken(Pref(Location.Protected, String::class, "")),

View file

@ -7,7 +7,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.isOnline import ani.dantotsu.isOnline
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime
@ -22,7 +22,7 @@ class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) { when (intent?.action) {
Intent.ACTION_BOOT_COMPLETED -> tryWith(true) { Intent.ACTION_BOOT_COMPLETED -> tryWith(true) {
logger("Starting Dantotsu Subscription Service on Boot") Logger.log("Starting Dantotsu Subscription Service on Boot")
context?.startSubscription() context?.startSubscription()
} }
} }

View file

@ -10,6 +10,7 @@ import ani.dantotsu.parsers.Episode
import ani.dantotsu.parsers.MangaChapter import ani.dantotsu.parsers.MangaChapter
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -27,7 +28,7 @@ class Subscription {
alreadyStarted = true alreadyStarted = true
SubscriptionWorker.enqueue(this) SubscriptionWorker.enqueue(this)
AlarmReceiver.alarm(this) AlarmReceiver.alarm(this)
} else logger("Already Subscribed") } else Logger.log("Already Subscribed")
} }
private var currentlyPerforming = false private var currentlyPerforming = false

View file

@ -0,0 +1,146 @@
package ani.dantotsu.util
import android.content.Context
import android.content.Intent
import androidx.core.content.FileProvider
import ani.dantotsu.BuildConfig
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.util.Date
import java.util.concurrent.Executors
object Logger {
var file: File? = null
private val loggerExecutor = Executors.newSingleThreadExecutor()
fun init(context: Context) {
try {
if (!PrefManager.getVal<Boolean>(PrefName.LogToFile) || file != null) return
file = File(context.getExternalFilesDir(null), "log.txt")
if (file?.exists() == true) {
val oldFile = File(context.getExternalFilesDir(null), "old_log.txt")
file?.copyTo(oldFile, true)
} else {
file?.createNewFile()
}
file?.writeText("log started\n")
file?.appendText("date/time: ${Date()}\n")
file?.appendText("device: ${android.os.Build.MODEL}\n")
file?.appendText("os version: ${android.os.Build.VERSION.RELEASE}\n")
file?.appendText(
"app version: ${
context.packageManager.getPackageInfo(
context.packageName,
0
).versionName
}\n"
)
file?.appendText(
"app version code: ${
context.packageManager.getPackageInfo(
context.packageName,
0
).versionCode
}\n"
)
file?.appendText("sdk version: ${android.os.Build.VERSION.SDK_INT}\n")
file?.appendText("manufacturer: ${android.os.Build.MANUFACTURER}\n")
file?.appendText("brand: ${android.os.Build.BRAND}\n")
file?.appendText("product: ${android.os.Build.PRODUCT}\n")
file?.appendText("device: ${android.os.Build.DEVICE}\n")
file?.appendText("hardware: ${android.os.Build.HARDWARE}\n")
file?.appendText("host: ${android.os.Build.HOST}\n")
file?.appendText("id: ${android.os.Build.ID}\n")
file?.appendText("type: ${android.os.Build.TYPE}\n")
file?.appendText("user: ${android.os.Build.USER}\n")
file?.appendText("tags: ${android.os.Build.TAGS}\n")
file?.appendText("time: ${android.os.Build.TIME}\n")
file?.appendText("radio: ${android.os.Build.RADIO}\n")
file?.appendText("bootloader: ${android.os.Build.BOOTLOADER}\n")
file?.appendText("board: ${android.os.Build.BOARD}\n")
file?.appendText("fingerprint: ${android.os.Build.FINGERPRINT}\n")
file?.appendText("supported_abis: ${android.os.Build.SUPPORTED_ABIS.joinToString()}\n")
file?.appendText("supported_32_bit_abis: ${android.os.Build.SUPPORTED_32_BIT_ABIS.joinToString()}\n")
file?.appendText("supported_64_bit_abis: ${android.os.Build.SUPPORTED_64_BIT_ABIS.joinToString()}\n")
file?.appendText("is emulator: ${android.os.Build.FINGERPRINT.contains("generic")}\n")
file?.appendText("--------------------------------\n")
} catch (e: Exception) {
Injekt.get<CrashlyticsInterface>().logException(e)
file = null
}
}
fun log(message: String) {
val trace = Thread.currentThread().stackTrace[3]
loggerExecutor.execute {
if (file == null) println(message)
else {
val className = trace.className
val methodName = trace.methodName
val lineNumber = trace.lineNumber
file?.appendText("date/time: ${Date()} | $className.$methodName($lineNumber)\n")
file?.appendText("message: $message\n-\n")
}
}
}
fun log(e: Exception) {
loggerExecutor.execute {
if (file == null) e.printStackTrace() else {
file?.appendText("---------------------------Exception---------------------------\n")
file?.appendText("date/time: ${Date()} | ${e.message}\n")
file?.appendText("trace: ${e.stackTrace}\n")
}
}
}
fun log(e: Throwable) {
loggerExecutor.execute {
if (file == null) e.printStackTrace() else {
file?.appendText("---------------------------Exception---------------------------\n")
file?.appendText("date/time: ${Date()} | ${e.message}\n")
file?.appendText("trace: ${e.stackTrace}\n")
}
}
}
fun shareLog(context: Context) {
if (file == null) {
snackString("No log file found")
return
}
val oldFile = File(context.getExternalFilesDir(null), "old_log.txt")
val fileToUse = if (oldFile.exists()) {
file?.readText()?.let { oldFile.appendText(it) }
oldFile
} else {
file
}
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.type = "text/plain"
shareIntent.putExtra(
Intent.EXTRA_STREAM,
FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", fileToUse!!)
)
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Log file")
shareIntent.putExtra(Intent.EXTRA_TEXT, "Log file")
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(Intent.createChooser(shareIntent, "Share log file"))
}
}
class FinalExceptionHandler : Thread.UncaughtExceptionHandler {
private val defaultUEH: Thread.UncaughtExceptionHandler? =
Thread.getDefaultUncaughtExceptionHandler()
override fun uncaughtException(t: Thread, e: Throwable) {
Logger.log(e)
Injekt.get<CrashlyticsInterface>().logException(e)
defaultUEH?.uncaughtException(t, e)
}
}

View file

@ -7,7 +7,7 @@ import android.graphics.BitmapFactory
import android.widget.RemoteViews import android.widget.RemoteViews
import android.widget.RemoteViewsService import android.widget.RemoteViewsService
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.logger import ani.dantotsu.util.Logger
import java.io.InputStream import java.io.InputStream
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
@ -19,7 +19,7 @@ class CurrentlyAiringRemoteViewsFactory(private val context: Context, intent: In
override fun onCreate() { override fun onCreate() {
// 4 items for testing // 4 items for testing
widgetItems.clear() widgetItems.clear()
logger("CurrentlyAiringRemoteViewsFactory onCreate") Logger.log("CurrentlyAiringRemoteViewsFactory onCreate")
widgetItems = List(4) { widgetItems = List(4) {
WidgetItem( WidgetItem(
"Show $it", "Show $it",
@ -31,7 +31,7 @@ class CurrentlyAiringRemoteViewsFactory(private val context: Context, intent: In
override fun onDataSetChanged() { override fun onDataSetChanged() {
// 4 items for testing // 4 items for testing
logger("CurrentlyAiringRemoteViewsFactory onDataSetChanged") Logger.log("CurrentlyAiringRemoteViewsFactory onDataSetChanged")
widgetItems.clear() widgetItems.clear()
widgetItems.add( widgetItems.add(
WidgetItem( WidgetItem(
@ -79,7 +79,7 @@ class CurrentlyAiringRemoteViewsFactory(private val context: Context, intent: In
} }
override fun getViewAt(position: Int): RemoteViews { override fun getViewAt(position: Int): RemoteViews {
logger("CurrentlyAiringRemoteViewsFactory getViewAt") Logger.log("CurrentlyAiringRemoteViewsFactory getViewAt")
val item = widgetItems[position] val item = widgetItems[position]
val rv = RemoteViews(context.packageName, R.layout.item_currently_airing_widget).apply { val rv = RemoteViews(context.packageName, R.layout.item_currently_airing_widget).apply {
setTextViewText(R.id.text_show_title, item.title) setTextViewText(R.id.text_show_title, item.title)

View file

@ -2,11 +2,11 @@ package ani.dantotsu.widgets
import android.content.Intent import android.content.Intent
import android.widget.RemoteViewsService import android.widget.RemoteViewsService
import ani.dantotsu.logger import ani.dantotsu.util.Logger
class CurrentlyAiringRemoteViewsService : RemoteViewsService() { class CurrentlyAiringRemoteViewsService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
logger("CurrentlyAiringRemoteViewsFactory onGetViewFactory") Logger.log("CurrentlyAiringRemoteViewsFactory onGetViewFactory")
return CurrentlyAiringRemoteViewsFactory(applicationContext, intent) return CurrentlyAiringRemoteViewsFactory(applicationContext, intent)
} }
} }

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension.anime
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.anime.api.AnimeExtensionGithubApi import eu.kanade.tachiyomi.extension.anime.api.AnimeExtensionGithubApi
@ -16,11 +17,9 @@ import eu.kanade.tachiyomi.util.preference.plusAssign
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import logcat.LogPriority
import rx.Observable import rx.Observable
import tachiyomi.core.util.lang.launchNow import tachiyomi.core.util.lang.launchNow
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.source.anime.model.AnimeSourceData import tachiyomi.domain.source.anime.model.AnimeSourceData
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -120,7 +119,7 @@ class AnimeExtensionManager(
val extensions: List<AnimeExtension.Available> = try { val extensions: List<AnimeExtension.Available> = try {
api.findExtensions() api.findExtensions()
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR, e) Logger.log(e)
withUIContext { snackString("Failed to get extensions list") } withUIContext { snackString("Failed to get extensions list") }
emptyList() emptyList()
} }

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension.anime.api package eu.kanade.tachiyomi.extension.anime.api
import android.content.Context import android.content.Context
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
@ -13,11 +14,9 @@ import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import logcat.LogPriority
import tachiyomi.core.preference.Preference import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date import java.util.Date
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
@ -45,7 +44,7 @@ internal class AnimeExtensionGithubApi {
.newCall(GET("${REPO_URL_PREFIX}index.min.json")) .newCall(GET("${REPO_URL_PREFIX}index.min.json"))
.awaitSuccess() .awaitSuccess()
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Failed to get extensions from GitHub" } Logger.log("Failed to get extensions from GitHub")
requiresFallbackSource = true requiresFallbackSource = true
null null
} }

View file

@ -10,12 +10,11 @@ import android.content.pm.PackageInstaller
import android.os.Build import android.os.Build
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.util.lang.use import eu.kanade.tachiyomi.util.lang.use
import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
import eu.kanade.tachiyomi.util.system.getUriSize import eu.kanade.tachiyomi.util.system.getUriSize
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
class PackageInstallerInstallerAnime(private val service: Service) : InstallerAnime(service) { class PackageInstallerInstallerAnime(private val service: Service) : InstallerAnime(service) {
@ -30,7 +29,7 @@ class PackageInstallerInstallerAnime(private val service: Service) : InstallerAn
PackageInstaller.STATUS_PENDING_USER_ACTION -> { PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT) val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT)
if (userAction == null) { if (userAction == null) {
logcat(LogPriority.ERROR) { "Fatal error for $intent" } Logger.log("Fatal error for $intent")
continueQueue(InstallStep.Error) continueQueue(InstallStep.Error)
return return
} }
@ -89,11 +88,8 @@ class PackageInstallerInstallerAnime(private val service: Service) : InstallerAn
session.commit(intentSender) session.commit(intentSender)
} }
} catch (e: Exception) { } catch (e: Exception) {
logcat( Logger.log(e)
LogPriority.ERROR, Logger.log("Failed to install extension ${entry.downloadId} ${entry.uri}")
e
) { "Failed to install extension ${entry.downloadId} ${entry.uri}" }
logcat(LogPriority.ERROR) { "Exception: $e" }
snackString("Failed to install extension ${entry.downloadId} ${entry.uri}") snackString("Failed to install extension ${entry.downloadId} ${entry.uri}")
activeSession?.let { (_, sessionId) -> activeSession?.let { (_, sessionId) ->
packageInstaller.abandonSession(sessionId) packageInstaller.abandonSession(sessionId)

View file

@ -5,15 +5,14 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.extension.anime.model.AnimeLoadResult import eu.kanade.tachiyomi.extension.anime.model.AnimeLoadResult
import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
import logcat.LogPriority
import tachiyomi.core.util.lang.launchNow import tachiyomi.core.util.lang.launchNow
import tachiyomi.core.util.system.logcat
/** /**
* Broadcast receiver that listens for the system's packages installed, updated or removed, and only * Broadcast receiver that listens for the system's packages installed, updated or removed, and only
@ -103,7 +102,7 @@ internal class AnimeExtensionInstallReceiver(private val listener: Listener) :
private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): AnimeLoadResult { private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): AnimeLoadResult {
val pkgName = getPackageNameFromIntent(intent) val pkgName = getPackageNameFromIntent(intent)
if (pkgName == null) { if (pkgName == null) {
logcat(LogPriority.WARN) { "Package name not found" } Logger.log("Package name not found")
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) {

View file

@ -8,6 +8,7 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.util.Logger
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.anime.installer.InstallerAnime import eu.kanade.tachiyomi.extension.anime.installer.InstallerAnime
@ -15,8 +16,6 @@ import eu.kanade.tachiyomi.extension.anime.installer.PackageInstallerInstallerAn
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionInstaller.Companion.EXTRA_DOWNLOAD_ID import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionInstaller.Companion.EXTRA_DOWNLOAD_ID
import eu.kanade.tachiyomi.util.system.getSerializableExtraCompat import eu.kanade.tachiyomi.util.system.getSerializableExtraCompat
import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notificationBuilder
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
class AnimeExtensionInstallService : Service() { class AnimeExtensionInstallService : Service() {
@ -60,7 +59,7 @@ class AnimeExtensionInstallService : Service() {
) )
else -> { else -> {
logcat(LogPriority.ERROR) { "Not implemented for installer $installerUsed" } Logger.log("Not implemented for installer $installerUsed")
stopSelf() stopSelf()
return START_NOT_STICKY return START_NOT_STICKY
} }

View file

@ -10,16 +10,15 @@ import android.os.Environment
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.net.toUri import androidx.core.net.toUri
import ani.dantotsu.util.Logger
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.anime.installer.InstallerAnime import eu.kanade.tachiyomi.extension.anime.installer.InstallerAnime
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
import logcat.LogPriority
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -248,7 +247,7 @@ internal class AnimeExtensionInstaller(private val context: Context) {
// Set next installation step // Set next installation step
if (uri == null) { if (uri == null) {
logcat(LogPriority.ERROR) { "Couldn't locate downloaded APK" } Logger.log("Couldn't locate downloaded APK")
downloadsRelay.call(id to InstallStep.Error) downloadsRelay.call(id to InstallStep.Error)
return return
} }

View file

@ -6,6 +6,7 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import ani.dantotsu.util.Logger
import dalvik.system.PathClassLoader import dalvik.system.PathClassLoader
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
@ -17,8 +18,6 @@ import eu.kanade.tachiyomi.util.lang.Hash
import eu.kanade.tachiyomi.util.system.getApplicationIcon import eu.kanade.tachiyomi.util.system.getApplicationIcon
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
/** /**
@ -91,11 +90,11 @@ internal object AnimeExtensionLoader {
context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS) context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS)
} catch (error: PackageManager.NameNotFoundException) { } catch (error: PackageManager.NameNotFoundException) {
// Unlikely, but the package may have been uninstalled at this point // Unlikely, but the package may have been uninstalled at this point
logcat(LogPriority.ERROR, error) Logger.log(error)
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
if (!isPackageAnExtension(pkgInfo)) { if (!isPackageAnExtension(pkgInfo)) {
logcat(LogPriority.WARN) { "Tried to load a package that wasn't a extension ($pkgName)" } Logger.log("Tried to load a package that wasn't a extension ($pkgName)")
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
return loadExtension(context, pkgName, pkgInfo) return loadExtension(context, pkgName, pkgInfo)
@ -119,7 +118,7 @@ internal object AnimeExtensionLoader {
pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA) pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
} catch (error: PackageManager.NameNotFoundException) { } catch (error: PackageManager.NameNotFoundException) {
// Unlikely, but the package may have been uninstalled at this point // Unlikely, but the package may have been uninstalled at this point
logcat(LogPriority.ERROR, error) Logger.log(error)
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
@ -128,24 +127,23 @@ internal object AnimeExtensionLoader {
val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo) val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo)
if (versionName.isNullOrEmpty()) { if (versionName.isNullOrEmpty()) {
logcat(LogPriority.WARN) { "Missing versionName for extension $extName" } Logger.log("Missing versionName for extension $extName")
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
// Validate lib version // Validate lib version
val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull() val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull()
if (libVersion == null || libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) { if (libVersion == null || libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) {
logcat(LogPriority.WARN) { Logger.log("Lib version is $libVersion, while only versions " +
"Lib version is $libVersion, while only versions " +
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed" "$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
} )
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
val signatureHash = getSignatureHash(pkgInfo) val signatureHash = getSignatureHash(pkgInfo)
if (signatureHash == null) { if (signatureHash == null) {
logcat(LogPriority.WARN) { "Package $pkgName isn't signed" } Logger.log("Package $pkgName isn't signed")
return AnimeLoadResult.Error return AnimeLoadResult.Error
} else if (signatureHash !in trustedSignatures) { } else if (signatureHash !in trustedSignatures) {
val extension = AnimeExtension.Untrusted( val extension = AnimeExtension.Untrusted(
@ -156,13 +154,13 @@ internal object AnimeExtensionLoader {
libVersion, libVersion,
signatureHash signatureHash
) )
logcat(LogPriority.WARN, message = { "Extension $pkgName isn't trusted" }) Logger.log("Extension $pkgName isn't trusted")
return AnimeLoadResult.Untrusted(extension) return AnimeLoadResult.Untrusted(extension)
} }
val isNsfw = appInfo.metaData.getInt(METADATA_NSFW) == 1 val isNsfw = appInfo.metaData.getInt(METADATA_NSFW) == 1
if (!loadNsfwSource && isNsfw) { if (!loadNsfwSource && isNsfw) {
logcat(LogPriority.WARN) { "NSFW extension $pkgName not allowed" } Logger.log("NSFW extension $pkgName not allowed")
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
@ -189,7 +187,7 @@ internal object AnimeExtensionLoader {
else -> throw Exception("Unknown source class type! ${obj.javaClass}") else -> throw Exception("Unknown source class type! ${obj.javaClass}")
} }
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Extension load error: $extName ($it)" } Logger.log("Extension load error: $extName ($it)")
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
} }

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension.manga
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.manga.api.MangaExtensionGithubApi import eu.kanade.tachiyomi.extension.manga.api.MangaExtensionGithubApi
@ -16,11 +17,9 @@ import eu.kanade.tachiyomi.util.preference.plusAssign
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import logcat.LogPriority
import rx.Observable import rx.Observable
import tachiyomi.core.util.lang.launchNow import tachiyomi.core.util.lang.launchNow
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.source.manga.model.MangaSourceData import tachiyomi.domain.source.manga.model.MangaSourceData
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -117,7 +116,7 @@ class MangaExtensionManager(
val extensions: List<MangaExtension.Available> = try { val extensions: List<MangaExtension.Available> = try {
api.findExtensions() api.findExtensions()
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR, e) Logger.log(e)
withUIContext { snackString("Failed to get manga extensions") } withUIContext { snackString("Failed to get manga extensions") }
emptyList() emptyList()
} }

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension.manga.api package eu.kanade.tachiyomi.extension.manga.api
import android.content.Context import android.content.Context
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.extension.manga.model.AvailableMangaSources import eu.kanade.tachiyomi.extension.manga.model.AvailableMangaSources
@ -13,11 +14,9 @@ import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import logcat.LogPriority
import tachiyomi.core.preference.Preference import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date import java.util.Date
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
@ -45,7 +44,7 @@ internal class MangaExtensionGithubApi {
.newCall(GET("${REPO_URL_PREFIX}index.min.json")) .newCall(GET("${REPO_URL_PREFIX}index.min.json"))
.awaitSuccess() .awaitSuccess()
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Failed to get extensions from GitHub" } Logger.log("Failed to get extensions from GitHub")
requiresFallbackSource = true requiresFallbackSource = true
null null
} }

View file

@ -10,12 +10,11 @@ import android.content.pm.PackageInstaller
import android.os.Build import android.os.Build
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.util.lang.use import eu.kanade.tachiyomi.util.lang.use
import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
import eu.kanade.tachiyomi.util.system.getUriSize import eu.kanade.tachiyomi.util.system.getUriSize
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
class PackageInstallerInstallerManga(private val service: Service) : InstallerManga(service) { class PackageInstallerInstallerManga(private val service: Service) : InstallerManga(service) {
@ -30,7 +29,7 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
PackageInstaller.STATUS_PENDING_USER_ACTION -> { PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT) val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT)
if (userAction == null) { if (userAction == null) {
logcat(LogPriority.ERROR) { "Fatal error for $intent" } Logger.log("Fatal error for $intent")
continueQueue(InstallStep.Error) continueQueue(InstallStep.Error)
return return
} }
@ -89,8 +88,8 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
session.commit(intentSender) session.commit(intentSender)
} }
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR) { "Failed to install extension ${entry.downloadId} ${entry.uri}" } Logger.log("Failed to install extension ${entry.downloadId} ${entry.uri}")
logcat(LogPriority.ERROR) { "Exception: $e" } Logger.log(e)
snackString("Failed to install extension ${entry.downloadId} ${entry.uri}") snackString("Failed to install extension ${entry.downloadId} ${entry.uri}")
activeSession?.let { (_, sessionId) -> activeSession?.let { (_, sessionId) ->
packageInstaller.abandonSession(sessionId) packageInstaller.abandonSession(sessionId)

View file

@ -5,15 +5,14 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.extension.manga.model.MangaLoadResult import eu.kanade.tachiyomi.extension.manga.model.MangaLoadResult
import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
import logcat.LogPriority
import tachiyomi.core.util.lang.launchNow import tachiyomi.core.util.lang.launchNow
import tachiyomi.core.util.system.logcat
/** /**
* Broadcast receiver that listens for the system's packages installed, updated or removed, and only * Broadcast receiver that listens for the system's packages installed, updated or removed, and only
@ -103,7 +102,7 @@ internal class MangaExtensionInstallReceiver(private val listener: Listener) :
private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): MangaLoadResult { private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): MangaLoadResult {
val pkgName = getPackageNameFromIntent(intent) val pkgName = getPackageNameFromIntent(intent)
if (pkgName == null) { if (pkgName == null) {
logcat(LogPriority.WARN) { "Package name not found" } Logger.log("Package name not found")
return MangaLoadResult.Error return MangaLoadResult.Error
} }
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) {

View file

@ -8,6 +8,7 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.util.Logger
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.manga.installer.InstallerManga import eu.kanade.tachiyomi.extension.manga.installer.InstallerManga
@ -15,8 +16,6 @@ import eu.kanade.tachiyomi.extension.manga.installer.PackageInstallerInstallerMa
import eu.kanade.tachiyomi.extension.manga.util.MangaExtensionInstaller.Companion.EXTRA_DOWNLOAD_ID import eu.kanade.tachiyomi.extension.manga.util.MangaExtensionInstaller.Companion.EXTRA_DOWNLOAD_ID
import eu.kanade.tachiyomi.util.system.getSerializableExtraCompat import eu.kanade.tachiyomi.util.system.getSerializableExtraCompat
import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notificationBuilder
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
class MangaExtensionInstallService : Service() { class MangaExtensionInstallService : Service() {
@ -60,7 +59,7 @@ class MangaExtensionInstallService : Service() {
) )
else -> { else -> {
logcat(LogPriority.ERROR) { "Not implemented for installer $installerUsed" } Logger.log("Not implemented for installer $installerUsed")
stopSelf() stopSelf()
return START_NOT_STICKY return START_NOT_STICKY
} }

View file

@ -10,16 +10,15 @@ import android.os.Environment
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.net.toUri import androidx.core.net.toUri
import ani.dantotsu.util.Logger
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.manga.installer.InstallerManga import eu.kanade.tachiyomi.extension.manga.installer.InstallerManga
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
import logcat.LogPriority
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -245,7 +244,7 @@ internal class MangaExtensionInstaller(private val context: Context) {
// Set next installation step // Set next installation step
if (uri == null) { if (uri == null) {
logcat(LogPriority.ERROR) { "Couldn't locate downloaded APK" } Logger.log("Couldn't locate downloaded APK")
downloadsRelay.call(id to InstallStep.Error) downloadsRelay.call(id to InstallStep.Error)
return return
} }

View file

@ -6,6 +6,7 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import ani.dantotsu.util.Logger
import dalvik.system.PathClassLoader import dalvik.system.PathClassLoader
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
@ -17,8 +18,6 @@ import eu.kanade.tachiyomi.util.lang.Hash
import eu.kanade.tachiyomi.util.system.getApplicationIcon import eu.kanade.tachiyomi.util.system.getApplicationIcon
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
/** /**
@ -91,11 +90,11 @@ internal object MangaExtensionLoader {
context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS) context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS)
} catch (error: PackageManager.NameNotFoundException) { } catch (error: PackageManager.NameNotFoundException) {
// Unlikely, but the package may have been uninstalled at this point // Unlikely, but the package may have been uninstalled at this point
logcat(LogPriority.ERROR, error) Logger.log(error)
return MangaLoadResult.Error return MangaLoadResult.Error
} }
if (!isPackageAnExtension(pkgInfo)) { if (!isPackageAnExtension(pkgInfo)) {
logcat(LogPriority.WARN) { "Tried to load a package that wasn't a extension ($pkgName)" } Logger.log("Tried to load a package that wasn't a extension ($pkgName)")
return MangaLoadResult.Error return MangaLoadResult.Error
} }
return loadMangaExtension(context, pkgName, pkgInfo) return loadMangaExtension(context, pkgName, pkgInfo)
@ -119,7 +118,7 @@ internal object MangaExtensionLoader {
pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA) pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
} catch (error: PackageManager.NameNotFoundException) { } catch (error: PackageManager.NameNotFoundException) {
// Unlikely, but the package may have been uninstalled at this point // Unlikely, but the package may have been uninstalled at this point
logcat(LogPriority.ERROR, error) Logger.log(error)
return MangaLoadResult.Error return MangaLoadResult.Error
} }
@ -129,17 +128,16 @@ internal object MangaExtensionLoader {
val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo) val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo)
if (versionName.isNullOrEmpty()) { if (versionName.isNullOrEmpty()) {
logcat(LogPriority.WARN) { "Missing versionName for extension $extName" } Logger.log("Missing versionName for extension $extName")
return MangaLoadResult.Error return MangaLoadResult.Error
} }
// Validate lib version // Validate lib version
val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull() val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull()
if (libVersion == null || libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) { if (libVersion == null || libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) {
logcat(LogPriority.WARN) { Logger.log("Lib version is $libVersion, while only versions " +
"Lib version is $libVersion, while only versions " +
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed" "$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
} )
return MangaLoadResult.Error return MangaLoadResult.Error
} }
@ -147,18 +145,18 @@ internal object MangaExtensionLoader {
/* temporarily disabling signature check TODO: remove? /* temporarily disabling signature check TODO: remove?
if (signatureHash == null) { if (signatureHash == null) {
logcat(LogPriority.WARN) { "Package $pkgName isn't signed" } Logger.log("Package $pkgName isn't signed")
return MangaLoadResult.Error return MangaLoadResult.Error
} else if (signatureHash !in trustedSignatures) { } else if (signatureHash !in trustedSignatures) {
val extension = MangaExtension.Untrusted(extName, pkgName, versionName, versionCode, libVersion, signatureHash) val extension = MangaExtension.Untrusted(extName, pkgName, versionName, versionCode, libVersion, signatureHash)
logcat(LogPriority.WARN) { "Extension $pkgName isn't trusted" } Logger.log("Extension $pkgName isn't trusted")
return MangaLoadResult.Untrusted(extension) return MangaLoadResult.Untrusted(extension)
} }
*/ */
val isNsfw = appInfo.metaData.getInt(METADATA_NSFW) == 1 val isNsfw = appInfo.metaData.getInt(METADATA_NSFW) == 1
if (!loadNsfwSource && isNsfw) { if (!loadNsfwSource && isNsfw) {
logcat(LogPriority.WARN) { "NSFW extension $pkgName not allowed" } Logger.log("NSFW extension $pkgName not allowed")
return MangaLoadResult.Error return MangaLoadResult.Error
} }
@ -185,7 +183,7 @@ internal object MangaExtensionLoader {
else -> throw Exception("Unknown source class type! ${obj.javaClass}") else -> throw Exception("Unknown source class type! ${obj.javaClass}")
} }
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Extension load error: $extName ($it)" } Logger.log("Extension load error: $extName ($it)")
return MangaLoadResult.Error return MangaLoadResult.Error
} }
} }

View file

@ -21,10 +21,9 @@ import androidx.core.graphics.blue
import androidx.core.graphics.green import androidx.core.graphics.green
import androidx.core.graphics.red import androidx.core.graphics.red
import androidx.core.net.toUri import androidx.core.net.toUri
import ani.dantotsu.util.Logger
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.util.lang.truncateCenter import eu.kanade.tachiyomi.util.lang.truncateCenter
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import java.io.File import java.io.File
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -47,7 +46,7 @@ fun Context.copyToClipboard(label: String, content: String) {
toast("Copied to clipboard: " + content.truncateCenter(50)) toast("Copied to clipboard: " + content.truncateCenter(50))
} }
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) Logger.log(e)
toast("Failed to copy to clipboard") toast("Failed to copy to clipboard")
} }
} }

View file

@ -2,8 +2,7 @@ package eu.kanade.tachiyomi.util.system
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Build import android.os.Build
import logcat.LogPriority import ani.dantotsu.util.Logger
import tachiyomi.core.util.system.logcat
object DeviceUtil { object DeviceUtil {
@ -72,7 +71,7 @@ object DeviceUtil {
.getDeclaredMethod("get", String::class.java) .getDeclaredMethod("get", String::class.java)
.invoke(null, key) as String .invoke(null, key) as String
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.WARN, e) { "Unable to use SystemProperties.get()" } Logger.log("Unable to use SystemProperties.get()")
null null
} }
} }

View file

@ -6,8 +6,7 @@ import android.content.pm.PackageManager
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import logcat.LogPriority import ani.dantotsu.util.Logger
import tachiyomi.core.util.system.logcat
object WebViewUtil { object WebViewUtil {
const val SPOOF_PACKAGE_NAME = "org.chromium.chrome" const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
@ -20,7 +19,7 @@ object WebViewUtil {
// is not installed // is not installed
CookieManager.getInstance() CookieManager.getInstance()
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) Logger.log(e)
return false return false
} }

View file

@ -989,6 +989,52 @@
app:showText="false" app:showText="false"
app:thumbTint="@color/button_switch_track" /> app:thumbTint="@color/button_switch_track" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/settingsLogToFile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_edit_note_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="@string/log_to_file"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:alpha="0.58"
android:fontFamily="@font/poppins_bold"
android:text="@string/logging_warning" />
<ImageButton
android:id="@+id/settingsShareLog"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_round_share_24"
android:padding="16dp" />
</LinearLayout>
</ani.dantotsu.others.Xpandable> </ani.dantotsu.others.Xpandable>
<ani.dantotsu.others.Xpandable <ani.dantotsu.others.Xpandable

View file

@ -692,5 +692,7 @@ Et magni quasi vel nemo omnis et voluptate quisquam vel corporis fuga ut consequ
Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exercitationem adipisci quo nemo aliquam ea numquam beatae. Eum Quis dolore aut quia accusantium sed vero autem vel quaerat eaque et beatae dicta non delectus galisum non ullam nulla. Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exercitationem adipisci quo nemo aliquam ea numquam beatae. Eum Quis dolore aut quia accusantium sed vero autem vel quaerat eaque et beatae dicta non delectus galisum non ullam nulla.
</string> </string>
<string name="log_to_file">Log to File</string>
<string name="logging_warning">Logging to a file will slow down the app. Only enable if you are experiencing issues.</string>
</resources> </resources>