diff --git a/app/src/main/java/io/heckel/ntfy/db/Repository.kt b/app/src/main/java/io/heckel/ntfy/db/Repository.kt index 34e9eb2..a85b7b7 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Repository.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Repository.kt @@ -353,7 +353,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun addLastShareTopic(topic: String) { - val topics = (getLastShareTopics() + topic).takeLast(LAST_TOPICS_COUNT) + val topics = (getLastShareTopics().filterNot { it == topic } + topic).takeLast(LAST_TOPICS_COUNT) sharedPrefs.edit() .putString(SHARED_PREFS_LAST_TOPICS, topics.joinToString(separator = "\n")) .apply() @@ -437,7 +437,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL" const val SHARED_PREFS_LAST_TOPICS = "LastTopics" - private const val LAST_TOPICS_COUNT = 5 + private const val LAST_TOPICS_COUNT = 3 const val MUTED_UNTIL_SHOW_ALL = 0L const val MUTED_UNTIL_FOREVER = 1L diff --git a/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt b/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt index 1ce5c33..d0d41f9 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt @@ -13,7 +13,6 @@ import io.heckel.ntfy.R import io.heckel.ntfy.db.ConnectionState import io.heckel.ntfy.db.Repository import io.heckel.ntfy.db.Subscription -import io.heckel.ntfy.util.isDarkThemeOn import io.heckel.ntfy.util.topicShortUrl import java.text.DateFormat import java.util.* diff --git a/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt index bd807fa..8dc0b30 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt @@ -7,15 +7,15 @@ import android.os.Bundle import android.os.Parcelable import android.text.Editable import android.text.TextWatcher -import android.view.Menu -import android.view.MenuItem -import android.view.View +import android.view.* import android.widget.* import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.RecyclerView import com.google.android.material.textfield.TextInputLayout import io.heckel.ntfy.R import io.heckel.ntfy.app.Application +import io.heckel.ntfy.db.Subscription import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.util.* import kotlinx.coroutines.Dispatchers @@ -31,6 +31,9 @@ class ShareActivity : AppCompatActivity() { // Lazy-loaded things from Repository private lateinit var baseUrls: List + // Context-dependent things + private lateinit var appBaseUrl: String + // UI elements private lateinit var menu: Menu private lateinit var sendItem: MenuItem @@ -43,6 +46,7 @@ class ShareActivity : AppCompatActivity() { private lateinit var baseUrlLayout: TextInputLayout private lateinit var baseUrlText: AutoCompleteTextView private lateinit var useAnotherServerCheckbox: CheckBox + private lateinit var lastTopicsList: RecyclerView private lateinit var progress: ProgressBar private lateinit var errorText: TextView private lateinit var errorImage: ImageView @@ -60,6 +64,9 @@ class ShareActivity : AppCompatActivity() { // Show 'Back' button supportActionBar?.setDisplayHomeAsUpEnabled(true) + // Context-dependent things + appBaseUrl = getString(R.string.app_base_url) + // UI elements contentText = findViewById(R.id.share_content_text) contentImage = findViewById(R.id.share_content_image) @@ -73,6 +80,7 @@ class ShareActivity : AppCompatActivity() { baseUrlText = findViewById(R.id.share_base_url_text) //baseUrlText.background = topicText.background useAnotherServerCheckbox = findViewById(R.id.share_use_another_server_checkbox) + lastTopicsList = findViewById(R.id.share_last_topics) progress = findViewById(R.id.share_progress) progress.visibility = View.GONE errorText = findViewById(R.id.share_error_text) @@ -93,6 +101,7 @@ class ShareActivity : AppCompatActivity() { } contentText.addTextChangedListener(textWatcher) topicText.addTextChangedListener(textWatcher) + baseUrlText.addTextChangedListener(textWatcher) // Add behavior to "use another" checkbox useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked -> @@ -100,9 +109,25 @@ class ShareActivity : AppCompatActivity() { validateInput() } + // Populate "last topics" + val reversedLastTopics = repository.getLastShareTopics().reversed() + lastTopicsList.adapter = TopicAdapter(reversedLastTopics) { topicUrl -> + try { + val (baseUrl, topic) = splitTopicUrl(topicUrl) + topicText.text = topic + if (baseUrl == appBaseUrl) { + useAnotherServerCheckbox.isChecked = false + } else { + useAnotherServerCheckbox.isChecked = true + baseUrlText.setText(baseUrl) + } + } catch (e: Exception) { + Log.w(TAG, "Invalid topicUrl $topicUrl", e) + } + } + // Add baseUrl auto-complete behavior lifecycleScope.launch(Dispatchers.IO) { - val appBaseUrl = getString(R.string.app_base_url) baseUrls = repository.getSubscriptions() .groupBy { it.baseUrl } .map { it.key } @@ -111,14 +136,20 @@ class ShareActivity : AppCompatActivity() { val activity = this@ShareActivity activity.runOnUiThread { initBaseUrlDropdown(baseUrls, baseUrlText, baseUrlLayout) - useAnotherServerCheckbox.isChecked = baseUrls.count() == 1 + useAnotherServerCheckbox.isChecked = if (reversedLastTopics.isNotEmpty()) { + try { + val (baseUrl, _) = splitTopicUrl(reversedLastTopics.first()) + baseUrl != appBaseUrl + } catch (_: Exception) { + false + } + } else { + baseUrls.count() == 1 + } + baseUrlLayout.visibility = if (useAnotherServerCheckbox.isChecked) View.VISIBLE else View.GONE } } - // Populate "last topics" - val lastTopics = repository.getLastShareTopics() - Log.d(TAG, "last topics: $lastTopics") - // Incoming intent val intent = intent ?: return if (intent.action != Intent.ACTION_SEND) return @@ -284,6 +315,24 @@ class ShareActivity : AppCompatActivity() { } } + class TopicAdapter(private val topicUrls: List, val onClick: (String) -> Unit) : RecyclerView.Adapter() { + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.fragment_share_item, viewGroup, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + viewHolder.topicName.text = shortUrl(topicUrls[position]) + viewHolder.view.setOnClickListener { onClick(topicUrls[position]) } + } + + override fun getItemCount() = topicUrls.size + + class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { + val topicName: TextView = view.findViewById(R.id.share_item_text) + } + } + companion object { const val TAG = "NtfyShareActivity" } diff --git a/app/src/main/java/io/heckel/ntfy/util/Util.kt b/app/src/main/java/io/heckel/ntfy/util/Util.kt index f9308f8..a61fde7 100644 --- a/app/src/main/java/io/heckel/ntfy/util/Util.kt +++ b/app/src/main/java/io/heckel/ntfy/util/Util.kt @@ -44,6 +44,11 @@ fun shortUrl(url: String) = url .replace("http://", "") .replace("https://", "") +fun splitTopicUrl(topicUrl: String): Pair { + if (topicUrl.lastIndexOf("/") == -1) throw Exception("Invalid argument $topicUrl") + return Pair(topicUrl.substringBeforeLast("/"), topicUrl.substringAfterLast("/")) +} + fun validTopic(topic: String): Boolean { return "[-_A-Za-z0-9]{1,64}".toRegex().matches(topic) // Must match server side! } diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml index 44265e2..f2e120a 100644 --- a/app/src/main/res/layout/activity_share.xml +++ b/app/src/main/res/layout/activity_share.xml @@ -1,159 +1,166 @@ - - - - - - + android:layout_height="match_parent"> - + android:layout_height="wrap_content" + android:orientation="horizontal" android:paddingStart="15dp" android:paddingEnd="15dp" android:paddingTop="10dp" android:paddingBottom="10dp"> + + + + + - - - - - - + + + + + + + + + + - - - - - - + android:paddingBottom="3dp" + android:text="@string/share_previous_topics" + android:textAlignment="viewStart" + android:textAppearance="@style/TextAppearance.AppCompat.Medium" + app:layout_constraintTop_toBottomOf="@id/share_base_url_layout" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="15dp"/> + + + - + + diff --git a/app/src/main/res/layout/fragment_share_item.xml b/app/src/main/res/layout/fragment_share_item.xml new file mode 100644 index 0000000..95f0107 --- /dev/null +++ b/app/src/main/res/layout/fragment_share_item.xml @@ -0,0 +1,27 @@ + + + + + +