From e135871100583371242ae97f85e8ceb2f5e310b4 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Tue, 1 Jan 2019 22:53:07 +0100
Subject: [PATCH] Implement context-sensitive warnings for outdated cores

---
 .../quasseldroid/ui/chat/ChatActivity.kt      | 42 +++------
 .../chat/buffers/BufferViewConfigFragment.kt  | 13 +++
 .../quasseldroid/util/helper/ThemeHelper.kt   |  8 +-
 .../quasseldroid/util/ui/WarningBarView.kt    | 91 +++++++++++++++++++
 app/src/main/res/drawable/ic_alert.xml        |  9 ++
 app/src/main/res/layout-land/layout_main.xml  |  7 +-
 .../res/layout-sw600dp-land/layout_main.xml   |  7 +-
 .../main/res/layout/fragment_chat_list.xml    |  6 ++
 app/src/main/res/layout/layout_main.xml       |  7 +-
 ...tion_status.xml => widget_warning_bar.xml} | 16 ++--
 app/src/main/res/values/strings.xml           |  2 +
 app/src/main/res/values/styles_widgets.xml    | 12 +++
 .../viewmodel/QuasselViewModel.kt             |  5 +
 13 files changed, 179 insertions(+), 46 deletions(-)
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/WarningBarView.kt
 create mode 100644 app/src/main/res/drawable/ic_alert.xml
 rename app/src/main/res/layout/{widget_connection_status.xml => widget_warning_bar.xml} (77%)

diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index 41fa189d7..e9229a9d9 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -35,11 +35,8 @@ import android.view.Menu
 import android.view.MenuItem
 import android.view.View
 import android.widget.EditText
-import android.widget.LinearLayout
-import android.widget.TextView
 import android.widget.Toast
 import androidx.appcompat.app.ActionBarDrawerToggle
-import androidx.appcompat.widget.AppCompatImageView
 import androidx.appcompat.widget.Toolbar
 import androidx.core.content.pm.ShortcutInfoCompat
 import androidx.core.content.pm.ShortcutManagerCompat
@@ -97,9 +94,9 @@ import de.kuschku.quasseldroid.util.service.ServiceBoundActivity
 import de.kuschku.quasseldroid.util.ui.DragInterceptBottomSheetBehavior
 import de.kuschku.quasseldroid.util.ui.MaterialContentLoadingProgressBar
 import de.kuschku.quasseldroid.util.ui.NickCountDrawable
+import de.kuschku.quasseldroid.util.ui.WarningBarView
 import de.kuschku.quasseldroid.viewmodel.EditorViewModel
 import de.kuschku.quasseldroid.viewmodel.data.BufferData
-import me.zhanghai.android.materialprogressbar.MaterialProgressBar
 import org.threeten.bp.Instant
 import org.threeten.bp.ZoneId
 import org.threeten.bp.format.DateTimeFormatter
@@ -121,16 +118,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   lateinit var progressBar: MaterialContentLoadingProgressBar
 
   @BindView(R.id.connection_status)
-  lateinit var connectionStatusDisplay: LinearLayout
-
-  @BindView(R.id.connection_status_icon)
-  lateinit var connectionStatusDisplayIcon: AppCompatImageView
-
-  @BindView(R.id.connection_status_progress)
-  lateinit var connectionStatusDisplayProgress: MaterialProgressBar
-
-  @BindView(R.id.connection_status_text)
-  lateinit var connectionStatusDisplayText: TextView
+  lateinit var connectionStatusDisplay: WarningBarView
 
   @BindView(R.id.autocomplete_list)
   lateinit var autoCompleteList: RecyclerView
@@ -643,28 +631,22 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
         ConnectionState.CLOSED     -> {
           progressBar.visibility = View.INVISIBLE
 
-          connectionStatusDisplay.visibility = View.VISIBLE
-          connectionStatusDisplayText.text = getString(R.string.label_status_disconnected)
-          connectionStatusDisplayIcon.visibility = View.VISIBLE
-          connectionStatusDisplayProgress.visibility = View.GONE
+          connectionStatusDisplay.setMode(WarningBarView.MODE_ICON)
+          connectionStatusDisplay.setText(getString(R.string.label_status_disconnected))
         }
         ConnectionState.CONNECTING -> {
           progressBar.visibility = View.VISIBLE
           progressBar.isIndeterminate = true
 
-          connectionStatusDisplay.visibility = View.VISIBLE
-          connectionStatusDisplayText.text = getString(R.string.label_status_connecting)
-          connectionStatusDisplayIcon.visibility = View.GONE
-          connectionStatusDisplayProgress.visibility = View.VISIBLE
+          connectionStatusDisplay.setMode(WarningBarView.MODE_PROGRESS)
+          connectionStatusDisplay.setText(getString(R.string.label_status_connecting))
         }
         ConnectionState.HANDSHAKE  -> {
           progressBar.visibility = View.VISIBLE
           progressBar.isIndeterminate = true
 
-          connectionStatusDisplay.visibility = View.VISIBLE
-          connectionStatusDisplayText.text = getString(R.string.label_status_handshake)
-          connectionStatusDisplayIcon.visibility = View.GONE
-          connectionStatusDisplayProgress.visibility = View.VISIBLE
+          connectionStatusDisplay.setMode(WarningBarView.MODE_PROGRESS)
+          connectionStatusDisplay.setText(getString(R.string.label_status_handshake))
         }
         ConnectionState.INIT       -> {
           progressBar.visibility = View.VISIBLE
@@ -673,14 +655,12 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
           progressBar.progress = progress
           progressBar.max = max
 
-          connectionStatusDisplay.visibility = View.VISIBLE
-          connectionStatusDisplayText.text = getString(R.string.label_status_init)
-          connectionStatusDisplayIcon.visibility = View.GONE
-          connectionStatusDisplayProgress.visibility = View.VISIBLE
+          connectionStatusDisplay.setMode(WarningBarView.MODE_PROGRESS)
+          connectionStatusDisplay.setText(getString(R.string.label_status_init))
         }
         ConnectionState.CONNECTED  -> {
           progressBar.visibility = View.INVISIBLE
-          connectionStatusDisplay.visibility = View.GONE
+          connectionStatusDisplay.setMode(WarningBarView.MODE_NONE)
         }
       }
     })
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
index 8bfeb1dac..f9a396634 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
@@ -37,6 +37,7 @@ import de.kuschku.libquassel.protocol.Buffer_Activity
 import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.protocol.Message_Type
 import de.kuschku.libquassel.quassel.BufferInfo
+import de.kuschku.libquassel.quassel.ExtendedFeature
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.util.flag.hasFlag
@@ -55,6 +56,7 @@ import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
+import de.kuschku.quasseldroid.util.ui.WarningBarView
 import de.kuschku.quasseldroid.viewmodel.data.BufferHiddenState
 import de.kuschku.quasseldroid.viewmodel.data.BufferListItem
 import de.kuschku.quasseldroid.viewmodel.data.BufferState
@@ -71,6 +73,9 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
   @BindView(R.id.chatList)
   lateinit var chatList: RecyclerView
 
+  @BindView(R.id.feature_context_bufferactivitysync)
+  lateinit var featureContextBufferactivitysync: WarningBarView
+
   @Inject
   lateinit var appearanceSettings: AppearanceSettings
 
@@ -279,6 +284,14 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
         hasRestoredChatListState = true
       }
     }
+
+    viewModel.features.toLiveData().observe(this, Observer {
+      featureContextBufferactivitysync.setMode(
+        if (it.hasFeature(ExtendedFeature.BufferActivitySync)) WarningBarView.MODE_NONE
+        else WarningBarView.MODE_ICON
+      )
+    })
+
     combineLatest(viewModel.bufferList,
                   viewModel.expandedNetworks,
                   viewModel.selectedBuffer).toLiveData().switchMapNotNull { a ->
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/helper/ThemeHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/helper/ThemeHelper.kt
index bf4d6ff1a..f5ff79eb8 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/helper/ThemeHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/helper/ThemeHelper.kt
@@ -28,7 +28,9 @@ inline fun <R> Resources.Theme.styledAttributes(vararg attributes: Int, f: Typed
   }
 
 inline fun <R> TypedArray.use(block: (TypedArray) -> R): R {
-  val result = block(this)
-  recycle()
-  return result
+  try {
+    return block(this)
+  } finally {
+    recycle()
+  }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/WarningBarView.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/WarningBarView.kt
new file mode 100644
index 000000000..e94d65615
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/WarningBarView.kt
@@ -0,0 +1,91 @@
+package de.kuschku.quasseldroid.util.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.annotation.IntDef
+import androidx.annotation.StringRes
+import androidx.appcompat.widget.AppCompatImageView
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.util.helper.use
+
+class WarningBarView : LinearLayout {
+  @BindView(R.id.icon)
+  lateinit var icon: AppCompatImageView
+
+  @BindView(R.id.progress)
+  lateinit var progress: View
+
+  @BindView(R.id.text)
+  lateinit var text: TextView
+
+  constructor(context: Context) :
+    this(context, null)
+
+  constructor(context: Context, attrs: AttributeSet?) :
+    this(context, attrs, 0)
+
+  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
+    super(context, attrs, defStyleAttr) {
+
+    LayoutInflater.from(context).inflate(R.layout.widget_warning_bar, this, true)
+    ButterKnife.bind(this)
+
+    context.theme.obtainStyledAttributes(attrs, R.styleable.WarningBarView, 0, 0).use {
+      if (it.hasValue(R.styleable.WarningBarView_icon))
+        icon.setImageDrawable(it.getDrawable(R.styleable.WarningBarView_icon))
+
+      if (it.hasValue(R.styleable.WarningBarView_text))
+        text.text = it.getString(R.styleable.WarningBarView_text)
+
+      if (it.hasValue(R.styleable.WarningBarView_mode))
+        setMode(it.getInt(R.styleable.WarningBarView_mode, MODE_NONE))
+    }
+  }
+
+  fun setText(content: String) {
+    text.text = content
+  }
+
+  fun setText(@StringRes content: Int) {
+    text.setText(content)
+  }
+
+  fun setMode(@WarningMode mode: Int) {
+    when (mode) {
+      MODE_NONE     -> {
+        visibility = View.GONE
+      }
+      MODE_TEXT     -> {
+        visibility = View.VISIBLE
+        icon.visibility = View.GONE
+        progress.visibility = View.GONE
+      }
+      MODE_ICON     -> {
+        visibility = View.VISIBLE
+        icon.visibility = View.VISIBLE
+        progress.visibility = View.GONE
+      }
+      MODE_PROGRESS -> {
+        visibility = View.VISIBLE
+        icon.visibility = View.GONE
+        progress.visibility = View.VISIBLE
+      }
+    }
+  }
+
+  @IntDef(value = [MODE_NONE, MODE_TEXT, MODE_ICON, MODE_PROGRESS])
+  annotation class WarningMode
+
+  companion object {
+    const val MODE_NONE = 0
+    const val MODE_TEXT = 1
+    const val MODE_ICON = 2
+    const val MODE_PROGRESS = 3
+  }
+}
diff --git a/app/src/main/res/drawable/ic_alert.xml b/app/src/main/res/drawable/ic_alert.xml
new file mode 100644
index 000000000..1a1c6a166
--- /dev/null
+++ b/app/src/main/res/drawable/ic_alert.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:width="24dp"
+  android:height="24dp"
+  android:viewportWidth="24"
+  android:viewportHeight="24">
+  <path
+    android:fillColor="#000"
+    android:pathData="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z" />
+</vector>
diff --git a/app/src/main/res/layout-land/layout_main.xml b/app/src/main/res/layout-land/layout_main.xml
index a25bb7be2..cac102c65 100644
--- a/app/src/main/res/layout-land/layout_main.xml
+++ b/app/src/main/res/layout-land/layout_main.xml
@@ -44,7 +44,12 @@
         android:layout_height="match_parent"
         tools:layout="@layout/fragment_messages" />
 
-      <include layout="@layout/widget_connection_status" />
+      <de.kuschku.quasseldroid.util.ui.WarningBarView
+        android:id="@+id/connection_status"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:icon="@drawable/ic_disconnected"
+        app:mode="none" />
     </FrameLayout>
 
     <androidx.recyclerview.widget.RecyclerView
diff --git a/app/src/main/res/layout-sw600dp-land/layout_main.xml b/app/src/main/res/layout-sw600dp-land/layout_main.xml
index cf5d0c86b..475bb3877 100644
--- a/app/src/main/res/layout-sw600dp-land/layout_main.xml
+++ b/app/src/main/res/layout-sw600dp-land/layout_main.xml
@@ -50,7 +50,12 @@
           android:layout_height="match_parent"
           tools:layout="@layout/fragment_messages" />
 
-        <include layout="@layout/widget_connection_status" />
+        <de.kuschku.quasseldroid.util.ui.WarningBarView
+          android:id="@+id/connection_status"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          app:icon="@drawable/ic_disconnected"
+          app:mode="none" />
       </FrameLayout>
 
       <androidx.recyclerview.widget.RecyclerView
diff --git a/app/src/main/res/layout/fragment_chat_list.xml b/app/src/main/res/layout/fragment_chat_list.xml
index 6706a0b83..4de32ee07 100644
--- a/app/src/main/res/layout/fragment_chat_list.xml
+++ b/app/src/main/res/layout/fragment_chat_list.xml
@@ -48,6 +48,12 @@
 
   </com.google.android.material.appbar.AppBarLayout>
 
+  <de.kuschku.quasseldroid.util.ui.WarningBarView
+    android:id="@+id/feature_context_bufferactivitysync"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    app:icon="@drawable/ic_alert"
+    app:text="@string/label_feature_context_bufferactivitysync" />
 
   <de.kuschku.quasseldroid.util.ui.fastscroll.views.FastScrollRecyclerView
     android:id="@+id/chatList"
diff --git a/app/src/main/res/layout/layout_main.xml b/app/src/main/res/layout/layout_main.xml
index 9f37466ae..9bcdc4822 100644
--- a/app/src/main/res/layout/layout_main.xml
+++ b/app/src/main/res/layout/layout_main.xml
@@ -50,7 +50,12 @@
           android:layout_height="match_parent"
           tools:layout="@layout/fragment_messages" />
 
-        <include layout="@layout/widget_connection_status" />
+        <de.kuschku.quasseldroid.util.ui.WarningBarView
+          android:id="@+id/connection_status"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          app:icon="@drawable/ic_disconnected"
+          app:mode="none" />
 
       </FrameLayout>
 
diff --git a/app/src/main/res/layout/widget_connection_status.xml b/app/src/main/res/layout/widget_warning_bar.xml
similarity index 77%
rename from app/src/main/res/layout/widget_connection_status.xml
rename to app/src/main/res/layout/widget_warning_bar.xml
index 1414ff1e9..29a35c059 100644
--- a/app/src/main/res/layout/widget_connection_status.xml
+++ b/app/src/main/res/layout/widget_warning_bar.xml
@@ -2,7 +2,6 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
-  android:id="@+id/connection_status"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="?colorBackgroundSnackbar"
@@ -11,21 +10,20 @@
   android:paddingTop="6dp"
   android:paddingRight="16dp"
   android:paddingBottom="6dp"
-  android:visibility="gone"
-  android:theme="?attr/actionBarTheme"
-  tools:visibility="visible">
+  android:theme="?attr/actionBarTheme">
 
   <androidx.appcompat.widget.AppCompatImageView
-    android:id="@+id/connection_status_icon"
+    android:id="@+id/icon"
     android:layout_width="20dp"
     android:layout_height="20dp"
-    app:srcCompat="@drawable/ic_disconnected"
+    android:layout_gravity="center_vertical"
     android:visibility="gone"
     app:tint="?android:textColorPrimary"
+    tools:srcCompat="@drawable/ic_alert"
     tools:visibility="visible" />
 
   <me.zhanghai.android.materialprogressbar.MaterialProgressBar
-    android:id="@+id/connection_status_progress"
+    android:id="@+id/progress"
     style="@style/Widget.MaterialProgressBar.ProgressBar"
     android:layout_width="20dp"
     android:layout_height="20dp"
@@ -34,12 +32,12 @@
     app:mpb_indeterminateTint="?android:textColorPrimary" />
 
   <TextView
-    android:id="@+id/connection_status_text"
+    android:id="@+id/text"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginStart="16dp"
     android:layout_marginLeft="16dp"
     android:textColor="?android:textColorPrimary"
-    tools:text="@string/label_status_connecting" />
+    tools:text="@string/label_feature_context_bufferactivitysync" />
 
 </LinearLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2c49f211a..cd7b72e6a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -137,6 +137,8 @@
   <string name="label_feature_richmessages">Required for seeing real names or IRCv3 or Gravatar avatars in messages</string>
   <string name="label_feature_backlogfiltertype">Required for receiving past notifications after connecting</string>
 
+  <string name="label_feature_context_bufferactivitysync">Quasseldroid cannot highlight unread chats. Upgrade your core to Quassel v0.13 to resolve this.</string>
+
   <string name="notification_channel_background" translatable="false">background</string>
   <string name="notification_channel_connection_title">Connection</string>
   <string name="notification_channel_highlight" translatable="false">highlight</string>
diff --git a/app/src/main/res/values/styles_widgets.xml b/app/src/main/res/values/styles_widgets.xml
index 4e88a61ea..feef8d4c3 100644
--- a/app/src/main/res/values/styles_widgets.xml
+++ b/app/src/main/res/values/styles_widgets.xml
@@ -335,4 +335,16 @@
   <style name="Widget.NavigationDrawerLayout" parent="">
     <item name="insetBackground">#4000</item>
   </style>
+
+  <!-- NavigationDrawerLayout -->
+  <declare-styleable name="WarningBarView">
+    <attr name="icon" format="reference" />
+    <attr name="text" format="string" />
+    <attr name="mode">
+      <enum name="none" value="0" />
+      <enum name="text" value="1" />
+      <enum name="icon" value="2" />
+      <enum name="progress" value="3" />
+    </attr>
+  </declare-styleable>
 </resources>
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
index 3b728f75a..843b2b583 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
@@ -21,8 +21,10 @@ package de.kuschku.quasseldroid.viewmodel
 
 import androidx.lifecycle.ViewModel
 import de.kuschku.libquassel.connection.ConnectionState
+import de.kuschku.libquassel.connection.Features
 import de.kuschku.libquassel.protocol.*
 import de.kuschku.libquassel.quassel.BufferInfo
+import de.kuschku.libquassel.quassel.QuasselFeatures
 import de.kuschku.libquassel.quassel.syncables.*
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.session.Backend
@@ -83,6 +85,9 @@ class QuasselViewModel : ViewModel() {
   val sessionManager = backend.mapMap(Backend::sessionManager)
   val session = sessionManager.mapSwitchMap(SessionManager::session)
   val rpcHandler = session.mapMapNullable(ISession::rpcHandler)
+  val features = session.mapMap(ISession::features)
+    .mapMap(Features::negotiated)
+    .mapOrElse(QuasselFeatures.empty())
 
   val connectionProgress = sessionManager.mapSwitchMap(SessionManager::connectionProgress)
     .mapOrElse(Triple(ConnectionState.DISCONNECTED, 0, 0))
-- 
GitLab