This looks reasonably nice
This commit is contained in:
parent
8e333e55bc
commit
3a2e6cbf57
6 changed files with 244 additions and 157 deletions
|
@ -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
|
||||
|
|
|
@ -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.*
|
||||
|
|
|
@ -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,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<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"
|
||||
}
|
||||
|
|
|
@ -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!
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<?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>
|
||||
|
|
27
app/src/main/res/layout/fragment_share_item.xml
Normal file
27
app/src/main/res/layout/fragment_share_item.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal" android:clickable="true"
|
||||
android:focusable="true"
|
||||
>
|
||||
<ImageView
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp" app:srcCompat="@drawable/ic_sms_gray_24dp"
|
||||
android:id="@+id/share_item_image"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/share_item_text"
|
||||
app:layout_constraintTop_toTopOf="@+id/share_item_text" android:layout_marginStart="2dp"/>
|
||||
<TextView
|
||||
android:text="ntfy.sh/example"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" android:id="@+id/share_item_text"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/share_item_image"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textColor="?android:attr/textColorPrimary" android:layout_marginTop="7dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="7dp" android:layout_marginStart="4dp"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
Loading…
Reference in a new issue