Refactoring
This commit is contained in:
@@ -11,9 +11,9 @@ import org.koitharu.kotatsu.utils.ext.getParcelableCompat
|
|||||||
import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat
|
import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat
|
||||||
|
|
||||||
class MangaIntent private constructor(
|
class MangaIntent private constructor(
|
||||||
val manga: Manga?,
|
@JvmField val manga: Manga?,
|
||||||
val mangaId: Long,
|
@JvmField val mangaId: Long,
|
||||||
val uri: Uri?,
|
@JvmField val uri: Uri?,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
constructor(intent: Intent?) : this(
|
constructor(intent: Intent?) : this(
|
||||||
|
|||||||
@@ -26,34 +26,33 @@ import org.koitharu.kotatsu.R
|
|||||||
import org.koitharu.kotatsu.base.ui.util.ActionModeDelegate
|
import org.koitharu.kotatsu.base.ui.util.ActionModeDelegate
|
||||||
import org.koitharu.kotatsu.base.ui.util.BaseActivityEntryPoint
|
import org.koitharu.kotatsu.base.ui.util.BaseActivityEntryPoint
|
||||||
import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
|
import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
|
||||||
import org.koitharu.kotatsu.base.ui.util.inject
|
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
||||||
import org.koitharu.kotatsu.utils.ext.getThemeColor
|
import org.koitharu.kotatsu.utils.ext.getThemeColor
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
|
@Suppress("LeakingThis")
|
||||||
abstract class BaseActivity<B : ViewBinding> :
|
abstract class BaseActivity<B : ViewBinding> :
|
||||||
AppCompatActivity(),
|
AppCompatActivity(),
|
||||||
WindowInsetsDelegate.WindowInsetsListener {
|
WindowInsetsDelegate.WindowInsetsListener {
|
||||||
|
|
||||||
@Inject
|
private var isAmoledTheme = false
|
||||||
lateinit var settings: AppSettings
|
|
||||||
|
|
||||||
protected lateinit var binding: B
|
protected lateinit var binding: B
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@JvmField
|
||||||
protected val exceptionResolver = ExceptionResolver(this)
|
protected val exceptionResolver = ExceptionResolver(this)
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@JvmField
|
||||||
protected val insetsDelegate = WindowInsetsDelegate(this)
|
protected val insetsDelegate = WindowInsetsDelegate(this)
|
||||||
|
|
||||||
|
@JvmField
|
||||||
val actionModeDelegate = ActionModeDelegate()
|
val actionModeDelegate = ActionModeDelegate()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
EntryPointAccessors.fromApplication(this, BaseActivityEntryPoint::class.java).inject(this)
|
val settings = EntryPointAccessors.fromApplication(this, BaseActivityEntryPoint::class.java).settings
|
||||||
|
isAmoledTheme = settings.isAmoledTheme
|
||||||
setTheme(settings.colorScheme.styleResId)
|
setTheme(settings.colorScheme.styleResId)
|
||||||
if (settings.isAmoledTheme) {
|
if (isAmoledTheme) {
|
||||||
setTheme(R.style.ThemeOverlay_Kotatsu_Amoled)
|
setTheme(R.style.ThemeOverlay_Kotatsu_Amoled)
|
||||||
}
|
}
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -108,7 +107,7 @@ abstract class BaseActivity<B : ViewBinding> :
|
|||||||
protected fun isDarkAmoledTheme(): Boolean {
|
protected fun isDarkAmoledTheme(): Boolean {
|
||||||
val uiMode = resources.configuration.uiMode
|
val uiMode = resources.configuration.uiMode
|
||||||
val isNight = uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
val isNight = uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||||
return isNight && settings.isAmoledTheme
|
return isNight && isAmoledTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.koitharu.kotatsu.base.ui.util.ActionModeDelegate
|
|||||||
import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
|
import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||||
|
|
||||||
|
@Suppress("LeakingThis")
|
||||||
abstract class BaseFragment<B : ViewBinding> :
|
abstract class BaseFragment<B : ViewBinding> :
|
||||||
Fragment(),
|
Fragment(),
|
||||||
WindowInsetsDelegate.WindowInsetsListener {
|
WindowInsetsDelegate.WindowInsetsListener {
|
||||||
@@ -19,10 +20,10 @@ abstract class BaseFragment<B : ViewBinding> :
|
|||||||
protected val binding: B
|
protected val binding: B
|
||||||
get() = checkNotNull(viewBinding)
|
get() = checkNotNull(viewBinding)
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@JvmField
|
||||||
protected val exceptionResolver = ExceptionResolver(this)
|
protected val exceptionResolver = ExceptionResolver(this)
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@JvmField
|
||||||
protected val insetsDelegate = WindowInsetsDelegate(this)
|
protected val insetsDelegate = WindowInsetsDelegate(this)
|
||||||
|
|
||||||
protected val actionModeDelegate: ActionModeDelegate
|
protected val actionModeDelegate: ActionModeDelegate
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import androidx.core.view.updatePadding
|
|||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
|
||||||
import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
|
import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
|
||||||
import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
|
import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.settings.SettingsHeadersFragment
|
import org.koitharu.kotatsu.settings.SettingsHeadersFragment
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Suppress("LeakingThis")
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
||||||
PreferenceFragmentCompat(),
|
PreferenceFragmentCompat(),
|
||||||
@@ -24,7 +25,7 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var settings: AppSettings
|
lateinit var settings: AppSettings
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@JvmField
|
||||||
protected val insetsDelegate = WindowInsetsDelegate(this)
|
protected val insetsDelegate = WindowInsetsDelegate(this)
|
||||||
|
|
||||||
override val recyclerView: RecyclerView
|
override val recyclerView: RecyclerView
|
||||||
@@ -55,7 +56,6 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UsePropertyAccessSyntax")
|
|
||||||
protected fun setTitle(title: CharSequence) {
|
protected fun setTitle(title: CharSequence) {
|
||||||
(parentFragment as? SettingsHeadersFragment)?.setTitle(title)
|
(parentFragment as? SettingsHeadersFragment)?.setTitle(title)
|
||||||
?: activity?.setTitle(title)
|
?: activity?.setTitle(title)
|
||||||
|
|||||||
@@ -3,16 +3,24 @@ package org.koitharu.kotatsu.base.ui
|
|||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koitharu.kotatsu.base.ui.util.CountedBooleanLiveData
|
import org.koitharu.kotatsu.base.ui.util.CountedBooleanLiveData
|
||||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
abstract class BaseViewModel : ViewModel() {
|
abstract class BaseViewModel : ViewModel() {
|
||||||
|
|
||||||
|
@JvmField
|
||||||
protected val loadingCounter = CountedBooleanLiveData()
|
protected val loadingCounter = CountedBooleanLiveData()
|
||||||
|
|
||||||
|
@JvmField
|
||||||
protected val errorEvent = SingleLiveEvent<Throwable>()
|
protected val errorEvent = SingleLiveEvent<Throwable>()
|
||||||
|
|
||||||
val onError: LiveData<Throwable>
|
val onError: LiveData<Throwable>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.koitharu.kotatsu.base.ui
|
package org.koitharu.kotatsu.base.ui
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
@@ -20,7 +19,7 @@ abstract class CoroutineIntentService : BaseService() {
|
|||||||
final override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
final override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
super.onStartCommand(intent, flags, startId)
|
super.onStartCommand(intent, flags, startId)
|
||||||
launchCoroutine(intent, startId)
|
launchCoroutine(intent, startId)
|
||||||
return Service.START_REDELIVER_INTENT
|
return START_REDELIVER_INTENT
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchCoroutine(intent: Intent?, startId: Int) = lifecycleScope.launch(errorHandler(startId)) {
|
private fun launchCoroutine(intent: Intent?, startId: Int) = lifecycleScope.launch(errorHandler(startId)) {
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
abstract class BoundsScrollListener(private val offsetTop: Int, private val offsetBottom: Int) :
|
abstract class BoundsScrollListener(private val offsetTop: Int, private val offsetBottom: Int) :
|
||||||
RecyclerView.OnScrollListener() {
|
RecyclerView.OnScrollListener() {
|
||||||
|
|
||||||
constructor(offset: Int = 0) : this(offset, offset)
|
|
||||||
|
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
super.onScrolled(recyclerView, dx, dy)
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
|
val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.base.ui.util
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import org.koitharu.kotatsu.base.ui.DefaultActivityLifecycleCallbacks
|
import org.koitharu.kotatsu.base.ui.DefaultActivityLifecycleCallbacks
|
||||||
import java.util.WeakHashMap
|
import java.util.WeakHashMap
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -22,6 +23,6 @@ class ActivityRecreationHandle @Inject constructor() : DefaultActivityLifecycleC
|
|||||||
|
|
||||||
fun recreateAll() {
|
fun recreateAll() {
|
||||||
val snapshot = activities.keys.toList()
|
val snapshot = activities.keys.toList()
|
||||||
snapshot.forEach { it.recreate() }
|
snapshot.forEach { ActivityCompat.recreate(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.base.ui.util
|
|||||||
import dagger.hilt.EntryPoint
|
import dagger.hilt.EntryPoint
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
|
||||||
@EntryPoint
|
@EntryPoint
|
||||||
@@ -11,8 +10,3 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
|
|||||||
interface BaseActivityEntryPoint {
|
interface BaseActivityEntryPoint {
|
||||||
val settings: AppSettings
|
val settings: AppSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hilt cannot inject into parametrized classes
|
|
||||||
fun BaseActivityEntryPoint.inject(activity: BaseActivity<*>) {
|
|
||||||
activity.settings = settings
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ class WindowInsetsDelegate(
|
|||||||
private val listener: WindowInsetsListener,
|
private val listener: WindowInsetsListener,
|
||||||
) : OnApplyWindowInsetsListener, View.OnLayoutChangeListener {
|
) : OnApplyWindowInsetsListener, View.OnLayoutChangeListener {
|
||||||
|
|
||||||
|
@JvmField
|
||||||
var handleImeInsets: Boolean = false
|
var handleImeInsets: Boolean = false
|
||||||
|
|
||||||
|
@JvmField
|
||||||
var interceptingWindowInsetsListener: OnApplyWindowInsetsListener? = null
|
var interceptingWindowInsetsListener: OnApplyWindowInsetsListener? = null
|
||||||
|
|
||||||
private var lastInsets: Insets? = null
|
private var lastInsets: Insets? = null
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
|||||||
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
|
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import org.koitharu.kotatsu.utils.ext.toList
|
import org.koitharu.kotatsu.utils.ext.toList
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@@ -28,10 +29,14 @@ class MangaLoaderContextImpl @Inject constructor(
|
|||||||
@ApplicationContext private val androidContext: Context,
|
@ApplicationContext private val androidContext: Context,
|
||||||
) : MangaLoaderContext() {
|
) : MangaLoaderContext() {
|
||||||
|
|
||||||
|
private var webViewCached: WeakReference<WebView>? = null
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
override suspend fun evaluateJs(script: String): String? = withContext(Dispatchers.Main) {
|
override suspend fun evaluateJs(script: String): String? = withContext(Dispatchers.Main) {
|
||||||
val webView = WebView(androidContext)
|
val webView = webViewCached?.get() ?: WebView(androidContext).also {
|
||||||
webView.settings.javaScriptEnabled = true
|
it.settings.javaScriptEnabled = true
|
||||||
|
webViewCached = WeakReference(it)
|
||||||
|
}
|
||||||
suspendCoroutine { cont ->
|
suspendCoroutine { cont ->
|
||||||
webView.evaluateJavascript(script) { result ->
|
webView.evaluateJavascript(script) { result ->
|
||||||
cont.resume(result?.takeUnless { it == "null" })
|
cont.resume(result?.takeUnless { it == "null" })
|
||||||
|
|||||||
@@ -12,13 +12,12 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||||
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
|
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
|
||||||
|
|
||||||
class ExitCallback(
|
class ExitCallback(
|
||||||
private val activity: BaseActivity<*>,
|
private val activity: MainActivity,
|
||||||
private val snackbarHost: View,
|
private val snackbarHost: View,
|
||||||
) : OnBackPressedCallback(false) {
|
) : OnBackPressedCallback(false) {
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import org.koitharu.kotatsu.R
|
|||||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||||
import org.koitharu.kotatsu.base.ui.widgets.SlidingBottomNavigationView
|
import org.koitharu.kotatsu.base.ui.widgets.SlidingBottomNavigationView
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ActivityMainBinding
|
import org.koitharu.kotatsu.databinding.ActivityMainBinding
|
||||||
import org.koitharu.kotatsu.details.service.MangaPrefetchService
|
import org.koitharu.kotatsu.details.service.MangaPrefetchService
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
@@ -68,6 +69,7 @@ import org.koitharu.kotatsu.utils.ext.resolve
|
|||||||
import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf
|
import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf
|
||||||
import org.koitharu.kotatsu.utils.ext.setNavigationBarTransparentCompat
|
import org.koitharu.kotatsu.utils.ext.setNavigationBarTransparentCompat
|
||||||
import org.koitharu.kotatsu.utils.ext.tryLaunch
|
import org.koitharu.kotatsu.utils.ext.tryLaunch
|
||||||
|
import javax.inject.Inject
|
||||||
import com.google.android.material.R as materialR
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
private const val TAG_SEARCH = "search"
|
private const val TAG_SEARCH = "search"
|
||||||
@@ -82,6 +84,9 @@ class MainActivity :
|
|||||||
SearchSuggestionListener,
|
SearchSuggestionListener,
|
||||||
MainNavigationDelegate.OnFragmentChangedListener {
|
MainNavigationDelegate.OnFragmentChangedListener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var settings: AppSettings
|
||||||
|
|
||||||
private val viewModel by viewModels<MainViewModel>()
|
private val viewModel by viewModels<MainViewModel>()
|
||||||
private val searchSuggestionViewModel by viewModels<SearchSuggestionViewModel>()
|
private val searchSuggestionViewModel by viewModels<SearchSuggestionViewModel>()
|
||||||
private val voiceInputLauncher = registerForActivityResult(VoiceInputContract(), VoiceInputCallback())
|
private val voiceInputLauncher = registerForActivityResult(VoiceInputContract(), VoiceInputCallback())
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import org.koitharu.kotatsu.base.ui.BaseFullscreenActivity
|
|||||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.DialogErrorObserver
|
import org.koitharu.kotatsu.core.exceptions.resolve.DialogErrorObserver
|
||||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||||
import org.koitharu.kotatsu.databinding.ActivityReaderBinding
|
import org.koitharu.kotatsu.databinding.ActivityReaderBinding
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
@@ -66,6 +67,9 @@ class ReaderActivity :
|
|||||||
OnApplyWindowInsetsListener,
|
OnApplyWindowInsetsListener,
|
||||||
IdlingDetector.Callback {
|
IdlingDetector.Callback {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var settings: AppSettings
|
||||||
|
|
||||||
private val idlingDetector = IdlingDetector(TimeUnit.SECONDS.toMillis(10), this)
|
private val idlingDetector = IdlingDetector(TimeUnit.SECONDS.toMillis(10), this)
|
||||||
|
|
||||||
private val viewModel: ReaderViewModel by viewModels()
|
private val viewModel: ReaderViewModel by viewModels()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.scrobbling.mal.data
|
package org.koitharu.kotatsu.scrobbling.mal.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Base64
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
@@ -20,7 +21,7 @@ import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga
|
|||||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo
|
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo
|
||||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
||||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser
|
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser
|
||||||
import org.koitharu.kotatsu.utils.PKCEGenerator
|
import java.security.SecureRandom
|
||||||
|
|
||||||
private const val REDIRECT_URI = "kotatsu://mal-auth"
|
private const val REDIRECT_URI = "kotatsu://mal-auth"
|
||||||
private const val BASE_WEB_URL = "https://myanimelist.net"
|
private const val BASE_WEB_URL = "https://myanimelist.net"
|
||||||
@@ -35,7 +36,7 @@ class MALRepository(
|
|||||||
) : ScrobblerRepository {
|
) : ScrobblerRepository {
|
||||||
|
|
||||||
private val clientId = context.getString(R.string.mal_clientId)
|
private val clientId = context.getString(R.string.mal_clientId)
|
||||||
private var codeVerifier: String = getPKCEChallengeCode()
|
private val codeVerifier: String by lazy(::generateCodeVerifier)
|
||||||
|
|
||||||
override val oauthUrl: String
|
override val oauthUrl: String
|
||||||
get() = "$BASE_WEB_URL/v1/oauth2/authorize?" +
|
get() = "$BASE_WEB_URL/v1/oauth2/authorize?" +
|
||||||
@@ -177,11 +178,6 @@ class MALRepository(
|
|||||||
storage.clear()
|
storage.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPKCEChallengeCode(): String {
|
|
||||||
codeVerifier = PKCEGenerator.generateCodeVerifier()
|
|
||||||
return codeVerifier
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun jsonToManga(json: JSONObject): ScrobblerManga? {
|
private fun jsonToManga(json: JSONObject): ScrobblerManga? {
|
||||||
for (i in 0 until json.length()) {
|
for (i in 0 until json.length()) {
|
||||||
val node = json.getJSONObject("node")
|
val node = json.getJSONObject("node")
|
||||||
@@ -210,4 +206,10 @@ class MALRepository(
|
|||||||
avatar = json.getString("picture") ?: AVATAR_STUB,
|
avatar = json.getString("picture") ?: AVATAR_STUB,
|
||||||
service = ScrobblerService.MAL,
|
service = ScrobblerService.MAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun generateCodeVerifier(): String {
|
||||||
|
val codeVerifier = ByteArray(50)
|
||||||
|
SecureRandom().nextBytes(codeVerifier)
|
||||||
|
return Base64.encodeToString(codeVerifier, Base64.NO_WRAP or Base64.NO_PADDING or Base64.URL_SAFE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.koitharu.kotatsu.R
|
|||||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||||
import org.koitharu.kotatsu.base.ui.list.ListSelectionController
|
import org.koitharu.kotatsu.base.ui.list.ListSelectionController
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ActivitySearchMultiBinding
|
import org.koitharu.kotatsu.databinding.ActivitySearchMultiBinding
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||||
@@ -45,6 +46,9 @@ class MultiSearchActivity :
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var coil: ImageLoader
|
lateinit var coil: ImageLoader
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var settings: AppSettings
|
||||||
|
|
||||||
private val viewModel by viewModels<MultiSearchViewModel>()
|
private val viewModel by viewModels<MultiSearchViewModel>()
|
||||||
private lateinit var adapter: MultiSearchAdapter
|
private lateinit var adapter: MultiSearchAdapter
|
||||||
private lateinit var selectionController: ListSelectionController
|
private lateinit var selectionController: ListSelectionController
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.Resources
|
|
||||||
|
|
||||||
object InternalResourceHelper {
|
|
||||||
|
|
||||||
fun getBoolean(context: Context, resName: String, defaultValue: Boolean): Boolean {
|
|
||||||
val id = getResourceId(resName, "bool")
|
|
||||||
return if (id != 0) {
|
|
||||||
context.createPackageContext("android", 0).resources.getBoolean(id)
|
|
||||||
} else {
|
|
||||||
defaultValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get resource id from system resources
|
|
||||||
* @param resName resource name to get
|
|
||||||
* @param type resource type of [resName] to get
|
|
||||||
* @return 0 if not available
|
|
||||||
*/
|
|
||||||
private fun getResourceId(resName: String, type: String): Int {
|
|
||||||
return Resources.getSystem().getIdentifier(resName, type, "android")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.utils
|
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import java.security.SecureRandom
|
|
||||||
|
|
||||||
object PKCEGenerator {
|
|
||||||
|
|
||||||
private const val PKCE_BASE64_ENCODE_SETTINGS = Base64.NO_WRAP or Base64.NO_PADDING or Base64.URL_SAFE
|
|
||||||
|
|
||||||
fun generateCodeVerifier(): String {
|
|
||||||
val codeVerifier = ByteArray(50)
|
|
||||||
SecureRandom().nextBytes(codeVerifier)
|
|
||||||
return Base64.encodeToString(codeVerifier, PKCE_BASE64_ENCODE_SETTINGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
package org.koitharu.kotatsu.utils
|
package org.koitharu.kotatsu.utils
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.OptIn
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import com.google.android.material.badge.BadgeDrawable
|
import com.google.android.material.badge.BadgeDrawable
|
||||||
import com.google.android.material.badge.BadgeUtils
|
import com.google.android.material.badge.BadgeUtils
|
||||||
|
import com.google.android.material.badge.ExperimentalBadgeUtils
|
||||||
|
|
||||||
|
@OptIn(ExperimentalBadgeUtils::class)
|
||||||
class ViewBadge(
|
class ViewBadge(
|
||||||
private val anchor: View,
|
private val anchor: View,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import org.json.JSONException
|
|||||||
import org.jsoup.internal.StringUtil.StringJoiner
|
import org.jsoup.internal.StringUtil.StringJoiner
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.utils.InternalResourceHelper
|
|
||||||
import org.xmlpull.v1.XmlPullParser
|
import org.xmlpull.v1.XmlPullParser
|
||||||
import org.xmlpull.v1.XmlPullParserException
|
import org.xmlpull.v1.XmlPullParserException
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
@@ -108,7 +107,7 @@ fun SyncResult.onError(error: Throwable) {
|
|||||||
|
|
||||||
fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float, alphaFactor: Float = 0.7f) {
|
fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float, alphaFactor: Float = 0.7f) {
|
||||||
navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
|
navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
|
||||||
!InternalResourceHelper.getBoolean(context, "config_navBarNeedsScrim", true)
|
!context.getSystemBoolean("config_navBarNeedsScrim", true)
|
||||||
) {
|
) {
|
||||||
Color.TRANSPARENT
|
Color.TRANSPARENT
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.utils.ext
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import androidx.annotation.Px
|
import androidx.annotation.Px
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@@ -9,3 +11,13 @@ fun Resources.resolveDp(dp: Int) = (dp * displayMetrics.density).roundToInt()
|
|||||||
|
|
||||||
@Px
|
@Px
|
||||||
fun Resources.resolveDp(dp: Float) = dp * displayMetrics.density
|
fun Resources.resolveDp(dp: Float) = dp * displayMetrics.density
|
||||||
|
|
||||||
|
@SuppressLint("DiscouragedApi")
|
||||||
|
fun Context.getSystemBoolean(resName: String, fallback: Boolean): Boolean {
|
||||||
|
val id = Resources.getSystem().getIdentifier(resName, "bool", "android")
|
||||||
|
return if (id != 0) {
|
||||||
|
createPackageContext("android", 0).resources.getBoolean(id)
|
||||||
|
} else {
|
||||||
|
fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user