Skip to content

Commit

Permalink
Fix crashes
Browse files Browse the repository at this point in the history
  • Loading branch information
Koitharu committed Jun 14, 2023
1 parent cd23b04 commit d81c22b
Show file tree
Hide file tree
Showing 14 changed files with 115 additions and 42 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdkVersion 21
targetSdkVersion 33
versionCode 552
versionName '5.2'
versionCode 553
versionName '5.2.1'
generatedDensities = []
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

Expand Down
7 changes: 7 additions & 0 deletions app/src/main/kotlin/org/koitharu/kotatsu/KotatsuApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.acra.ACRA
import org.acra.ReportField
import org.acra.config.dialog
import org.acra.config.httpSender
import org.acra.data.StringFormat
import org.acra.ktx.initAcra
import org.acra.sender.HttpSender
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.os.AppValidator
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.WorkServiceStopHelper
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
Expand Down Expand Up @@ -46,8 +48,12 @@ class KotatsuApp : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

@Inject
lateinit var appValidator: AppValidator

override fun onCreate() {
super.onCreate()
ACRA.errorReporter.putCustomData("isOriginalApp", appValidator.isOriginalApp.toString())
if (BuildConfig.DEBUG) {
enableStrictMode()
}
Expand Down Expand Up @@ -90,6 +96,7 @@ class KotatsuApp : Application(), Configuration.Provider {
ReportField.CUSTOM_DATA,
ReportField.SHARED_PREFERENCES,
)

dialog {
text = getString(R.string.crash_text)
title = getString(R.string.error_occurred)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package org.koitharu.kotatsu.core.github

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand All @@ -14,28 +10,22 @@ import org.json.JSONArray
import org.json.JSONObject
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.network.BaseHttpClient
import org.koitharu.kotatsu.core.os.AppValidator
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.asArrayList
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.parsers.util.byte2HexFormatted
import org.koitharu.kotatsu.parsers.util.json.mapJSONNotNull
import org.koitharu.kotatsu.parsers.util.parseJsonArray
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.inject.Inject
import javax.inject.Singleton

private const val CERT_SHA1 = "2C:19:C7:E8:07:61:2B:8E:94:51:1B:FD:72:67:07:64:5D:C2:58:AE"
private const val CONTENT_TYPE_APK = "application/vnd.android.package-archive"

@Singleton
class AppUpdateRepository @Inject constructor(
@ApplicationContext private val context: Context,
private val appValidator: AppValidator,
private val settings: AppSettings,
@BaseHttpClient private val okHttp: OkHttpClient,
) {
Expand Down Expand Up @@ -85,7 +75,7 @@ class AppUpdateRepository @Inject constructor(
}

fun isUpdateSupported(): Boolean {
return BuildConfig.DEBUG || getCertificateSHA1Fingerprint() == CERT_SHA1
return BuildConfig.DEBUG || appValidator.isOriginalApp
}

suspend fun getCurrentVersionChangelog(): String? {
Expand All @@ -94,22 +84,6 @@ class AppUpdateRepository @Inject constructor(
return available.find { x -> x.versionId == currentVersion }?.description
}

@Suppress("DEPRECATION")
@SuppressLint("PackageManagerGetSignatures")
private fun getCertificateSHA1Fingerprint(): String? = runCatching {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)
val signatures = requireNotNull(packageInfo?.signatures)
val cert: ByteArray = signatures.first().toByteArray()
val input: InputStream = ByteArrayInputStream(cert)
val cf = CertificateFactory.getInstance("X509")
val c = cf.generateCertificate(input) as X509Certificate
val md: MessageDigest = MessageDigest.getInstance("SHA1")
val publicKey: ByteArray = md.digest(c.encoded)
return publicKey.byte2HexFormatted()
}.onFailure { error ->
error.printStackTraceDebug()
}.getOrNull()

private inline fun JSONArray.find(predicate: (JSONObject) -> Boolean): JSONObject? {
val size = length()
for (i in 0 until size) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class ParcelableManga(
return 0
}

override fun toString(): String {
return "ParcelableManga(manga=$manga, withChapters=$withChapters)"
}

companion object CREATOR : Parcelable.Creator<ParcelableManga> {
override fun createFromParcel(parcel: Parcel): ParcelableManga {
return ParcelableManga(parcel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class ParcelableMangaChapters(
return 0
}

override fun toString(): String {
return "ParcelableMangaChapters(chapters=$chapters)"
}

companion object CREATOR : Parcelable.Creator<ParcelableMangaChapters> {
override fun createFromParcel(parcel: Parcel): ParcelableMangaChapters {
return ParcelableMangaChapters(parcel)
Expand All @@ -32,4 +36,4 @@ class ParcelableMangaChapters(
return arrayOfNulls(size)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class ParcelableMangaPages(
return 0
}

override fun toString(): String {
return "ParcelableMangaPages(pages=$pages)"
}

companion object CREATOR : Parcelable.Creator<ParcelableMangaPages> {
override fun createFromParcel(parcel: Parcel): ParcelableMangaPages {
return ParcelableMangaPages(parcel)
Expand All @@ -32,4 +36,4 @@ class ParcelableMangaPages(
return arrayOfNulls(size)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ class ParcelableMangaTags(
return 0
}

override fun toString(): String {
return "ParcelableMangaTags(tags=$tags)"
}

companion object CREATOR : Parcelable.Creator<ParcelableMangaTags> {
override fun createFromParcel(parcel: Parcel): ParcelableMangaTags {
return ParcelableMangaTags(parcel)
Expand Down
46 changes: 46 additions & 0 deletions app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppValidator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.koitharu.kotatsu.core.os

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import dagger.hilt.android.qualifiers.ApplicationContext
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.parsers.util.byte2HexFormatted
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class AppValidator @Inject constructor(
@ApplicationContext private val context: Context,
) {

val isOriginalApp by lazy {
getCertificateSHA1Fingerprint() == CERT_SHA1
}

@Suppress("DEPRECATION")
@SuppressLint("PackageManagerGetSignatures")
private fun getCertificateSHA1Fingerprint(): String? = runCatching {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)
val signatures = requireNotNull(packageInfo?.signatures)
val cert: ByteArray = signatures.first().toByteArray()
val input: InputStream = ByteArrayInputStream(cert)
val cf = CertificateFactory.getInstance("X509")
val c = cf.generateCertificate(input) as X509Certificate
val md: MessageDigest = MessageDigest.getInstance("SHA1")
val publicKey: ByteArray = md.digest(c.encoded)
return publicKey.byte2HexFormatted()
}.onFailure { error ->
error.printStackTraceDebug()
}.getOrNull()

private companion object {

private const val CERT_SHA1 = "2C:19:C7:E8:07:61:2B:8E:94:51:1B:FD:72:67:07:64:5D:C2:58:AE"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,48 @@ import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.WeakHashMap
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class AcraScreenLogger @Inject constructor() : FragmentLifecycleCallbacks(), DefaultActivityLifecycleCallbacks {

private val timeFormat = SimpleDateFormat.getTimeInstance(DateFormat.DEFAULT, Locale.ROOT)
private val keys = WeakHashMap<Any, String>()

override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) {
super.onFragmentAttached(fm, f, context)
ACRA.errorReporter.putCustomData(f.key(), "${time()}: ${f.arguments}")
ACRA.errorReporter.putCustomData(f.key(), f.arguments.contentToString())
}

override fun onFragmentDetached(fm: FragmentManager, f: Fragment) {
super.onFragmentDetached(fm, f)
ACRA.errorReporter.removeCustomData(f.key())
keys.remove(f)
}

override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
super.onActivityCreated(activity, savedInstanceState)
ACRA.errorReporter.putCustomData(activity.key(), "${time()}: ${activity.intent}")
ACRA.errorReporter.putCustomData(activity.key(), activity.intent.extras.contentToString())
(activity as? FragmentActivity)?.supportFragmentManager?.registerFragmentLifecycleCallbacks(this, true)
}

override fun onActivityDestroyed(activity: Activity) {
super.onActivityDestroyed(activity)
ACRA.errorReporter.removeCustomData(activity.key())
keys.remove(activity)
}

private fun Activity.key() = "Activity[${javaClass.simpleName}]"

private fun Fragment.key() = "Fragment[${javaClass.simpleName}]"
private fun Any.key() = keys.getOrPut(this) {
"${time()}: ${javaClass.simpleName}"
}

private fun time() = timeFormat.format(Date())

@Suppress("DEPRECATION")
private fun Bundle?.contentToString() = this?.keySet()?.joinToString { k ->
val v = get(k)
"$k=$v"
} ?: toString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ fun <T> Flow<T>.observe(owner: LifecycleOwner, collector: FlowCollector<T>) {
}
}

fun <T> Flow<T>.observe(owner: LifecycleOwner, minState: Lifecycle.State, collector: FlowCollector<T>) {
owner.lifecycleScope.launch {
owner.lifecycle.repeatOnLifecycle(minState) {
collect(collector)
}
}
}

fun <T> Flow<Event<T>?>.observeEvent(owner: LifecycleOwner, collector: FlowCollector<T>) {
owner.lifecycleScope.launch {
owner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
Expand Down Expand Up @@ -127,7 +128,7 @@ class ReaderActivity :
},
),
)
viewModel.readerMode.observe(this, this::onInitReader)
viewModel.readerMode.observe(this, Lifecycle.State.STARTED, this::onInitReader)
viewModel.onPageSaved.observeEvent(this, this::onPageSaved)
viewModel.uiState.zipWithPrevious().observe(this, this::onUiStateChanged)
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ class MangaListActivity :
RemoteListFragment.newInstance(source)
}
replace(R.id.container, fragment)
runOnCommit { initFilter() }
if (!tags.isNullOrEmpty() && fragment is RemoteListFragment) {
runOnCommit(ApplyFilterRunnable(fragment, tags))
}
runOnCommit { initFilter() }
}
} else {
initFilter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.fragment.app.viewModels
import coil.ImageLoader
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.filterNotNull
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.util.ext.observe
Expand Down Expand Up @@ -38,7 +39,8 @@ class NewSourcesDialogFragment :
binding.recyclerView.adapter = adapter
binding.textViewTitle.setText(R.string.new_sources_text)

viewModel.sources.observe(viewLifecycleOwner) { adapter.items = it }
viewModel.sources.filterNotNull()
.observe(viewLifecycleOwner) { adapter.items = it }
}

override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.koitharu.kotatsu.settings.newsources

import androidx.annotation.WorkerThread
import androidx.core.os.LocaleListCompat
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import org.koitharu.kotatsu.core.model.getLocaleTitle
import org.koitharu.kotatsu.core.prefs.AppSettings
Expand All @@ -15,8 +17,14 @@ class NewSourcesViewModel @Inject constructor(
private val settings: AppSettings,
) : BaseViewModel() {

val sources = MutableStateFlow<List<SourceConfigItem>>(buildList())
private val initialList = settings.newSources
val sources = MutableStateFlow<List<SourceConfigItem>?>(null)

init {
launchJob(Dispatchers.Default) {
sources.value = buildList()
}
}

fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean) {
if (isEnabled) {
Expand All @@ -30,6 +38,7 @@ class NewSourcesViewModel @Inject constructor(
settings.markKnownSources(initialList)
}

@WorkerThread
private fun buildList(): List<SourceConfigItem.SourceItem> {
val locales = LocaleListCompat.getDefault().mapToSet { it.language }
val pendingHidden = HashSet<String>()
Expand Down

0 comments on commit d81c22b

Please sign in to comment.