I have to wake up my application every 15 minutes. I use setExactAndAllowWhileIdle for that. It sends intent to W2BWakeUpReceiver to do some event.
It works onXiaomi but it does not work on Sumsung
Why it does not work on Samsung???
fun startAlarm() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pi = PendingIntent.getBroadcast(app_context, 0, Intent(action_wake_up), 0)
alarmManager.setExactAndAllowWhileIdle(RTC_WAKEUP, System.currentTimeMillis() + periodWakeUp, pi)
}
}
Receiver
class W2BWakeUpReceiver : BroadcastReceiver() {
companion object {
val action_wake_up = "AlarmWakeUpW2B"
}
private val service: W2BService?
get() = W2BApp.service
private val testTime: Long?
get() = W2BApp.service?.testTime
override fun onReceive(context: Context?, intent: Intent?) {
service?.startAlarm()
val now = Calendar.getInstance().timeInMillis
if (Repository.selectedDevice?.isConnect == true
&& testTime != null
&& now - testTime!! > 50_000
) {
ControllerThread.getThread(TypeThread.Worker).workInThread {
service?.restartMeasurement()
}
}
}
Manifest
<receiver android:name=".service.wake_up.W2BWakeUpReceiver">
<intent-filter>
<action android:name="AlarmWakeUpW2B" />
</intent-filter>
</receiver>
Related
I have implemented OTP verification using firebase,now I want add auto-detect OTP verification functionality in tha. But before autodetect app should ask about the permission for auto detect. How to do that?
Here You Go
in gradle
implementation 'com.google.android.gms:play-services-auth:20.1.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
class SMSReceiver : BroadcastReceiver() {
private var otpListener: OTPReceiveListener? = null
fun setOTPListener(otpListener: OTPReceiveListener?) {
this.otpListener = otpListener
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == SmsRetriever.SMS_RETRIEVED_ACTION) {
val extras = intent.extras
val status = extras!![SmsRetriever.EXTRA_STATUS] as Status?
when (status!!.statusCode) {
CommonStatusCodes.SUCCESS -> {
val sms = extras[SmsRetriever.EXTRA_SMS_MESSAGE] as String?
sms?.let {
val p = Pattern.compile("\\d+")
val m = p.matcher(it)
if (m.find()) {
val otp = m.group()
if (otpListener != null) {
otpListener!!.onOTPReceived(otp)
}
}
}
}
}
}
}
interface OTPReceiveListener {
fun onOTPReceived(otp: String?)
}
}
Manifest
<receiver
android:name=".SMSReceiver"
android:exported="true"
android:permission="com.google.android.gms.auth.api.phone.permission.SEND">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
</intent-filter>
</receiver>
There is a task to make it so that when an application is deleted from the phone, my application sends a notification: "application name" has been deleted.
I do it like this:
Manifest
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="servicereload" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" >
</service>
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val notificationManagerCompat = NotificationManagerCompat.from(this)
notificationManagerCompat.cancelAll()
val serviceIntent = Intent(this, MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(this, serviceIntent)
} else {
startService(Intent(this, MyService::class.java))
}
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addDataScheme("package")
registerReceiver(MyReceiver(), intentFilter)
}
}
MyReceiver
class MyReceiver: BroadcastReceiver() {
val CHANNEL_ID = "ServiceChannel"
#SuppressLint("RemoteViewLayout")
override fun onReceive(context: Context?, intent: Intent?) {
var packageName = ""
try {
packageName = Objects.requireNonNull(intent!!.data)!!.encodedSchemeSpecificPart
Toast.makeText(context, "USER UNINSTALL : $packageName", Toast.LENGTH_SHORT).show()
Log.i("MyLog", "USER UNINSTALL : $packageName")
} catch (ex: Exception) {
Log.i("MyLog", "Exception: $ex")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(CHANNEL_ID, "Channel", NotificationManager.IMPORTANCE_DEFAULT)
val notificationManager = context!!.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(notificationChannel)
}
val notifyIntent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val notifyPendingIntent = PendingIntent.getActivity(context, 200, notifyIntent, 0)
val builder: NotificationCompat.Builder = NotificationCompat.Builder(context!!, CHANNEL_ID).apply {
setSmallIcon(R.mipmap.ic_launcher)
setContentTitle(context.getString(R.string.app_name))
setContentText("App ${packageName.substringAfterLast(".")} has been deleted")
priority = NotificationCompat.PRIORITY_DEFAULT
setContentIntent(notifyPendingIntent)
}
with(NotificationManagerCompat.from(context!!)) {
notify(200, builder.build())
}
}
}
MyService
class MyService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
#RequiresApi(api = Build.VERSION_CODES.N)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("MyLog", "onStartCommand()")
someTask()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
Log.i("MyLog", "Service onDestroy()")
val intent = Intent()
intent.action = "servicereload"
intent.setClass(this, MyReceiver::class.java)
this.sendBroadcast(intent)
}
override fun onCreate() {
super.onCreate()
Log.i("MyLog", "Service onCreate()")
}
override fun onTaskRemoved(rootIntent: Intent?) {
val intent = Intent(applicationContext, this.javaClass)
intent.setPackage(packageName)
val pendingIntent = PendingIntent.getService(applicationContext, 1, intent, PendingIntent.FLAG_ONE_SHOT)
val alarmManager = applicationContext.getSystemService(ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.ELAPSED_REALTIME,1000,pendingIntent)
super.onTaskRemoved(rootIntent)
}
private fun someTask() {
Log.i("MyLog", "Service someTask()")
// val intentFilter = IntentFilter()
// intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
// intentFilter.addDataScheme("package")
// registerReceiver(MyReceiver(), intentFilter)
Thread {
for (i in 1..50) {
Log.i("MyLog", "Service i = $i")
try {
TimeUnit.SECONDS.sleep(1)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
// stopSelf()
}.start()
}
}
someTask() method, just to see if the service is up and running. I check on API 29+ emulators, if I manage to delete the application before 10 seconds, it works well but starts the second service in parallel, which is not good, and I don’t understand why?! If there is nothing to do, after 10 seconds it throws android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{ab817c0 u0 com.testappremovel/.MyService} If I remove onDestroy () or onTaskRemoved () in the service, there is no problem with parallel launch, but after 10 seconds it is cut down in any case and, accordingly, does not turn back on. I also have methods for determining the name and icon of the application by package in order to display them in the notification, but of course, when the application is already remote, I cannot get this data. Please tell me, how to do it right, so that everything works as it should?
I`m trying to build a service that detects if the screen of a device is locked/unlocked (which I will later user as a native module in React). However, it seems like my service is not starting, and I don't receive the expected logs. Where is my mistake? (It's my first time dealing with native android & Kotlin, so apologies if this is a dumb question, and duplicates were related to java code)..
I have defined a Broadcast Receiver for each event here:
ScreenOnReceiver.kt
class screenOnReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if(intent!!.action == Intent.ACTION_SCREEN_ON) {
val screenOff = false
val i = Intent(context, PowerButtonService::class.java)
i.putExtra("screenState", screenOff)
context!!.startService(i)
}
}
}
ScreenOffReceiver.kt
class screenOffReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if(intent!!.action == Intent.ACTION_SCREEN_ON) {
val screenOff = false
val i = Intent(context, PowerButtonService::class.java)
i.putExtra("screenState", screenOff)
context!!.startService(i)
}
}
}
ScreenChangeService.kt:
class ScreenChangeService: Service() {
override fun onBind(p0: Intent?): IBinder? = null
override fun onCreate() {
val screenOnReceiver = screenONReceiver()
val screenOnFilter = IntentFilter(Intent.ACTION_SCREEN_ON)
registerReceiver(screenOnReceiver, screenOnFilter)
val screenOffReceiver = screenOffReceiver()
val screenOffFilter = IntentFilter(Intent.ACTION_SCREEN_OFF)
registerReceiver(screenOffreceiver, screenOffFilter)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val screenState = intent!!.getBooleanExtra("screenState", false)
if (screenState == true) {
Log.d("TAG", "Screen On")
} else {
Log.d("TAG", "Screen Off")
}
return START_NOT_STICKY
}
}
Manifest.xml
<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.Test">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".PowerButtonService"/>
</application>
I am working on a foreground location service for users tracking. Each time the location updates this service sends a request to the API to update current position. The issue is when the app is put to the background or the screen is locked the service stops sending requests after some time (usually around 1 minute during which around 10 requests are sent). After the application is restored the service starts working again and after minimizing/locking the screen the scenario repeats.
Inside the onStartCommand I tried to return multiple start options, neither has worked. I have tested the app on Android 10 and 11.
The service source code:
class LocationService: Service() {
#Inject
lateinit var apiService: ApiService
private val composite = CompositeDisposable()
private var locationManager: LocationManager? = null
private var locationListener: LocationListener? = null
override fun onBind(p0: Intent?): IBinder? =
null
val NOTIFICATION_CHANNEL_ID = "location_tracking"
val NOTIFICATION_CHANNEL_NAME = "Location tracking"
val NOTIFICATION_ID = 101
var isFirstRun = true
#SuppressLint("MissingPermission")
override fun onCreate() {
App.component.inject(this)
setupLocationListener()
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager?
val criteria = Criteria()
criteria.accuracy = Criteria.ACCURACY_FINE
val provider = locationManager?.getBestProvider(criteria, true)
val minTime = 5*1000L
if(provider != null) locationManager?.requestLocationUpdates(provider, minTime, 0f, locationListener)
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (isFirstRun) {
startForegroundService()
isFirstRun = false
} else {
Timber.d {"Resuming service"}
}
return super.onStartCommand(intent, flags, startId)
}
private fun startForegroundService() {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) createNotificationChannel(notificationManager)
val notificationBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setAutoCancel(false)
.setOngoing(true)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(NOTIFICATION_CHANNEL_NAME)
.setContentIntent(getMainActivityIntent())
startForeground(NOTIFICATION_ID, notificationBuilder.build())
}
private fun getMainActivityIntent()
= PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java)
.also { it.action = R.id.action_global_navigationScreenFragment.toString() }, FLAG_UPDATE_CURRENT)
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(notificationManager: NotificationManager) {
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, IMPORTANCE_LOW)
notificationManager.createNotificationChannel(channel)
}
private fun setupLocationListener() {
locationListener = object: LocationListener {
override fun onLocationChanged(location: Location) {
val cords = GeoCoordinatesDTO(location.latitude.toFloat(), location.longitude.toFloat())
try {
composite.add(apiService.mobileUserAccountReportPosition(cords)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{},
{ t ->
if(t is RuntimeException) {
e(t)
}
}
))
} catch(e: Exception) {
Log.e("GPS", "error: $e")
}
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
}
override fun onDestroy() {
try {
locationManager?.removeUpdates(locationListener)
} catch(e: DeadObjectException) {}
super.onDestroy()
}
}
The service is started from onStart funciorn in MainActivity
private fun initializeLocationMonitor() {
locationService = Intent(this, LocationService::class.java)
if(!this.isServiceRunning(LocationService::class.java)) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
startForegroundService(locationService)
} else {
startService(locationService)
}
sendBroadcast(locationService)
}
}
I have following permissions in the manifest as well as registered the serivce:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<service
android:name=".Services.LocationService"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService,InnerclassSeparator"
android:foregroundServiceType="location"/>
Keep the device awake - https://developer.android.com/training/scheduling/wakelock
// global service variable
private var wakeLock: PowerManager.WakeLock? = null
...
// initialize before setupLocationListener() is called
wakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "LocationService::lock").apply {
acquire(10*1000L) // 10 seconds
}
}
Manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
Check if your app has restricted data usage and battery sever inside application settings.
It is preferable to use WorkManager if your min sdk is 14+. It will save you from a lot of hassles of using and managing a foreground service.
https://developer.android.com/topic/libraries/architecture/workmanager
Sending location updates is one of the best use case of WorkManager.
I'm building a simple app, that keep monitoring the media level, and adjust it to be 20% of the maximum level all the time, if the user increased,it should back to 20%again.
Th concept I followed is doing the monitoring process via a service, once this service is destroyed it calls a broadcast receiver, which in its turn calls the receiver again, and so on, as endless cycle, but looks something wrong in the code below,soit is not working as desired, and service/broadcast not keep calling each others!
I started the mainActivity as:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val audio = this.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val level = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
val maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val percent = 0.2f
val twintyVolume = (maxVolume * percent).toInt()
if ( level > twintyVolume) {
Toast.makeText(this,"audio level is $level", Toast.LENGTH_LONG).show()
audio.setStreamVolume(AudioManager.STREAM_MUSIC,twintyVolume,0)
}
this.startService(Intent(this, VolumeCheck::class.java))
}
}
The above make initial check and reduce the media volume to 20% of the max volume, then start the service, which is doing the same with the below code:
class VolumeCheck : Service() {
private lateinit var context: Context
override fun onCreate() {
super.onCreate()
context = this
Toast.makeText(this, "service created", Toast.LENGTH_SHORT).show();
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mediaPlayer = MediaPlayer()
// Thread().run {
val maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val percent = 0.2f
val twintyVolume = (maxVolume * percent).toInt()
if (mediaPlayer.isPlaying) {
val level = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
if ( level > twintyVolume) {
Toast.makeText(context,"audio level is $level", Toast.LENGTH_LONG).show()
audio.setStreamVolume(AudioManager.STREAM_MUSIC,twintyVolume,0)
}
}
// Thread.sleep(3000)
// }
stopSelf()
return Service.START_STICKY
}
override fun onBind(intent: Intent): IBinder? {
//TODO for communication return IBinder implementation
return null
}
override fun onDestroy() {
super.onDestroy()
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
val intent = Intent("com.kortex.mediafix.BootUpReceiver")
sendBroadcast(intent)
}
}
Once the service is destroyed, it calls the boot broadcast receiver, which in its turn call the service again:
class BootUpReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.startService(Intent(context, VolumeCheck::class.java))
}
}
The Manifest is:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kortex.mediafix">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".BootUpReceiver"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service android:name=".VolumeCheck" />
</application>
</manifest>
val intent = Intent("com.kortex.mediafix.BootUpReceiver")
This line does not call your broadcast receiver but rather makes an intent with intent action as "com.kortex.mediafix.BootUpReceiver"
Change your BootUpReceiver's entry in manifest to receive this action
<receiver android:name=".BootUpReceiver"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.kortex.mediafix.BootUpReceiver" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
Another way to do it, without Service is to call ContentObserver from BroadcastReceiver
BroadcastReceiver:
class BootUpReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val myObserver = VolumeOnserver(context, Handler())
// Register the VolumeOnserver for setting changes
context.contentResolver.registerContentObserver(
android.provider.Settings.System.CONTENT_URI ,true,
myObserver)
}
}
ContentObserver:
class VolumeOnserver (context: Context, h: Handler?): ContentObserver(h) {
private val context = context
override fun onChange(selfChange: Boolean) {
onChange(selfChange, uri = null)
}
// Implement the onChange(boolean, Uri) method to take advantage of the new Uri argument.
override fun onChange(selfChange: Boolean, uri: Uri?) {
// Handle change.
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val level = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
val maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val percent = 0.2f
val twintyVolume = (maxVolume * percent).toInt()
if ( level > twintyVolume) audio.setStreamVolume(AudioManager.STREAM_MUSIC,twintyVolume,0)
}
}
MainActivity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val audio = this.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val level = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
val maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val percent = 0.2f
val twintyVolume = (maxVolume * percent).toInt()
if ( level > twintyVolume) audio.setStreamVolume(AudioManager.STREAM_MUSIC,twintyVolume,0)
finish()
}
}
I solved it by using ContentObserver that is called from the service, so my code now is:
MainActivity to launch the app, and do first time adjustment, and to start the service:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val audio = this.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val level = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
val maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val percent = 0.2f
val twintyVolume = (maxVolume * percent).toInt()
if ( level > twintyVolume) audio.setStreamVolume(AudioManager.STREAM_MUSIC,twintyVolume,0)
Toast.makeText(this,"Audio level adjusted to 20% instead of $level", Toast.LENGTH_LONG).show()
this.startService(Intent(this, VolumeCheck::class.java))
finish()
}
}
Broadcast to start the app and the service each time the device is rebooted:
class BootUpReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.startService(Intent(context, VolumeCheck::class.java))
}
}
Service to register and call the observer:
class VolumeCheck : Service() {
private lateinit var context: Context
private lateinit var myObserver: VolumeOnserver
override fun onCreate() {
super.onCreate()
context = this
// Define the VolumeOnserver
myObserver = VolumeOnserver(context, Handler())
Toast.makeText(this, "service created", Toast.LENGTH_SHORT).show();
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// Register the VolumeOnserver for setting changes
contentResolver.registerContentObserver(android.provider.Settings.System.CONTENT_URI ,true, myObserver);
return Service.START_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onDestroy() {
super.onDestroy()
// Unregister the VolumeOnserver
contentResolver.unregisterContentObserver(myObserver);
}
}
Observer that observe any changes in the settings and check the media volume and adjust it if required:
class VolumeOnserver (context: Context, h: Handler?): ContentObserver(h) {
private val context = context
override fun onChange(selfChange: Boolean) {
onChange(selfChange, uri = null)
}
// Implement the onChange(boolean, Uri) method to take advantage of the new Uri argument.
override fun onChange(selfChange: Boolean, uri: Uri?) {
// Handle change.
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val level = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
val maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val percent = 0.2f
val twintyVolume = (maxVolume * percent).toInt()
if ( level > twintyVolume) audio.setStreamVolume(AudioManager.STREAM_MUSIC,twintyVolume,0)
// Toast.makeText(context,"audio level is $level", Toast.LENGTH_LONG).show()
}
}
The Manifest file is:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kortex.mediafix">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".BootUpReceiver"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.kortex.mediafix.BootUpReceiver" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service android:name=".VolumeCheck" />
</application>
</manifest>
And my running app is here.
But have an issue it is not stable, I the user insist to change the volume, after many trials the service and/or the observer is/are no more working till the app is restarted.