Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdf3a529a5 | ||
|
|
28bc857788 | ||
|
|
d52382ca5b | ||
|
|
db81f6a51e |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,4 +11,5 @@ captures/
|
|||||||
*.apk
|
*.apk
|
||||||
*.aab
|
*.aab
|
||||||
keystore.properties
|
keystore.properties
|
||||||
*.jks
|
*.jks
|
||||||
|
tools/.push.secrets
|
||||||
@@ -18,8 +18,8 @@ android {
|
|||||||
applicationId = "ru.ua1zbe.aprsmap"
|
applicationId = "ru.ua1zbe.aprsmap"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 2
|
versionCode = 4
|
||||||
versionName = "1.0.1"
|
versionName = "1.0.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
@@ -55,4 +55,8 @@ kotlin {
|
|||||||
compilerOptions {
|
compilerOptions {
|
||||||
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("ru.rustore.sdk:pushclient:7.4.0")
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
@@ -12,6 +15,22 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.APRS.Main">
|
android:theme="@style/Theme.APRS.Main">
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".PushMessagingService"
|
||||||
|
android:exported="true"
|
||||||
|
tools:ignore="ExportedService">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="ru.rustore.sdk.pushclient.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="ru.rustore.sdk.pushclient.default_notification_icon"
|
||||||
|
android:resource="@drawable/ic_launcher_foreground" />
|
||||||
|
<meta-data
|
||||||
|
android:name="ru.rustore.sdk.pushclient.default_notification_channel_id"
|
||||||
|
android:value="@string/push_channel_id" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".SplashActivity"
|
android:name=".SplashActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
60
app/src/main/java/ru/ua1zbe/aprsmap/App.kt
Normal file
60
app/src/main/java/ru/ua1zbe/aprsmap/App.kt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package ru.ua1zbe.aprsmap
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import ru.rustore.sdk.pushclient.RuStorePushClient
|
||||||
|
|
||||||
|
class App : Application() {
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
createPushChannel()
|
||||||
|
initRuStorePush()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createPushChannel() {
|
||||||
|
getSystemService(NotificationManager::class.java).createNotificationChannel(
|
||||||
|
NotificationChannel(
|
||||||
|
PushMessagingService.CHANNEL_ID,
|
||||||
|
getString(R.string.push_channel_name),
|
||||||
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initRuStorePush() {
|
||||||
|
if (RUSTORE_PROJECT_ID.isBlank()) {
|
||||||
|
Log.w(TAG, "RuStore project_id not set yet, init skipped")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
RuStorePushClient.init(application = this, projectId = RUSTORE_PROJECT_ID)
|
||||||
|
RuStorePushClient.getToken()
|
||||||
|
.addOnSuccessListener { token ->
|
||||||
|
Log.i(TAG, "push token: $token")
|
||||||
|
saveToken(token)
|
||||||
|
RuStorePushClient.subscribeToTopic(PUSH_TOPIC)
|
||||||
|
.addOnSuccessListener { Log.i(TAG, "subscribed to $PUSH_TOPIC") }
|
||||||
|
.addOnFailureListener { e -> Log.w(TAG, "subscribe failed", e) }
|
||||||
|
}
|
||||||
|
.addOnFailureListener { e -> Log.w(TAG, "getToken failed", e) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "init failed", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveToken(token: String) {
|
||||||
|
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||||
|
.edit().putString("push_token", token).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RuStorePush"
|
||||||
|
private const val RUSTORE_PROJECT_ID = "IiZJx8LXrFuVclMLbX08nfrShS9WWB6k"
|
||||||
|
private const val PUSH_TOPIC = "aprsmap"
|
||||||
|
const val PREFS = "aprsmap"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package ru.ua1zbe.aprsmap
|
package ru.ua1zbe.aprsmap
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.PorterDuffColorFilter
|
import android.graphics.PorterDuffColorFilter
|
||||||
@@ -104,10 +106,24 @@ class MainActivity : Activity() {
|
|||||||
root.addView(progress)
|
root.addView(progress)
|
||||||
setContentView(root)
|
setContentView(root)
|
||||||
|
|
||||||
|
requestNotificationPermission()
|
||||||
|
|
||||||
webView.loadUrl(getString(R.string.map_url))
|
webView.loadUrl(getString(R.string.map_url))
|
||||||
checkForUpdates()
|
checkForUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun requestNotificationPermission() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||||
|
checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) !=
|
||||||
|
PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
requestPermissions(
|
||||||
|
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
||||||
|
REQ_NOTIFICATIONS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkForUpdates() {
|
private fun checkForUpdates() {
|
||||||
Thread {
|
Thread {
|
||||||
val version = try {
|
val version = try {
|
||||||
@@ -237,5 +253,6 @@ class MainActivity : Activity() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "APRSmap"
|
private const val TAG = "APRSmap"
|
||||||
|
private const val REQ_NOTIFICATIONS = 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
55
app/src/main/java/ru/ua1zbe/aprsmap/PushMessagingService.kt
Normal file
55
app/src/main/java/ru/ua1zbe/aprsmap/PushMessagingService.kt
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package ru.ua1zbe.aprsmap
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import ru.rustore.sdk.pushclient.messaging.exception.RuStorePushClientException
|
||||||
|
import ru.rustore.sdk.pushclient.messaging.model.RemoteMessage
|
||||||
|
import ru.rustore.sdk.pushclient.messaging.service.RuStoreMessagingService
|
||||||
|
|
||||||
|
class PushMessagingService : RuStoreMessagingService() {
|
||||||
|
|
||||||
|
override fun onNewToken(token: String) {
|
||||||
|
Log.i(TAG, "onNewToken: $token")
|
||||||
|
getSharedPreferences(App.PREFS, Context.MODE_PRIVATE)
|
||||||
|
.edit().putString("push_token", token).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessageReceived(message: RemoteMessage) {
|
||||||
|
if (message.notification != null) {
|
||||||
|
Log.i(TAG, "push displayed by SDK: ${message.data}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val title = message.data["title"] ?: getString(R.string.app_name)
|
||||||
|
val body = message.data["body"] ?: ""
|
||||||
|
if (title.isNotEmpty() || body.isNotEmpty()) {
|
||||||
|
showPush(title, body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDeletedMessages() {
|
||||||
|
Log.i(TAG, "onDeletedMessages")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(errors: List<RuStorePushClientException>) {
|
||||||
|
errors.forEach { Log.w(TAG, "error", it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showPush(title: String, body: String) {
|
||||||
|
val nm = getSystemService(NotificationManager::class.java)
|
||||||
|
val n = Notification.Builder(this, CHANNEL_ID)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(body)
|
||||||
|
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.build()
|
||||||
|
nm.notify(NOTIFICATION_ID, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RuStorePush"
|
||||||
|
const val CHANNEL_ID = "aprs_push"
|
||||||
|
private const val NOTIFICATION_ID = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">APRS RadioRubka</string>
|
<string name="app_name">APRS RadioRubka</string>
|
||||||
<string name="map_url">https://aprs.radiorubka.org/?center=67.5096,32.8271&zoom=8</string>
|
<string name="map_url">https://aprs.radiorubka.org/?center=67.5096,32.8271&zoom=8</string>
|
||||||
|
<string name="push_channel_id">aprs_push</string>
|
||||||
|
<string name="push_channel_name">Пуш-уведомления</string>
|
||||||
<string name="update_available_title">Доступно обновление</string>
|
<string name="update_available_title">Доступно обновление</string>
|
||||||
<string name="update_available_msg">Версия %1$s уже вышла. Обновить приложение?</string>
|
<string name="update_available_msg">Версия %1$s уже вышла. Обновить приложение?</string>
|
||||||
<string name="update_btn">Обновить</string>
|
<string name="update_btn">Обновить</string>
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ org.gradle.parallel=false
|
|||||||
org.gradle.caching=false
|
org.gradle.caching=false
|
||||||
kotlin.compiler.execution.strategy=in-process
|
kotlin.compiler.execution.strategy=in-process
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
android.useAndroidX=false
|
android.useAndroidX=true
|
||||||
@@ -11,6 +11,7 @@ dependencyResolutionManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven("https://nexus-external.rustore.ru/repository/maven-rustore-exposed/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
tools/push.sh
Executable file
23
tools/push.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Рассылка пуша всем подписчикам топика "aprsmap" (RuStore Push).
|
||||||
|
# Использование: ./push.sh "Заголовок" "Текст"
|
||||||
|
# project_id и service token читаются из tools/.push.secrets (в git не хранится).
|
||||||
|
set -euo pipefail
|
||||||
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
[ -f "$DIR/.push.secrets" ] && . "$DIR/.push.secrets"
|
||||||
|
|
||||||
|
SERVICE_TOKEN="${SERVICE_TOKEN:-${SSP:-}}"
|
||||||
|
PROJECT_ID="${PROJECT_ID:-}"
|
||||||
|
|
||||||
|
[ -n "$SERVICE_TOKEN" ] || { echo "SERVICE_TOKEN is not set" >&2; exit 1; }
|
||||||
|
[ -n "$PROJECT_ID" ] || { echo "PROJECT_ID is not set" >&2; exit 1; }
|
||||||
|
|
||||||
|
title="${1:?usage: push.sh <title> <body>}"
|
||||||
|
body="${2:-}"
|
||||||
|
|
||||||
|
curl -s -X POST \
|
||||||
|
"https://vkpns-topics.rustore.ru/v1/projects/${PROJECT_ID}/topics/aprsmap/publish" \
|
||||||
|
-H "Authorization: Bearer ${SERVICE_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"message\":{\"notification\":{\"title\":\"${title}\",\"body\":\"${body}\"}}}"
|
||||||
|
echo
|
||||||
Reference in New Issue
Block a user