Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a2e6cbf authored by Philipp Heckel's avatar Philipp Heckel
Browse files

This looks reasonably nice

parent 8e333e55
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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
+0 −1
Original line number Diff line number Diff line
@@ -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.*
+58 −9
Original line number Diff line number Diff line
@@ -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<String>

    // 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,13 +136,19 @@ 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
@@ -284,6 +315,24 @@ class ShareActivity : AppCompatActivity() {
        }
    }

    class TopicAdapter(private val topicUrls: List<String>, val onClick: (String) -> Unit) : RecyclerView.Adapter<TopicAdapter.ViewHolder>() {
        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"
    }
+5 −0
Original line number Diff line number Diff line
@@ -44,6 +44,11 @@ fun shortUrl(url: String) = url
    .replace("http://", "")
    .replace("https://", "")

fun splitTopicUrl(topicUrl: String): Pair<String, String> {
    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!
}
+150 −143
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<ScrollView
        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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_height="match_parent">
    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" android:paddingStart="15dp" android:paddingEnd="15dp" android:paddingTop="10dp" android:paddingBottom="10dp">

        <ProgressBar
@@ -21,9 +24,9 @@
                android:paddingBottom="2dp"
                android:text="@string/share_content_title"
                android:textAlignment="viewStart"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
                android:textAppearance="@style/TextAppearance.AppCompat.Medium"
                app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" android:paddingStart="2dp"/>
                app:layout_constraintTop_toTopOf="parent"/>
        <com.google.android.material.imageview.ShapeableImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" app:srcCompat="@drawable/ic_cancel_gray_24dp"
@@ -75,24 +78,24 @@
                android:paddingBottom="3dp"
                android:text="@string/share_topic_title"
                android:textAlignment="viewStart"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
                android:textAppearance="@style/TextAppearance.AppCompat.Medium"
                app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="15dp" android:paddingStart="2dp"/>
                app:layout_constraintTop_toBottomOf="@id/share_content_file_box" android:layout_marginTop="15dp"/>
        <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/share_topic_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint"
                android:importantForAutofill="no"
            android:maxLines="1" android:inputType="text" android:maxLength="64"
                android:maxLines="1" android:inputType="text|textNoSuggestions" android:maxLength="64"
                app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/share_topic_title"/>
                app:layout_constraintTop_toBottomOf="@id/share_topic_title" android:layout_marginStart="-3dp"/>
        <CheckBox
                android:text="@string/add_dialog_use_another_server"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" android:id="@+id/share_use_another_server_checkbox"
            android:layout_marginStart="-3dp" app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/share_topic_text"/>
                app:layout_constraintTop_toBottomOf="@id/share_topic_text" android:paddingTop="-5dp" android:layout_marginTop="-5dp" android:layout_marginStart="-5dp"/>
        <com.google.android.material.textfield.TextInputLayout
                style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
                android:id="@+id/share_base_url_layout"
@@ -100,29 +103,26 @@
                android:layout_height="wrap_content"
                android:layout_margin="0dp"
                android:padding="0dp"
            android:visibility="gone"
                android:visibility="visible"
                app:endIconMode="custom"
                app:hintEnabled="false"
            app:boxBackgroundColor="@null" app:layout_constraintStart_toStartOf="parent"
                app:boxBackgroundColor="@null"
                app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/share_use_another_server_checkbox">
                app:layout_constraintTop_toBottomOf="@id/share_use_another_server_checkbox" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="-5dp">
            <AutoCompleteTextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/share_base_url_text"
                    android:hint="@string/app_base_url"
                    android:maxLines="1"
                android:layout_marginTop="0dp"
                    android:layout_marginTop="-5dp"
                    android:layout_marginBottom="0dp"
                android:inputType="textNoSuggestions"
                    android:inputType="textUri|textNoSuggestions"
                    android:paddingStart="0dp"
                    android:paddingEnd="0dp"
                    android:paddingTop="5dp"
                    android:paddingBottom="5dp"
                android:layout_marginStart="4dp"
                android:layout_marginEnd="4dp"
                android:textAppearance="?android:attr/textAppearanceMedium"
        />
                    android:layout_marginStart="4dp" android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
        </com.google.android.material.textfield.TextInputLayout>
        <TextView
                android:id="@+id/share_last_title"
@@ -132,13 +132,19 @@
                android:paddingBottom="3dp"
                android:text="@string/share_previous_topics"
                android:textAlignment="viewStart"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            android:paddingStart="2dp" app:layout_constraintTop_toBottomOf="@id/share_base_url_layout" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="15dp"/>
    <LinearLayout
            android:orientation="vertical"
            android:layout_width="0dp"
            android:layout_height="wrap_content" android:id="@+id/share_last_layout" app:layout_constraintTop_toBottomOf="@id/share_last_title" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="5dp">
    </LinearLayout>
                android:textAppearance="@style/TextAppearance.AppCompat.Medium"
                app:layout_constraintTop_toBottomOf="@id/share_base_url_layout" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="15dp"/>
        <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:id="@+id/share_last_topics"
                app:layout_constraintTop_toBottomOf="@id/share_last_title"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                android:clickable="true"
                android:focusable="true"
                android:background="?android:attr/selectableItemBackground"
                app:layoutManager="LinearLayoutManager"/>
        <TextView
                android:text="Unable to resolve host example.com"
                android:layout_width="0dp"
@@ -148,7 +154,7 @@
                android:paddingEnd="4dp"
                android:textAppearance="@style/DangerText"
                app:layout_constraintStart_toEndOf="@id/share_error_image"
            android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_last_title"/>
                android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_last_topics"/>
        <ImageView
                android:layout_width="20dp"
                android:layout_height="20dp" app:srcCompat="@drawable/ic_error_red_24dp"
@@ -157,3 +163,4 @@
                app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/share_error_text" android:layout_marginTop="2dp"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
Loading