Polish
This commit is contained in:
parent
a498d68bcf
commit
18261263dd
4 changed files with 51 additions and 29 deletions
app/src/main
java/io/heckel/ntfy/ui
res
|
@ -1,8 +1,10 @@
|
||||||
package io.heckel.ntfy.ui
|
package io.heckel.ntfy.ui
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -67,6 +69,7 @@ class DetailSettingsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
private lateinit var resolver: ContentResolver
|
||||||
private lateinit var repository: Repository
|
private lateinit var repository: Repository
|
||||||
private lateinit var serviceManager: SubscriberServiceManager
|
private lateinit var serviceManager: SubscriberServiceManager
|
||||||
private lateinit var subscription: Subscription
|
private lateinit var subscription: Subscription
|
||||||
|
@ -81,6 +84,7 @@ class DetailSettingsActivity : AppCompatActivity() {
|
||||||
// Dependencies (Fragments need a default constructor)
|
// Dependencies (Fragments need a default constructor)
|
||||||
repository = Repository.getInstance(requireActivity())
|
repository = Repository.getInstance(requireActivity())
|
||||||
serviceManager = SubscriberServiceManager(requireActivity())
|
serviceManager = SubscriberServiceManager(requireActivity())
|
||||||
|
resolver = requireContext().applicationContext.contentResolver
|
||||||
|
|
||||||
// Create result launcher for custom icon (must be created in onCreatePreferences() directly)
|
// Create result launcher for custom icon (must be created in onCreatePreferences() directly)
|
||||||
iconSetLauncher = createIconPickLauncher()
|
iconSetLauncher = createIconPickLauncher()
|
||||||
|
@ -251,28 +255,26 @@ class DetailSettingsActivity : AppCompatActivity() {
|
||||||
private fun loadIconRemovePref() {
|
private fun loadIconRemovePref() {
|
||||||
val prefId = context?.getString(R.string.detail_settings_appearance_icon_remove_key) ?: return
|
val prefId = context?.getString(R.string.detail_settings_appearance_icon_remove_key) ?: return
|
||||||
iconRemovePref = findPreference(prefId) ?: return
|
iconRemovePref = findPreference(prefId) ?: return
|
||||||
|
iconRemovePref.isVisible = subscription.icon != null
|
||||||
|
iconRemovePref.preferenceDataStore = object : PreferenceDataStore() { } // Dummy store to protect from accidentally overwriting
|
||||||
|
iconRemovePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
|
||||||
|
iconRemovePref.isVisible = false
|
||||||
|
iconSetPref.isVisible = true
|
||||||
|
deleteIcon(subscription.icon)
|
||||||
|
save(subscription.copy(icon = null))
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME
|
// Set icon (if it exists)
|
||||||
|
|
||||||
if (subscription.icon != null) {
|
if (subscription.icon != null) {
|
||||||
try {
|
try {
|
||||||
val resolver = requireContext().applicationContext.contentResolver
|
|
||||||
val bitmapStream = resolver.openInputStream(Uri.parse(subscription.icon))
|
val bitmapStream = resolver.openInputStream(Uri.parse(subscription.icon))
|
||||||
val bitmap = BitmapFactory.decodeStream(bitmapStream)
|
val bitmap = BitmapFactory.decodeStream(bitmapStream)
|
||||||
iconRemovePref.icon = bitmap.toDrawable(resources)
|
iconRemovePref.icon = bitmap.toDrawable(resources)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// FIXME
|
Log.w(TAG, "Unable to set icon ${subscription.icon}", e)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iconRemovePref.isVisible = subscription.icon != null
|
|
||||||
iconRemovePref.preferenceDataStore = object : PreferenceDataStore() { } // Dummy store to protect from accidentally overwriting
|
|
||||||
iconRemovePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
|
|
||||||
save(subscription.copy(icon = null))
|
|
||||||
iconRemovePref.isVisible = false
|
|
||||||
iconSetPref.isVisible = true
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createIconPickLauncher(): ActivityResultLauncher<String> {
|
private fun createIconPickLauncher(): ActivityResultLauncher<String> {
|
||||||
|
@ -281,45 +283,54 @@ class DetailSettingsActivity : AppCompatActivity() {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val outputUri = createUri() ?: return@launch
|
||||||
try {
|
try {
|
||||||
// Write to cache storage
|
// Write to cache storage
|
||||||
val resolver = requireContext().applicationContext.contentResolver
|
|
||||||
val inputStream = resolver.openInputStream(inputUri) ?: throw IOException("Couldn't open content URI for reading")
|
val inputStream = resolver.openInputStream(inputUri) ?: throw IOException("Couldn't open content URI for reading")
|
||||||
val outputUri = createUri()
|
|
||||||
val outputStream = resolver.openOutputStream(outputUri) ?: throw IOException("Couldn't open content URI for writing")
|
val outputStream = resolver.openOutputStream(outputUri) ?: throw IOException("Couldn't open content URI for writing")
|
||||||
inputStream.copyTo(outputStream)
|
inputStream.use {
|
||||||
save(subscription.copy(icon = outputUri.toString()))
|
it.copyTo(outputStream)
|
||||||
|
}
|
||||||
// FIXME
|
|
||||||
// FIXME
|
|
||||||
|
|
||||||
iconSetPref.isVisible = false
|
|
||||||
|
|
||||||
|
// Read image and set as preference icon
|
||||||
val bitmapStream = resolver.openInputStream(Uri.parse(outputUri.toString()))
|
val bitmapStream = resolver.openInputStream(Uri.parse(outputUri.toString()))
|
||||||
val bitmap = BitmapFactory.decodeStream(bitmapStream)
|
val bitmap = BitmapFactory.decodeStream(bitmapStream)
|
||||||
|
|
||||||
|
// Display "remove" preference
|
||||||
iconRemovePref.icon = bitmap.toDrawable(resources)
|
iconRemovePref.icon = bitmap.toDrawable(resources)
|
||||||
iconRemovePref.isVisible = true
|
iconRemovePref.isVisible = true
|
||||||
|
iconSetPref.isVisible = false
|
||||||
|
|
||||||
|
// Finally, save (this is last!)
|
||||||
|
save(subscription.copy(icon = outputUri.toString()))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "Saving icon failed", e)
|
Log.w(TAG, "Saving icon failed", e)
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
// FIXME TOAST
|
Toast.makeText(context, getString(R.string.detail_settings_appearance_icon_error_saving, e.message), Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createUri(): Uri {
|
private fun createUri(): Uri? {
|
||||||
val dir = File(requireContext().cacheDir, SUBSCRIPTION_ICONS)
|
val dir = File(requireContext().cacheDir, SUBSCRIPTION_ICONS)
|
||||||
if (!dir.exists() && !dir.mkdirs()) {
|
if (!dir.exists() && !dir.mkdirs()) {
|
||||||
throw Exception("Cannot create cache directory for attachments: $dir")
|
return null
|
||||||
}
|
}
|
||||||
val file = File(dir, subscription.id.toString())
|
val file = File(dir, subscription.id.toString())
|
||||||
return FileProvider.getUriForFile(requireContext(), DownloadWorker.FILE_PROVIDER_AUTHORITY, file)
|
return FileProvider.getUriForFile(requireContext(), DownloadWorker.FILE_PROVIDER_AUTHORITY, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadBitmap() {
|
private fun deleteIcon(uri: String?) {
|
||||||
// FIXME
|
if (uri == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resolver.delete(Uri.parse(uri), null, null)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Unable to delete $uri", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun save(newSubscription: Subscription, refresh: Boolean = false) {
|
private fun save(newSubscription: Subscription, refresh: Boolean = false) {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package io.heckel.ntfy.ui
|
package io.heckel.ntfy.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import io.heckel.ntfy.db.*
|
import io.heckel.ntfy.db.*
|
||||||
import io.heckel.ntfy.up.Distributor
|
import io.heckel.ntfy.up.Distributor
|
||||||
|
import io.heckel.ntfy.util.Log
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.collections.List
|
import kotlin.collections.List
|
||||||
|
@ -32,6 +34,14 @@ class SubscriptionsViewModel(private val repository: Repository) : ViewModel() {
|
||||||
}
|
}
|
||||||
repository.removeAllNotifications(subscriptionId)
|
repository.removeAllNotifications(subscriptionId)
|
||||||
repository.removeSubscription(subscriptionId)
|
repository.removeSubscription(subscriptionId)
|
||||||
|
if (subscription.icon != null) {
|
||||||
|
val resolver = context.applicationContext.contentResolver
|
||||||
|
try {
|
||||||
|
resolver.delete(Uri.parse(subscription.icon), null, null)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// Don't care
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun get(baseUrl: String, topic: String): Subscription? {
|
suspend fun get(baseUrl: String, topic: String): Subscription? {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
android:layout_height="35dp" app:srcCompat="@drawable/ic_sms_gray_24dp"
|
android:layout_height="35dp" app:srcCompat="@drawable/ic_sms_gray_24dp"
|
||||||
android:id="@+id/main_item_image" app:layout_constraintTop_toTopOf="parent"
|
android:id="@+id/main_item_image" app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_marginTop="13dp"/>
|
android:layout_marginTop="17dp" android:scaleType="fitStart"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:text="ntfy.sh/example"
|
android:text="ntfy.sh/example"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -341,8 +341,9 @@
|
||||||
<string name="detail_settings_notifications_instant_summary_off">Notifications are delivered using Firebase. Delivery may be delayed, but consumes less battery.</string>
|
<string name="detail_settings_notifications_instant_summary_off">Notifications are delivered using Firebase. Delivery may be delayed, but consumes less battery.</string>
|
||||||
<string name="detail_settings_appearance_header">Appearance</string>
|
<string name="detail_settings_appearance_header">Appearance</string>
|
||||||
<string name="detail_settings_appearance_icon_title">Subscription icon</string>
|
<string name="detail_settings_appearance_icon_title">Subscription icon</string>
|
||||||
<string name="detail_settings_appearance_icon_set_summary_set">This icon is displayed in notifications. Tap to remove it.</string>
|
<string name="detail_settings_appearance_icon_set_summary_set">This icon is displayed in notifications to this topic. Tap to remove it.</string>
|
||||||
<string name="detail_settings_appearance_icon_set_summary_no_set">Set an icon to be displayed in notifications</string>
|
<string name="detail_settings_appearance_icon_set_summary_no_set">Set an icon to be displayed in notifications</string>
|
||||||
|
<string name="detail_settings_appearance_icon_error_saving">Unable to save icon: %1$s</string>
|
||||||
<string name="detail_settings_global_setting_title">Use global setting</string>
|
<string name="detail_settings_global_setting_title">Use global setting</string>
|
||||||
<string name="detail_settings_global_setting_suffix">global</string>
|
<string name="detail_settings_global_setting_suffix">global</string>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue