- Details
- Written by: Stanko Milosev
- Category: Android
- Hits: 740
<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: 762
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: 967
<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: 725
<?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.