From 38ce2dd64e18de495083d71cb8b870ea0c53c262 Mon Sep 17 00:00:00 2001 From: Niko Diamadis Date: Thu, 18 Dec 2025 00:42:29 +0100 Subject: [PATCH] Apply code warnings/hint --- .../main/java/io/heckel/ntfy/db/Repository.kt | 146 +++++++++--------- .../java/io/heckel/ntfy/msg/ApiService.kt | 6 +- .../ntfy/msg/DownloadAttachmentWorker.kt | 4 +- .../io/heckel/ntfy/msg/DownloadIconWorker.kt | 4 +- .../io/heckel/ntfy/msg/NotificationService.kt | 11 +- .../heckel/ntfy/service/SubscriberService.kt | 19 +-- .../java/io/heckel/ntfy/ui/AddFragment.kt | 9 +- .../java/io/heckel/ntfy/ui/DetailActivity.kt | 3 +- .../java/io/heckel/ntfy/ui/DetailAdapter.kt | 27 ++-- .../heckel/ntfy/ui/DetailSettingsActivity.kt | 9 +- .../java/io/heckel/ntfy/ui/MainActivity.kt | 30 ++-- .../java/io/heckel/ntfy/ui/MainAdapter.kt | 2 +- .../java/io/heckel/ntfy/ui/MainViewModel.kt | 4 +- .../io/heckel/ntfy/ui/NotificationFragment.kt | 1 - .../io/heckel/ntfy/ui/SettingsActivity.kt | 13 +- .../main/java/io/heckel/ntfy/util/Emoji.java | 8 +- .../java/io/heckel/ntfy/util/EmojiLoader.java | 7 +- app/src/main/java/io/heckel/ntfy/util/Util.kt | 11 +- .../java/io/heckel/ntfy/work/DeleteWorker.kt | 4 +- 19 files changed, 162 insertions(+), 156 deletions(-) diff --git a/app/src/main/java/io/heckel/ntfy/db/Repository.kt b/app/src/main/java/io/heckel/ntfy/db/Repository.kt index 3d0a253d..f748e3a0 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Repository.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Repository.kt @@ -11,8 +11,9 @@ import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.validUrl import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicLong +import androidx.core.content.edit -class Repository(private val sharedPrefs: SharedPreferences, private val database: Database) { +class Repository(private val sharedPrefs: SharedPreferences, database: Database) { private val subscriptionDao = database.subscriptionDao() private val notificationDao = database.notificationDao() private val userDao = database.userDao() @@ -190,9 +191,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setPollWorkerVersion(version: Int) { - sharedPrefs.edit() - .putInt(SHARED_PREFS_POLL_WORKER_VERSION, version) - .apply() + sharedPrefs.edit { + putInt(SHARED_PREFS_POLL_WORKER_VERSION, version) + } } fun getDeleteWorkerVersion(): Int { @@ -200,9 +201,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setDeleteWorkerVersion(version: Int) { - sharedPrefs.edit() - .putInt(SHARED_PREFS_DELETE_WORKER_VERSION, version) - .apply() + sharedPrefs.edit { + putInt(SHARED_PREFS_DELETE_WORKER_VERSION, version) + } } fun getAutoRestartWorkerVersion(): Int { @@ -210,20 +211,20 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setAutoRestartWorkerVersion(version: Int) { - sharedPrefs.edit() - .putInt(SHARED_PREFS_AUTO_RESTART_WORKER_VERSION, version) - .apply() + sharedPrefs.edit { + putInt(SHARED_PREFS_AUTO_RESTART_WORKER_VERSION, version) + } } fun setMinPriority(minPriority: Int) { if (minPriority <= MIN_PRIORITY_ANY) { - sharedPrefs.edit() - .remove(SHARED_PREFS_MIN_PRIORITY) - .apply() + sharedPrefs.edit { + remove(SHARED_PREFS_MIN_PRIORITY) + } } else { - sharedPrefs.edit() - .putInt(SHARED_PREFS_MIN_PRIORITY, minPriority) - .apply() + sharedPrefs.edit { + putInt(SHARED_PREFS_MIN_PRIORITY, minPriority) + } } } @@ -241,9 +242,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setAutoDownloadMaxSize(maxSize: Long) { - sharedPrefs.edit() - .putLong(SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE, maxSize) - .apply() + sharedPrefs.edit { + putLong(SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE, maxSize) + } } fun getAutoDeleteSeconds(): Long { @@ -251,20 +252,20 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setAutoDeleteSeconds(seconds: Long) { - sharedPrefs.edit() - .putLong(SHARED_PREFS_AUTO_DELETE_SECONDS, seconds) - .apply() + sharedPrefs.edit { + putLong(SHARED_PREFS_AUTO_DELETE_SECONDS, seconds) + } } fun setDarkMode(mode: Int) { if (mode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) { - sharedPrefs.edit() - .remove(SHARED_PREFS_DARK_MODE) - .apply() + sharedPrefs.edit { + remove(SHARED_PREFS_DARK_MODE) + } } else { - sharedPrefs.edit() - .putInt(SHARED_PREFS_DARK_MODE, mode) - .apply() + sharedPrefs.edit { + putInt(SHARED_PREFS_DARK_MODE, mode) + } } } @@ -273,9 +274,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setDynamicColorsEnabled(enabled: Boolean) { - sharedPrefs.edit() - .putBoolean(SHARED_PREFS_DYNAMIC_COLORS, enabled) - .commit() + sharedPrefs.edit(commit = true) { + putBoolean(SHARED_PREFS_DYNAMIC_COLORS, enabled) + } } fun getDynamicColorsEnabled(): Boolean { @@ -283,9 +284,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setConnectionProtocol(connectionProtocol: String) { - sharedPrefs.edit() - .putString(SHARED_PREFS_CONNECTION_PROTOCOL, connectionProtocol) - .apply() + sharedPrefs.edit { + putString(SHARED_PREFS_CONNECTION_PROTOCOL, connectionProtocol) + } } fun getConnectionProtocol(): String { @@ -297,9 +298,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setBroadcastEnabled(enabled: Boolean) { - sharedPrefs.edit() - .putBoolean(SHARED_PREFS_BROADCAST_ENABLED, enabled) - .apply() + sharedPrefs.edit { + putBoolean(SHARED_PREFS_BROADCAST_ENABLED, enabled) + } } fun getUnifiedPushEnabled(): Boolean { @@ -307,9 +308,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setUnifiedPushEnabled(enabled: Boolean) { - sharedPrefs.edit() - .putBoolean(SHARED_PREFS_UNIFIEDPUSH_ENABLED, enabled) - .apply() + sharedPrefs.edit { + putBoolean(SHARED_PREFS_UNIFIEDPUSH_ENABLED, enabled) + } } fun getInsistentMaxPriorityEnabled(): Boolean { @@ -317,9 +318,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setInsistentMaxPriorityEnabled(enabled: Boolean) { - sharedPrefs.edit() - .putBoolean(SHARED_PREFS_INSISTENT_MAX_PRIORITY_ENABLED, enabled) - .apply() + sharedPrefs.edit { + putBoolean(SHARED_PREFS_INSISTENT_MAX_PRIORITY_ENABLED, enabled) + } } fun getRecordLogs(): Boolean { @@ -327,9 +328,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setRecordLogsEnabled(enabled: Boolean) { - sharedPrefs.edit() - .putBoolean(SHARED_PREFS_RECORD_LOGS_ENABLED, enabled) - .apply() + sharedPrefs.edit { + putBoolean(SHARED_PREFS_RECORD_LOGS_ENABLED, enabled) + } } fun getBatteryOptimizationsRemindTime(): Long { @@ -337,9 +338,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setBatteryOptimizationsRemindTime(timeMillis: Long) { - sharedPrefs.edit() - .putLong(SHARED_PREFS_BATTERY_OPTIMIZATIONS_REMIND_TIME, timeMillis) - .apply() + sharedPrefs.edit { + putLong(SHARED_PREFS_BATTERY_OPTIMIZATIONS_REMIND_TIME, timeMillis) + } } fun getWebSocketRemindTime(): Long { @@ -347,9 +348,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setWebSocketRemindTime(timeMillis: Long) { - sharedPrefs.edit() - .putLong(SHARED_PREFS_WEBSOCKET_REMIND_TIME, timeMillis) - .apply() + sharedPrefs.edit { + putLong(SHARED_PREFS_WEBSOCKET_REMIND_TIME, timeMillis) + } } fun getWebSocketReconnectRemindTime(): Long { @@ -357,9 +358,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setWebSocketReconnectRemindTime(timeMillis: Long) { - sharedPrefs.edit() - .putLong(SHARED_PREFS_WEBSOCKET_RECONNECT_REMIND_TIME, timeMillis) - .apply() + sharedPrefs.edit { + putLong(SHARED_PREFS_WEBSOCKET_RECONNECT_REMIND_TIME, timeMillis) + } } fun getDefaultBaseUrl(): String? { @@ -369,16 +370,15 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas fun setDefaultBaseUrl(baseUrl: String) { if (baseUrl == "") { - sharedPrefs - .edit() - .remove(SHARED_PREFS_UNIFIED_PUSH_BASE_URL) // Remove legacy key - .remove(SHARED_PREFS_DEFAULT_BASE_URL) - .apply() + sharedPrefs.edit { + remove(SHARED_PREFS_UNIFIED_PUSH_BASE_URL) // Remove legacy key + .remove(SHARED_PREFS_DEFAULT_BASE_URL) + } } else { - sharedPrefs.edit() - .remove(SHARED_PREFS_UNIFIED_PUSH_BASE_URL) // Remove legacy key - .putString(SHARED_PREFS_DEFAULT_BASE_URL, baseUrl) - .apply() + sharedPrefs.edit { + remove(SHARED_PREFS_UNIFIED_PUSH_BASE_URL) // Remove legacy key + .putString(SHARED_PREFS_DEFAULT_BASE_URL, baseUrl) + } } } @@ -392,18 +392,18 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas } fun setGlobalMutedUntil(mutedUntilTimestamp: Long) { - sharedPrefs.edit() - .putLong(SHARED_PREFS_MUTED_UNTIL_TIMESTAMP, mutedUntilTimestamp) - .apply() + sharedPrefs.edit { + putLong(SHARED_PREFS_MUTED_UNTIL_TIMESTAMP, mutedUntilTimestamp) + } } fun checkGlobalMutedUntil(): Boolean { val mutedUntil = sharedPrefs.getLong(SHARED_PREFS_MUTED_UNTIL_TIMESTAMP, 0L) val expired = mutedUntil > 1L && System.currentTimeMillis()/1000 > mutedUntil if (expired) { - sharedPrefs.edit() - .putLong(SHARED_PREFS_MUTED_UNTIL_TIMESTAMP, 0L) - .apply() + sharedPrefs.edit { + putLong(SHARED_PREFS_MUTED_UNTIL_TIMESTAMP, 0L) + } return true } return false @@ -416,9 +416,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas fun addLastShareTopic(topic: String) { val topics = (getLastShareTopics().filterNot { it == topic } + topic).takeLast(LAST_TOPICS_COUNT) - sharedPrefs.edit() - .putString(SHARED_PREFS_LAST_TOPICS, topics.joinToString(separator = "\n")) - .apply() + sharedPrefs.edit { + putString(SHARED_PREFS_LAST_TOPICS, topics.joinToString(separator = "\n")) + } } private fun toSubscriptionList(list: List): List { diff --git a/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt b/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt index 54c48ea9..2040e1c2 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt @@ -94,8 +94,8 @@ class ApiService { if (!response.isSuccessful) { throw Exception("Unexpected response ${response.code} when polling topic $url") } - val body = response.body?.string()?.trim() - if (body.isNullOrEmpty()) return emptyList() + val body = response.body.string().trim() + if (body.isEmpty()) return emptyList() val notifications = body.lines().mapNotNull { line -> parser.parse(line, subscriptionId = subscriptionId, notificationId = 0) // No notification when we poll } @@ -124,7 +124,7 @@ class ApiService { if (!response.isSuccessful) { throw Exception("Unexpected response ${response.code} when subscribing to topic $url") } - val source = response.body?.source() ?: throw Exception("Unexpected response for $url: body is empty") + val source = response.body.source() while (!source.exhausted()) { val line = source.readUtf8Line() ?: throw Exception("Unexpected response for $url: line is null") val notification = parser.parseWithTopic(line, notificationId = Random.nextInt(), subscriptionId = 0) // subscriptionId to be set downstream diff --git a/app/src/main/java/io/heckel/ntfy/msg/DownloadAttachmentWorker.kt b/app/src/main/java/io/heckel/ntfy/msg/DownloadAttachmentWorker.kt index cffd5911..f1cf32a9 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/DownloadAttachmentWorker.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/DownloadAttachmentWorker.kt @@ -67,7 +67,7 @@ class DownloadAttachmentWorker(private val context: Context, params: WorkerParam .build() client.newCall(request).execute().use { response -> Log.d(TAG, "Download: headers received: $response") - if (!response.isSuccessful || response.body == null) { + if (!response.isSuccessful) { throw Exception("Unexpected response: ${response.code}") } save(updateAttachmentFromResponse(response)) @@ -84,7 +84,7 @@ class DownloadAttachmentWorker(private val context: Context, params: WorkerParam val outFile = resolver.openOutputStream(uri) ?: throw Exception("Cannot open output stream") val downloadLimit = getDownloadLimit(userAction) outFile.use { fileOut -> - val fileIn = response.body!!.byteStream() + val fileIn = response.body.byteStream() val buffer = ByteArray(BUFFER_SIZE) var bytes = fileIn.read(buffer) var lastProgress = 0L diff --git a/app/src/main/java/io/heckel/ntfy/msg/DownloadIconWorker.kt b/app/src/main/java/io/heckel/ntfy/msg/DownloadIconWorker.kt index 253ee23d..c7b560dc 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/DownloadIconWorker.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/DownloadIconWorker.kt @@ -70,7 +70,7 @@ class DownloadIconWorker(private val context: Context, params: WorkerParameters) .build() client.newCall(request).execute().use { response -> Log.d(TAG, "Headers received: $response, Content-Length: ${response.headers["Content-Length"]}") - if (!response.isSuccessful || response.body == null) { + if (!response.isSuccessful) { throw Exception("Unexpected response: ${response.code}") } else if (shouldAbortDownload(response)) { Log.d(TAG, "Aborting download: Content-Length is larger than auto-download setting") @@ -85,7 +85,7 @@ class DownloadIconWorker(private val context: Context, params: WorkerParameters) val outFile = resolver.openOutputStream(uri) ?: throw Exception("Cannot open output stream") val downloadLimit = getDownloadLimit() outFile.use { fileOut -> - val fileIn = response.body!!.byteStream() + val fileIn = response.body.byteStream() val buffer = ByteArray(BUFFER_SIZE) var bytes = fileIn.read(buffer) while (bytes >= 0) { diff --git a/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt b/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt index 0b803268..6d3ee959 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt @@ -20,6 +20,7 @@ import io.heckel.ntfy.ui.DetailActivity import io.heckel.ntfy.ui.MainActivity import io.heckel.ntfy.util.* import java.util.* +import androidx.core.net.toUri class NotificationService(val context: Context) { private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -184,10 +185,10 @@ class NotificationService(val context: Context) { builder.setContentIntent(detailActivityIntent(subscription)) } else { try { - val uri = Uri.parse(notification.click) + val uri = notification.click.toUri() val viewIntent = PendingIntent.getActivity(context, Random().nextInt(), Intent(Intent.ACTION_VIEW, uri), PendingIntent.FLAG_IMMUTABLE) builder.setContentIntent(viewIntent) - } catch (e: Exception) { + } catch (_: Exception) { builder.setContentIntent(detailActivityIntent(subscription)) } } @@ -207,7 +208,7 @@ class NotificationService(val context: Context) { return } if (notification.attachment?.contentUri != null) { - val contentUri = Uri.parse(notification.attachment.contentUri) + val contentUri = notification.attachment.contentUri.toUri() val intent = Intent(Intent.ACTION_VIEW, contentUri).apply { setDataAndType(contentUri, notification.attachment.type ?: "application/octet-stream") // Required for Android <= P addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) @@ -275,7 +276,7 @@ class NotificationService(val context: Context) { private fun addViewUserActionWithoutClear(builder: NotificationCompat.Builder, action: Action) { try { val url = action.url ?: return - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply { + val intent = Intent(Intent.ACTION_VIEW, url.toUri()).apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) } val pendingIntent = PendingIntent.getActivity(context, Random().nextInt(), intent, PendingIntent.FLAG_IMMUTABLE) @@ -473,7 +474,7 @@ class NotificationService(val context: Context) { // Immediately start the actual activity try { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply { + val intent = Intent(Intent.ACTION_VIEW, url.toUri()).apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(intent) diff --git a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt index bd39bea0..d7377b0a 100644 --- a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt +++ b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import java.util.concurrent.ConcurrentHashMap +import androidx.core.content.edit /** * The subscriber service manages the foreground service for instant delivery. @@ -120,7 +121,7 @@ class SubscriberService : Service() { Log.d(TAG, "Starting the foreground service task") isServiceStarted = true saveServiceState(this, ServiceState.STARTED) - wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run { + wakeLock = (getSystemService(POWER_SERVICE) as PowerManager).run { newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG) } refreshConnections() @@ -276,7 +277,7 @@ class SubscriberService : Service() { } private fun createNotificationChannel(): NotificationManager { - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager val channelName = getString(R.string.channel_subscriber_service_name) // Show's up in UI val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW).let { it.setShowBadge(false) // Don't show long-press badge @@ -313,8 +314,8 @@ class SubscriberService : Service() { 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 + applicationContext.getSystemService(ALARM_SERVICE) + val alarmService: AlarmManager = applicationContext.getSystemService(ALARM_SERVICE) as AlarmManager alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartServicePendingIntent) } @@ -361,14 +362,14 @@ class SubscriberService : Service() { private const val SHARED_PREFS_SERVICE_STATE = "ServiceState" fun saveServiceState(context: Context, state: ServiceState) { - val sharedPrefs = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) - sharedPrefs.edit() - .putString(SHARED_PREFS_SERVICE_STATE, state.name) - .apply() + val sharedPrefs = context.getSharedPreferences(SHARED_PREFS_ID, MODE_PRIVATE) + sharedPrefs.edit { + putString(SHARED_PREFS_SERVICE_STATE, state.name) + } } fun readServiceState(context: Context): ServiceState { - val sharedPrefs = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) + val sharedPrefs = context.getSharedPreferences(SHARED_PREFS_ID, MODE_PRIVATE) val value = sharedPrefs.getString(SHARED_PREFS_SERVICE_STATE, ServiceState.STOPPED.name) return ServiceState.valueOf(value!!) } diff --git a/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt b/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt index 26772075..3d5be8b3 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt @@ -7,7 +7,6 @@ import android.os.Bundle import android.view.MenuItem import android.view.View import android.view.ViewGroup -import android.view.WindowManager import android.view.inputmethod.InputMethodManager import android.widget.* import androidx.fragment.app.DialogFragment @@ -23,6 +22,8 @@ import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import androidx.core.view.isVisible +import androidx.core.view.isGone class AddFragment : DialogFragment() { private val api = ApiService() @@ -206,9 +207,9 @@ class AddFragment : DialogFragment() { private fun onActionButtonClick() { val topic = subscribeTopicText.text.toString() val baseUrl = getBaseUrl() - if (subscribeView.visibility == View.VISIBLE) { + if (subscribeView.isVisible) { checkReadAndMaybeShowLogin(baseUrl, topic) - } else if (loginView.visibility == View.VISIBLE) { + } else if (loginView.isVisible) { loginAndMaybeDismiss(baseUrl, topic) } } @@ -349,7 +350,7 @@ class AddFragment : DialogFragment() { if (!this::actionMenuItem.isInitialized || !this::loginUsernameText.isInitialized || !this::loginPasswordText.isInitialized) { return // As per crash seen in Google Play } - if (loginUsernameText.visibility == View.GONE) { + if (loginUsernameText.isGone) { actionMenuItem.isEnabled = true } else { actionMenuItem.isEnabled = (loginUsernameText.text?.isNotEmpty() ?: false) diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt index 29453037..2a3af052 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt @@ -55,6 +55,7 @@ import java.util.Date import kotlin.random.Random import androidx.core.view.size import androidx.core.view.get +import androidx.core.net.toUri class DetailActivity : AppCompatActivity(), NotificationFragment.NotificationSettingsListener { private val viewModel by viewModels { @@ -726,7 +727,7 @@ class DetailActivity : AppCompatActivity(), NotificationFragment.NotificationSet handleActionModeClick(notification) } else if (notification.click != "") { try { - startActivity(Intent(ACTION_VIEW, Uri.parse(notification.click))) + startActivity(Intent(ACTION_VIEW, notification.click.toUri())) } catch (e: Exception) { Log.w(TAG, "Cannot open click URL", e) runOnUiThread { diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt index 0a0c6556..6383a80b 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt @@ -5,11 +5,9 @@ import android.app.Activity import android.content.* import android.content.pm.PackageManager import android.graphics.Bitmap -import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore -import android.text.method.LinkMovementMethod import android.text.util.Linkify import android.view.LayoutInflater import android.view.View @@ -42,6 +40,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import me.saket.bettermovementmethod.BetterLinkMovementMethod +import androidx.core.net.toUri class DetailAdapter(private val activity: Activity, private val lifecycleScope: CoroutineScope, private val repository: Repository, private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) : ListAdapter(TopicDiffCallback) { @@ -71,7 +70,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: selected.add(notificationId) } - if (selected.size != 0) { + if (selected.isNotEmpty()) { val listIds = currentList.map { notification -> notification.id } val notificationPosition = listIds.indexOf(notificationId) notifyItemChanged(notificationPosition) @@ -205,7 +204,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: } val attachment = notification.attachment val image = attachment.contentUri != null && supportedImage(attachment.type) && previewableImage(attachmentFileStat) - val bitmap = if (image) attachment.contentUri?.readBitmapFromUriOrNull(context) else null + val bitmap = if (image) attachment.contentUri.readBitmapFromUriOrNull(context) else null maybeRenderAttachmentImage(context, bitmap, attachment) maybeRenderAttachmentBox(context, notification, attachment, attachmentFileStat, bitmap) } @@ -351,7 +350,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: if (expired) { infos.add(context.getString(R.string.detail_item_download_info_not_downloaded_expired)) } else if (expires) { - infos.add(context.getString(R.string.detail_item_download_info_not_downloaded_expires_x, formatDateShort(attachment.expires!!))) + infos.add(context.getString(R.string.detail_item_download_info_not_downloaded_expires_x, formatDateShort(attachment.expires))) } else { infos.add(context.getString(R.string.detail_item_download_info_not_downloaded)) } @@ -361,7 +360,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: if (expired) { infos.add(context.getString(R.string.detail_item_download_info_deleted_expired)) } else if (expires) { - infos.add(context.getString(R.string.detail_item_download_info_deleted_expires_x, formatDateShort(attachment.expires!!))) + infos.add(context.getString(R.string.detail_item_download_info_deleted_expires_x, formatDateShort(attachment.expires))) } else { infos.add(context.getString(R.string.detail_item_download_info_deleted)) } @@ -369,12 +368,12 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: if (expired) { infos.add(context.getString(R.string.detail_item_download_info_download_failed_expired)) } else if (expires) { - infos.add(context.getString(R.string.detail_item_download_info_download_failed_expires_x, formatDateShort(attachment.expires!!))) + infos.add(context.getString(R.string.detail_item_download_info_download_failed_expires_x, formatDateShort(attachment.expires))) } else { infos.add(context.getString(R.string.detail_item_download_info_download_failed)) } } - return if (infos.size > 0) { + return if (infos.isNotEmpty()) { "$name\n${infos.joinToString(", ")}" } else { name @@ -389,7 +388,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: try { Glide.with(context).load(attachment.contentUri).fitCenter().into(attachmentImageView) attachmentImageView.setOnClickListener { - StfalconImageViewer.Builder(context, listOf(bitmap)) { imageView, image -> + StfalconImageViewer.Builder(context, listOf(bitmap)) { imageView, _ -> Glide.with(context).load(attachment.contentUri).into(imageView) } .allowZooming(true) @@ -412,12 +411,12 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: } Log.d(TAG, "Opening file ${attachment.contentUri}") try { - val contentUri = Uri.parse(attachment.contentUri) + val contentUri = attachment.contentUri?.toUri() val intent = Intent(Intent.ACTION_VIEW, contentUri) intent.setDataAndType(contentUri, attachment.type ?: "application/octet-stream") // Required for Android <= P intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) context.startActivity(intent) - } catch (e: ActivityNotFoundException) { + } catch (_: ActivityNotFoundException) { Toast .makeText(context, context.getString(R.string.detail_item_cannot_open_not_found), Toast.LENGTH_LONG) .show() @@ -444,7 +443,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: put(MediaStore.MediaColumns.IS_PENDING, 1) // While downloading } } - val inUri = Uri.parse(attachment.contentUri) + val inUri = attachment.contentUri!!.toUri() val inFile = resolver.openInputStream(inUri) ?: throw Exception("Cannot open input stream") val outUri = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { val file = ensureSafeNewFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), attachment.name) @@ -475,7 +474,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: private fun deleteFile(context: Context, notification: Notification, attachment: Attachment): Boolean { try { - val contentUri = Uri.parse(attachment.contentUri) + val contentUri = attachment.contentUri!!.toUri() val resolver = context.applicationContext.contentResolver val deleted = resolver.delete(contentUri, null, null) > 0 if (!deleted) throw Exception("no rows deleted") @@ -537,7 +536,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope: private fun runViewAction(context: Context, action: Action) { try { val url = action.url ?: return - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply { + val intent = Intent(Intent.ACTION_VIEW, url.toUri()).apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } context.startActivity(intent) diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt index e012ba04..51a81b09 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.* import java.io.File import java.io.IOException import java.util.* +import androidx.core.net.toUri /** * Subscription settings @@ -143,10 +144,8 @@ class DetailSettingsActivity : AppCompatActivity() { loadInsistentMaxPriorityPref() loadIconSetPref() loadIconRemovePref() - if (notificationService.channelsSupported()) { - loadDedicatedChannelsPrefs() - loadOpenChannelsPrefs() - } + loadDedicatedChannelsPrefs() + loadOpenChannelsPrefs() } else { val notificationsHeaderId = context?.getString(R.string.detail_settings_notifications_header_key) ?: return val notificationsHeader: PreferenceCategory? = findPreference(notificationsHeaderId) @@ -507,7 +506,7 @@ class DetailSettingsActivity : AppCompatActivity() { return } try { - resolver.delete(Uri.parse(uri), null, null) + resolver.delete(uri.toUri(), null, null) } catch (e: Exception) { Log.w(TAG, "Unable to delete $uri", e) } diff --git a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt index 04b28fd9..102ef812 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt @@ -8,7 +8,6 @@ import android.app.AlertDialog import android.content.ActivityNotFoundException import android.content.Intent import android.content.pm.PackageManager -import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings @@ -75,6 +74,7 @@ import java.util.concurrent.TimeUnit import kotlin.random.Random import androidx.core.view.size import androidx.core.view.get +import androidx.core.net.toUri class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, NotificationFragment.NotificationSettingsListener { private val viewModel by viewModels { @@ -258,17 +258,17 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific } fixNowButton.setOnClickListener { try { - Log.d(TAG, Uri.parse("package:$packageName").toString()) + Log.d(TAG, "package:$packageName".toUri().toString()) startActivity( Intent( Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, - Uri.parse("package:$packageName") + "package:$packageName".toUri() ) ) - } catch (e: ActivityNotFoundException) { + } catch (_: ActivityNotFoundException) { try { startActivity(Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)) - } catch (e2: ActivityNotFoundException) { + } catch (_: ActivityNotFoundException) { startActivity(Intent(Settings.ACTION_SETTINGS)) } } @@ -558,19 +558,27 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific true } R.id.main_menu_report_bug -> { - startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.main_menu_report_bug_url)))) + startActivity( + Intent(Intent.ACTION_VIEW, getString(R.string.main_menu_report_bug_url).toUri()) + ) true } R.id.main_menu_rate -> { try { - startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName"))) - } catch (e: ActivityNotFoundException) { - startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageName"))) + startActivity( + Intent(Intent.ACTION_VIEW, "market://details?id=$packageName".toUri()) + ) + } catch (_: ActivityNotFoundException) { + startActivity( + Intent(Intent.ACTION_VIEW, "https://play.google.com/store/apps/details?id=$packageName".toUri()) + ) } true } R.id.main_menu_docs -> { - startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.main_menu_docs_url)))) + startActivity( + Intent(Intent.ACTION_VIEW, getString(R.string.main_menu_docs_url).toUri()) + ) true } else -> super.onOptionsItemSelected(item) @@ -683,7 +691,7 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific var errorMessage = "" // First error var newNotificationsCount = 0 repository.getSubscriptions().forEach { subscription -> - Log.d(TAG, "subscription: ${subscription}") + Log.d(TAG, "subscription: $subscription") try { val user = repository.getUser(subscription.baseUrl) // May be null val notifications = api.poll(subscription.id, subscription.baseUrl, subscription.topic, user, subscription.lastNotificationId) diff --git a/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt b/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt index d9d08434..b128658f 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt @@ -51,7 +51,7 @@ class MainAdapter( selected.add(subscriptionId) } - if (selected.size != 0) { + if (selected.isNotEmpty()) { val listIds = currentList.map { subscription -> subscription.id } val subscriptionPosition = listIds.indexOf(subscriptionId) notifyItemChanged(subscriptionPosition) diff --git a/app/src/main/java/io/heckel/ntfy/ui/MainViewModel.kt b/app/src/main/java/io/heckel/ntfy/ui/MainViewModel.kt index 19e384c9..c5f48d79 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/MainViewModel.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/MainViewModel.kt @@ -1,7 +1,6 @@ package io.heckel.ntfy.ui import android.content.Context -import android.net.Uri import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -10,6 +9,7 @@ import io.heckel.ntfy.db.* import io.heckel.ntfy.up.Distributor import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import androidx.core.net.toUri class SubscriptionsViewModel(private val repository: Repository) : ViewModel() { fun list(): LiveData> { @@ -35,7 +35,7 @@ class SubscriptionsViewModel(private val repository: Repository) : ViewModel() { if (subscription.icon != null) { val resolver = context.applicationContext.contentResolver try { - resolver.delete(Uri.parse(subscription.icon), null, null) + resolver.delete(subscription.icon.toUri(), null, null) } catch (_: Exception) { // Don't care } diff --git a/app/src/main/java/io/heckel/ntfy/ui/NotificationFragment.kt b/app/src/main/java/io/heckel/ntfy/ui/NotificationFragment.kt index 920d1bd4..a55697f0 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/NotificationFragment.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/NotificationFragment.kt @@ -1,6 +1,5 @@ package io.heckel.ntfy.ui -import android.app.AlertDialog import android.app.Dialog import android.content.Context import android.os.Bundle diff --git a/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt index 87dee0d7..b523c5b6 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt @@ -4,7 +4,6 @@ import android.Manifest import android.app.AlarmManager import android.content.ClipData import android.content.ClipboardManager -import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Build @@ -619,7 +618,7 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere versionPref?.summary = version versionPref?.onPreferenceClickListener = OnPreferenceClickListener { val context = context ?: return@OnPreferenceClickListener false - val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("ntfy version", version) clipboard.setPrimaryClip(clip) Toast @@ -647,7 +646,7 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere val context = context ?: return@launch val log = Log.getFormatted(context, scrub = scrub) requireActivity().runOnUiThread { - val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("ntfy logs", log) clipboard.setPrimaryClip(clip) if (scrub) { @@ -689,12 +688,12 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere if (!response.isSuccessful) { throw Exception("Unexpected response ${response.code}") } - val body = response.body?.string()?.trim() - if (body.isNullOrEmpty()) throw Exception("Return body is empty") + val body = response.body.string().trim() + if (body.isEmpty()) throw Exception("Return body is empty") Log.d(TAG, "Logs uploaded successfully: $body") - val resp = gson.fromJson(body.toString(), NopasteResponse::class.java) + val resp = gson.fromJson(body, NopasteResponse::class.java) requireActivity().runOnUiThread { - val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("logs URL", resp.url) clipboard.setPrimaryClip(clip) if (scrub) { diff --git a/app/src/main/java/io/heckel/ntfy/util/Emoji.java b/app/src/main/java/io/heckel/ntfy/util/Emoji.java index bda8afc3..12960ea9 100644 --- a/app/src/main/java/io/heckel/ntfy/util/Emoji.java +++ b/app/src/main/java/io/heckel/ntfy/util/Emoji.java @@ -1,6 +1,6 @@ package io.heckel.ntfy.util; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -16,11 +16,7 @@ public class Emoji { protected Emoji(List aliases, byte... bytes) { this.aliases = Collections.unmodifiableList(aliases); - try { - this.unicode = new String(bytes, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + this.unicode = new String(bytes, StandardCharsets.UTF_8); } public List getAliases() { diff --git a/app/src/main/java/io/heckel/ntfy/util/EmojiLoader.java b/app/src/main/java/io/heckel/ntfy/util/EmojiLoader.java index f14a5cf6..12712634 100644 --- a/app/src/main/java/io/heckel/ntfy/util/EmojiLoader.java +++ b/app/src/main/java/io/heckel/ntfy/util/EmojiLoader.java @@ -5,6 +5,7 @@ import org.json.JSONException; import org.json.JSONObject; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -37,7 +38,7 @@ public class EmojiLoader { InputStream stream ) throws IOException { StringBuilder sb = new StringBuilder(); - InputStreamReader isr = new InputStreamReader(stream, "UTF-8"); + InputStreamReader isr = new InputStreamReader(stream, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr); String read; while((read = br.readLine()) != null) { @@ -49,12 +50,12 @@ public class EmojiLoader { protected static Emoji buildEmojiFromJSON( JSONObject json - ) throws UnsupportedEncodingException, JSONException { + ) throws JSONException { if (!json.has("emoji")) { return null; } - byte[] bytes = json.getString("emoji").getBytes("UTF-8"); + byte[] bytes = json.getString("emoji").getBytes(StandardCharsets.UTF_8); List aliases = jsonArrayToStringList(json.getJSONArray("aliases")); return new Emoji(aliases, bytes); } diff --git a/app/src/main/java/io/heckel/ntfy/util/Util.kt b/app/src/main/java/io/heckel/ntfy/util/Util.kt index f6b84b75..28be312b 100644 --- a/app/src/main/java/io/heckel/ntfy/util/Util.kt +++ b/app/src/main/java/io/heckel/ntfy/util/Util.kt @@ -49,6 +49,7 @@ import java.text.StringCharacterIterator import java.util.Date import kotlin.math.abs import kotlin.math.absoluteValue +import androidx.core.net.toUri fun topicUrl(baseUrl: String, topic: String) = "${baseUrl}/${topic}" fun topicUrlUp(baseUrl: String, topic: String) = "${baseUrl}/${topic}?up=1" // UnifiedPush @@ -168,7 +169,7 @@ fun decodeMessage(notification: Notification): String { } else { notification.message } - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { notification.message + "(invalid base64)" } } @@ -180,7 +181,7 @@ fun decodeBytesMessage(notification: Notification): ByteArray { } else { notification.message.toByteArray() } - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { notification.message.toByteArray() } } @@ -230,7 +231,7 @@ fun maybeAppendActionErrors(message: CharSequence, notification: Notification): // Queries the filename of a content URI fun fileName(context: Context, contentUri: String?, fallbackName: String): String { return try { - val info = fileStat(context, Uri.parse(contentUri)) + val info = fileStat(context, contentUri?.toUri()) info.filename } catch (_: Exception) { fallbackName @@ -264,7 +265,7 @@ fun fileStat(context: Context, contentUri: Uri?): FileInfo { fun maybeFileStat(context: Context, contentUri: String?): FileInfo? { return try { - fileStat(context, Uri.parse(contentUri)) // Throws if the file does not exist + fileStat(context, contentUri?.toUri()) // Throws if the file does not exist } catch (_: Exception) { null } @@ -427,7 +428,7 @@ fun Uri.readBitmapFromUri(context: Context): Bitmap { } fun String.readBitmapFromUri(context: Context): Bitmap { - return Uri.parse(this).readBitmapFromUri(context) + return this.toUri().readBitmapFromUri(context) } fun String.readBitmapFromUriOrNull(context: Context): Bitmap? { diff --git a/app/src/main/java/io/heckel/ntfy/work/DeleteWorker.kt b/app/src/main/java/io/heckel/ntfy/work/DeleteWorker.kt index 52fe5656..b05fb4b2 100644 --- a/app/src/main/java/io/heckel/ntfy/work/DeleteWorker.kt +++ b/app/src/main/java/io/heckel/ntfy/work/DeleteWorker.kt @@ -1,7 +1,6 @@ package io.heckel.ntfy.work import android.content.Context -import android.net.Uri import androidx.core.content.FileProvider import androidx.work.CoroutineWorker import androidx.work.WorkerParameters @@ -15,6 +14,7 @@ import io.heckel.ntfy.util.topicShortUrl import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File +import androidx.core.net.toUri /** * Deletes notifications marked for deletion and attachments for deleted notifications. @@ -59,7 +59,7 @@ class DeleteWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx notifications.forEach { notification -> try { val attachment = notification.attachment ?: return - val contentUri = Uri.parse(attachment.contentUri ?: return) + val contentUri = (attachment.contentUri ?: return).toUri() Log.d(TAG, "Deleting attachment for notification ${notification.id}: ${attachment.contentUri} (${attachment.name})") val deleted = resolver.delete(contentUri, null, null) > 0 if (!deleted) {