- Details
- Written by: Stanko Milosev
- Category: Android
- Hits: 1339
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />Then in the MainActivity I have added the method "checkOptimization" and "openBatteryOptimization ":
@SuppressLint("NewApi", "BatteryLife")
private fun checkOptimization(context: Context) {
val packageName = applicationContext.packageName
val pm = applicationContext.getSystemService(POWER_SERVICE) as PowerManager
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
val intent = Intent()
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
intent.data = Uri.parse("package:" + context.packageName)
context.startActivity(intent)
}
}
fun openBatteryOptimization(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val intent = Intent()
intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
context.startActivity(intent)
} else {
//Timber.d("Battery optimization not necessary")
}
}
Both methods "checkOptimization" and "openBatteryOptimization" I have added to "onCreate" of MainActivity. For
Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGSIn import list I have added:
import android.provider.Settings import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONSIn Gradle I added:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"Service and receiver first I have copied from original article, then I have added following methods:
private val wakeLock: PowerManager.WakeLock by lazy {
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "neverEndingApplication:ServiceWakelock")
}
}
private fun acquireWakelock() {
try {
wakeLock.let {
wakeLock.setReferenceCounted(false)
if (!wakeLock.isHeld) {
wakeLock.acquire()
}
}
} catch (e: RuntimeException) {
}
}
private fun releaseWakelock() {
try {
wakeLock.let {
if (it.isHeld) {
it.release()
}
}
} catch (e: RuntimeException) {
}
}
Still in Service I have added acquireWakelock in onCreate:
override fun onCreate() {
super.onCreate()
acquireWakelock()
}
releaseWakelock() I have added when I want to stop the app:
if (intent?.action.equals("stopForeground")) {
job?.cancel()
releaseWakelock()
stopForeground(true)
stopSelfResult(startId)
}
MainActivity I also copied from my original article.
Example download from here.
- Details
- Written by: Stanko Milosev
- Category: Android
- Hits: 1422
val component = ComponentName(this, MyBroadcastReceiver::class.java) getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);Disable:
val component = ComponentName(this, MyBroadcastReceiver::class.java) getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);
- Details
- Written by: Stanko Milosev
- Category: Android
- Hits: 1846
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />and android:process=":MyService". My Manifest looks like:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.milosev.myapplication">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true" />
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:process=":MyService" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
In Gradle I added:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"So my gradle looks like:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.milosev.myapplication"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
}
MyBroadcastReceiver.kt looks like:
package com.milosev.myapplication
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.milosev.getgpslocation.MyService
class MyBroadcastReceiver : BroadcastReceiver() {
@RequiresApi(Build.VERSION_CODES.O)
override fun onReceive(context: Context, intent: Intent) {
when (intent?.action) {
"MyAction" -> {
val myIntent = Intent(context, MainActivity::class.java).setAction("MyAction")
LocalBroadcastManager.getInstance(context).sendBroadcast(myIntent)
}
"Restart" -> {
val intent = Intent(context, MyService::class.java)
intent.action = "startForeground"
context.startForegroundService(intent)
}
}
}
}
MyService.kt looks like:
package com.milosev.getgpslocation
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.CountDownTimer
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import kotlinx.coroutines.*
class MyService : Service(),CoroutineScope by MainScope() {
private var job: Job? = null
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
"startForeground" -> {
val channelId = createNotificationChannel("my_service", "My Background Service")
val notificationBuilder = NotificationCompat.Builder(this, channelId)
val notification = notificationBuilder.setOngoing(true)
.setContentTitle("test")
.setContentText("test")
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(1)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(101, notification)
val context = this
job = launch {
while(true) {
val myIntent = Intent(context, MyBroadcastReceiver::class.java).setAction("MyAction")
sendBroadcast(myIntent);
delay(1_000)
}
}
}
}
if (intent?.action.equals("stopForeground")) {
job?.cancel()
stopForeground(true)
stopSelfResult(startId)
}
return START_STICKY;
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String {
val chan = NotificationChannel(
channelId,
channelName, NotificationManager.IMPORTANCE_LOW
)
chan.lightColor = Color.RED
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
}
override fun onDestroy() {
super.onDestroy()
val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
sendBroadcast(myIntent);
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
sendBroadcast(myIntent);
}
}
POI:
override fun onDestroy() {
super.onDestroy()
val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
sendBroadcast(myIntent);
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
sendBroadcast(myIntent);
}
In main activity:
@RequiresApi(Build.VERSION_CODES.O)
override fun onResume() {
super.onResume()
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadCastReceiver, IntentFilter("MyAction"))
val intent = Intent(this, MyService::class.java)
intent.action = "startForeground"
startForegroundService(intent)
}
Notice that there is a problem with onResume because it will start the service immediately.
Edit: 2022-06-05:
Unfortunately after latest update of Huawei my app is again getting killed. Here you can find informations about this issuse.
- Details
- Written by: Stanko Milosev
- Category: Android
- Hits: 1346
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.milosev.sendbroadcastfromanotherprocess">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SendBroadcastFromAnotherProcess">
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true" />
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:process=":MyService" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
In gradle I have added coroutines:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"My gradle looks like:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.milosev.sendbroadcastfromanotherprocess"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
}
MyService.kt:
*2023-04-04 UPDATE: * To debug MyService you will have to attach to this process with debugger.
package com.milosev.sendbroadcastfromanotherprocess
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import kotlinx.coroutines.*
class MyService : Service(), CoroutineScope by MainScope() {
private var job: Job? = null
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
"startForeground" -> {
val channelId = createNotificationChannel("my_service", "My Background Service")
val notificationBuilder = NotificationCompat.Builder(this, channelId)
val notification = notificationBuilder.setOngoing(true)
.setContentTitle("test")
.setContentText("test")
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(1)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(101, notification)
val context = this
job = launch {
while(true) {
val myIntent = Intent(context, MyReceiver::class.java).setAction("MyAction")
myIntent.putExtra("message", "test")
sendBroadcast(myIntent);
delay(1_000)
}
}
}
}
if (intent?.action.equals("stopForeground")) {
job?.cancel()
stopForeground(true)
stopSelfResult(startId)
}
return START_STICKY;
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String {
val chan = NotificationChannel(
channelId,
channelName, NotificationManager.IMPORTANCE_NONE
)
chan.lightColor = Color.RED
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
}
}
MyReceiver.kt:
package com.milosev.sendbroadcastfromanotherprocess
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.localbroadcastmanager.content.LocalBroadcastManager
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val myIntent = Intent(context, MainActivity::class.java).setAction("MyAction")
myIntent.putExtra("message", "test")
LocalBroadcastManager.getInstance(context).sendBroadcast(myIntent)
}
}
MainActivity.kt:
package com.milosev.sendbroadcastfromanotherprocess
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.localbroadcastmanager.content.LocalBroadcastManager
class MainActivity : AppCompatActivity() {
val myReceiver: BroadcastReceiver = MyReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val filter = IntentFilter("MyAction")
registerReceiver(myReceiver, filter)
var textView = findViewById(R.id.myText) as TextView
val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
when (intent?.action) {
"MyAction" -> {
textView = findViewById(R.id.myText) as TextView
textView.text = intent.getStringExtra("message")
}
}
}
}
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadCastReceiver, IntentFilter("MyAction"))
}
@RequiresApi(Build.VERSION_CODES.O)
fun onClick(view: View) {
val intent = Intent(this, MyService::class.java)
intent.action = "startForeground"
startForegroundService(intent)
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/myText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Button"
tools:layout_editor_absoluteX="106dp"
tools:layout_editor_absoluteY="110dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
Example download from here.