mirror of
https://github.com/binwiederhier/ntfy-android.git
synced 2024-06-28 11:10:44 +12:00
Auto complete home servers
This commit is contained in:
parent
91a29c4d8e
commit
5f15be4bca
|
@ -6,25 +6,31 @@ import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.AutoCompleteTextView
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.CheckBox
|
import android.widget.CheckBox
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import io.heckel.ntfy.BuildConfig
|
||||||
import io.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.data.Database
|
import io.heckel.ntfy.data.Database
|
||||||
import io.heckel.ntfy.data.Repository
|
import io.heckel.ntfy.data.Repository
|
||||||
import io.heckel.ntfy.BuildConfig
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
class AddFragment : DialogFragment() {
|
class AddFragment : DialogFragment() {
|
||||||
private lateinit var repository: Repository
|
private lateinit var repository: Repository
|
||||||
private lateinit var subscribeListener: SubscribeListener
|
private lateinit var subscribeListener: SubscribeListener
|
||||||
|
|
||||||
private lateinit var topicNameText: TextInputEditText
|
private lateinit var topicNameText: TextInputEditText
|
||||||
private lateinit var baseUrlText: TextInputEditText
|
private lateinit var baseUrlLayout: TextInputLayout
|
||||||
|
private lateinit var baseUrlText: AutoCompleteTextView
|
||||||
private lateinit var useAnotherServerCheckbox: CheckBox
|
private lateinit var useAnotherServerCheckbox: CheckBox
|
||||||
private lateinit var useAnotherServerDescription: View
|
private lateinit var useAnotherServerDescription: View
|
||||||
private lateinit var instantDeliveryBox: View
|
private lateinit var instantDeliveryBox: View
|
||||||
|
@ -32,6 +38,8 @@ class AddFragment : DialogFragment() {
|
||||||
private lateinit var instantDeliveryDescription: View
|
private lateinit var instantDeliveryDescription: View
|
||||||
private lateinit var subscribeButton: Button
|
private lateinit var subscribeButton: Button
|
||||||
|
|
||||||
|
private lateinit var baseUrls: List<String> // List of base URLs already used, excluding app_base_url
|
||||||
|
|
||||||
interface SubscribeListener {
|
interface SubscribeListener {
|
||||||
fun onSubscribe(topic: String, baseUrl: String, instant: Boolean)
|
fun onSubscribe(topic: String, baseUrl: String, instant: Boolean)
|
||||||
}
|
}
|
||||||
|
@ -53,14 +61,73 @@ class AddFragment : DialogFragment() {
|
||||||
|
|
||||||
// Build root view
|
// Build root view
|
||||||
val view = requireActivity().layoutInflater.inflate(R.layout.fragment_add_dialog, null)
|
val view = requireActivity().layoutInflater.inflate(R.layout.fragment_add_dialog, null)
|
||||||
topicNameText = view.findViewById(R.id.add_dialog_topic_text) as TextInputEditText
|
topicNameText = view.findViewById(R.id.add_dialog_topic_text)
|
||||||
baseUrlText = view.findViewById(R.id.add_dialog_base_url_text) as TextInputEditText
|
baseUrlLayout = view.findViewById(R.id.add_dialog_base_url_layout)
|
||||||
|
baseUrlText = view.findViewById(R.id.add_dialog_base_url_text)
|
||||||
instantDeliveryBox = view.findViewById(R.id.add_dialog_instant_delivery_box)
|
instantDeliveryBox = view.findViewById(R.id.add_dialog_instant_delivery_box)
|
||||||
instantDeliveryCheckbox = view.findViewById(R.id.add_dialog_instant_delivery_checkbox) as CheckBox
|
instantDeliveryCheckbox = view.findViewById(R.id.add_dialog_instant_delivery_checkbox)
|
||||||
instantDeliveryDescription = view.findViewById(R.id.add_dialog_instant_delivery_description)
|
instantDeliveryDescription = view.findViewById(R.id.add_dialog_instant_delivery_description)
|
||||||
useAnotherServerCheckbox = view.findViewById(R.id.add_dialog_use_another_server_checkbox) as CheckBox
|
useAnotherServerCheckbox = view.findViewById(R.id.add_dialog_use_another_server_checkbox)
|
||||||
useAnotherServerDescription = view.findViewById(R.id.add_dialog_use_another_server_description)
|
useAnotherServerDescription = view.findViewById(R.id.add_dialog_use_another_server_description)
|
||||||
|
|
||||||
|
// Base URL dropdown behavior; Oh my, why is this so complicated?!
|
||||||
|
val toggleEndIcon = {
|
||||||
|
if (baseUrlText.text.isNotEmpty()) {
|
||||||
|
baseUrlLayout.setEndIconDrawable(R.drawable.ic_cancel_gray_24dp)
|
||||||
|
} else if (baseUrls.isEmpty()) {
|
||||||
|
baseUrlLayout.setEndIconDrawable(0)
|
||||||
|
} else {
|
||||||
|
baseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
baseUrlLayout.setEndIconOnClickListener {
|
||||||
|
if (baseUrlText.text.isNotEmpty()) {
|
||||||
|
baseUrlText.text.clear()
|
||||||
|
if (baseUrls.isEmpty()) {
|
||||||
|
baseUrlLayout.setEndIconDrawable(0)
|
||||||
|
} else {
|
||||||
|
baseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_down_gray_24dp)
|
||||||
|
}
|
||||||
|
} else if (baseUrlText.text.isEmpty() && baseUrls.isNotEmpty()) {
|
||||||
|
baseUrlLayout.setEndIconDrawable(R.drawable.ic_drop_up_gray_24dp)
|
||||||
|
baseUrlText.showDropDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
baseUrlText.setOnDismissListener { toggleEndIcon() }
|
||||||
|
baseUrlText.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
toggleEndIcon()
|
||||||
|
}
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fill autocomplete for base URL
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
// Sort existing base URLs by most frequently used
|
||||||
|
val appBaseUrl = getString(R.string.app_base_url)
|
||||||
|
baseUrls = repository.getSubscriptions()
|
||||||
|
.groupBy { it.baseUrl }
|
||||||
|
.mapValues { it.value.size }
|
||||||
|
.toList()
|
||||||
|
.sortedBy { (_, size) -> size }
|
||||||
|
.reversed()
|
||||||
|
.map { (baseUrl, _) -> baseUrl }
|
||||||
|
.filterNot { it == appBaseUrl }
|
||||||
|
val adapter = ArrayAdapter(requireActivity(), R.layout.fragment_add_dialog_dropdown_item, baseUrls)
|
||||||
|
requireActivity().runOnUiThread {
|
||||||
|
baseUrlText.threshold = 1
|
||||||
|
baseUrlText.setAdapter(adapter)
|
||||||
|
if (baseUrls.isNotEmpty()) {
|
||||||
|
baseUrlText.setText(baseUrls.first())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show/hide based on flavor
|
// Show/hide based on flavor
|
||||||
instantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
instantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
@ -109,12 +176,12 @@ class AddFragment : DialogFragment() {
|
||||||
useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
useAnotherServerCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
useAnotherServerDescription.visibility = View.VISIBLE
|
useAnotherServerDescription.visibility = View.VISIBLE
|
||||||
baseUrlText.visibility = View.VISIBLE
|
baseUrlLayout.visibility = View.VISIBLE
|
||||||
instantDeliveryBox.visibility = View.GONE
|
instantDeliveryBox.visibility = View.GONE
|
||||||
instantDeliveryDescription.visibility = View.GONE
|
instantDeliveryDescription.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
useAnotherServerDescription.visibility = View.GONE
|
useAnotherServerDescription.visibility = View.GONE
|
||||||
baseUrlText.visibility = View.GONE
|
baseUrlLayout.visibility = View.GONE
|
||||||
instantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
instantDeliveryBox.visibility = if (BuildConfig.FIREBASE_AVAILABLE) View.VISIBLE else View.GONE
|
||||||
if (instantDeliveryCheckbox.isChecked) instantDeliveryDescription.visibility = View.VISIBLE
|
if (instantDeliveryCheckbox.isChecked) instantDeliveryDescription.visibility = View.VISIBLE
|
||||||
else instantDeliveryDescription.visibility = View.GONE
|
else instantDeliveryDescription.visibility = View.GONE
|
||||||
|
|
28
app/src/main/res/drawable/ic_cancel_gray_24dp.xml
Normal file
28
app/src/main/res/drawable/ic_cancel_gray_24dp.xml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2019 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<path
|
||||||
|
android:fillColor="#888888"
|
||||||
|
android:pathData="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>
|
||||||
|
</vector>
|
27
app/src/main/res/drawable/ic_drop_down_gray_24dp.xml
Normal file
27
app/src/main/res/drawable/ic_drop_down_gray_24dp.xml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2019 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<path
|
||||||
|
android:fillColor="#888888"
|
||||||
|
android:pathData="M7 10l5 5 5-5z"/>
|
||||||
|
</vector>
|
28
app/src/main/res/drawable/ic_drop_up_gray_24dp.xml
Normal file
28
app/src/main/res/drawable/ic_drop_up_gray_24dp.xml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2019 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<path
|
||||||
|
android:fillColor="#888888"
|
||||||
|
android:pathData="M7 14l5-5 5 5z"/>
|
||||||
|
</vector>
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
|
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"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingLeft="16dp"
|
android:paddingLeft="16dp"
|
||||||
|
@ -37,12 +38,37 @@
|
||||||
android:layout_height="wrap_content" android:id="@+id/add_dialog_use_another_server_description"
|
android:layout_height="wrap_content" android:id="@+id/add_dialog_use_another_server_description"
|
||||||
android:paddingStart="4dp" android:paddingTop="0dp" android:layout_marginTop="-5dp"
|
android:paddingStart="4dp" android:paddingTop="0dp" android:layout_marginTop="-5dp"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/add_dialog_base_url_text"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
|
||||||
|
android:id="@+id/add_dialog_base_url_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:visibility="gone"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/app_base_url" android:inputType="textUri" android:maxLines="1"
|
android:layout_margin="0dp"
|
||||||
android:layout_marginTop="-2dp" android:layout_marginBottom="5dp"/>
|
android:background="@android:color/transparent"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:endIconMode="custom"
|
||||||
|
app:hintEnabled="false"
|
||||||
|
app:boxBackgroundColor="@android:color/transparent">
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/add_dialog_base_url_text"
|
||||||
|
android:hint="@string/app_base_url"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:layout_marginTop="-2dp"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:paddingStart="2dp"
|
||||||
|
android:paddingEnd="2dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
|
android:layout_marginEnd="2dp"/>
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/text1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textColor="?android:attr/textColorAlertDialogListItem"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="7dp"
|
||||||
|
android:paddingEnd="7dp"
|
||||||
|
android:paddingTop="7dp"
|
||||||
|
android:paddingBottom="7dp"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
/>
|
|
@ -60,8 +60,8 @@
|
||||||
<string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string>
|
<string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string>
|
||||||
<string name="add_dialog_use_another_server">Use another server</string>
|
<string name="add_dialog_use_another_server">Use another server</string>
|
||||||
<string name="add_dialog_use_another_server_description">
|
<string name="add_dialog_use_another_server_description">
|
||||||
You can subscribe to topics from your own server. Due to platform limitations, this option requires a foreground
|
You can subscribe to topics from your own server. This option requires a foreground service and
|
||||||
service and consumes more power, but also delivers notifications faster (even in doze mode).
|
consumes more power, but also delivers notifications faster (even in doze mode).
|
||||||
</string>
|
</string>
|
||||||
<string name="add_dialog_instant_delivery">Instant delivery in doze mode</string>
|
<string name="add_dialog_instant_delivery">Instant delivery in doze mode</string>
|
||||||
<string name="add_dialog_instant_delivery_description">
|
<string name="add_dialog_instant_delivery_description">
|
||||||
|
|
Loading…
Reference in a new issue