Merge pull request #414 from cyb3rko/sdk-36-update

Android 16 + miscellaneous updates
This commit is contained in:
Jannis Mattheis
2025-07-06 13:23:18 +02:00
committed by GitHub
17 changed files with 50 additions and 48 deletions

View File

@@ -5,16 +5,16 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
id("com.android.application") id("com.android.application")
id("kotlin-android") id("kotlin-android")
id("org.jmailen.kotlinter") version "4.4.1" id("org.jmailen.kotlinter") version "5.1.1"
} }
android { android {
namespace = "com.github.gotify" namespace = "com.github.gotify"
compileSdk = 35 compileSdk = 36
defaultConfig { defaultConfig {
applicationId = "com.github.gotify" applicationId = "com.github.gotify"
minSdk = 23 minSdk = 23
targetSdk = 35 targetSdk = 36
versionCode = 33 versionCode = 33
versionName = "2.8.2" versionName = "2.8.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@@ -79,15 +79,15 @@ dependencies {
val markwonVersion = "4.6.2" val markwonVersion = "4.6.2"
val tinylogVersion = "2.7.0" val tinylogVersion = "2.7.0"
implementation(project(":client")) implementation(project(":client"))
implementation("androidx.appcompat:appcompat:1.7.0") implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.core:core-splashscreen:1.0.1")
implementation("com.google.android.material:material:1.12.0") implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.vectordrawable:vectordrawable:1.2.0") implementation("androidx.vectordrawable:vectordrawable:1.2.0")
implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.preference:preference-ktx:1.2.1")
implementation("com.github.cyb3rko:QuickPermissions-Kotlin:1.1.5") implementation("com.github.cyb3rko:QuickPermissions-Kotlin:1.1.6")
implementation("io.coil-kt:coil:$coilVersion") implementation("io.coil-kt:coil:$coilVersion")
implementation("io.coil-kt:coil-svg:$coilVersion") implementation("io.coil-kt:coil-svg:$coilVersion")
implementation("io.noties.markwon:core:$markwonVersion") implementation("io.noties.markwon:core:$markwonVersion")
@@ -99,9 +99,9 @@ dependencies {
implementation("org.tinylog:tinylog-api-kotlin:$tinylogVersion") implementation("org.tinylog:tinylog-api-kotlin:$tinylogVersion")
implementation("org.tinylog:tinylog-impl:$tinylogVersion") implementation("org.tinylog:tinylog-impl:$tinylogVersion")
implementation("com.google.code.gson:gson:2.11.0") implementation("com.google.code.gson:gson:2.13.1")
implementation("com.squareup.retrofit2:retrofit:2.11.0") implementation("com.squareup.retrofit2:retrofit:3.0.0")
implementation("org.threeten:threetenbp:1.7.0") implementation("org.threeten:threetenbp:1.7.1")
} }
configurations { configurations {

View File

@@ -3,12 +3,12 @@ package com.github.gotify
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.net.Uri import android.net.Uri
import android.util.Base64 import android.util.Base64
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import coil.ImageLoader import coil.ImageLoader
import coil.annotation.ExperimentalCoilApi import coil.annotation.ExperimentalCoilApi
import coil.decode.DataSource import coil.decode.DataSource
@@ -148,7 +148,7 @@ class DataDecoderFactory : Fetcher.Factory<Uri> {
return Fetcher { return Fetcher {
DrawableResult( DrawableResult(
drawable = BitmapDrawable(options.context.resources, bitmap), drawable = bitmap.toDrawable(options.context.resources),
isSampled = false, isSampled = false,
dataSource = DataSource.MEMORY dataSource = DataSource.MEMORY
) )

View File

@@ -33,7 +33,7 @@ internal class MissedMessageUtil(private val api: MessageApi) {
val filtered = filter(messages, till) val filtered = filter(messages, till)
result.addAll(filtered) result.addAll(filtered)
if (messages.size != filtered.size || if (messages.size != filtered.size ||
messages.size == 0 || messages.isEmpty() ||
pagedMessages.paging.next == null pagedMessages.paging.next == null
) { ) {
break break

View File

@@ -2,6 +2,7 @@ package com.github.gotify
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.core.content.edit
import com.github.gotify.client.model.User import com.github.gotify.client.model.User
internal class Settings(context: Context) { internal class Settings(context: Context) {
@@ -9,10 +10,10 @@ internal class Settings(context: Context) {
val filesDir: String val filesDir: String
var url: String var url: String
get() = sharedPreferences.getString("url", "")!! get() = sharedPreferences.getString("url", "")!!
set(value) = sharedPreferences.edit().putString("url", value).apply() set(value) = sharedPreferences.edit { putString("url", value) }
var token: String? var token: String?
get() = sharedPreferences.getString("token", null) get() = sharedPreferences.getString("token", null)
set(value) = sharedPreferences.edit().putString("token", value).apply() set(value) = sharedPreferences.edit { putString("token", value) }
var user: User? = null var user: User? = null
get() { get() {
val username = sharedPreferences.getString("username", null) val username = sharedPreferences.getString("username", null)
@@ -26,22 +27,24 @@ internal class Settings(context: Context) {
private set private set
var serverVersion: String var serverVersion: String
get() = sharedPreferences.getString("version", "UNKNOWN")!! get() = sharedPreferences.getString("version", "UNKNOWN")!!
set(value) = sharedPreferences.edit().putString("version", value).apply() set(value) = sharedPreferences.edit { putString("version", value) }
var legacyCert: String? var legacyCert: String?
get() = sharedPreferences.getString("cert", null) get() = sharedPreferences.getString("cert", null)
set(value) = sharedPreferences.edit().putString("cert", value).commit().toUnit() set(value) = sharedPreferences.edit(commit = true) { putString("cert", value) }.toUnit()
var caCertPath: String? var caCertPath: String?
get() = sharedPreferences.getString("caCertPath", null) get() = sharedPreferences.getString("caCertPath", null)
set(value) = sharedPreferences.edit().putString("caCertPath", value).commit().toUnit() set(value) = sharedPreferences
.edit(commit = true) { putString("caCertPath", value) }
.toUnit()
var validateSSL: Boolean var validateSSL: Boolean
get() = sharedPreferences.getBoolean("validateSSL", true) get() = sharedPreferences.getBoolean("validateSSL", true)
set(value) = sharedPreferences.edit().putBoolean("validateSSL", value).apply() set(value) = sharedPreferences.edit { putBoolean("validateSSL", value) }
var clientCertPath: String? var clientCertPath: String?
get() = sharedPreferences.getString("clientCertPath", null) get() = sharedPreferences.getString("clientCertPath", null)
set(value) = sharedPreferences.edit().putString("clientCertPath", value).apply() set(value) = sharedPreferences.edit { putString("clientCertPath", value) }
var clientCertPassword: String? var clientCertPassword: String?
get() = sharedPreferences.getString("clientCertPass", null) get() = sharedPreferences.getString("clientCertPass", null)
set(value) = sharedPreferences.edit().putString("clientCertPass", value).apply() set(value) = sharedPreferences.edit { putString("clientCertPass", value) }
init { init {
sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE) sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE)
@@ -61,7 +64,7 @@ internal class Settings(context: Context) {
} }
fun setUser(name: String?, admin: Boolean) { fun setUser(name: String?, admin: Boolean) {
sharedPreferences.edit().putString("username", name).putBoolean("admin", admin).apply() sharedPreferences.edit { putString("username", name).putBoolean("admin", admin) }
} }
fun sslSettings(): SSLSettings { fun sslSettings(): SSLSettings {

View File

@@ -1,8 +1,6 @@
package com.github.gotify.api package com.github.gotify.api
import android.app.Activity import android.app.Activity
import com.github.gotify.api.Callback.ErrorCallback
import com.github.gotify.api.Callback.SuccessCallback
import org.tinylog.kotlin.Logger import org.tinylog.kotlin.Logger
import retrofit2.Call import retrofit2.Call
import retrofit2.Response import retrofit2.Response

View File

@@ -3,13 +3,13 @@ package com.github.gotify.init
import android.Manifest import android.Manifest
import android.app.AlarmManager import android.app.AlarmManager
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.github.gotify.R import com.github.gotify.R
import com.github.gotify.Settings import com.github.gotify.Settings
@@ -126,7 +126,7 @@ internal class InitializationActivity : AppCompatActivity() {
.setPositiveButton(getString(R.string.permissions_dialog_grant)) { _, _ -> .setPositiveButton(getString(R.string.permissions_dialog_grant)) { _, _ ->
Intent( Intent(
android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM, android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
Uri.parse("package:$packageName") "package:$packageName".toUri()
).apply { ).apply {
activityResultLauncher.launch(this) activityResultLauncher.launch(this)
} }

View File

@@ -2,7 +2,6 @@ package com.github.gotify.log
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@@ -73,7 +72,7 @@ internal class LogsActivity : AppCompatActivity() {
R.id.action_copy_logs -> { R.id.action_copy_logs -> {
val content = binding.logContent val content = binding.logContent
val clipboardManager = val clipboardManager =
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText("GotifyLog", content.text.toString()) val clipData = ClipData.newPlainText("GotifyLog", content.text.toString())
clipboardManager.setPrimaryClip(clipData) clipboardManager.setPrimaryClip(clipData)
Utils.showSnackBar(this, getString(R.string.logs_copied)) Utils.showSnackBar(this, getString(R.string.logs_copied))

View File

@@ -215,7 +215,7 @@ internal class LoginActivity : AppCompatActivity() {
try { try {
resultLauncher.launch(Intent.createChooser(intent, getString(descriptionId))) resultLauncher.launch(Intent.createChooser(intent, getString(descriptionId)))
} catch (e: ActivityNotFoundException) { } catch (_: ActivityNotFoundException) {
// case for user not having a file browser installed // case for user not having a file browser installed
Utils.showSnackBar(this, getString(R.string.please_install_file_browser)) Utils.showSnackBar(this, getString(R.string.please_install_file_browser))
} }

View File

@@ -1,9 +1,9 @@
package com.github.gotify.messages package com.github.gotify.messages
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import com.github.gotify.databinding.ActivityDialogIntentUrlBinding import com.github.gotify.databinding.ActivityDialogIntentUrlBinding
internal class IntentUrlDialogActivity : AppCompatActivity() { internal class IntentUrlDialogActivity : AppCompatActivity() {
@@ -18,7 +18,7 @@ internal class IntentUrlDialogActivity : AppCompatActivity() {
binding.openButton.setOnClickListener { binding.openButton.setOnClickListener {
finish() finish()
Intent(Intent.ACTION_VIEW).apply { Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(intentUrl) data = intentUrl?.toUri()
flags = Intent.FLAG_ACTIVITY_NEW_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(this) startActivity(this)
} }

View File

@@ -1,5 +1,6 @@
package com.github.gotify.messages package com.github.gotify.messages
import android.annotation.SuppressLint
import android.app.NotificationManager import android.app.NotificationManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
@@ -8,7 +9,6 @@ import android.content.IntentFilter
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
@@ -21,6 +21,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.graphics.drawable.toDrawable
import androidx.core.net.toUri
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener import androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@@ -188,7 +190,7 @@ internal class MessagesActivity :
} }
private fun openDocumentation() { private fun openDocumentation() {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://gotify.net/docs/pushmsg")) val browserIntent = Intent(Intent.ACTION_VIEW, "https://gotify.net/docs/pushmsg".toUri())
startActivity(browserIntent) startActivity(browserIntent)
} }
@@ -317,6 +319,7 @@ internal class MessagesActivity :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(receiver, filter, RECEIVER_EXPORTED) registerReceiver(receiver, filter, RECEIVER_EXPORTED)
} else { } else {
@SuppressLint("UnspecifiedRegisterReceiverFlag")
registerReceiver(receiver, filter) registerReceiver(receiver, filter)
} }
launchCoroutine { launchCoroutine {
@@ -409,7 +412,7 @@ internal class MessagesActivity :
icon = DrawableCompat.wrap(drawable.mutate()) icon = DrawableCompat.wrap(drawable.mutate())
DrawableCompat.setTint(icon!!, iconColorId) DrawableCompat.setTint(icon!!, iconColorId)
} }
background = ColorDrawable(backgroundColorId) background = backgroundColorId.toDrawable()
} }
override fun onMove( override fun onMove(

View File

@@ -10,12 +10,12 @@ import android.content.pm.ServiceInfo
import android.graphics.Color import android.graphics.Color
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.Network import android.net.Network
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import com.github.gotify.BuildConfig import com.github.gotify.BuildConfig
import com.github.gotify.CoilInstance import com.github.gotify.CoilInstance
import com.github.gotify.MarkwonFactory import com.github.gotify.MarkwonFactory
@@ -331,7 +331,7 @@ internal class WebSocketService : Service() {
if (url != null) { if (url != null) {
intent = Intent(Intent.ACTION_VIEW) intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url) intent.data = url.toUri()
} else { } else {
intent = Intent(this, MessagesActivity::class.java) intent = Intent(this, MessagesActivity::class.java)
} }

View File

@@ -5,13 +5,13 @@ import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.ListPreferenceDialogFragmentCompat import androidx.preference.ListPreferenceDialogFragmentCompat
import androidx.preference.Preference import androidx.preference.Preference
@@ -128,7 +128,7 @@ internal class SettingsActivity :
private fun openSystemAlertWindowPermissionPage(): Boolean { private fun openSystemAlertWindowPermissionPage(): Boolean {
Intent( Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:${requireContext().packageName}") "package:${requireContext().packageName}".toUri()
).apply { ).apply {
startActivity(this) startActivity(this)
} }

View File

@@ -3,8 +3,8 @@ import java.io.File
import java.net.URI import java.net.URI
plugins { plugins {
id("com.android.application") version "8.7.1" apply false id("com.android.application") version "8.11.0" apply false
id("org.jetbrains.kotlin.android") version "2.0.20" apply false id("org.jetbrains.kotlin.android") version "2.2.0" apply false
id("org.hidetake.swagger.generator") version "2.19.2" id("org.hidetake.swagger.generator") version "2.19.2"
} }

Binary file not shown.

View File

@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

9
gradlew vendored
View File

@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -115,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -206,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.

4
gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell