diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index e0a417b3f..d0766690f 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -44,7 +44,7 @@ body: label: Kotatsu version description: You can find your Kotatsu version in **Settings → About**. placeholder: | - Example: "3.3" + Example: "3.3.1" validations: required: true @@ -85,9 +85,9 @@ body: required: true - label: I have written a short but informative title. required: true - - label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/nv95/kotatsu-parsers/issues/new). + - label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/KotatsuApp/kotatsu-parsers/issues/new). required: true - - label: I have updated the app to version **[3.3](https://github.com/nv95/Kotatsu/releases/latest)**. + - label: I have updated the app to version **[3.3.1](https://github.com/KotatsuApp/Kotatsu/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index d4b373203..7bd0e002e 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -31,9 +31,9 @@ body: required: true - label: I have written a short but informative title. required: true - - label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/nv95/kotatsu-parsers/issues/new). + - label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/KotatsuApp/kotatsu-parsers/issues/new). required: true - - label: I have updated the app to version **[3.3](https://github.com/nv95/Kotatsu/releases/latest)**. + - label: I have updated the app to version **[3.3.1](https://github.com/KotatsuApp/Kotatsu/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true \ No newline at end of file diff --git a/README.md b/README.md index 6714250e3..1d693109e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Kotatsu is a free and open source manga reader for Android. alt="Get it on F-Droid" height="80">](https://f-droid.org/packages/org.koitharu.kotatsu) -Download APK from Github Releases: +Download APK from GitHub Releases: - [Latest release](https://github.com/KotatsuApp/Kotatsu/releases/latest) diff --git a/app/build.gradle b/app/build.gradle index a79a1f1a2..ad1ec0511 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdkVersion 21 targetSdkVersion 32 - versionCode 410 - versionName '3.3.1' + versionCode 411 + versionName '3.3.2' generatedDensities = [] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -73,7 +73,7 @@ afterEvaluate { } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) - implementation('com.github.nv95:kotatsu-parsers:8a3b6df91d') { + implementation('com.github.nv95:kotatsu-parsers:c92f89f307') { exclude group: 'org.json', module: 'json' } @@ -82,19 +82,20 @@ dependencies { implementation 'androidx.core:core-ktx:1.8.0' implementation 'androidx.activity:activity-ktx:1.5.0-rc01' implementation 'androidx.fragment:fragment-ktx:1.5.0-rc01' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0-rc01' - implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0-rc01' - implementation 'androidx.lifecycle:lifecycle-service:2.5.0-rc01' - implementation 'androidx.lifecycle:lifecycle-process:2.5.0-rc01' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0-rc02' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0-rc02' + implementation 'androidx.lifecycle:lifecycle-service:2.5.0-rc02' + implementation 'androidx.lifecycle:lifecycle-process:2.5.0-rc02' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.work:work-runtime-ktx:2.7.1' + implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04' implementation 'com.google.android.material:material:1.7.0-alpha02' //noinspection LifecycleAnnotationProcessorWithJava8 - kapt 'androidx.lifecycle:lifecycle-compiler:2.5.0-rc01' + kapt 'androidx.lifecycle:lifecycle-compiler:2.5.0-rc02' implementation 'androidx.room:room-runtime:2.4.2' implementation 'androidx.room:room-ktx:2.4.2' diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt index 2125d044c..7db0f6e22 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt @@ -6,14 +6,12 @@ import androidx.annotation.CallSuper import androidx.annotation.StringRes import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import androidx.fragment.app.Fragment import androidx.preference.PreferenceFragmentCompat import androidx.recyclerview.widget.RecyclerView import org.koin.android.ext.android.inject import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.SettingsHeadersFragment abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/dialog/TextInputDialog.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/dialog/TextInputDialog.kt deleted file mode 100644 index 4b5c02ca6..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/dialog/TextInputDialog.kt +++ /dev/null @@ -1,89 +0,0 @@ -package org.koitharu.kotatsu.base.ui.dialog - -import android.content.Context -import android.content.DialogInterface -import android.text.InputFilter -import android.view.LayoutInflater -import androidx.annotation.StringRes -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import org.koitharu.kotatsu.databinding.DialogInputBinding - -class TextInputDialog private constructor( - private val delegate: AlertDialog, -) : DialogInterface by delegate { - - fun show() = delegate.show() - - class Builder(context: Context) { - - private val binding = DialogInputBinding.inflate(LayoutInflater.from(context)) - - private val delegate = MaterialAlertDialogBuilder(context) - .setView(binding.root) - - fun setTitle(@StringRes titleResId: Int): Builder { - delegate.setTitle(titleResId) - return this - } - - fun setTitle(title: CharSequence): Builder { - delegate.setTitle(title) - return this - } - - fun setHint(@StringRes hintResId: Int): Builder { - binding.inputEdit.hint = binding.root.context.getString(hintResId) - return this - } - - fun setMaxLength(maxLength: Int, strict: Boolean): Builder { - with(binding.inputLayout) { - counterMaxLength = maxLength - isCounterEnabled = maxLength > 0 - } - if (strict && maxLength > 0) { - binding.inputEdit.filters += InputFilter.LengthFilter(maxLength) - } - return this - } - - fun setInputType(inputType: Int): Builder { - binding.inputEdit.inputType = inputType - return this - } - - fun setText(text: String): Builder { - binding.inputEdit.setText(text) - binding.inputEdit.setSelection(text.length) - return this - } - - fun setPositiveButton( - @StringRes textId: Int, - listener: (DialogInterface, String) -> Unit - ): Builder { - delegate.setPositiveButton(textId) { dialog, _ -> - listener(dialog, binding.inputEdit.text?.toString().orEmpty()) - } - return this - } - - fun setNegativeButton( - @StringRes textId: Int, - listener: DialogInterface.OnClickListener? = null - ): Builder { - delegate.setNegativeButton(textId, listener) - return this - } - - fun setOnCancelListener(listener: DialogInterface.OnCancelListener): Builder { - delegate.setOnCancelListener(listener) - return this - } - - fun create() = - TextInputDialog(delegate.create()) - - } -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupZipOutput.kt b/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupZipOutput.kt index f01dc73d9..8a6217d04 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupZipOutput.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupZipOutput.kt @@ -15,11 +15,11 @@ class BackupZipOutput(val file: File) : Closeable { private val output = ZipOutput(file, Deflater.BEST_COMPRESSION) - suspend fun put(entry: BackupEntry) { + suspend fun put(entry: BackupEntry) = runInterruptible(Dispatchers.IO) { output.put(entry.name, entry.data.toString(2)) } - suspend fun finish() { + suspend fun finish() = runInterruptible(Dispatchers.IO) { output.finish() } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt index 5a8cd055c..ef20b4fb0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt @@ -1,8 +1,6 @@ package org.koitharu.kotatsu.core.exceptions -import androidx.annotation.StringRes import okio.IOException -import org.koitharu.kotatsu.R class CloudFlareProtectedException( val url: String diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index ffa294262..45fbb85a4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -40,7 +40,7 @@ class AppSettings(context: Context) { get() = Collections.unmodifiableSet(remoteSources) var listMode: ListMode - get() = prefs.getEnumValue(KEY_LIST_MODE, ListMode.DETAILED_LIST) + get() = prefs.getEnumValue(KEY_LIST_MODE, ListMode.GRID) set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE, value) } var defaultSection: AppSection @@ -125,6 +125,10 @@ class AppSettings(context: Context) { get() = prefs.getString(KEY_APP_PASSWORD, null) set(value) = prefs.edit { if (value != null) putString(KEY_APP_PASSWORD, value) else remove(KEY_APP_PASSWORD) } + var isBiometricProtectionEnabled: Boolean + get() = prefs.getBoolean(KEY_PROTECT_APP_BIOMETRIC, true) + set(value) = prefs.edit { putBoolean(KEY_PROTECT_APP_BIOMETRIC, value) } + var sourcesOrder: List get() = prefs.getString(KEY_SOURCES_ORDER, null) ?.split('|') @@ -293,6 +297,7 @@ class AppSettings(context: Context) { const val KEY_READER_MODE_DETECT = "reader_mode_detect" const val KEY_APP_PASSWORD = "app_password" const val KEY_PROTECT_APP = "protect_app" + const val KEY_PROTECT_APP_BIOMETRIC = "protect_app_bio" const val KEY_APP_VERSION = "app_version" const val KEY_ZOOM_MODE = "zoom_mode" const val KEY_BACKUP = "backup" @@ -316,9 +321,6 @@ class AppSettings(context: Context) { const val KEY_APP_UPDATE = "app_update" const val KEY_APP_UPDATE_AUTO = "app_update_auto" const val KEY_APP_TRANSLATION = "about_app_translation" - const val KEY_FEEDBACK_4PDA = "about_feedback_4pda" - const val KEY_FEEDBACK_DISCORD = "about_feedback_discord" - const val KEY_FEEDBACK_GITHUB = "about_feedback_github" private const val NETWORK_NEVER = 0 private const val NETWORK_ALWAYS = 1 diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt index f7a98c078..cece6607f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt @@ -4,6 +4,7 @@ import android.content.Context import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.FavouriteCategory +import com.google.android.material.R as materialR class CategoriesEditDelegate( private val context: Context, @@ -11,9 +12,10 @@ class CategoriesEditDelegate( ) { fun deleteCategory(category: FavouriteCategory) { - MaterialAlertDialogBuilder(context) + MaterialAlertDialogBuilder(context, materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered) .setMessage(context.getString(R.string.category_delete_confirm, category.title)) .setTitle(R.string.remove_category) + .setIcon(R.drawable.ic_delete) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.remove) { _, _ -> callback.onDeleteCategory(category) diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt index b38dfec26..96de7ea86 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt @@ -3,8 +3,6 @@ package org.koitharu.kotatsu.favourites.ui.categories.edit import android.content.Context import android.content.Intent import android.os.Bundle -import android.view.Menu -import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.AdapterView @@ -24,7 +22,8 @@ import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.utils.ext.getDisplayMessage -class FavouritesCategoryEditActivity : BaseActivity(), AdapterView.OnItemClickListener { +class FavouritesCategoryEditActivity : BaseActivity(), AdapterView.OnItemClickListener, + View.OnClickListener { private val viewModel by viewModel { parametersOf(intent.getLongExtra(EXTRA_ID, NO_ID)) @@ -39,6 +38,7 @@ class FavouritesCategoryEditActivity : BaseActivity setHomeAsUpIndicator(com.google.android.material.R.drawable.abc_ic_clear_material) } initSortSpinner() + binding.buttonDone.setOnClickListener(this) viewModel.onSaved.observe(this) { finishAfterTransition() } viewModel.category.observe(this, ::onCategoryChanged) @@ -62,22 +62,14 @@ class FavouritesCategoryEditActivity : BaseActivity } } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.opt_config, menu) - menu.findItem(R.id.action_done)?.setTitle(R.string.save) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { - R.id.action_done -> { - viewModel.save( + override fun onClick(v: View) { + when (v.id) { + R.id.button_done -> viewModel.save( title = binding.editName.text?.toString().orEmpty(), sortOrder = getSelectedSortOrder(), isTrackerEnabled = binding.switchTracker.isChecked, ) - true } - else -> super.onOptionsItemSelected(item) } override fun onWindowInsetsChanged(insets: Insets) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt index aa2bacbbe..f13cce9a7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt @@ -2,11 +2,9 @@ package org.koitharu.kotatsu.favourites.ui.categories.select import android.os.Bundle import android.view.LayoutInflater -import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.Toast -import androidx.appcompat.widget.Toolbar import androidx.fragment.app.FragmentManager import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf @@ -28,7 +26,7 @@ class FavouriteCategoriesBottomSheet : BaseBottomSheet(), OnListItemClickListener, CategoriesEditDelegate.CategoriesEditCallback, - Toolbar.OnMenuItemClickListener, View.OnClickListener { + View.OnClickListener { private val viewModel by viewModel { parametersOf(requireNotNull(arguments?.getParcelableArrayList(KEY_MANGA_LIST)).map { it.manga }) @@ -45,7 +43,7 @@ class FavouriteCategoriesBottomSheet : super.onViewCreated(view, savedInstanceState) adapter = MangaCategoriesAdapter(this) binding.recyclerViewCategories.adapter = adapter - binding.toolbar.setOnMenuItemClickListener(this) + binding.buttonDone.setOnClickListener(this) binding.itemCreate.setOnClickListener(this) viewModel.content.observe(viewLifecycleOwner, this::onContentChanged) @@ -57,19 +55,10 @@ class FavouriteCategoriesBottomSheet : super.onDestroyView() } - override fun onMenuItemClick(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.action_done -> { - dismiss() - true - } - else -> false - } - } - override fun onClick(v: View) { when (v.id) { R.id.item_create -> startActivity(FavouritesCategoryEditActivity.newIntent(requireContext())) + R.id.button_done -> dismiss() } } diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt index b27629ce6..fe0daa58c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt @@ -7,6 +7,7 @@ import android.view.MenuItem import androidx.core.view.MenuProvider import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.koitharu.kotatsu.R +import com.google.android.material.R as materialR class HistoryListMenuProvider( private val context: Context, @@ -19,9 +20,10 @@ class HistoryListMenuProvider( override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { R.id.action_clear_history -> { - MaterialAlertDialogBuilder(context) + MaterialAlertDialogBuilder(context, materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered) .setTitle(R.string.clear_history) .setMessage(R.string.text_clear_history_prompt) + .setIcon(R.drawable.ic_delete) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.clear) { _, _ -> viewModel.clearHistory() diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 28556ac48..568bb6427 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -9,7 +9,6 @@ import androidx.collection.ArraySet import androidx.core.graphics.Insets import androidx.core.view.isNotEmpty import androidx.core.view.updatePadding -import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.GridLayoutManager import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.snackbar.Snackbar diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt index 55cd36559..b327ae030 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt @@ -299,8 +299,9 @@ class MainActivity : } override fun onClearSearchHistory() { - MaterialAlertDialogBuilder(this) + MaterialAlertDialogBuilder(this, materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered) .setTitle(R.string.clear_search_history) + .setIcon(R.drawable.ic_clear_all) .setMessage(R.string.text_clear_search_history_prompt) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.clear) { _, _ -> diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt index bf3c865eb..0da2ee55f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt @@ -10,6 +10,11 @@ import android.view.View import android.view.WindowManager import android.view.inputmethod.EditorInfo import android.widget.TextView +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK +import androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS +import androidx.biometric.BiometricPrompt +import androidx.biometric.BiometricPrompt.AuthenticationCallback import androidx.core.graphics.Insets import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R @@ -17,8 +22,11 @@ import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityProtectBinding import org.koitharu.kotatsu.utils.ext.getDisplayMessage -class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, - TextWatcher, View.OnClickListener { +class ProtectActivity : + BaseActivity(), + TextView.OnEditorActionListener, + TextWatcher, + View.OnClickListener { private val viewModel by viewModel() @@ -39,7 +47,9 @@ class ProtectActivity : BaseActivity(), TextView.OnEdito finishAfterTransition() } - binding.editPassword.requestFocus() + if (!useFingerprint()) { + binding.editPassword.requestFocus() + } } override fun onWindowInsetsChanged(insets: Insets) { @@ -85,6 +95,31 @@ class ProtectActivity : BaseActivity(), TextView.OnEdito binding.layoutPassword.isEnabled = !isLoading } + private fun useFingerprint(): Boolean { + if (!viewModel.isBiometricEnabled) { + return false + } + if (BiometricManager.from(this).canAuthenticate(BIOMETRIC_WEAK) != BIOMETRIC_SUCCESS) { + return false + } + val prompt = BiometricPrompt(this, BiometricCallback()) + val promptInfo = BiometricPrompt.PromptInfo.Builder() + .setAllowedAuthenticators(BIOMETRIC_WEAK) + .setTitle(getString(R.string.app_name)) + .setConfirmationRequired(false) + .setNegativeButtonText(getString(android.R.string.cancel)) + .build() + prompt.authenticate(promptInfo) + return true + } + + private inner class BiometricCallback : AuthenticationCallback() { + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + super.onAuthenticationSucceeded(result) + viewModel.unlock() + } + } + companion object { private const val EXTRA_INTENT = "src_intent" diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt index 07646482b..85ffe23cb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt @@ -19,6 +19,9 @@ class ProtectViewModel( val onUnlockSuccess = SingleLiveEvent() + val isBiometricEnabled + get() = settings.isBiometricProtectionEnabled + fun tryUnlock(password: String) { if (job?.isActive == true) { return @@ -27,12 +30,16 @@ class ProtectViewModel( val passwordHash = password.md5() val appPasswordHash = settings.appPassword if (passwordHash == appPasswordHash) { - protectHelper.unlock() - onUnlockSuccess.call(Unit) + unlock() } else { delay(PASSWORD_COMPARE_DELAY) throw WrongPasswordException() } } } + + fun unlock() { + protectHelper.unlock() + onUnlockSuccess.call(Unit) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt index 30b696297..d3980c687 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt @@ -16,6 +16,7 @@ abstract class BasePageHolder( exceptionResolver: ExceptionResolver ) : RecyclerView.ViewHolder(binding.root), PageHolderDelegate.Callback { + @Suppress("LeakingThis") protected val delegate = PageHolderDelegate(loader, settings, this, exceptionResolver) protected val bindingInfo = LayoutPageInfoBinding.bind(binding.root) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt index b6adc87b1..d097c1bc2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt @@ -11,10 +11,11 @@ import org.koitharu.kotatsu.utils.ext.resetTransformations import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine +@Suppress("LeakingThis") abstract class BaseReaderAdapter>( private val loader: PageLoader, private val settings: AppSettings, - private val exceptionResolver: ExceptionResolver + private val exceptionResolver: ExceptionResolver, ) : RecyclerView.Adapter() { private val differ = AsyncListDiffer(this, DiffCallback()) diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt index 3d075c1b3..ea5551701 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt @@ -3,7 +3,6 @@ package org.koitharu.kotatsu.search.ui import android.content.Context import android.content.Intent import android.os.Bundle -import android.os.Parcelable import android.view.ViewGroup import androidx.appcompat.widget.SearchView import androidx.core.graphics.Insets diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt index 8b82b327c..b05b31e76 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -89,7 +89,7 @@ class SettingsActivity : val fm = supportFragmentManager val fragment = fm.fragmentFactory.instantiate(classLoader, pref.fragment ?: return false) fragment.arguments = pref.extras - // fragment.setTargetFragment(caller, 0) + fragment.setTargetFragment(caller, 0) openFragment(fragment) return true } @@ -118,6 +118,7 @@ class SettingsActivity : val fragment = when (intent?.action) { ACTION_READER -> ReaderSettingsFragment() ACTION_SUGGESTIONS -> SuggestionsSettingsFragment() + ACTION_TRACKER -> TrackerSettingsFragment() ACTION_SOURCE -> SourceSettingsFragment.newInstance( intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL ) @@ -133,6 +134,7 @@ class SettingsActivity : private const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS" private const val ACTION_SUGGESTIONS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SUGGESTIONS" + private const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER" private const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS" private const val EXTRA_SOURCE = "source" @@ -146,6 +148,10 @@ class SettingsActivity : Intent(context, SettingsActivity::class.java) .setAction(ACTION_SUGGESTIONS) + fun newTrackerSettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_TRACKER) + fun newSourceSettingsIntent(context: Context, source: MangaSource) = Intent(context, SettingsActivity::class.java) .setAction(ACTION_SOURCE) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt index a0145362a..f88a8dad9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt @@ -1,5 +1,7 @@ package org.koitharu.kotatsu.settings.protect +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -7,9 +9,11 @@ import android.view.KeyEvent import android.view.View import android.view.WindowManager import android.view.inputmethod.EditorInfo +import android.widget.CompoundButton import android.widget.TextView import androidx.core.graphics.Insets import androidx.core.view.isGone +import androidx.core.view.isVisible import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity @@ -18,7 +22,7 @@ import org.koitharu.kotatsu.databinding.ActivitySetupProtectBinding private const val MIN_PASSWORD_LENGTH = 4 class ProtectSetupActivity : BaseActivity(), TextWatcher, - View.OnClickListener, TextView.OnEditorActionListener { + View.OnClickListener, TextView.OnEditorActionListener, CompoundButton.OnCheckedChangeListener { private val viewModel by viewModel() @@ -31,6 +35,9 @@ class ProtectSetupActivity : BaseActivity(), TextWa binding.buttonNext.setOnClickListener(this) binding.buttonCancel.setOnClickListener(this) + binding.switchBiometric.isChecked = viewModel.isBiometricEnabled + binding.switchBiometric.setOnCheckedChangeListener(this) + viewModel.isSecondStep.observe(this, this::onStepChanged) viewModel.onPasswordSet.observe(this) { finishAfterTransition() @@ -62,6 +69,10 @@ class ProtectSetupActivity : BaseActivity(), TextWa } } + override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) { + viewModel.setBiometricEnabled(isChecked) + } + override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean { return if (actionId == EditorInfo.IME_ACTION_DONE && binding.buttonNext.isEnabled) { binding.buttonNext.performClick() @@ -85,6 +96,7 @@ class ProtectSetupActivity : BaseActivity(), TextWa private fun onStepChanged(isSecondStep: Boolean) { binding.buttonCancel.isGone = isSecondStep + binding.switchBiometric.isVisible = isSecondStep && isBiometricAvailable() if (isSecondStep) { binding.layoutPassword.helperText = getString(R.string.repeat_password) binding.buttonNext.setText(R.string.confirm) @@ -93,4 +105,9 @@ class ProtectSetupActivity : BaseActivity(), TextWa binding.buttonNext.setText(R.string.next) } } + + private fun isBiometricAvailable(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt index 1244c836e..c9013d23d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt @@ -22,6 +22,9 @@ class ProtectSetupViewModel( val onPasswordMismatch = SingleLiveEvent() val onClearText = SingleLiveEvent() + val isBiometricEnabled + get() = settings.isBiometricProtectionEnabled + fun onNextClick(password: String) { if (firstPassword.value == null) { firstPassword.value = password @@ -35,4 +38,8 @@ class ProtectSetupViewModel( } } } + + fun setBiometricEnabled(isEnabled: Boolean) { + settings.isBiometricProtectionEnabled = isEnabled + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt index e8044fdde..10453c27e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt @@ -106,7 +106,13 @@ class SourcesSettingsFragment : searchView.queryHint = searchMenuItem.title } - override fun onMenuItemSelected(menuItem: MenuItem): Boolean = false + override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { + R.id.action_disable_all -> { + viewModel.disableAll() + true + } + else -> false + } override fun onMenuItemActionExpand(item: MenuItem?): Boolean { (activity as? AppBarOwner)?.appBar?.setExpanded(false, true) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt index da3eba14f..3a5340fe8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt @@ -7,6 +7,7 @@ import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.model.getLocaleTitle import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem import org.koitharu.kotatsu.utils.ext.map @@ -58,6 +59,13 @@ class SourcesSettingsViewModel( buildList() } + fun disableAll() { + settings.hiddenSources = settings.getMangaSources(includeHidden = true).mapToSet { + it.name + } + buildList() + } + fun expandOrCollapse(headerId: String?) { if (headerId in expandedGroups) { expandedGroups.remove(headerId) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt b/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt index 1bf8b7856..3920cb32c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt @@ -29,6 +29,6 @@ class RingtonePickContract(private val title: String?) : ActivityResultContract< } override fun parseResult(resultCode: Int, intent: Intent?): Uri? { - return intent?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) + return intent?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt b/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt index d334f31ef..398a0a0f0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt @@ -32,14 +32,14 @@ class SuggestionRepository( suspend fun replace(suggestions: Iterable) { db.withTransaction { db.suggestionDao.deleteAll() - suggestions.forEach { x -> - val tags = x.manga.tags.toEntities() + suggestions.forEach { (manga, relevance) -> + val tags = manga.tags.toEntities() db.tagsDao.upsert(tags) - db.mangaDao.upsert(x.manga.toEntity(), tags) + db.mangaDao.upsert(manga.toEntity(), tags) db.suggestionDao.upsert( SuggestionEntity( - mangaId = x.manga.id, - relevance = x.relevance, + mangaId = manga.id, + relevance = relevance, createdAt = System.currentTimeMillis(), ) ) diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedMenuProvider.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedMenuProvider.kt index 6787ff7da..655f02f8b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedMenuProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedMenuProvider.kt @@ -9,6 +9,7 @@ import androidx.core.view.MenuProvider import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.tracker.work.TrackWorker class FeedMenuProvider( @@ -43,6 +44,11 @@ class FeedMenuProvider( }.show() true } + R.id.action_settings -> { + val intent = SettingsActivity.newTrackerSettingsIntent(context) + context.startActivity(intent) + true + } else -> false } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ScreenOrientationHelper.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ScreenOrientationHelper.kt index 6c856f4d4..4cecbd2a8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ScreenOrientationHelper.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ScreenOrientationHelper.kt @@ -35,7 +35,7 @@ class ScreenOrientationHelper(private val activity: Activity) { isLandscape = !isLandscape } - fun observeAutoOrientation() = callbackFlow { + fun observeAutoOrientation() = callbackFlow { val observer = object : ContentObserver(Handler(activity.mainLooper)) { override fun onChange(selfChange: Boolean) { trySendBlocking(isAutoRotationEnabled) diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/InsetsExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/InsetsExt.kt deleted file mode 100644 index 7276dab57..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/InsetsExt.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.koitharu.kotatsu.utils.ext - -import android.view.View -import androidx.core.graphics.Insets - -fun Insets.getStart(view: View): Int { - return if (view.layoutDirection == View.LAYOUT_DIRECTION_RTL) { - right - } else { - left - } -} - -fun Insets.getEnd(view: View): Int { - return if (view.layoutDirection == View.LAYOUT_DIRECTION_RTL) { - left - } else { - right - } -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt index e3cd66b43..03123c692 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt @@ -4,11 +4,10 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.lifecycle.liveData -import kotlinx.coroutines.Deferred -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.flow.Flow import org.koitharu.kotatsu.utils.BufferedObserver +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext fun LiveData.observeNotNull(owner: LifecycleOwner, observer: Observer) { this.observe(owner) { diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt index 67c30830e..586e40eef 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt @@ -5,8 +5,6 @@ import android.graphics.Rect import android.view.View import android.view.ViewGroup import android.view.inputmethod.InputMethodManager -import androidx.annotation.StringRes -import androidx.appcompat.widget.TooltipCompat import androidx.core.view.children import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt index 3e4125a21..3a2142032 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt @@ -7,6 +7,9 @@ import android.widget.RemoteViewsService import coil.ImageLoader import coil.executeBlocking import coil.request.ImageRequest +import coil.size.Scale +import coil.size.Size +import coil.transform.RoundedCornersTransformation import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaIntent @@ -22,9 +25,15 @@ class RecentListFactory( ) : RemoteViewsService.RemoteViewsFactory { private val dataSet = ArrayList() + private val transformation = RoundedCornersTransformation( + context.resources.getDimension(R.dimen.appwidget_corner_radius_inner) + ) + private val coverSize = Size( + context.resources.getDimensionPixelSize(R.dimen.widget_cover_width), + context.resources.getDimensionPixelSize(R.dimen.widget_cover_height), + ) - override fun onCreate() { - } + override fun onCreate() = Unit override fun getLoadingView() = null @@ -45,6 +54,9 @@ class RecentListFactory( val cover = coil.executeBlocking( ImageRequest.Builder(context) .data(item.coverUrl) + .size(coverSize) + .scale(Scale.FILL) + .transformations(transformation) .build() ).requireBitmap() views.setImageViewBitmap(R.id.imageView_cover, cover) @@ -61,6 +73,5 @@ class RecentListFactory( override fun getViewTypeCount() = 1 - override fun onDestroy() { - } + override fun onDestroy() = Unit } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt index b7eea4171..44a2cc632 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt @@ -4,15 +4,12 @@ import android.app.Activity import android.appwidget.AppWidgetManager import android.content.Intent import android.os.Bundle -import android.view.Menu -import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.core.graphics.Insets +import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.divider.MaterialDividerItemDecoration import com.google.android.material.snackbar.Snackbar import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R @@ -26,7 +23,7 @@ import org.koitharu.kotatsu.widget.shelf.model.CategoryItem import com.google.android.material.R as materialR class ShelfConfigActivity : BaseActivity(), - OnListItemClickListener { + OnListItemClickListener, View.OnClickListener { private val viewModel by viewModel() @@ -41,10 +38,9 @@ class ShelfConfigActivity : BaseActivity(), setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material) } adapter = CategorySelectAdapter(this) - binding.recyclerView.addItemDecoration( - MaterialDividerItemDecoration(this, RecyclerView.VERTICAL) - ) binding.recyclerView.adapter = adapter + binding.buttonDone.isVisible = true + binding.buttonDone.setOnClickListener(this) binding.fabAdd.hide() val appWidgetId = intent?.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, @@ -61,23 +57,18 @@ class ShelfConfigActivity : BaseActivity(), viewModel.onError.observe(this, this::onError) } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.opt_config, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { - R.id.action_done -> { - config.categoryId = viewModel.checkedId - updateWidget() - setResult( - Activity.RESULT_OK, - Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, config.widgetId) - ) - finish() - true + override fun onClick(v: View) { + when (v.id) { + R.id.button_done -> { + config.categoryId = viewModel.checkedId + updateWidget() + setResult( + Activity.RESULT_OK, + Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, config.widgetId) + ) + finish() + } } - else -> super.onOptionsItemSelected(item) } override fun onItemClick(item: CategoryItem, view: View) { diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt index 61fd0d9ab..58b9fef3a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt @@ -7,6 +7,9 @@ import android.widget.RemoteViewsService import coil.ImageLoader import coil.executeBlocking import coil.request.ImageRequest +import coil.size.Scale +import coil.size.Size +import coil.transform.RoundedCornersTransformation import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaIntent @@ -20,14 +23,20 @@ class ShelfListFactory( private val context: Context, private val favouritesRepository: FavouritesRepository, private val coil: ImageLoader, - widgetId: Int + widgetId: Int, ) : RemoteViewsService.RemoteViewsFactory { private val dataSet = ArrayList() private val config = AppWidgetConfig(context, widgetId) + private val transformation = RoundedCornersTransformation( + context.resources.getDimension(R.dimen.appwidget_corner_radius_inner) + ) + private val coverSize = Size( + context.resources.getDimensionPixelSize(R.dimen.widget_cover_width), + context.resources.getDimensionPixelSize(R.dimen.widget_cover_height), + ) - override fun onCreate() { - } + override fun onCreate() = Unit override fun getLoadingView() = null @@ -56,6 +65,9 @@ class ShelfListFactory( val cover = coil.executeBlocking( ImageRequest.Builder(context) .data(item.coverUrl) + .size(coverSize) + .scale(Scale.FILL) + .transformations(transformation) .build() ).requireBitmap() views.setImageViewBitmap(R.id.imageView_cover, cover) diff --git a/app/src/main/res/color/selector_switch_thumb.xml b/app/src/main/res/color/selector_switch_thumb.xml deleted file mode 100644 index ef1a3cc36..000000000 --- a/app/src/main/res/color/selector_switch_thumb.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/color/selector_switch_track.xml b/app/src/main/res/color/selector_switch_track.xml deleted file mode 100644 index 3779a794a..000000000 --- a/app/src/main/res/color/selector_switch_track.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/badge.xml b/app/src/main/res/drawable/bg_appwidget_card.xml similarity index 50% rename from app/src/main/res/drawable/badge.xml rename to app/src/main/res/drawable/bg_appwidget_card.xml index a15864699..35a460504 100644 --- a/app/src/main/res/drawable/badge.xml +++ b/app/src/main/res/drawable/bg_appwidget_card.xml @@ -2,6 +2,6 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_expand_less.xml b/app/src/main/res/drawable/ic_expand_less.xml index 466a91a2e..86c53a93b 100644 --- a/app/src/main/res/drawable/ic_expand_less.xml +++ b/app/src/main/res/drawable/ic_expand_less.xml @@ -1,7 +1,11 @@ - \ No newline at end of file + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportWidth="24" + android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/ic_expand_more.xml b/app/src/main/res/drawable/ic_expand_more.xml index f7cae8b82..63e31a16f 100644 --- a/app/src/main/res/drawable/ic_expand_more.xml +++ b/app/src/main/res/drawable/ic_expand_more.xml @@ -1,7 +1,11 @@ - \ No newline at end of file + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportWidth="24" + android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/ic_list_add.xml b/app/src/main/res/drawable/ic_list_add.xml deleted file mode 100644 index 26ca07f23..000000000 --- a/app/src/main/res/drawable/ic_list_add.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_pause.xml b/app/src/main/res/drawable/ic_pause.xml deleted file mode 100644 index e63766078..000000000 --- a/app/src/main/res/drawable/ic_pause.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_resume.xml b/app/src/main/res/drawable/ic_resume.xml deleted file mode 100644 index 448628b18..000000000 --- a/app/src/main/res/drawable/ic_resume.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/switch_thumb.xml b/app/src/main/res/drawable/switch_thumb.xml deleted file mode 100644 index 548ff3bd8..000000000 --- a/app/src/main/res/drawable/switch_thumb.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/switch_track.xml b/app/src/main/res/drawable/switch_track.xml deleted file mode 100644 index ff149c727..000000000 --- a/app/src/main/res/drawable/switch_track.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_categories.xml b/app/src/main/res/layout/activity_categories.xml index ff28de4f7..e427c9be0 100644 --- a/app/src/main/res/layout/activity_categories.xml +++ b/app/src/main/res/layout/activity_categories.xml @@ -8,9 +8,9 @@ + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> + app:layout_collapseMode="pin"> + +