Single-instance app, talker info in notification
- MainActivity singleTask, SplashActivity singleTop: re-launching from launcher or notification never creates a second instance - Splash skips the 4s animation when the main screen is already open - Foreground notification shows 'Говорит: CALLSIGN' / 'Последний: CALLSIGN'; tapping it brings the app back to the front
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".SplashActivity"
|
android:name=".SplashActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.SvxRemote">
|
android:theme="@style/Theme.SvxRemote">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
android:launchMode="singleTask"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden" />
|
android:configChanges="orientation|screenSize|keyboardHidden" />
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
SplashActivity.mainAlive = true
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
checkForUpdates()
|
checkForUpdates()
|
||||||
@@ -174,6 +175,7 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
SplashActivity.mainAlive = false
|
||||||
svc?.listener = null
|
svc?.listener = null
|
||||||
unbindService(svcConn)
|
unbindService(svcConn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,23 @@ import android.view.animation.AccelerateDecelerateInterpolator
|
|||||||
/** Красивая заставка с плавным появлением логотипа. */
|
/** Красивая заставка с плавным появлением логотипа. */
|
||||||
class SplashActivity : Activity() {
|
class SplashActivity : Activity() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
var mainAlive = false
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
if (mainAlive) {
|
||||||
|
openMain()
|
||||||
|
return
|
||||||
|
}
|
||||||
setContentView(R.layout.activity_splash)
|
setContentView(R.layout.activity_splash)
|
||||||
|
|
||||||
val title = findViewById<View>(R.id.splash_title)
|
val title = findViewById<View>(R.id.splash_title)
|
||||||
val subtitle = findViewById<View>(R.id.splash_subtitle)
|
val subtitle = findViewById<View>(R.id.splash_subtitle)
|
||||||
val line = findViewById<View>(R.id.splash_line)
|
val line = findViewById<View>(R.id.splash_line)
|
||||||
|
|
||||||
val baseAlpha = title.alpha
|
|
||||||
title.alpha = 0f
|
title.alpha = 0f
|
||||||
subtitle.alpha = 0f
|
subtitle.alpha = 0f
|
||||||
line.alpha = 0f
|
line.alpha = 0f
|
||||||
@@ -44,10 +52,19 @@ class SplashActivity : Activity() {
|
|||||||
.start()
|
.start()
|
||||||
|
|
||||||
window.decorView.postDelayed({
|
window.decorView.postDelayed({
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
openMain()
|
||||||
startActivity(intent)
|
|
||||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
|
||||||
finish()
|
|
||||||
}, 4000)
|
}, 4000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openMain() {
|
||||||
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
.addFlags(
|
||||||
|
Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||||
|
Intent.FLAG_ACTIVITY_CLEAR_TOP or
|
||||||
|
Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
|
)
|
||||||
|
startActivity(intent)
|
||||||
|
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ package ru.ua1zbe.svxremote
|
|||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
@@ -53,11 +54,22 @@ class SvxService : Service(), ReflectorClient.Callbacks {
|
|||||||
nm.createNotificationChannel(
|
nm.createNotificationChannel(
|
||||||
NotificationChannel("svx", "SVXRemote", NotificationManager.IMPORTANCE_LOW)
|
NotificationChannel("svx", "SVXRemote", NotificationManager.IMPORTANCE_LOW)
|
||||||
)
|
)
|
||||||
|
updateNotification(getString(R.string.notif_connected))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateNotification(text: String) {
|
||||||
|
val contentIntent = PendingIntent.getActivity(
|
||||||
|
this, 0,
|
||||||
|
Intent(this, MainActivity::class.java)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
val n = Notification.Builder(this, "svx")
|
val n = Notification.Builder(this, "svx")
|
||||||
.setContentTitle("SVXRemote")
|
.setContentTitle("SVXRemote")
|
||||||
.setContentText(getString(R.string.notif_connected))
|
.setContentText(text)
|
||||||
.setSmallIcon(android.R.drawable.ic_menu_call)
|
.setSmallIcon(android.R.drawable.ic_menu_call)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
|
.setContentIntent(contentIntent)
|
||||||
.build()
|
.build()
|
||||||
startForeground(1, n)
|
startForeground(1, n)
|
||||||
}
|
}
|
||||||
@@ -86,7 +98,10 @@ class SvxService : Service(), ReflectorClient.Callbacks {
|
|||||||
|
|
||||||
override fun onState(state: Int, detail: String?) {
|
override fun onState(state: Int, detail: String?) {
|
||||||
when (state) {
|
when (state) {
|
||||||
ReflectorClient.STATE_READY -> engine.startDecoder()
|
ReflectorClient.STATE_READY -> {
|
||||||
|
updateNotification(getString(R.string.notif_connected))
|
||||||
|
engine.startDecoder()
|
||||||
|
}
|
||||||
ReflectorClient.STATE_DISCONNECTED -> {
|
ReflectorClient.STATE_DISCONNECTED -> {
|
||||||
engine.stopDecoder()
|
engine.stopDecoder()
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
@@ -100,10 +115,12 @@ class SvxService : Service(), ReflectorClient.Callbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onTalkerStart(tg: Long, callsign: String) {
|
override fun onTalkerStart(tg: Long, callsign: String) {
|
||||||
|
updateNotification(getString(R.string.notif_talking, callsign))
|
||||||
listener?.onTalkerStart(tg, callsign)
|
listener?.onTalkerStart(tg, callsign)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTalkerStop(tg: Long, callsign: String) {
|
override fun onTalkerStop(tg: Long, callsign: String) {
|
||||||
|
updateNotification(getString(R.string.notif_last_talker, callsign))
|
||||||
listener?.onTalkerStop(tg, callsign)
|
listener?.onTalkerStop(tg, callsign)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
<string name="status_auth_failed">Ошибка авторизации</string>
|
<string name="status_auth_failed">Ошибка авторизации</string>
|
||||||
<string name="status_error">Ошибка: %1$s</string>
|
<string name="status_error">Ошибка: %1$s</string>
|
||||||
<string name="notif_connected">Соединение с рефлектором активно</string>
|
<string name="notif_connected">Соединение с рефлектором активно</string>
|
||||||
|
<string name="notif_talking">Говорит: %1$s</string>
|
||||||
|
<string name="notif_last_talker">Последний: %1$s</string>
|
||||||
|
|
||||||
<string name="btn_connect">Подключиться</string>
|
<string name="btn_connect">Подключиться</string>
|
||||||
<string name="btn_disconnect">Отключиться</string>
|
<string name="btn_disconnect">Отключиться</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user