浏览代码

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
arkon 5 年之前
父节点
当前提交
f3e228e8a4

+ 15 - 8
app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt

@@ -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 hasUpdate = availableExt.versionCode > installedExt.versionCode
-            if (installedExt.hasUpdate != hasUpdate) {
-                mutInstalledExtensions[index] = installedExt.copy(hasUpdate = hasUpdate)
-                changed = true
+            val availableExt = availableExtensions.find { it.pkgName == pkgName }
+
+            if (availableExt == null) {
+                if (!installedExt.isObsolete) {
+                    mutInstalledExtensions[index] = installedExt.copy(isObsolete = 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) {

+ 2 - 1
app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt

@@ -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,

+ 4 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt

@@ -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()

+ 21 - 5
app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt

@@ -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) {
-                isActivated = true
-                setText(R.string.ext_update)
-            } else {
-                setText(R.string.ext_details)
+            when {
+                extension.hasUpdate -> {
+                    isActivated = true
+                    setText(R.string.ext_update)
+                }
+                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)

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionPresenter.kt

@@ -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

+ 26 - 0
app/src/main/res/drawable/button_bg_error.xml

@@ -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>

+ 17 - 1
app/src/main/res/layout/extension_detail_controller.xml

@@ -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"

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -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>