- Details
- Written by: Stanko Milosev
- Category: Android
- Hits: 284
<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: 322
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: 384
<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: 304
<?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:
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>