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.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
@ -70,9 +69,8 @@ class NotificationService(val context: Context) {
.setContentTitle(title)
.setOnlyAlertOnce(true) // Do not vibrate or play sound if already showing (updates!)
.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)
maybeSetIcon(builder, subscription)
maybeSetSound(builder, update)
maybeSetProgress(builder, notification)
maybeAddOpenAction(builder, notification)
@ -85,18 +83,6 @@ class NotificationService(val context: Context) {
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) {
if (!update) {
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 isSupportedImage = supportedImage(notification.attachment?.type)
val subscriptionIcon = if (subscription.icon != null) subscription.icon.readBitmapFromUriOrNull(context) else null
if (contentUri != null && isSupportedImage) {
try {
val resolver = context.applicationContext.contentResolver
val bitmapStream = resolver.openInputStream(Uri.parse(contentUri))
val bitmap = BitmapFactory.decodeStream(bitmapStream)
val attachmentBitmap = contentUri.readBitmapFromUri(context)
builder
.setContentText(maybeAppendActionErrors(formatMessage(notification), notification))
.setLargeIcon(bitmap)
.setLargeIcon(attachmentBitmap)
.setStyle(NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.bigLargeIcon(null))
.bigPicture(attachmentBitmap)
.bigLargeIcon(subscriptionIcon)) // May be null
} catch (_: Exception) {
val message = maybeAppendActionErrors(formatMessageMaybeWithAttachmentInfos(notification), notification)
builder
@ -131,6 +116,7 @@ class NotificationService(val context: Context) {
builder
.setContentText(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)
3 -> getString(R.string.channel_subscriber_notification_instant_text_three)
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 {
@ -241,6 +243,8 @@ class SubscriberService : Service() {
2 -> getString(R.string.channel_subscriber_notification_noinstant_text_two)
3 -> getString(R.string.channel_subscriber_notification_noinstant_text_three)
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)
}
}

View file

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

View file

@ -268,8 +268,7 @@ class DetailSettingsActivity : AppCompatActivity() {
// Set icon (if it exists)
if (subscription.icon != null) {
try {
val bitmapStream = resolver.openInputStream(Uri.parse(subscription.icon))
val bitmap = BitmapFactory.decodeStream(bitmapStream)
val bitmap = subscription.icon!!.readBitmapFromUri(requireContext())
iconRemovePref.icon = bitmap.toDrawable(resources)
} catch (e: Exception) {
Log.w(TAG, "Unable to set icon ${subscription.icon}", e)
@ -292,11 +291,8 @@ class DetailSettingsActivity : AppCompatActivity() {
it.copyTo(outputStream)
}
// Read image and set as preference icon
val bitmapStream = resolver.openInputStream(Uri.parse(outputUri.toString()))
val bitmap = BitmapFactory.decodeStream(bitmapStream)
// Display "remove" preference
// Read image & display "remove" preference
val bitmap = outputUri.readBitmapFromUri(requireContext())
iconRemovePref.icon = bitmap.toDrawable(resources)
iconRemovePref.isVisible = true
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.msg.NotificationService
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.readBitmapFromUriOrNull
import io.heckel.ntfy.util.topicShortUrl
import java.text.DateFormat
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 showMutedUntilIcon = !showMutedForeverIcon && (subscription.mutedUntil > 1L || globalMutedUntil > 1L) && !isUnifiedPush
if (subscription.icon != null) {
try {
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)
}
imageView.setImageBitmap(subscription.icon.readBitmapFromUriOrNull(context))
}
nameView.text = topicShortUrl(subscription.baseUrl, subscription.topic)
statusView.text = statusMessage

View file

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

View file

@ -9,6 +9,8 @@ import android.content.Context
import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.RippleDrawable
import android.net.Uri
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
class AfterChangedTextWatcher(val afterTextChangedFn: (s: Editable?) -> Unit) : TextWatcher {
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_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_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_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_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_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>
<!-- 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_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_icon_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_no_set">Set an icon to be displayed in notifications</string>
<string name="detail_settings_appearance_icon_set_title">Subscription icon</string>
<string name="detail_settings_appearance_icon_set_summary">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_global_setting_title">Use global setting</string>
<string name="detail_settings_global_setting_suffix">global</string>

View file

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