I am attempting to listen for the insertion of headphones using the system service:
class MainActivity : AppCompatActivity(){
private lateinit var headphoneIntentFilter: IntentFilter
private lateinit var headphoneReceiver:HeadphoneReceiver
override fun onStart() {
super.onStart()
headphoneIntentFilter = IntentFilter(ACTION_HEADSET_PLUG)
headphoneReceiver = HeadphoneReceiver()
registerReceiver(headphoneReceiver,headphoneIntentFilter)
}
}
The receiver itself:
class HeadphoneReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
AppLog.i(TAG,"HeadphoneBroadcastReceived")
if (intent?.action == Intent.ACTION_HEADSET_PLUG) {
when (intent.getIntExtra("state", -1)) {
0 -> {
AppLog.d(TAG, "Headset is unplugged")
TTSUtils.getInstance(context!!).isEnabled = false
}
1 -> {
AppLog.i(TAG, "Headset plugged in")
TTSUtils.getInstance(context!!).loadService()
}
}
}
}
}
I am not receiving the broadcast at all. I am using a custom fork of OSS android on an embedded device, but I'm hoping the problem is with my code.
Try ACTION_AUDIO_BECOMING_NOISY filter.
/**
* Helper class for listening for when headphones are unplugged.
*/
private class BecomingNoisyReceiver(private val context: Context) : BroadcastReceiver() {
private val noisyIntentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
private var registered = false
fun register() {
if (!registered) {
context.registerReceiver(this, noisyIntentFilter)
registered = true
}
}
fun unregister() {
if (registered) {
context.unregisterReceiver(this)
registered = false
}
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) {
//Pause audio
}
}
}
Related
I have this simple MainActivity in which I use the WorkManager to enqueue a unique OneTimeWorkRequest:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
WorkManager.getInstance(this).enqueueUniqueWork("work", ExistingWorkPolicy.REPLACE, OneTimeWorkRequest.Builder(BackgroundTask::class.java).build())
findViewById<Button>(R.id.button).setOnClickListener {
BackgroundTask.stopHandler = true
}
}
}
The OneTimeWorkRequest launch a background Worker that is responsible to take updates from the GPS_PROVIDER whether the user keep the App in foreground or the user put the App in background:
class BackgroundTask (context : Context, params : WorkerParameters) : Worker(context, params){
private lateinit var mLocationManager: LocationManager
private lateinit var mLocationListener: LocationUpdaterListener
private var mHandler: Handler = Handler(Looper.getMainLooper())
private var mHandlerTask: Runnable = object : Runnable {
override fun run() {
if (Looper.myLooper() == null) {
Looper.prepare()
}
Toast.makeText(applicationContext,"mHandlerTask running...",Toast.LENGTH_SHORT).show()
if (stopHandler) {
stopH()
return
}
if (!isListening) {
startListening()
}
mHandler.postDelayed(this, TEN_SECONDS)
}
}
fun stopH() {
stopListening()
mHandler.removeCallbacks(mHandlerTask)
}
override fun doWork(): Result {
mLocationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
mLocationListener = LocationUpdaterListener()
mHandlerTask.run()
return Result.success()
}
private fun startListening() {
if (ContextCompat.checkSelfPermission( applicationContext, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission( applicationContext, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, mLocationListener, Looper.getMainLooper())
}
isListening = true
}
private fun stopListening() {
if (ContextCompat.checkSelfPermission( applicationContext, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission( applicationContext, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) {
mLocationManager.removeUpdates(mLocationListener)
}
isListening = false
}
inner class LocationUpdaterListener : LocationListener {
override fun onLocationChanged(location: Location) {
try {
Log.d("DEBUG", "${location.latitude} , ${location.longitude}")
} catch (e: Exception) {
e.printStackTrace()
} finally {
stopListening()
}
}
override fun onProviderDisabled(provider: String) {
stopListening()
}
}
companion object {
const val TEN_SECONDS: Long = 10000
var isListening = false
var stopHandler = false
}
}
I put a Toast message inside the run of the Runnable object just to have confirmation the Runnable is going.
If I use the button in the MainActivity to stop the Handler everything works fine: the stop() function is called and then the LocationListener and the mHandlerTask callback are removed.
I was wondering what happens if I suddenly close the App (swiping up the App from the Recents Screen), because I can't see the Toast message prints anymore and so I guess someway the Runnable object is killed, but I want to be sure that also the LocationListener and the mHandlerTask callback are removed.
Does this happen automatically?
I'm trying to implement a service that will listen to the incoming notifications. I've googled some and found NotificationListenerService.
The way I want to work with the service is the following:
user can start/stop the service by interacting with two buttons (Start service, stop service)
once the service is started it should run in the background until it is explicitly stopped by the user (using the stop service button)
when the service is started, if the user opens the app I want to let the user interact with the service (as an example, call cancelAllNotifications() on a button click)
Buttons - user interaction with the service
button_start.setOnClickListener {
logDebug("Button start clicked.")
startService()
}
//BUG service doesn't seem to stop
button_stop.setOnClickListener {
logDebug("Button stop clicked.")
stopService()
}
button_cancel_notif.setOnClickListener {
logDebug("Button cancel all notifications clicked.")
cancelAllNotifications()
}
I've managed to start the service and bind it to the fragment, but when I call stopService(), even though it returns true, the service won't stop.
Here is my code:
AndroidManifest
<service android:name=".services.NLService"
android:label="#string/service_name"
android:exported="false"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
class NLService
class NLService : NotificationListenerService() {
private val binder = NLBinder()
var isBound: Boolean = false
override fun onBind(intent: Intent?): IBinder? {
logDebug("NLService is bind!")
isBound = true
val action = intent?.action
logDebug("onBind action: $action")
return if (action == SERVICE_INTERFACE) {
logDebug("Bound by system")
super.onBind(intent)
} else {
logDebug("Bound by application")
binder
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return Service.START_STICKY
}
override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn)
logDebug(sbn.toString())
}
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
super.onNotificationRemoved(sbn)
logDebug(sbn.toString())
}
inner class NLBinder : Binder() {
fun getService(): NLService = this#NLService
}
}
ServiceFragment
open class ServiceFragment : Fragment(), INLServiceActions, INLBinderActions {
private lateinit var notificationListenerIntent: Intent
private var notificationListenerService: NLService? = null
private lateinit var binder: NLService.NLBinder
private val connection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
logDebug("onServiceConnected called.")
binder = service as NLService.NLBinder
notificationListenerService = binder.getService()
}
}
override fun onStop() {
super.onStop()
unbindService(connection, notificationListenerService)
}
override fun onResume() {
super.onResume()
notificationListenerIntent = Intent(activity?.applicationContext, NLService::class.java)
activity?.isServiceRunning(NLService::class.java)?.let { isServiceRunning ->
if (isServiceRunning) {
bindService(notificationListenerIntent, connection, 0)
} else {
logDebug("Service is not running!")
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_NOTIFICATION_LISTENER -> startService(notificationListenerIntent)
}
}
override fun stopService() {
//BUG service doesn't seem to stop
activity?.isServiceRunning(NLService::class.java)?.let { isServiceRunning ->
if (isServiceRunning) {
unbindService(connection, notificationListenerService)
stopService(notificationListenerIntent)
} else {
logDebug("Service is not running!")
}
}
}
override fun startService() {
activity?.isServiceRunning(NLService::class.java)?.let { isServiceRunning ->
if (!isServiceRunning) {
requestEnableNLService()
} else {
logDebug("Service is already running!")
}
}
}
override fun cancelAllNotifications() {
notificationListenerService?.cancelAllNotifications()
}
private fun requestEnableNLService() {
val intent = Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")
startActivityForResult(intent, REQUEST_NOTIFICATION_LISTENER)
}
}
Extension functions
#Suppress("DEPRECATION") // Deprecated for third party Services.
fun <T> Activity.isServiceRunning(serviceClass: Class<T>): Boolean {
return (getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
.getRunningServices(Integer.MAX_VALUE)
.any { it.service.className == serviceClass.name }
}
fun Fragment.bindService(
intent: Intent,
connection: ServiceConnection,
flags: Int
) {
val isBind = activity?.applicationContext?.bindService(intent, connection, flags)
logDebug("Service bind: $isBind.")
}
fun Fragment.unbindService(
connection: ServiceConnection,
notificationListenerService: NLService?
) {
notificationListenerService?.isBound?.let {
if (it) {
activity?.applicationContext?.unbindService(connection)
notificationListenerService.isBound = false
logDebug("Service unbind.")
}
}
}
fun Fragment.startService(service: Intent) {
activity?.applicationContext?.startService(service)
logDebug("Service started")
}
fun Fragment.stopService(service: Intent) {
val isStopped = activity?.applicationContext?.stopService(service)
logDebug("Service stopped: $isStopped")
}
I know this:
A broadcast receiver is ALWAYS called because It sets the alarm for the next day.
When It works if the App is in Foreground, or if it is closed or if it is closed screen.
I use 'com.github.thefuntasty.hauler:library:2.0.0' to dismiss the activity with swipe
No other alarm is set at that time
I don't get any crash reports
I have NO idea, why the activity doesn't start. I tried doing try and catch, to see if that would help but it didn't.
EDIT:
Expected:
When the Broadcast reciever triggers, set the alarm for the next day, start the Activity in the foreground, even if the phone is locked and play the alarm.
On swipe down destroy the activity
Currently:
Sometimes it opens the activity other times it doesn't and I don't know why. If I set it for 5 min from now (locked screen, closed app), it works without problem. If I set it for tomorrow, it works 90% of the time.
I'm trying to figure out, why the activity sometimes doesn't start, because I'm not getting any crash reports from the firebase.
This is the code:
Manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver
android:name=".views.alarm.broadcast.ResumeOnBootAlarm"
android:enabled="true"
android:exported="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver
android:name=".views.alarm.broadcast.MyAlarm"
android:exported="true" />
<activity
android:name=".views.alarm.broadcast.TriggeredAlarmActivity"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:theme="#style/AppTheme.Draggable" />
This is my BroadCastReciever:
class MyAlarm : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val alarmID = intent.extras?.getInt(ARG_ALARM_ID)
//Immediatly set new alarm
val realm = Realm.getDefaultInstance()
val alarmFromRealm = DataHelper.getAlarm(realm, alarmID!!)
alarmFromRealm?.let { alarm ->
val shouldEnableAlarm = alarm.isEnabled && alarm.daysList!!.isNotEmpty()
DataHelper.enableAlarm(alarmID, shouldEnableAlarm, realm)
if (shouldEnableAlarm) {
setAlarm(context, alarm, false)
} else {
cancelAlarm(context, alarm.id)
}
}
try {
context.startActivity(TriggeredAlarmActivity.getIntent(context, alarmID))
} catch (ex: Exception) {
Crashlytics.logException(ex)
context.startActivity(TriggeredAlarmActivity.getIntent(MyApplication.appContext, null))
}
}
}
And this is the TriggeredActivity:
class TriggeredAlarmActivity : BaseActivity() {
private var currentUserVolume: Int = 0
private lateinit var timer: Timer
private lateinit var realm: Realm
private lateinit var vibrator: Vibrator
private var mediaPlayer: MediaPlayer? = null
private var shouldResumePlaying: Boolean = false
private val alarmID: Int?
get() {
return intent.extras?.getInt(ARG_ALARM_ID)
}
private val mAudioManager: AudioManager by lazy {
baseContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
}
/*
LifeCycle
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_triggered_alarm)
initTextClock()
showIfScreenIsLocked()
showDanceAnimation()
if (alarmID == null) {
val uri = getDefaultRingtone()
mediaPlayer= MediaPlayer()
mediaPlayer?.setDataSource(this,uri)
mediaPlayer?.prepare()
mediaPlayer?.start()
} else {
realm = Realm.getDefaultInstance()
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
val alarmFromRealm = DataHelper.getAlarm(realm, alarmID!!)
alarmFromRealm?.let { alarm ->
try {
if (alarm.useDefaultRingtone) {
val uri = getDefaultRingtone()
mediaPlayer = MediaPlayer()
mediaPlayer?.let { increaseVolumeOverTime(it, alarm.shouldVibrate) }
mediaPlayer?.setDataSource(this, uri)
mediaPlayer?.isLooping = true
shouldResumePlaying = alarm.shouldResumePlaying
mediaPlayer?.setOnPreparedListener {
if (alarm.shouldResumePlaying) {
mediaPlayer?.seekTo(alarm.secondsPlayed)
}
mediaPlayer?.start()
}
mediaPlayer?.prepareAsync()
} else {
initMediaPlayer(alarm)
}
} catch (exception: Exception) {
Crashlytics.logException(exception)
val uri = getDefaultRingtone()
mediaPlayer = MediaPlayer()
mediaPlayer?.let { increaseVolumeOverTime(it, true) }
mediaPlayer?.setDataSource(this, uri)
mediaPlayer?.isLooping = true
mediaPlayer?.setOnPreparedListener {
mediaPlayer?.start()
}
mediaPlayer?.prepareAsync()
}
}
}
haulerView?.setOnDragDismissedListener {
finish() // finish activity when dismissed
}
}
private fun getDefaultRingtone(): Uri {
var uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
}
}
return uri
}
private fun initTextClock() {
val period = 5000L
val timer = Timer()
val formatter = SimpleDateFormat("HH:mm")
val task = object : TimerTask() {
override fun run() {
val today = Date()
runOnUiThread {
tvCurrentTimeActual?.text = formatter.format(today)
}
}
}
timer.scheduleAtFixedRate(task, 0L, period)
}
override fun onDestroy() {
super.onDestroy()
if (shouldResumePlaying) {
mediaPlayer?.let {
alarmID?.let { it1 -> DataHelper.updateProgress(it1, it.currentPosition) }
}
}
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentUserVolume, 0)
timer.cancel()
mediaPlayer?.stop()
vibrator.cancel()
realm.close()
}
/*
Private
*/
private fun showIfScreenIsLocked() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
} else {
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
)
}
}
private fun showDanceAnimation() {
val lottieFiles = mutableListOf(
"lottie/dance/chicken_6.json",
"lottie/dance/sound.json", //White
"lottie/dance/pinguin.json" //White
)
val file = lottieFiles.random()
messageLottie?.setAnimation(file)
if (file == "lottie/dance/pinguin.json"
|| file == "lottie/dance/sound.json"
) {
messageLottie?.addValueCallback(
KeyPath("**"), LottieProperty.COLOR_FILTER,
{ PorterDuffColorFilter(getColor(R.color.white), PorterDuff.Mode.SRC_ATOP) }
)
}
messageLottie?.playAnimation()
}
private fun increaseVolumeOverTime(mediaPlayer: MediaPlayer, shouldVibrate: Boolean) {
mediaPlayer.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
)
currentUserVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
var currentVolume = 1
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0)
if (shouldVibrate) {
startVibrating()
}
timer = Timer()
timer.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
currentVolume += 1
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0)
if (currentVolume % 10 == 0) {
if (shouldVibrate) {
startVibrating(currentVolume)
}
}
if (currentVolume >= 90) this.cancel()
}
}, 0, 2000)
}
private fun startVibrating(currentVolume: Int = 10) {
val vibratorLength = ((50 * currentVolume) / 1.2).roundToInt().toLong()
val patternShort = longArrayOf(1200, vibratorLength)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(patternShort, 0))
} else {
vibrator.vibrate(patternShort, 0)
}
}
private fun initMediaPlayer(alarm: Alarm) {
mediaPlayer = MediaPlayer()
mediaPlayer?.let { increaseVolumeOverTime(it, alarm.shouldVibrate) }
val currentlySelectedPath = alarm.currentlySelectedPath
if (currentlySelectedPath == null) {
var uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
}
}
mediaPlayer?.setDataSource(this, uri)
} else {
mediaPlayer?.setDataSource(this, Uri.parse(currentlySelectedPath))
}
mediaPlayer?.isLooping = false
mediaPlayer?.setOnCompletionListener {
it?.stop()
it?.reset()
it?.isLooping = false
val path = alarm.songsList?.random()?.path
it?.setDataSource(this, Uri.parse(path))
alarmID?.let { it1 -> DataHelper.nextRandomSong(it1, path) }
it?.setOnPreparedListener {
mediaPlayer?.start()
}
it?.prepareAsync()
}
mediaPlayer?.setOnPreparedListener {
if (alarm.shouldResumePlaying) {
mediaPlayer?.seekTo(alarm.secondsPlayed)
}
mediaPlayer?.start()
}
mediaPlayer?.prepareAsync()
shouldResumePlaying = alarm.shouldResumePlaying
}
companion object {
const val ARG_ALARM_ID = "AlarmID"
fun getIntent(context: Context, alarmID: Int?): Intent {
val intent = Intent(context, TriggeredAlarmActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY) //If it doesn't hide in recent use or Intent.FLAG_ACTIVITY_CLEAR_TASK
intent.putExtra(ARG_ALARM_ID, alarmID)
return intent
}
}
}
I want to detect if the user turned off the location at runtime. I can check if he turns it on or if the location was turned off by user before the app was started but I can't check if he turned it off after.
Code Sample:
MapEntity extends LocationListener
class MapViewer(a: MainActivity, parentView: ViewGroup) : MapEntity(a, parentView) {
override fun onProviderEnabled(provider: String?) {
activity.hideGpsSnackbar()
}
override fun onProviderDisabled(provider: String?) {
activity.showGpsSnackbar()
}
}
For realtime GPS location checking, I'm using GnssStatus.Callback()
UPDATE:
I've created BroadcastReceiver according to the answer below.
abstract class GPSReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
try {
val locationManager = context.getSystemService(LOCATION_SERVICE) as LocationManager
if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
onGpsChanged(true)
} else {
onGpsChanged(false)
}
} catch (ex: Exception) {
App.log("IsGPSEnabled: $ex")
}
}
abstract fun onGpsChanged(isEnabled: Boolean)
}
Code inside one of my Activities:
private val gpsStatusReceiver = object : GPSReceiver() {
override fun onGpsChanged(isEnabled: Boolean) {
if (isEnabled){
hideGpsSnackbar()
} else {
showGpsSnackbar()
}
}
}
override fun onStart() {
super.onStart()
registerReceiver(gpsStatusReceiver, IntentFilter())
}
override fun onStop() {
super.onStop()
unregisterReceiver(gpsStatusReceiver)
}
UPDATE
If you want to support Android 6.0, you cannot use abstract class. Because it will try to create object out of this class defined in AndroidManifest. Android 8.0+ will not check receiver inside AndroidManifest so you can instantiate object out of Abstract Class. So instead of it create interface.
I'm actually doing it with a BroadcastReceiver.
I can share my code; it's java but I think you can easily convert it into kotlin.
Create a Broadcastreceiver
Example:
public class GPSBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
LocationManager locationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
//isGPSEnabled = true;
} else {
//isGPSEnabled = false;
}
}catch (Exception ex){
}
}
}
Add your BroadcastReceiver to the manifest
Example:
<receiver android:name=".others.GPSBroadcastReceiver">
<intent-filter>
<action android:name="android.location.PROVIDERS_CHANGED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
Then (for my case) I manage it in my ApplicationContext as follows:
private GPSBroadcastReceiver gpsBroadcastReceiver = new GPSBroadcastReceiver();
#Override
public void onCreate() {
....
registerReceiver(gpsBroadcastReceiver, new IntentFilter());
}
#Override
public void onTerminate() {
...
unregisterReceiver(gpsBroadcastReceiver);
}
That's just an example, there might be other ways for it but actually I'm fine with that one; good luck!
Edit:
try adding this permission in your manifest
<uses-permission android:name="android.permission.ACCESS_GPS" />
Edit:
For Android 8.0+ register your BroadcastReceiver like this:
registerReceiver(gpsBroadcastReceiver,IntentFilter("android.location.PROVIDERS_CHANGED"))
Adding action inside AndroidManifest will not work.
You can use Broadcast Recievers for that purpose that will be triggered when state has changed.
Check this Answer
Please use the following approach to enable location settings:
LocationServices
.getSettingsClient(this)
.checkLocationSettings(mLocationSettingsRequest)
.addOnSuccessListener(this, object : OnSuccessListener<LocationSettingsResponse> {
override fun onSuccess(locationSettingsResponse: LocationSettingsResponse) {
Log.i(TAG, "LocationManager: All location settings are satisfied.");
mLocationCallback?.let {
fusedLocationClient?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
}
//updateUI();
}
})
.addOnFailureListener(this, object : OnFailureListener {
override fun onFailure(e: Exception) {
var statusCode = (e as ApiException).getStatusCode()
when (statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
try {
// Show the dialog by calling startResolutionForResult(), and check the
// result in onActivityResult().
var rae = e as ResolvableApiException;
rae.startResolutionForResult(this#BaseLocationActivity, REQUEST_CHECK_SETTINGS);
} catch (sie: IntentSender.SendIntentException) {
Log.i(TAG, "PendingIntent unable to execute request.");
}
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
mRequestingLocationUpdates = false;
}
}
}
})
Create location request
private fun createLocationRequest(interval: Long, distance: Float = 0f) {
mLocationRequest = LocationRequest()
mLocationRequest.interval = interval
mLocationRequest.fastestInterval = interval
Log.v(TAG, "distance => $distance")
if (distance > 0) {
mLocationRequest.smallestDisplacement = distance
}
mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
<application>
<receiver android:name=".MyBroadcastReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_SCREEN_ON"></action>
<action android:name="android.intent.action.ACTION_SCREEN_OFF"></action>
</intent-filter>
</receiver>
...
</application>
MyBroadcastReceiver is set just to spit foo to the logs. Does nothing.
Any suggestions please? Do I need to assign any permissions to catch the intent?
I believe that those actions can only be received by receivers registered in Java code (via registerReceiver()) rather than through receivers registered in the manifest.
Alternatively you can use the power manager to detect screen locking.
#Override
protected void onPause()
{
super.onPause();
// If the screen is off then the device has been locked
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
boolean isScreenOn;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
isScreenOn = powerManager.isInteractive();
} else {
isScreenOn = powerManager.isScreenOn();
}
if (!isScreenOn) {
// The screen has been locked
// do stuff...
}
}
"android.intent.action.HEADSET_PLUG"
"android.intent.action.ACTION_SCREEN_ON"
"android.intent.action.ACTION_SCREEN_OFF"
Three of them above, They cannot register using Manifest.
Android core added "Intent.FLAG_RECEIVER_REGISTERED_ONLY" to them (maybe.. I checked codes only in case of "HEADSET_PLUG".
So, We should use "dynamic register".
Like below...
private BroadcastReceiver mPowerKeyReceiver = null;
private void registBroadcastReceiver() {
final IntentFilter theFilter = new IntentFilter();
/** System Defined Broadcast */
theFilter.addAction(Intent.ACTION_SCREEN_ON);
theFilter.addAction(Intent.ACTION_SCREEN_OFF);
mPowerKeyReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String strAction = intent.getAction();
if (strAction.equals(Intent.ACTION_SCREEN_OFF) || strAction.equals(Intent.ACTION_SCREEN_ON)) {
// > Your playground~!
}
}
};
getApplicationContext().registerReceiver(mPowerKeyReceiver, theFilter);
}
private void unregisterReceiver() {
int apiLevel = Build.VERSION.SDK_INT;
if (apiLevel >= 7) {
try {
getApplicationContext().unregisterReceiver(mPowerKeyReceiver);
}
catch (IllegalArgumentException e) {
mPowerKeyReceiver = null;
}
}
else {
getApplicationContext().unregisterReceiver(mPowerKeyReceiver);
mPowerKeyReceiver = null;
}
}
The way I implemented this is by registering the receiver in my main activity in onCreate(), just define the receiver somewhere beforehand:
lockScreenReceiver = new LockScreenReceiver();
IntentFilter lockFilter = new IntentFilter();
lockFilter.addAction(Intent.ACTION_SCREEN_ON);
lockFilter.addAction(Intent.ACTION_SCREEN_OFF);
lockFilter.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(lockScreenReceiver, lockFilter);
And then onDestroy():
unregisterReceiver(lockScreenReceiver);
In receiver you must catch the following cases:
public class LockScreenReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent != null && intent.getAction() != null)
{
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON))
{
// Screen is on but not unlocked (if any locking mechanism present)
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF))
{
// Screen is locked
}
else if (intent.getAction().equals(Intent.ACTION_USER_PRESENT))
{
// Screen is unlocked
}
}
}
}
Here is the kotlin version of #cmcromance (Thanks for your answer. Please don't forget to upvote the original answer)
private var mPowerKeyReceiver: BroadcastReceiver? = null
private fun registBroadcastReceiver() {
val theFilter = IntentFilter()
/** System Defined Broadcast */
theFilter.addAction(Intent.ACTION_SCREEN_ON)
theFilter.addAction(Intent.ACTION_SCREEN_OFF)
mPowerKeyReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.e("onReceive", "onReceive called")
val strAction = intent!!.action
// if (strAction == Intent.ACTION_SCREEN_OFF || strAction == Intent.ACTION_SCREEN_ON) {
if (strAction == Intent.ACTION_SCREEN_ON) {
// > Your playground~!
Log.e("strAction", strAction)
val intent = Intent(context, SplashScreenMainAppActivity::class.java)
startActivity(intent)
}
}
}
applicationContext.registerReceiver(mPowerKeyReceiver, theFilter)
}
private fun unregisterReceiver() {
val apiLevel = Build.VERSION.SDK_INT
if (apiLevel >= 7) {
try {
applicationContext.unregisterReceiver(mPowerKeyReceiver)
} catch (e: IllegalArgumentException) {
mPowerKeyReceiver = null
}
} else {
applicationContext.unregisterReceiver(mPowerKeyReceiver)
mPowerKeyReceiver = null
}
}