1
0
Fork 0

Polishing

This commit is contained in:
Philipp Heckel 2022-05-08 22:57:52 -04:00
parent 18261263dd
commit ac3496d7fa
9 changed files with 52 additions and 51 deletions

View file

@ -5,7 +5,6 @@ import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.BitmapFactory
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -70,9 +69,8 @@ class NotificationService(val context: Context) {
.setContentTitle(title) .setContentTitle(title)
.setOnlyAlertOnce(true) // Do not vibrate or play sound if already showing (updates!) .setOnlyAlertOnce(true) // Do not vibrate or play sound if already showing (updates!)
.setAutoCancel(true) // Cancel when notification is clicked .setAutoCancel(true) // Cancel when notification is clicked
setStyleAndText(builder, notification) // Preview picture or big text style setStyleAndText(builder, subscription, notification) // Preview picture or big text style
setClickAction(builder, subscription, notification) setClickAction(builder, subscription, notification)
maybeSetIcon(builder, subscription)
maybeSetSound(builder, update) maybeSetSound(builder, update)
maybeSetProgress(builder, notification) maybeSetProgress(builder, notification)
maybeAddOpenAction(builder, notification) maybeAddOpenAction(builder, notification)
@ -85,18 +83,6 @@ class NotificationService(val context: Context) {
notificationManager.notify(notification.notificationId, builder.build()) notificationManager.notify(notification.notificationId, builder.build())
} }
private fun maybeSetIcon(builder: NotificationCompat.Builder, subscription: Subscription) {
val icon = subscription.icon ?: return
try {
val resolver = context.applicationContext.contentResolver
val bitmapStream = resolver.openInputStream(Uri.parse(icon))
val bitmap = BitmapFactory.decodeStream(bitmapStream)
builder.setLargeIcon(bitmap)
} catch (e: Exception) {
Log.w(TAG, "Cannot load subscription icon", e)
}
}
private fun maybeSetSound(builder: NotificationCompat.Builder, update: Boolean) { private fun maybeSetSound(builder: NotificationCompat.Builder, update: Boolean) {
if (!update) { if (!update) {
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
@ -106,20 +92,19 @@ class NotificationService(val context: Context) {
} }
} }
private fun setStyleAndText(builder: NotificationCompat.Builder, notification: Notification) { private fun setStyleAndText(builder: NotificationCompat.Builder, subscription: Subscription, notification: Notification) {
val contentUri = notification.attachment?.contentUri val contentUri = notification.attachment?.contentUri
val isSupportedImage = supportedImage(notification.attachment?.type) val isSupportedImage = supportedImage(notification.attachment?.type)
val subscriptionIcon = if (subscription.icon != null) subscription.icon.readBitmapFromUriOrNull(context) else null
if (contentUri != null && isSupportedImage) { if (contentUri != null && isSupportedImage) {
try { try {
val resolver = context.applicationContext.contentResolver val attachmentBitmap = contentUri.readBitmapFromUri(context)
val bitmapStream = resolver.openInputStream(Uri.parse(contentUri))
val bitmap = BitmapFactory.decodeStream(bitmapStream)
builder builder
.setContentText(maybeAppendActionErrors(formatMessage(notification), notification)) .setContentText(maybeAppendActionErrors(formatMessage(notification), notification))
.setLargeIcon(bitmap) .setLargeIcon(attachmentBitmap)
.setStyle(NotificationCompat.BigPictureStyle() .setStyle(NotificationCompat.BigPictureStyle()
.bigPicture(bitmap) .bigPicture(attachmentBitmap)
.bigLargeIcon(null)) .bigLargeIcon(subscriptionIcon)) // May be null
} catch (_: Exception) { } catch (_: Exception) {
val message = maybeAppendActionErrors(formatMessageMaybeWithAttachmentInfos(notification), notification) val message = maybeAppendActionErrors(formatMessageMaybeWithAttachmentInfos(notification), notification)
builder builder
@ -131,6 +116,7 @@ class NotificationService(val context: Context) {
builder builder
.setContentText(message) .setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message)) .setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setLargeIcon(subscriptionIcon) // May be null
} }
} }

View file

@ -233,6 +233,8 @@ class SubscriberService : Service() {
2 -> getString(R.string.channel_subscriber_notification_instant_text_two) 2 -> getString(R.string.channel_subscriber_notification_instant_text_two)
3 -> getString(R.string.channel_subscriber_notification_instant_text_three) 3 -> getString(R.string.channel_subscriber_notification_instant_text_three)
4 -> getString(R.string.channel_subscriber_notification_instant_text_four) 4 -> getString(R.string.channel_subscriber_notification_instant_text_four)
5 -> getString(R.string.channel_subscriber_notification_instant_text_five)
6 -> getString(R.string.channel_subscriber_notification_instant_text_six)
else -> getString(R.string.channel_subscriber_notification_instant_text_more, instantSubscriptions.size) else -> getString(R.string.channel_subscriber_notification_instant_text_more, instantSubscriptions.size)
} }
} else { } else {
@ -241,6 +243,8 @@ class SubscriberService : Service() {
2 -> getString(R.string.channel_subscriber_notification_noinstant_text_two) 2 -> getString(R.string.channel_subscriber_notification_noinstant_text_two)
3 -> getString(R.string.channel_subscriber_notification_noinstant_text_three) 3 -> getString(R.string.channel_subscriber_notification_noinstant_text_three)
4 -> getString(R.string.channel_subscriber_notification_noinstant_text_four) 4 -> getString(R.string.channel_subscriber_notification_noinstant_text_four)
5 -> getString(R.string.channel_subscriber_notification_noinstant_text_five)
6 -> getString(R.string.channel_subscriber_notification_noinstant_text_six)
else -> getString(R.string.channel_subscriber_notification_noinstant_text_more, instantSubscriptions.size) else -> getString(R.string.channel_subscriber_notification_noinstant_text_more, instantSubscriptions.size)
} }
} }

View file

@ -289,9 +289,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope:
return return
} }
try { try {
val resolver = context.applicationContext.contentResolver val bitmap = attachment.contentUri?.readBitmapFromUri(context) ?: throw Exception("uri empty")
val bitmapStream = resolver.openInputStream(Uri.parse(attachment.contentUri))
val bitmap = BitmapFactory.decodeStream(bitmapStream)
attachmentImageView.setImageBitmap(bitmap) attachmentImageView.setImageBitmap(bitmap)
attachmentImageView.setOnClickListener { attachmentImageView.setOnClickListener {
val loadImage = { view: ImageView, image: Bitmap -> view.setImageBitmap(image) } val loadImage = { view: ImageView, image: Bitmap -> view.setImageBitmap(image) }

View file

@ -268,8 +268,7 @@ class DetailSettingsActivity : AppCompatActivity() {
// Set icon (if it exists) // Set icon (if it exists)
if (subscription.icon != null) { if (subscription.icon != null) {
try { try {
val bitmapStream = resolver.openInputStream(Uri.parse(subscription.icon)) val bitmap = subscription.icon!!.readBitmapFromUri(requireContext())
val bitmap = BitmapFactory.decodeStream(bitmapStream)
iconRemovePref.icon = bitmap.toDrawable(resources) iconRemovePref.icon = bitmap.toDrawable(resources)
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "Unable to set icon ${subscription.icon}", e) Log.w(TAG, "Unable to set icon ${subscription.icon}", e)
@ -292,11 +291,8 @@ class DetailSettingsActivity : AppCompatActivity() {
it.copyTo(outputStream) it.copyTo(outputStream)
} }
// Read image and set as preference icon // Read image & display "remove" preference
val bitmapStream = resolver.openInputStream(Uri.parse(outputUri.toString())) val bitmap = outputUri.readBitmapFromUri(requireContext())
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 iconSetPref.isVisible = false

View file

@ -18,6 +18,7 @@ import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.msg.NotificationService import io.heckel.ntfy.msg.NotificationService
import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.readBitmapFromUriOrNull
import io.heckel.ntfy.util.topicShortUrl import io.heckel.ntfy.util.topicShortUrl
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
@ -91,14 +92,7 @@ class MainAdapter(private val repository: Repository, private val onClick: (Subs
val showMutedForeverIcon = (subscription.mutedUntil == 1L || globalMutedUntil == 1L) && !isUnifiedPush val showMutedForeverIcon = (subscription.mutedUntil == 1L || globalMutedUntil == 1L) && !isUnifiedPush
val showMutedUntilIcon = !showMutedForeverIcon && (subscription.mutedUntil > 1L || globalMutedUntil > 1L) && !isUnifiedPush val showMutedUntilIcon = !showMutedForeverIcon && (subscription.mutedUntil > 1L || globalMutedUntil > 1L) && !isUnifiedPush
if (subscription.icon != null) { if (subscription.icon != null) {
try { imageView.setImageBitmap(subscription.icon.readBitmapFromUriOrNull(context))
val resolver = context.applicationContext.contentResolver
val bitmapStream = resolver.openInputStream(Uri.parse(subscription.icon))
val bitmap = BitmapFactory.decodeStream(bitmapStream)
imageView.setImageBitmap(bitmap)
} catch (e: Exception) {
Log.w(TAG, "Cannot load subscription icon", e)
}
} }
nameView.text = topicShortUrl(subscription.baseUrl, subscription.topic) nameView.text = topicShortUrl(subscription.baseUrl, subscription.topic)
statusView.text = statusMessage statusView.text = statusMessage

View file

@ -193,10 +193,7 @@ class ShareActivity : AppCompatActivity() {
return return
} }
try { try {
val resolver = applicationContext.contentResolver contentImage.setImageBitmap(fileUri!!.readBitmapFromUri(applicationContext))
val bitmapStream = resolver.openInputStream(fileUri!!)
val bitmap = BitmapFactory.decodeStream(bitmapStream)
contentImage.setImageBitmap(bitmap)
contentText.text = getString(R.string.share_content_image_text) contentText.text = getString(R.string.share_content_image_text)
show(image = true) show(image = true)
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -9,6 +9,8 @@ import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.RippleDrawable import android.graphics.drawable.RippleDrawable
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -394,6 +396,25 @@ fun View.ripple(scope: CoroutineScope) {
} }
} }
fun Uri.readBitmapFromUri(context: Context): Bitmap {
val resolver = context.applicationContext.contentResolver
val bitmapStream = resolver.openInputStream(this)
return BitmapFactory.decodeStream(bitmapStream)
}
fun String.readBitmapFromUri(context: Context): Bitmap {
return Uri.parse(this).readBitmapFromUri(context)
}
fun String.readBitmapFromUriOrNull(context: Context): Bitmap? {
return try {
this.readBitmapFromUri(context)
} catch (_: Exception) {
null
}
}
// TextWatcher that only implements the afterTextChanged method // TextWatcher that only implements the afterTextChanged method
class AfterChangedTextWatcher(val afterTextChangedFn: (s: Editable?) -> Unit) : TextWatcher { class AfterChangedTextWatcher(val afterTextChangedFn: (s: Editable?) -> Unit) : TextWatcher {
override fun afterTextChanged(s: Editable?) { override fun afterTextChanged(s: Editable?) {

View file

@ -12,12 +12,16 @@
<string name="channel_subscriber_notification_instant_text_two">Subscribed to two instant delivery topics</string> <string name="channel_subscriber_notification_instant_text_two">Subscribed to two instant delivery topics</string>
<string name="channel_subscriber_notification_instant_text_three">Subscribed to three instant delivery topics</string> <string name="channel_subscriber_notification_instant_text_three">Subscribed to three instant delivery topics</string>
<string name="channel_subscriber_notification_instant_text_four">Subscribed to four instant delivery topics</string> <string name="channel_subscriber_notification_instant_text_four">Subscribed to four instant delivery topics</string>
<string name="channel_subscriber_notification_instant_text_five">Subscribed to five instant delivery topics</string>
<string name="channel_subscriber_notification_instant_text_six">Subscribed to six instant delivery topics</string>
<string name="channel_subscriber_notification_instant_text_more">Subscribed to %1$d instant delivery topics</string> <string name="channel_subscriber_notification_instant_text_more">Subscribed to %1$d instant delivery topics</string>
<string name="channel_subscriber_notification_noinstant_text">Subscribed to topics</string> <string name="channel_subscriber_notification_noinstant_text">Subscribed to topics</string>
<string name="channel_subscriber_notification_noinstant_text_one">Subscribed to one topic</string> <string name="channel_subscriber_notification_noinstant_text_one">Subscribed to one topic</string>
<string name="channel_subscriber_notification_noinstant_text_two">Subscribed to two topics</string> <string name="channel_subscriber_notification_noinstant_text_two">Subscribed to two topics</string>
<string name="channel_subscriber_notification_noinstant_text_three">Subscribed to three topics</string> <string name="channel_subscriber_notification_noinstant_text_three">Subscribed to three topics</string>
<string name="channel_subscriber_notification_noinstant_text_four">Subscribed to four topics</string> <string name="channel_subscriber_notification_noinstant_text_four">Subscribed to four topics</string>
<string name="channel_subscriber_notification_noinstant_text_five">Subscribed to five topics</string>
<string name="channel_subscriber_notification_noinstant_text_six">Subscribed to six topics</string>
<string name="channel_subscriber_notification_noinstant_text_more">Subscribed to %1$d topics</string> <string name="channel_subscriber_notification_noinstant_text_more">Subscribed to %1$d topics</string>
<!-- Common refresh toasts --> <!-- Common refresh toasts -->
@ -340,9 +344,10 @@
<string name="detail_settings_notifications_instant_summary_on">Notifications are delivered instantly. Requires a foreground service and consumes more battery.</string> <string name="detail_settings_notifications_instant_summary_on">Notifications are delivered instantly. Requires a foreground service and consumes more 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_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_set_title">Subscription icon</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">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_remove_title">Subscription icon (tap to remove)</string>
<string name="detail_settings_appearance_icon_remove_summary">Icon displayed in notifications for this topic</string>
<string name="detail_settings_appearance_icon_error_saving">Unable to save icon: %1$s</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>

View file

@ -30,13 +30,13 @@
<PreferenceCategory app:title="@string/detail_settings_appearance_header"> <PreferenceCategory app:title="@string/detail_settings_appearance_header">
<Preference <Preference
app:key="@string/detail_settings_appearance_icon_set_key" app:key="@string/detail_settings_appearance_icon_set_key"
app:title="@string/detail_settings_appearance_icon_title" app:title="@string/detail_settings_appearance_icon_set_title"
app:summary="@string/detail_settings_appearance_icon_set_summary_no_set" app:summary="@string/detail_settings_appearance_icon_set_summary"
app:isPreferenceVisible="false"/> app:isPreferenceVisible="false"/>
<Preference <Preference
app:key="@string/detail_settings_appearance_icon_remove_key" app:key="@string/detail_settings_appearance_icon_remove_key"
app:title="@string/detail_settings_appearance_icon_title" app:title="@string/detail_settings_appearance_icon_remove_title"
app:summary="@string/detail_settings_appearance_icon_set_summary_set" app:summary="@string/detail_settings_appearance_icon_remove_summary"
app:isPreferenceVisible="false"/> app:isPreferenceVisible="false"/>
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>