Apply code warnings/hint
This commit is contained in:
parent
395fe72fd6
commit
38ce2dd64e
19 changed files with 162 additions and 156 deletions
|
|
@ -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<SubscriptionWithMetadata>): List<Subscription> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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!!)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<DetailViewModel> {
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<Notification, DetailAdapter.DetailViewHolder>(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<Any?>(context, listOf(bitmap)) { imageView, image ->
|
||||
StfalconImageViewer.Builder<Any?>(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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SubscriptionsViewModel> {
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<List<Subscription>> {
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<String> 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<String> getAliases() {
|
||||
|
|
|
|||
|
|
@ -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<String> aliases = jsonArrayToStringList(json.getJSONArray("aliases"));
|
||||
return new Emoji(aliases, bytes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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? {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue