android: only start service when app is in the background, change service icon (#790)

* android: only start service when app is in the background, change service icon

* update version v3.0 (38)

* set flag
This commit is contained in:
Evgeny Poberezkin
2022-07-07 19:05:03 +01:00
committed by GitHub
parent 2ae3748489
commit 2b7de2a7a6
12 changed files with 53 additions and 25 deletions

View File

@@ -11,7 +11,7 @@ android {
applicationId "chat.simplex.app"
minSdk 29
targetSdk 32
versionCode 37
versionCode 38
versionName "3.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -24,9 +24,11 @@ Java_chat_simplex_app_SimplexAppKt_initHS(__unused JNIEnv *env, __unused jclass
// from simplex-chat
typedef void* chat_ctrl;
extern chat_ctrl chat_init(const char * path);
extern chat_ctrl chat_init(const char *path);
extern char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
extern char *chat_recv_msg(chat_ctrl ctrl);
extern char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait);
extern char *chat_parse_markdown(const char *str);
JNIEXPORT jlong JNICALL
Java_chat_simplex_app_SimplexAppKt_chatInit(JNIEnv *env, __unused jclass clazz, jstring datadir) {
@@ -48,3 +50,16 @@ JNIEXPORT jstring JNICALL
Java_chat_simplex_app_SimplexAppKt_chatRecvMsg(JNIEnv *env, __unused jclass clazz, jlong controller) {
return (*env)->NewStringUTF(env, chat_recv_msg((void*)controller));
}
JNIEXPORT jstring JNICALL
Java_chat_simplex_app_SimplexAppKt_chatRecvMsgWait(JNIEnv *env, __unused jclass clazz, jlong controller, jint wait) {
return (*env)->NewStringUTF(env, chat_recv_msg_wait((void*)controller, wait));
}
JNIEXPORT jstring JNICALL
Java_chat_simplex_app_SimplexAppKt_chatParseMarkdown(JNIEnv *env, __unused jclass clazz, jstring str) {
const char *_str = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
jstring res = (*env)->NewStringUTF(env, chat_parse_markdown(_str));
(*env)->ReleaseStringUTFChars(env, str, _str);
return res;
}

View File

@@ -24,8 +24,10 @@ external fun pipeStdOutToSocket(socketName: String) : Int
// SimpleX API
typealias ChatCtrl = Long
external fun chatInit(path: String): ChatCtrl
external fun chatSendCmd(ctrl: ChatCtrl, msg: String) : String
external fun chatRecvMsg(ctrl: ChatCtrl) : String
external fun chatSendCmd(ctrl: ChatCtrl, msg: String): String
external fun chatRecvMsg(ctrl: ChatCtrl): String
external fun chatRecvMsgWait(ctrl: ChatCtrl, timeout: Int): String
external fun chatParseMarkdown(str: String): String
class SimplexApp: Application(), LifecycleEventObserver {
val chatController: ChatController by lazy {
@@ -55,7 +57,6 @@ class SimplexApp: Application(), LifecycleEventObserver {
chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo
} else {
chatController.startChat(user)
SimplexService.start(applicationContext)
chatController.showBackgroundServiceNoticeIfNeeded()
}
}
@@ -66,9 +67,9 @@ class SimplexApp: Application(), LifecycleEventObserver {
withApi {
when (event) {
Lifecycle.Event.ON_STOP ->
if (!appPreferences.runServiceInBackground.get()) SimplexService.stop(applicationContext)
if (appPreferences.runServiceInBackground.get()) SimplexService.start(applicationContext)
Lifecycle.Event.ON_START ->
SimplexService.start(applicationContext)
SimplexService.stop(applicationContext)
Lifecycle.Event.ON_RESUME ->
if (chatModel.onboardingStage.value == OnboardingStage.OnboardingComplete) {
chatController.showBackgroundServiceNoticeIfNeeded()

View File

@@ -20,6 +20,7 @@ class SimplexService: Service() {
private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false
private var isStartingService = false
private var isStoppingService = false
private var notificationManager: NotificationManager? = null
private var serviceNotification: Notification? = null
private val chatController by lazy { (application as SimplexApp).chatController }
@@ -71,7 +72,6 @@ class SimplexService: Service() {
} else {
Log.w(TAG, "Starting foreground service")
chatController.startChat(user)
chatController.startReceiver()
isServiceStarted = true
saveServiceState(self, ServiceState.STARTED)
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
@@ -88,6 +88,8 @@ class SimplexService: Service() {
private fun stopService() {
Log.d(TAG, "Stopping foreground service")
if (!isServiceStarted || isStoppingService) return
isStoppingService = true
try {
wakeLock?.let {
while (it.isHeld) it.release() // release all, in case acquired more than once
@@ -98,7 +100,7 @@ class SimplexService: Service() {
} catch (e: Exception) {
Log.d(TAG, "Service stopped without being started: ${e.message}")
}
isStoppingService = false
isServiceStarted = false
saveServiceState(this, ServiceState.STOPPED)
}
@@ -121,7 +123,7 @@ class SimplexService: Service() {
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
}
return NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ntf_icon)
.setSmallIcon(R.drawable.ntf_service_icon)
.setColor(0x88FFFF)
.setContentTitle(title)
.setContentText(text)

View File

@@ -117,8 +117,11 @@ class AppPreferences(val context: Context) {
}
}
private const val MESSAGE_TIMEOUT: Int = 15_000_000
open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager, val appContext: Context, val appPrefs: AppPreferences) {
val chatModel = ChatModel(this)
private var receiverStarted = false
init {
chatModel.runServiceInBackground.value = appPrefs.runServiceInBackground.get()
@@ -142,6 +145,7 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
chatModel.currentUser.value = user
chatModel.userCreated.value = true
chatModel.onboardingStage.value = OnboardingStage.OnboardingComplete
startReceiver()
Log.d(TAG, "chat started")
} catch (e: Error) {
Log.e(TAG, "failed starting chat $e")
@@ -149,8 +153,9 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
}
}
fun startReceiver() {
private fun startReceiver() {
Log.d(TAG, "ChatController startReceiver")
if (receiverStarted) return
thread(name="receiver") {
GlobalScope.launch { withContext(Dispatchers.IO) { recvMspLoop() } }
}
@@ -176,18 +181,23 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
}
}
suspend fun recvMsg(): CR {
private suspend fun recvMsg(): CR? {
return withContext(Dispatchers.IO) {
val json = chatRecvMsg(ctrl)
val r = APIResponse.decodeStr(json).resp
Log.d(TAG, "chatRecvMsg: ${r.responseType}")
if (r is CR.Response || r is CR.Invalid) Log.d(TAG, "chatRecvMsg json: $json")
r
val json = chatRecvMsgWait(ctrl, MESSAGE_TIMEOUT)
if (json == "") {
null
} else {
val r = APIResponse.decodeStr(json).resp
Log.d(TAG, "chatRecvMsg: ${r.responseType}")
if (r is CR.Response || r is CR.Invalid) Log.d(TAG, "chatRecvMsg json: $json")
r
}
}
}
suspend fun recvMspLoop() {
processReceivedMsg(recvMsg())
private suspend fun recvMspLoop() {
val msg = recvMsg()
if (msg != null) processReceivedMsg(msg)
recvMspLoop()
}
@@ -206,7 +216,7 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
throw Error("user not created ${r.responseType} ${r.details}")
}
suspend fun apiStartChat(): Boolean {
private suspend fun apiStartChat(): Boolean {
val r = sendCmd(CC.StartChat())
when (r) {
is CR.ChatStarted -> return true
@@ -215,13 +225,13 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
}
}
suspend fun apiSetFilesFolder(filesFolder: String) {
private suspend fun apiSetFilesFolder(filesFolder: String) {
val r = sendCmd(CC.SetFilesFolder(filesFolder))
if (r is CR.CmdOk) return
throw Error("failed to set files folder: ${r.responseType} ${r.details}")
}
suspend fun apiGetChats(): List<Chat> {
private suspend fun apiGetChats(): List<Chat> {
val r = sendCmd(CC.ApiGetChats())
if (r is CR.ApiChats ) return r.chats
throw Error("failed getting the list of chats: ${r.responseType} ${r.details}")
@@ -256,7 +266,7 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
return null
}
suspend fun getUserSMPServers(): List<String>? {
private suspend fun getUserSMPServers(): List<String>? {
val r = sendCmd(CC.GetUserSMPServers())
if (r is CR.UserSMPServers) return r.smpServers
Log.e(TAG, "getUserSMPServers bad response: ${r.responseType} ${r.details}")
@@ -383,7 +393,7 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
return false
}
suspend fun apiGetUserAddress(): String? {
private suspend fun apiGetUserAddress(): String? {
val r = sendCmd(CC.ShowMyAddress())
if (r is CR.UserContactLink) return r.connReqContact
if (r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore

View File

@@ -114,7 +114,6 @@ fun createProfile(chatModel: ChatModel, displayName: String, fullName: String) {
Profile(displayName, fullName, null)
)
chatModel.controller.startChat(user)
SimplexService.start(chatModel.controller.appContext)
chatModel.controller.showBackgroundServiceNoticeIfNeeded()
chatModel.onboardingStage.value = OnboardingStage.OnboardingComplete
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -179,6 +179,7 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
if case let .ntfMessages(connEntity, msgTs, ntfMessages) = r {
return NtfMessages(connEntity: connEntity, msgTs: msgTs, ntfMessages: ntfMessages)
}
logger.debug("apiGetNtfMessage ignored response: \(String.init(describing: r), privacy: .public)")
return nil
}