Fix Android 5 crashes on unsubscribing

This commit is contained in:
Philipp Heckel 2022-12-06 16:17:05 -05:00
parent 6a8d222e12
commit e64cd79c28
11 changed files with 43 additions and 59 deletions

View file

@ -14,8 +14,8 @@ android {
minSdkVersion 21
targetSdkVersion 33
versionCode 31
versionName "1.15.2"
versionCode 32
versionName "1.16.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -1,5 +1,6 @@
package io.heckel.ntfy.firebase
@Suppress("UNUSED_PARAMETER")
class FirebaseMessenger {
fun subscribe(topic: String) {
// Dummy to keep F-Droid flavor happy

View file

@ -89,7 +89,7 @@ class Backuper(val context: Context) {
private suspend fun applySubscriptions(subscriptions: List<Subscription>?) {
if (subscriptions == null) {
return;
return
}
val appBaseUrl = context.getString(R.string.app_base_url)
subscriptions.forEach { s ->
@ -119,7 +119,7 @@ class Backuper(val context: Context) {
private suspend fun applyNotifications(notifications: List<Notification>?) {
if (notifications == null) {
return;
return
}
notifications.forEach { n ->
try {
@ -188,7 +188,7 @@ class Backuper(val context: Context) {
private suspend fun applyUsers(users: List<User>?) {
if (users == null) {
return;
return
}
users.forEach { u ->
try {

View file

@ -310,11 +310,11 @@ class SubscriberService : Service() {
override fun onTaskRemoved(rootIntent: Intent) {
val restartServiceIntent = Intent(applicationContext, SubscriberService::class.java).also {
it.setPackage(packageName)
};
val restartServicePendingIntent: PendingIntent = PendingIntent.getService(this, 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE);
applicationContext.getSystemService(Context.ALARM_SERVICE);
val alarmService: AlarmManager = applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager;
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartServicePendingIntent);
}
val restartServicePendingIntent: PendingIntent = PendingIntent.getService(this, 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE)
applicationContext.getSystemService(Context.ALARM_SERVICE)
val alarmService: AlarmManager = applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartServicePendingIntent)
}
/* This re-starts the service on reboot; see manifest */

View file

@ -7,6 +7,7 @@ import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_VIEW
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Html
import android.view.ActionMode
@ -178,7 +179,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
howToExample.linksClickable = true
val howToText = getString(R.string.detail_how_to_example, topicUrl)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
howToExample.text = Html.fromHtml(howToText, Html.FROM_HTML_MODE_LEGACY)
} else {
howToExample.text = Html.fromHtml(howToText)
@ -241,7 +242,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0) {
Log.d(TAG, "$itemCount item(s) inserted at $positionStart, scrolling to the top")
Log.d(TAG, "$itemCount item(s) inserted at 0, scrolling to the top")
mainList.scrollToPosition(positionStart)
}
}
@ -571,7 +572,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
dialog.setOnShowListener {
dialog
.getButton(AlertDialog.BUTTON_POSITIVE)
.setTextAppearance(R.style.DangerText)
.dangerButton(this)
}
dialog.show()
}
@ -609,7 +610,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
dialog.setOnShowListener {
dialog
.getButton(AlertDialog.BUTTON_POSITIVE)
.setTextAppearance(R.style.DangerText)
.dangerButton(this)
}
dialog.show()
}
@ -619,7 +620,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
handleActionModeClick(notification)
} else if (notification.click != "") {
try {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(notification.click)))
startActivity(Intent(ACTION_VIEW, Uri.parse(notification.click)))
} catch (e: Exception) {
Log.w(TAG, "Cannot open click URL", e)
runOnUiThread {
@ -720,7 +721,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
dialog.setOnShowListener {
dialog
.getButton(AlertDialog.BUTTON_POSITIVE)
.setTextAppearance(R.style.DangerText)
.dangerButton(this)
}
dialog.show()
}

View file

@ -35,7 +35,6 @@ import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.firebase.FirebaseMessenger
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.DownloadManager
import io.heckel.ntfy.msg.DownloadType
@ -48,7 +47,6 @@ import io.heckel.ntfy.work.PollWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.security.SecureRandom
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.random.Random
@ -622,7 +620,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
dialog.setOnShowListener {
dialog
.getButton(AlertDialog.BUTTON_POSITIVE)
.setTextAppearance(R.style.DangerText)
.dangerButton(this)
}
dialog.show()
}

View file

@ -119,7 +119,7 @@ class MainAdapter(private val repository: Repository, private val onClick: (Subs
if (selected.contains(subscription.id)) {
itemView.setBackgroundResource(Colors.itemSelectedBackground(context))
} else {
itemView.setBackgroundColor(Color.TRANSPARENT);
itemView.setBackgroundColor(Color.TRANSPARENT)
}
}
}

View file

@ -1,7 +1,6 @@
package io.heckel.ntfy.ui
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
@ -295,7 +294,7 @@ class ShareActivity : AppCompatActivity() {
.show()
}
} catch (e: Exception) {
val message = if (e is ApiService.UnauthorizedException) {
val errorMessage = if (e is ApiService.UnauthorizedException) {
if (e.user != null) {
getString(R.string.detail_test_message_error_unauthorized_user, e.user.username)
} else {
@ -308,7 +307,7 @@ class ShareActivity : AppCompatActivity() {
}
runOnUiThread {
progress.visibility = View.GONE
errorText.text = message
errorText.text = errorMessage
errorImage.visibility = View.VISIBLE
errorText.visibility = View.VISIBLE
}

View file

@ -5,8 +5,6 @@ import android.app.Dialog
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.WindowManager
import android.widget.Button
@ -16,6 +14,8 @@ import androidx.fragment.app.DialogFragment
import com.google.android.material.textfield.TextInputEditText
import io.heckel.ntfy.R
import io.heckel.ntfy.db.User
import io.heckel.ntfy.util.AfterChangedTextWatcher
import io.heckel.ntfy.util.dangerButton
import io.heckel.ntfy.util.validUrl
class UserFragment : DialogFragment() {
@ -98,28 +98,14 @@ class UserFragment : DialogFragment() {
// Delete button should be red
if (user != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
dialog
.getButton(AlertDialog.BUTTON_NEUTRAL)
.setTextAppearance(R.style.DangerText)
} else {
dialog
.getButton(AlertDialog.BUTTON_NEUTRAL)
.setTextColor(ContextCompat.getColor(requireContext(), Colors.dangerText(requireContext())))
}
dialog
.getButton(AlertDialog.BUTTON_NEUTRAL)
.dangerButton(requireContext())
}
// Validate input when typing
val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
validateInput()
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// Nothing
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// Nothing
}
val textWatcher = AfterChangedTextWatcher {
validateInput()
}
baseUrlView.addTextChangedListener(textWatcher)
usernameView.addTextChangedListener(textWatcher)
@ -140,7 +126,7 @@ class UserFragment : DialogFragment() {
}
// Show keyboard when the dialog is shown (see https://stackoverflow.com/a/19573049/1440785)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
return dialog
}

View file

@ -22,13 +22,16 @@ import android.util.Base64
import android.util.TypedValue
import android.view.View
import android.view.Window
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.db.*
import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64
import io.heckel.ntfy.ui.Colors
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@ -218,16 +221,6 @@ fun maybeAppendActionErrors(message: String, notification: Notification): String
}
}
// Checks in the most horrible way if a content URI exists; I couldn't find a better way
fun fileExists(context: Context, contentUri: String?): Boolean {
return try {
fileStat(context, Uri.parse(contentUri)) // Throws if the file does not exist
true
} catch (_: Exception) {
false
}
}
// Queries the filename of a content URI
fun fileName(context: Context, contentUri: String?, fallbackName: String): String {
return try {
@ -455,10 +448,6 @@ fun String.readBitmapFromUriOrNull(context: Context): Bitmap? {
}
}
fun Long.nullIfZero(): Long? {
return if (this == 0L) return null else this
}
// TextWatcher that only implements the afterTextChanged method
class AfterChangedTextWatcher(val afterTextChangedFn: (s: Editable?) -> Unit) : TextWatcher {
override fun afterTextChanged(s: Editable?) {
@ -506,3 +495,11 @@ fun String.sha256(): String {
val digest = md.digest(this.toByteArray())
return digest.fold("") { str, it -> str + "%02x".format(it) }
}
fun Button.dangerButton(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setTextAppearance(R.style.DangerText)
} else {
setTextColor(ContextCompat.getColor(context, Colors.dangerText(context)))
}
}

View file

@ -0,0 +1,2 @@
Bug fixes:
* Android 5 (SDK 21): Fix crash on unsubscribing (#528, thanks to Roger M.)