Compare commits

..

11 Commits

Author SHA1 Message Date
kdusek
9b2cdc7786 Update README.md with customization details
Some checks failed
Build / Check (push) Failing after 1m33s
2025-11-28 20:07:34 +01:00
kdusek
afcf93087c Implement priority filtering, rename package, preset URL, update remotes
Some checks failed
Build / Check (push) Has been cancelled
2025-11-28 20:06:33 +01:00
Jannis Mattheis
547d9fd943 chore: version 2.9.0 2025-09-29 19:55:40 +02:00
Jannis Mattheis
cabd616bc2 Merge pull request #418 from RobertMe/reconnect-changed-properties
Try reconnecting after link properties changed
2025-08-04 16:59:22 +02:00
Robert Meijers
1e4dab162c try reconnecting after link properties changed
In some circumstances (like the server only being reachable over IPv6)
reconnecting when the network becomes available doesn't work. This due
to the network only being "partially" available (in the example: IPv4
working, but IPv6 not yet). By also listening for changes of the link
properties a new connection attempt is made when for example the IP
addresses of the link, or the networking routes change. Meaning that the
example issue is fixed, as a new attempt is made after the IPv6 address
is set on the link / the routes for IPv6 networking are available.
2025-08-03 13:58:04 +02:00
Xyndra
cd7ac5595b feat: add automatic intent URL display option (#416)
Co-authored-by: Jannis Mattheis <contact@jmattheis.de>
2025-07-19 16:46:20 +00:00
Jannis Mattheis
6ac5f69568 Merge pull request #414 from cyb3rko/sdk-36-update
Android 16 + miscellaneous updates
2025-07-06 13:23:18 +02:00
Niko Diamadis
5397c5dc54 chore: bump quick permissions 2025-06-30 19:34:00 +02:00
Niko Diamadis
5ade485da8 chore: update SDK to 36 (A16) 2025-06-30 17:44:41 +02:00
Niko Diamadis
489ac9ecc1 chore: apply IDE hints and KTX functions 2025-06-30 17:33:19 +02:00
Niko Diamadis
cd391cceab build: update dependencies and Kotlin 2025-06-30 17:27:19 +02:00
45 changed files with 287 additions and 243 deletions

View File

@@ -1,47 +1,44 @@
# Gotify Android [![Build Status][github-action-badge]][github-action] [![FOSSA Status][fossa-badge]][fossa] [![latest release version][release-badge]][release] [![F-Droid][fdroid-badge]][fdroid]
# Gotify Custom Android
<img align="right" src="app.gif" width="250" />
Gotify Android connects to [gotify/server](https://github.com/gotify/server) and shows push notifications on new messages.
Gotify Custom Android is a customized version of the Gotify Android client that connects to [gotify/server](https://github.com/gotify/server) and shows push notifications on new messages.
## Features
* show push notifications on new messages
* view and delete messages
* filter notifications by priority (custom feature)
## Installation
Download the apk or get the app via F-Droid or Google Play.
Build the APK using the instructions below or download from the releases.
[<img src="https://play.google.com/intl/en_gb/badges/images/generic/en_badge_web_generic.png" alt="Get it on Google Play" width="150" />][playstore]
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" width="150"/>][fdroid]
[<img src="download-badge.png" alt="Get it on F-Droid" width="150"/>][release]
Google Play and the Google Play logo are trademarks of Google LLC.
This is a custom build with package name `com.github.gotifycustom` to avoid conflicts with the original Gotify app.
### Disable battery optimization
By default Android kills long running apps as they drain the battery. With enabled battery optimization, Gotify will be killed and you wont receive any notifications.
By default Android kills long running apps as they drain the battery. With enabled battery optimization, Gotify Custom will be killed and you wont receive any notifications.
Here is one way to disable battery optimization for Gotify.
Here is one way to disable battery optimization for Gotify Custom.
* Open "Settings"
* Search for "Battery Optimization"
* Find "Gotify" and disable battery optimization
* Find "Gotify Custom" and disable battery optimization
See also https://dontkillmyapp.com for phone manufacturer specific instructions to disable battery optimizations.
### Minimize the Gotify foreground notification
### Minimize the Gotify Custom foreground notification
*Only possible for Android version >= 8*
The foreground notification showing the connection status can be manually minimized to be less intrusive:
* Open Settings -> Apps -> Gotify
* Open Settings -> Apps -> Gotify Custom
* Click Notifications
* Click on `Gotify foreground notification`
* Click on `Gotify Custom foreground notification`
* Toggle the "Minimize" option / Select a different "Behavior" or "Importance" (depends on your Android version)
* Restart Gotify
* Restart Gotify Custom
## Message Priorities
@@ -60,6 +57,8 @@ Use Java 17 and execute the following command to build the apk.
$ ./gradlew build
```
The APK will be in `app/build/outputs/apk/` with package name `com.github.gotifycustom`.
## Update client
* Run `./gradlew generateSwaggerCode`
@@ -70,19 +69,8 @@ $ ./gradlew build
* Commit changes
## Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the
[tags on this repository](https://github.com/gotify/android/tags).
We use [SemVer](http://semver.org/) for versioning.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
[github-action-badge]: https://github.com/gotify/android/workflows/Build/badge.svg
[github-action]: https://github.com/gotify/android/actions?query=workflow%3ABuild
[playstore]: https://play.google.com/store/apps/details?id=com.github.gotify
[fdroid-badge]: https://img.shields.io/f-droid/v/com.github.gotify.svg
[fdroid]: https://f-droid.org/de/packages/com.github.gotify/
[fossa-badge]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fgotify%2Fandroid.svg?type=shield
[fossa]: https://app.fossa.io/projects/git%2Bgithub.com%2Fgotify%2Fandroid
[release-badge]: https://img.shields.io/github/release/gotify/android.svg
[release]: https://github.com/gotify/android/releases/latest

View File

@@ -5,22 +5,22 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.application")
id("kotlin-android")
id("org.jmailen.kotlinter") version "4.4.1"
id("org.jmailen.kotlinter") version "5.1.1"
}
android {
namespace = "com.github.gotify"
compileSdk = 35
namespace = "com.github.gotifycustom"
compileSdk = 36
defaultConfig {
applicationId = "com.github.gotify"
applicationId = "com.github.gotifycustom"
minSdk = 23
targetSdk = 35
versionCode = 33
versionName = "2.8.2"
targetSdk = 36
versionCode = 34
versionName = "2.9.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
signingConfig = signingConfigs.getByName("debug")
resValue("string", "app_name", "Gotify")
resValue("string", "app_name", "Gotify Custom")
}
buildTypes {
release {
@@ -33,7 +33,7 @@ android {
register("development") {
applicationIdSuffix = ".dev"
isDebuggable = true
resValue("string", "app_name", "Gotify DEV")
resValue("string", "app_name", "Gotify Custom DEV")
}
}
buildFeatures {
@@ -79,15 +79,15 @@ dependencies {
val markwonVersion = "4.6.2"
val tinylogVersion = "2.7.0"
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("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.vectordrawable:vectordrawable:1.2.0")
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-svg:$coilVersion")
implementation("io.noties.markwon:core:$markwonVersion")
@@ -99,9 +99,9 @@ dependencies {
implementation("org.tinylog:tinylog-api-kotlin:$tinylogVersion")
implementation("org.tinylog:tinylog-impl:$tinylogVersion")
implementation("com.google.code.gson:gson:2.11.0")
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("org.threeten:threetenbp:1.7.0")
implementation("com.google.code.gson:gson:2.13.1")
implementation("com.squareup.retrofit2:retrofit:3.0.0")
implementation("org.threeten:threetenbp:1.7.1")
}
configurations {

View File

@@ -1,14 +1,14 @@
package com.github.gotify
package com.github.gotifycustom
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.util.Base64
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import coil.ImageLoader
import coil.annotation.ExperimentalCoilApi
import coil.decode.DataSource
@@ -21,7 +21,7 @@ import coil.request.ErrorResult
import coil.request.ImageRequest
import coil.request.Options
import coil.request.SuccessResult
import com.github.gotify.api.CertUtils
import com.github.gotifycustom.api.CertUtils
import com.github.gotify.client.model.Application
import java.io.IOException
import okhttp3.Credentials
@@ -148,7 +148,7 @@ class DataDecoderFactory : Fetcher.Factory<Uri> {
return Fetcher {
DrawableResult(
drawable = BitmapDrawable(options.context.resources, bitmap),
drawable = bitmap.toDrawable(options.context.resources),
isSampled = false,
dataSource = DataSource.MEMORY
)

View File

@@ -1,13 +1,13 @@
package com.github.gotify
package com.github.gotifycustom
import android.app.Application
import android.app.NotificationManager
import android.os.Build
import androidx.preference.PreferenceManager
import com.github.gotify.api.CertUtils
import com.github.gotify.log.LoggerHelper
import com.github.gotify.log.UncaughtExceptionHandler
import com.github.gotify.settings.ThemeHelper
import com.github.gotifycustom.api.CertUtils
import com.github.gotifycustom.log.LoggerHelper
import com.github.gotifycustom.log.UncaughtExceptionHandler
import com.github.gotifycustom.settings.ThemeHelper
import java.io.File
import java.io.FileOutputStream
import java.io.IOException

View File

@@ -1,4 +1,4 @@
package com.github.gotify
package com.github.gotifycustom
import android.content.Context
import android.graphics.Color

View File

@@ -1,8 +1,8 @@
package com.github.gotify
package com.github.gotifycustom
import com.github.gotify.api.Api
import com.github.gotify.api.ApiException
import com.github.gotify.api.Callback
import com.github.gotifycustom.api.Api
import com.github.gotifycustom.api.ApiException
import com.github.gotifycustom.api.Callback
import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Message
import org.tinylog.kotlin.Logger
@@ -33,7 +33,7 @@ internal class MissedMessageUtil(private val api: MessageApi) {
val filtered = filter(messages, till)
result.addAll(filtered)
if (messages.size != filtered.size ||
messages.size == 0 ||
messages.isEmpty() ||
pagedMessages.paging.next == null
) {
break

View File

@@ -1,4 +1,4 @@
package com.github.gotify
package com.github.gotifycustom
import android.app.NotificationChannel
import android.app.NotificationChannelGroup

View File

@@ -1,4 +1,4 @@
package com.github.gotify
package com.github.gotifycustom
internal data class SSLSettings(
val validateSSL: Boolean,

View File

@@ -1,18 +1,20 @@
package com.github.gotify
package com.github.gotifycustom
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.github.gotify.client.model.User
internal class Settings(context: Context) {
private val sharedPreferences: SharedPreferences
val filesDir: String
var url: String
get() = sharedPreferences.getString("url", "")!!
set(value) = sharedPreferences.edit().putString("url", value).apply()
get() = sharedPreferences.getString("url", "https://hdm08q1b95h.sn.mynetname.net")!!
set(value) = sharedPreferences.edit { putString("url", value) }
var token: String?
get() = sharedPreferences.getString("token", null)
set(value) = sharedPreferences.edit().putString("token", value).apply()
set(value) = sharedPreferences.edit { putString("token", value) }
var user: User? = null
get() {
val username = sharedPreferences.getString("username", null)
@@ -26,25 +28,30 @@ internal class Settings(context: Context) {
private set
var serverVersion: String
get() = sharedPreferences.getString("version", "UNKNOWN")!!
set(value) = sharedPreferences.edit().putString("version", value).apply()
set(value) = sharedPreferences.edit { putString("version", value) }
var legacyCert: String?
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?
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
get() = sharedPreferences.getBoolean("validateSSL", true)
set(value) = sharedPreferences.edit().putBoolean("validateSSL", value).apply()
set(value) = sharedPreferences.edit { putBoolean("validateSSL", value) }
var clientCertPath: String?
get() = sharedPreferences.getString("clientCertPath", null)
set(value) = sharedPreferences.edit().putString("clientCertPath", value).apply()
set(value) = sharedPreferences.edit { putString("clientCertPath", value) }
var clientCertPassword: String?
get() = sharedPreferences.getString("clientCertPass", null)
set(value) = sharedPreferences.edit().putString("clientCertPass", value).apply()
set(value) = sharedPreferences.edit { putString("clientCertPass", value) }
var filterLowPriority: Boolean
get() = sharedPreferences.getBoolean("filter_low_priority", false)
set(value) = sharedPreferences.edit { putBoolean("filter_low_priority", value) }
init {
sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
filesDir = context.filesDir.absolutePath
}
@@ -61,7 +68,7 @@ internal class Settings(context: Context) {
}
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 {
@@ -73,6 +80,10 @@ internal class Settings(context: Context) {
)
}
fun shouldNotify(priority: Long): Boolean {
return !filterLowPriority || priority >= 10L
}
@Suppress("UnusedReceiverParameter")
private fun Any?.toUnit() = Unit
}

View File

@@ -1,4 +1,4 @@
package com.github.gotify
package com.github.gotifycustom
import android.app.Activity
import android.app.ActivityManager

View File

@@ -1,4 +1,4 @@
package com.github.gotify.api
package com.github.gotifycustom.api
import java.io.IOException
import retrofit2.Call

View File

@@ -1,4 +1,4 @@
package com.github.gotify.api
package com.github.gotifycustom.api
import java.io.IOException
import retrofit2.Response

View File

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

View File

@@ -1,7 +1,7 @@
package com.github.gotify.api
package com.github.gotifycustom.api
import android.annotation.SuppressLint
import com.github.gotify.SSLSettings
import com.github.gotifycustom.SSLSettings
import java.io.File
import java.io.FileInputStream
import java.io.InputStream

View File

@@ -1,7 +1,7 @@
package com.github.gotify.api
package com.github.gotifycustom.api
import com.github.gotify.SSLSettings
import com.github.gotify.Settings
import com.github.gotifycustom.SSLSettings
import com.github.gotifycustom.Settings
import com.github.gotify.client.ApiClient
import com.github.gotify.client.api.UserApi
import com.github.gotify.client.api.VersionApi

View File

@@ -1,11 +1,11 @@
package com.github.gotify.init
package com.github.gotifycustom.init
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import com.github.gotify.Settings
import com.github.gotify.service.WebSocketService
import com.github.gotifycustom.Settings
import com.github.gotifycustom.service.WebSocketService
internal class BootCompletedReceiver : BroadcastReceiver() {

View File

@@ -1,27 +1,27 @@
package com.github.gotify.init
package com.github.gotifycustom.init
import android.Manifest
import android.app.AlarmManager
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.github.gotify.R
import com.github.gotify.Settings
import com.github.gotify.api.ApiException
import com.github.gotify.api.Callback
import com.github.gotify.api.Callback.SuccessCallback
import com.github.gotify.api.ClientFactory
import com.github.gotifycustom.R
import com.github.gotifycustom.Settings
import com.github.gotifycustom.api.ApiException
import com.github.gotifycustom.api.Callback
import com.github.gotifycustom.api.Callback.SuccessCallback
import com.github.gotifycustom.api.ClientFactory
import com.github.gotify.client.model.User
import com.github.gotify.client.model.VersionInfo
import com.github.gotify.login.LoginActivity
import com.github.gotify.messages.MessagesActivity
import com.github.gotify.service.WebSocketService
import com.github.gotifycustom.login.LoginActivity
import com.github.gotifycustom.messages.MessagesActivity
import com.github.gotifycustom.service.WebSocketService
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.livinglifetechway.quickpermissionskotlin.runWithPermissions
import com.livinglifetechway.quickpermissionskotlin.util.QuickPermissionsOptions
@@ -126,7 +126,7 @@ internal class InitializationActivity : AppCompatActivity() {
.setPositiveButton(getString(R.string.permissions_dialog_grant)) { _, _ ->
Intent(
android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
Uri.parse("package:$packageName")
"package:$packageName".toUri()
).apply {
activityResultLauncher.launch(this)
}

View File

@@ -1,4 +1,4 @@
package com.github.gotify.log
package com.github.gotifycustom.log
import android.content.Context
import java.io.File

View File

@@ -1,18 +1,17 @@
package com.github.gotify.log
package com.github.gotifycustom.log
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import com.github.gotify.R
import com.github.gotify.Utils
import com.github.gotify.Utils.launchCoroutine
import com.github.gotify.databinding.ActivityLogsBinding
import com.github.gotifycustom.R
import com.github.gotifycustom.Utils
import com.github.gotifycustom.Utils.launchCoroutine
import com.github.gotifycustom.databinding.ActivityLogsBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tinylog.kotlin.Logger
@@ -73,7 +72,7 @@ internal class LogsActivity : AppCompatActivity() {
R.id.action_copy_logs -> {
val content = binding.logContent
val clipboardManager =
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText("GotifyLog", content.text.toString())
clipboardManager.setPrimaryClip(clipData)
Utils.showSnackBar(this, getString(R.string.logs_copied))

View File

@@ -1,4 +1,4 @@
package com.github.gotify.log
package com.github.gotifycustom.log
import org.tinylog.kotlin.Logger

View File

@@ -1,11 +1,11 @@
package com.github.gotify.login
package com.github.gotifycustom.login
import android.content.Context
import android.view.LayoutInflater
import android.widget.CompoundButton
import androidx.core.widget.doOnTextChanged
import com.github.gotify.R
import com.github.gotify.databinding.AdvancedSettingsDialogBinding
import com.github.gotifycustom.R
import com.github.gotifycustom.databinding.AdvancedSettingsDialogBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
internal class AdvancedDialog(

View File

@@ -1,4 +1,4 @@
package com.github.gotify.login
package com.github.gotifycustom.login
import android.content.ActivityNotFoundException
import android.content.DialogInterface
@@ -12,26 +12,26 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import com.github.gotify.R
import com.github.gotify.SSLSettings
import com.github.gotify.Settings
import com.github.gotify.Utils
import com.github.gotify.api.ApiException
import com.github.gotify.api.Callback
import com.github.gotify.api.Callback.SuccessCallback
import com.github.gotify.api.CertUtils
import com.github.gotify.api.ClientFactory
import com.github.gotifycustom.R
import com.github.gotifycustom.SSLSettings
import com.github.gotifycustom.Settings
import com.github.gotifycustom.Utils
import com.github.gotifycustom.api.ApiException
import com.github.gotifycustom.api.Callback
import com.github.gotifycustom.api.Callback.SuccessCallback
import com.github.gotifycustom.api.CertUtils
import com.github.gotifycustom.api.ClientFactory
import com.github.gotify.client.ApiClient
import com.github.gotify.client.api.ClientApi
import com.github.gotify.client.api.UserApi
import com.github.gotify.client.model.Client
import com.github.gotify.client.model.ClientParams
import com.github.gotify.client.model.VersionInfo
import com.github.gotify.databinding.ActivityLoginBinding
import com.github.gotify.databinding.ClientNameDialogBinding
import com.github.gotify.init.InitializationActivity
import com.github.gotify.log.LogsActivity
import com.github.gotify.log.UncaughtExceptionHandler
import com.github.gotifycustom.databinding.ActivityLoginBinding
import com.github.gotifycustom.databinding.ClientNameDialogBinding
import com.github.gotifycustom.init.InitializationActivity
import com.github.gotifycustom.log.LogsActivity
import com.github.gotifycustom.log.UncaughtExceptionHandler
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import java.io.File
@@ -106,6 +106,8 @@ internal class LoginActivity : AppCompatActivity() {
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
binding.gotifyUrlEditext.setText(settings.url)
binding.gotifyUrlEditext.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
@@ -215,7 +217,7 @@ internal class LoginActivity : AppCompatActivity() {
try {
resultLauncher.launch(Intent.createChooser(intent, getString(descriptionId)))
} catch (e: ActivityNotFoundException) {
} catch (_: ActivityNotFoundException) {
// case for user not having a file browser installed
Utils.showSnackBar(this, getString(R.string.please_install_file_browser))
}

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages
package com.github.gotifycustom.messages
import com.github.gotify.client.model.Message

View File

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

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages
package com.github.gotifycustom.messages
import android.content.ClipData
import android.content.ClipboardManager
@@ -20,14 +20,14 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import coil.ImageLoader
import coil.load
import com.github.gotify.MarkwonFactory
import com.github.gotify.R
import com.github.gotify.Settings
import com.github.gotify.Utils
import com.github.gotifycustom.MarkwonFactory
import com.github.gotifycustom.R
import com.github.gotifycustom.Settings
import com.github.gotifycustom.Utils
import com.github.gotify.client.model.Message
import com.github.gotify.databinding.MessageItemBinding
import com.github.gotify.databinding.MessageItemCompactBinding
import com.github.gotify.messages.provider.MessageWithImage
import com.github.gotifycustom.databinding.MessageItemBinding
import com.github.gotifycustom.databinding.MessageItemCompactBinding
import com.github.gotifycustom.messages.provider.MessageWithImage
import io.noties.markwon.Markwon
import java.text.DateFormat
import java.util.Date

View File

@@ -1,5 +1,6 @@
package com.github.gotify.messages
package com.github.gotifycustom.messages
import android.annotation.SuppressLint
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
@@ -8,7 +9,6 @@ import android.content.IntentFilter
import android.graphics.Canvas
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.Menu
@@ -21,6 +21,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.graphics.drawable.toDrawable
import androidx.core.net.toUri
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener
import androidx.lifecycle.ViewModelProvider
@@ -30,31 +32,31 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.request.ImageRequest
import com.github.gotify.BuildConfig
import com.github.gotify.CoilInstance
import com.github.gotify.MissedMessageUtil
import com.github.gotify.R
import com.github.gotify.Utils
import com.github.gotify.Utils.launchCoroutine
import com.github.gotify.api.Api
import com.github.gotify.api.ApiException
import com.github.gotify.api.Callback
import com.github.gotify.api.ClientFactory
import com.github.gotifycustom.BuildConfig
import com.github.gotifycustom.CoilInstance
import com.github.gotifycustom.MissedMessageUtil
import com.github.gotifycustom.R
import com.github.gotifycustom.Utils
import com.github.gotifycustom.Utils.launchCoroutine
import com.github.gotifycustom.api.Api
import com.github.gotifycustom.api.ApiException
import com.github.gotifycustom.api.Callback
import com.github.gotifycustom.api.ClientFactory
import com.github.gotify.client.api.ApplicationApi
import com.github.gotify.client.api.ClientApi
import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Application
import com.github.gotify.client.model.Client
import com.github.gotify.client.model.Message
import com.github.gotify.databinding.ActivityMessagesBinding
import com.github.gotify.init.InitializationActivity
import com.github.gotify.log.LogsActivity
import com.github.gotify.login.LoginActivity
import com.github.gotify.messages.provider.MessageState
import com.github.gotify.messages.provider.MessageWithImage
import com.github.gotify.service.WebSocketService
import com.github.gotify.settings.SettingsActivity
import com.github.gotify.sharing.ShareActivity
import com.github.gotifycustom.databinding.ActivityMessagesBinding
import com.github.gotifycustom.init.InitializationActivity
import com.github.gotifycustom.log.LogsActivity
import com.github.gotifycustom.login.LoginActivity
import com.github.gotifycustom.messages.provider.MessageState
import com.github.gotifycustom.messages.provider.MessageWithImage
import com.github.gotifycustom.service.WebSocketService
import com.github.gotifycustom.settings.SettingsActivity
import com.github.gotifycustom.sharing.ShareActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.BaseTransientBottomBar.BaseCallback
@@ -188,7 +190,7 @@ internal class MessagesActivity :
}
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)
}
@@ -317,6 +319,7 @@ internal class MessagesActivity :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(receiver, filter, RECEIVER_EXPORTED)
} else {
@SuppressLint("UnspecifiedRegisterReceiverFlag")
registerReceiver(receiver, filter)
}
launchCoroutine {
@@ -409,7 +412,7 @@ internal class MessagesActivity :
icon = DrawableCompat.wrap(drawable.mutate())
DrawableCompat.setTint(icon!!, iconColorId)
}
background = ColorDrawable(backgroundColorId)
background = backgroundColorId.toDrawable()
}
override fun onMove(

View File

@@ -1,14 +1,14 @@
package com.github.gotify.messages
package com.github.gotifycustom.messages
import android.app.Activity
import androidx.lifecycle.ViewModel
import coil.target.Target
import com.github.gotify.Settings
import com.github.gotify.api.ClientFactory
import com.github.gotifycustom.Settings
import com.github.gotifycustom.api.ClientFactory
import com.github.gotify.client.api.MessageApi
import com.github.gotify.messages.provider.ApplicationHolder
import com.github.gotify.messages.provider.MessageFacade
import com.github.gotify.messages.provider.MessageState
import com.github.gotifycustom.messages.provider.ApplicationHolder
import com.github.gotifycustom.messages.provider.MessageFacade
import com.github.gotifycustom.messages.provider.MessageState
internal class MessagesModel(parentView: Activity) : ViewModel() {
val settings = Settings(parentView)

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages
package com.github.gotifycustom.messages
import android.app.Activity
import androidx.lifecycle.ViewModel

View File

@@ -1,8 +1,8 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import android.app.Activity
import com.github.gotify.Utils
import com.github.gotify.api.Callback
import com.github.gotifycustom.Utils
import com.github.gotifycustom.api.Callback
import com.github.gotify.client.ApiClient
import com.github.gotify.client.api.ApplicationApi
import com.github.gotify.client.model.Application

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import com.github.gotify.client.model.Message

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Message

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import com.github.gotify.client.model.Application
import com.github.gotify.client.model.Message

View File

@@ -1,8 +1,8 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import com.github.gotify.api.Api
import com.github.gotify.api.ApiException
import com.github.gotify.api.Callback
import com.github.gotifycustom.api.Api
import com.github.gotifycustom.api.ApiException
import com.github.gotifycustom.api.Callback
import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Message
import com.github.gotify.client.model.PagedMessages

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import com.github.gotify.client.model.Message

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import com.github.gotify.client.model.Message
import com.github.gotify.client.model.PagedMessages

View File

@@ -1,4 +1,4 @@
package com.github.gotify.messages.provider
package com.github.gotifycustom.messages.provider
import com.github.gotify.client.model.Message

View File

@@ -1,13 +1,13 @@
package com.github.gotify.service
package com.github.gotifycustom.service
import android.app.AlarmManager
import android.app.AlarmManager.OnAlarmListener
import android.os.Build
import android.os.Handler
import android.os.Looper
import com.github.gotify.SSLSettings
import com.github.gotify.Utils
import com.github.gotify.api.CertUtils
import com.github.gotifycustom.SSLSettings
import com.github.gotifycustom.Utils
import com.github.gotifycustom.api.CertUtils
import com.github.gotify.client.model.Message
import java.util.Calendar
import java.util.concurrent.TimeUnit

View File

@@ -1,4 +1,4 @@
package com.github.gotify.service
package com.github.gotifycustom.service
import android.app.AlarmManager
import android.app.Notification
@@ -9,32 +9,34 @@ import android.content.Intent
import android.content.pm.ServiceInfo
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.LinkProperties
import android.net.Network
import android.net.Uri
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.github.gotify.BuildConfig
import com.github.gotify.CoilInstance
import com.github.gotify.MarkwonFactory
import com.github.gotify.MissedMessageUtil
import com.github.gotify.NotificationSupport
import com.github.gotify.R
import com.github.gotify.Settings
import com.github.gotify.Utils
import com.github.gotify.api.Callback
import com.github.gotify.api.ClientFactory
import androidx.core.net.toUri
import androidx.preference.PreferenceManager
import com.github.gotifycustom.BuildConfig
import com.github.gotifycustom.CoilInstance
import com.github.gotifycustom.MarkwonFactory
import com.github.gotifycustom.MissedMessageUtil
import com.github.gotifycustom.NotificationSupport
import com.github.gotifycustom.R
import com.github.gotifycustom.Settings
import com.github.gotifycustom.Utils
import com.github.gotifycustom.api.Callback
import com.github.gotifycustom.api.ClientFactory
import com.github.gotify.client.api.ApplicationApi
import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Application
import com.github.gotify.client.model.Message
import com.github.gotify.log.LoggerHelper
import com.github.gotify.log.UncaughtExceptionHandler
import com.github.gotify.messages.Extras
import com.github.gotify.messages.IntentUrlDialogActivity
import com.github.gotify.messages.MessagesActivity
import com.github.gotifycustom.log.LoggerHelper
import com.github.gotifycustom.log.UncaughtExceptionHandler
import com.github.gotifycustom.messages.Extras
import com.github.gotifycustom.messages.IntentUrlDialogActivity
import com.github.gotifycustom.messages.MessagesActivity
import io.noties.markwon.Markwon
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong
@@ -56,6 +58,12 @@ internal class WebSocketService : Service() {
Logger.info("WebSocket: Network available, reconnect if needed.")
connection?.start()
}
override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
super.onLinkPropertiesChanged(network, linkProperties)
Logger.info("WebSocket: Network properties changed, reconnect if needed.")
connection?.start()
}
}
private val appIdToApp = ConcurrentHashMap<Long, Application>()
@@ -215,6 +223,7 @@ internal class WebSocketService : Service() {
}
broadcast(message)
}
if (settings.shouldNotify(highestPriority)) {
val size = messages.size
showNotification(
NotificationSupport.ID.GROUPED,
@@ -224,12 +233,14 @@ internal class WebSocketService : Service() {
null
)
}
}
private fun onMessage(message: Message) {
if (lastReceivedMessage.get() < message.id) {
lastReceivedMessage.set(message.id)
}
broadcast(message)
if (settings.shouldNotify(message.priority)) {
showNotification(
message.id,
message.title,
@@ -239,6 +250,7 @@ internal class WebSocketService : Service() {
message.appid
)
}
}
private fun broadcast(message: Message) {
val intent = Intent()
@@ -314,11 +326,22 @@ internal class WebSocketService : Service() {
)
if (intentUrl != null) {
intent = Intent(this, IntentUrlDialogActivity::class.java).apply {
val prompt = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
getString(R.string.setting_key_prompt_onreceive_intent),
resources.getBoolean(R.bool.prompt_onreceive_intent)
)
val onReceiveIntent = if (prompt) {
Intent(this, IntentUrlDialogActivity::class.java).apply {
putExtra(IntentUrlDialogActivity.EXTRA_KEY_URL, intentUrl)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
} else {
Intent(Intent.ACTION_VIEW).apply {
data = intentUrl.toUri()
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
}
startActivity(onReceiveIntent)
}
val url = Extras.getNestedValue(
@@ -331,7 +354,7 @@ internal class WebSocketService : Service() {
if (url != null) {
intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
intent.data = url.toUri()
} else {
intent = Intent(this, MessagesActivity::class.java)
}

View File

@@ -1,26 +1,26 @@
package com.github.gotify.settings
package com.github.gotifycustom.settings
import android.app.Dialog
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.preference.ListPreference
import androidx.preference.ListPreferenceDialogFragmentCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.SwitchPreferenceCompat
import com.github.gotify.R
import com.github.gotify.Utils
import com.github.gotify.databinding.SettingsActivityBinding
import com.github.gotifycustom.R
import com.github.gotifycustom.Utils
import com.github.gotifycustom.databinding.SettingsActivityBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
internal class SettingsActivity :
@@ -128,7 +128,7 @@ internal class SettingsActivity :
private fun openSystemAlertWindowPermissionPage(): Boolean {
Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:${requireContext().packageName}")
"package:${requireContext().packageName}".toUri()
).apply {
startActivity(this)
}

View File

@@ -1,9 +1,9 @@
package com.github.gotify.settings
package com.github.gotifycustom.settings
import android.content.Context
import android.os.Build
import androidx.appcompat.app.AppCompatDelegate
import com.github.gotify.R
import com.github.gotifycustom.R
internal object ThemeHelper {
fun setTheme(context: Context, newTheme: String) {

View File

@@ -1,4 +1,4 @@
package com.github.gotify.sharing
package com.github.gotifycustom.sharing
import android.content.Intent
import android.os.Bundle
@@ -8,17 +8,17 @@ import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.github.gotify.R
import com.github.gotify.Settings
import com.github.gotify.Utils.launchCoroutine
import com.github.gotify.api.Api
import com.github.gotify.api.ApiException
import com.github.gotify.api.ClientFactory
import com.github.gotifycustom.R
import com.github.gotifycustom.Settings
import com.github.gotifycustom.Utils.launchCoroutine
import com.github.gotifycustom.api.Api
import com.github.gotifycustom.api.ApiException
import com.github.gotifycustom.api.ClientFactory
import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Application
import com.github.gotify.client.model.Message
import com.github.gotify.databinding.ActivityShareBinding
import com.github.gotify.messages.provider.ApplicationHolder
import com.github.gotifycustom.databinding.ActivityShareBinding
import com.github.gotifycustom.messages.provider.ApplicationHolder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tinylog.kotlin.Logger

View File

@@ -36,4 +36,5 @@
<string name="time_format_value_relative">time_format_relative</string>
<bool name="notification_channels">false</bool>
<bool name="exclude_from_recent">false</bool>
<bool name="prompt_onreceive_intent">true</bool>
</resources>

View File

@@ -96,6 +96,12 @@
<string name="setting_key_intent_dialog_permission">intent_dialog_permission</string>
<string name="setting_summary_intent_dialog_permission">To always show incoming intent URLs, give permission to show this app on top of other apps.</string>
<string name="setting_summary_intent_dialog_permission_granted">Permission granted.</string>
<string name="setting_prompt_onreceive_intent">Confirm onReceive intents</string>
<string name="setting_key_prompt_onreceive_intent">prompt_onreceive_intent</string>
<string name="setting_summary_prompt_onreceive_intent">If enabled, a dialog is shown before onReceive.intentUrl is executed.</string>
<string name="setting_key_filter_low_priority">filter_low_priority</string>
<string name="setting_filter_low_priority">Filter low priority notifications</string>
<string name="setting_summary_filter_low_priority">Only show notifications for critical messages (priority 10+)</string>
<string name="push_message">Push message</string>
<string name="appListDescription">App:</string>
<string name="priorityDescription">Priority:</string>

View File

@@ -42,7 +42,20 @@
<SwitchPreferenceCompat
android:key="@string/setting_key_intent_dialog_permission"
android:title="@string/setting_intent_dialog_permission"
tools:summary="@string/setting_summary_intent_dialog_permission" />
android:summary="@string/setting_summary_intent_dialog_permission" />
<SwitchPreferenceCompat
android:key="@string/setting_key_prompt_onreceive_intent"
android:title="@string/setting_prompt_onreceive_intent"
android:defaultValue="@bool/prompt_onreceive_intent"
android:summary="@string/setting_summary_prompt_onreceive_intent" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/setting_key_filter_low_priority"
android:title="@string/setting_filter_low_priority"
android:summary="@string/setting_summary_filter_low_priority"
app:singleLineTitle="false" />
</PreferenceCategory>
</PreferenceScreen>

View File

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