Indicate obsolete extensions (#2494)

* Indicate obsolete extensions

* Make obsolete indicators red

* Move obsolete extensions up the list

* Add base button theme for holder

* Use red button color state instead of explicit text color
This commit is contained in:
arkon 2020-01-12 18:27:04 -05:00 committed by GitHub
parent 6b5742c1ff
commit f3e228e8a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 87 additions and 15 deletions

View File

@ -71,7 +71,7 @@ class ExtensionManager(
private set(value) { private set(value) {
field = value field = value
availableExtensionsRelay.call(value) availableExtensionsRelay.call(value)
setUpdateFieldOfInstalledExtensions(value) updatedInstalledExtensionsStatuses(value)
} }
/** /**
@ -158,18 +158,25 @@ class ExtensionManager(
* *
* @param availableExtensions The list of extensions given by the [api]. * @param availableExtensions The list of extensions given by the [api].
*/ */
private fun setUpdateFieldOfInstalledExtensions(availableExtensions: List<Extension.Available>) { private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
val mutInstalledExtensions = installedExtensions.toMutableList() val mutInstalledExtensions = installedExtensions.toMutableList()
var changed = false var changed = false
for ((index, installedExt) in mutInstalledExtensions.withIndex()) { for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
val pkgName = installedExt.pkgName val pkgName = installedExt.pkgName
val availableExt = availableExtensions.find { it.pkgName == pkgName } ?: continue val availableExt = availableExtensions.find { it.pkgName == pkgName }
val hasUpdate = availableExt.versionCode > installedExt.versionCode if (availableExt == null) {
if (installedExt.hasUpdate != hasUpdate) { if (!installedExt.isObsolete) {
mutInstalledExtensions[index] = installedExt.copy(hasUpdate = hasUpdate) mutInstalledExtensions[index] = installedExt.copy(isObsolete = true)
changed = true changed = true
}
} else {
val hasUpdate = availableExt.versionCode > installedExt.versionCode
if (installedExt.hasUpdate != hasUpdate) {
mutInstalledExtensions[index] = installedExt.copy(hasUpdate = hasUpdate)
changed = true
}
} }
} }
if (changed) { if (changed) {

View File

@ -16,7 +16,8 @@ sealed class Extension {
override val versionCode: Int, override val versionCode: Int,
val sources: List<Source>, val sources: List<Source>,
override val lang: String, override val lang: String,
val hasUpdate: Boolean = false) : Extension() val hasUpdate: Boolean = false,
val isObsolete: Boolean = false) : Extension()
data class Available(override val name: String, data class Available(override val name: String,
override val pkgName: String, override val pkgName: String,

View File

@ -69,6 +69,10 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
presenter.uninstallExtension() presenter.uninstallExtension()
} }
if (extension.isObsolete) {
extension_obsolete.visibility = View.VISIBLE
}
val themedContext by lazy { getPreferenceThemeContext() } val themedContext by lazy { getPreferenceThemeContext() }
val manager = PreferenceManager(themedContext) val manager = PreferenceManager(themedContext)
manager.preferenceDataStore = EmptyPreferenceDataStore() manager.preferenceDataStore = EmptyPreferenceDataStore()

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.ui.extension package eu.kanade.tachiyomi.ui.extension
import android.view.View import android.view.View
import androidx.core.content.ContextCompat
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
@ -52,11 +54,15 @@ class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
bindButton(item) bindButton(item)
} }
@Suppress("ResourceType")
fun bindButton(item: ExtensionItem) = with(ext_button) { fun bindButton(item: ExtensionItem) = with(ext_button) {
isEnabled = true isEnabled = true
isClickable = true isClickable = true
isActivated = false isActivated = false
background = VectorDrawableCompat.create(resources!!, R.drawable.button_bg_transparent, null)
setTextColor(ContextCompat.getColorStateList(context, R.drawable.button_bg_transparent))
val extension = item.extension val extension = item.extension
val installStep = item.installStep val installStep = item.installStep
@ -73,11 +79,21 @@ class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
isClickable = false isClickable = false
} }
} else if (extension is Extension.Installed) { } else if (extension is Extension.Installed) {
if (extension.hasUpdate) { when {
isActivated = true extension.hasUpdate -> {
setText(R.string.ext_update) isActivated = true
} else { setText(R.string.ext_update)
setText(R.string.ext_details) }
extension.isObsolete -> {
// Red outline
background = VectorDrawableCompat.create(resources, R.drawable.button_bg_error, null)
setTextColor(ContextCompat.getColorStateList(context, R.drawable.button_bg_error))
setText(R.string.ext_obsolete)
}
else -> {
setText(R.string.ext_details)
}
} }
} else if (extension is Extension.Untrusted) { } else if (extension is Extension.Untrusted) {
setText(R.string.ext_trust) setText(R.string.ext_trust)

View File

@ -62,7 +62,7 @@ open class ExtensionPresenter(
val items = mutableListOf<ExtensionItem>() val items = mutableListOf<ExtensionItem>()
val installedSorted = installed.sortedWith(compareBy({ !it.hasUpdate }, { it.pkgName })) val installedSorted = installed.sortedWith(compareBy({ !it.hasUpdate }, { !it.isObsolete }, { it.pkgName }))
val untrustedSorted = untrusted.sortedBy { it.pkgName } val untrustedSorted = untrusted.sortedBy { it.pkgName }
val availableSorted = available val availableSorted = available
// Filter out already installed extensions and disabled languages // Filter out already installed extensions and disabled languages

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:color="@color/md_white_1000">
<shape android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="@color/red_error" />
<padding android:left="8dp" android:right="8dp" />
</shape>
</item>
<item android:state_enabled="false" android:color="@color/textColorHintLight">
<shape android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="@android:color/transparent" />
<stroke android:width="1dp" android:color="@color/textColorHintLight" />
<padding android:left="8dp" android:right="8dp" />
</shape>
</item>
<item android:color="@color/red_error">
<shape android:color="@color/red_error" android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="@android:color/transparent" />
<stroke android:width="1dp" android:color="@color/red_error" />
<padding android:left="8dp" android:right="8dp" />
</shape>
</item>
</selector>

View File

@ -60,6 +60,22 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
tools:text="eu.kanade.tachiyomi.extension.en.myext"/> tools:text="eu.kanade.tachiyomi.extension.en.myext"/>
<TextView
android:id="@+id/extension_obsolete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@color/red_error"
android:gravity="center"
android:padding="16dp"
android:text="@string/obsolete_extension_message"
android:textColor="@android:color/white"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/extension_pkg" />
<Button <Button
android:id="@+id/extension_uninstall_button" android:id="@+id/extension_uninstall_button"
android:layout_width="0dp" android:layout_width="0dp"
@ -71,7 +87,7 @@
style="@style/Theme.Widget.Button.Colored" style="@style/Theme.Widget.Button.Colored"
app:layout_constraintStart_toStartOf="@id/guideline" app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/extension_pkg" /> app:layout_constraintTop_toBottomOf="@id/extension_obsolete" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/extension_prefs_recycler" android:id="@+id/extension_prefs_recycler"

View File

@ -161,6 +161,7 @@
<string name="all_lang">All</string> <string name="all_lang">All</string>
<string name="ext_details">Details</string> <string name="ext_details">Details</string>
<string name="ext_update">Update</string> <string name="ext_update">Update</string>
<string name="ext_obsolete">Obsolete</string>
<string name="ext_install">Install</string> <string name="ext_install">Install</string>
<string name="ext_pending">Pending</string> <string name="ext_pending">Pending</string>
<string name="ext_downloading">Downloading</string> <string name="ext_downloading">Downloading</string>
@ -173,6 +174,7 @@
<string name="ext_available">Available</string> <string name="ext_available">Available</string>
<string name="untrusted_extension">Untrusted extension</string> <string name="untrusted_extension">Untrusted extension</string>
<string name="untrusted_extension_message">This extension was signed with an untrusted certificate and wasn\'t activated.\n\nA malicious extension could read any login credentials stored in Tachiyomi or execute arbitrary code.\n\nBy trusting this certificate you accept these risks.</string> <string name="untrusted_extension_message">This extension was signed with an untrusted certificate and wasn\'t activated.\n\nA malicious extension could read any login credentials stored in Tachiyomi or execute arbitrary code.\n\nBy trusting this certificate you accept these risks.</string>
<string name="obsolete_extension_message">This extension is no longer available.</string>
<string name="ext_version_info">Version: %1$s</string> <string name="ext_version_info">Version: %1$s</string>
<string name="ext_language_info">Language: %1$s</string> <string name="ext_language_info">Language: %1$s</string>
<string name="ext_empty_preferences">No preferences to edit for this extension</string> <string name="ext_empty_preferences">No preferences to edit for this extension</string>