Android 12 Location updates not working when app is on background. and work fine when app is on forground.
I also "allow all time" permission for for location. Acc to docs after this location updates start work on background. but in my case it stop updates when app is on foreground.
locationUpdatePendingIntent
private val locationUpdatePendingIntent: PendingIntent by lazy {
val intent = Intent(context, LocationUpdatesBroadcastReceiver::class.java)
intent.action = LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES
PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_MUTABLE)
}
Firstly I use Location Request. inside Automatic Workmanager
val mLocationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 0L)
.setWaitForAccurateLocation(false)
.build()
if (ActivityCompat.checkSelfPermission(
context.applicationContext,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return Result.success();
}
fusedLocationProviderClient?.requestLocationUpdates(
mLocationRequest,
locationUpdatePendingIntent
)
then listen to location updates used inside Automatic Workmanager
override fun onLocationUpdate(location: Location) {
val executor: ExecutorService = Executors.newSingleThreadExecutor()
executor.execute {
lastLocation = location
updateLocation()
}
}
Location update broadcast (in this location update)
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.location.Location
import android.util.Log
import com.google.android.gms.location.LocationAvailability
import com.google.android.gms.location.LocationResult
class LocationUpdatesBroadcastReceiver: BroadcastReceiver() {
private val TAG = "LocationUpdatesBroadcas";
override fun onReceive(context: Context?, intent: Intent?) {
Log.d(TAG, "onReceive() context:$context, intent:$intent")
if (intent?.action == ACTION_PROCESS_UPDATES) {
// Checks for location availability changes.
LocationAvailability.extractLocationAvailability(intent)?.let { locationAvailability ->
if (!locationAvailability.isLocationAvailable) {
Log.d(TAG, "Location services are no longer available!")
}
}
LocationResult.extractResult(intent)?.let { locationResult ->
Log.e(TAG, "onReceive: lastLocation ${locationResult.locations.size}", )
if(locationResult.lastLocation != null){
ShareLocationToAutomaticWorker.INSTANCE?.reciveLocation(locationResult.lastLocation!!)
}
}
}
}
companion object {
const val ACTION_PROCESS_UPDATES =
"action." +
"PROCESS_UPDATES"
}
interface UpdatedLocation{
fun onLocationUpdate(location: Location)
}
}
class ShareLocationToAutomaticWorker(var updateLocation: LocationUpdatesBroadcastReceiver.UpdatedLocation){
private val TAG = "LocationUpdatesBroadcas"
fun reciveLocation( location:Location){
Log.e(TAG, "reciveLocation:--- ${location} " )
updateLocation.onLocationUpdate(location)
}
companion object {
var INSTANCE: ShareLocationToAutomaticWorker? = null
fun getInstance(updateLocation: LocationUpdatesBroadcastReceiver.UpdatedLocation): ShareLocationToAutomaticWorker {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: ShareLocationToAutomaticWorker(
updateLocation )
.also { INSTANCE = it }
}
}
}
}
I fixed this issue by changing "AutomaticLocationWorker" to "AutomaticLocationBackgroundService:LifecycleService()" or change worker to lifecycleService.
private var fusedLocationProviderClient: FusedLocationProviderClient? = null
private val locationUpdatePendingIntent: PendingIntent by lazy {
val intent = Intent(this, LocationUpdatesBroadcastReceiver::class.java)
intent.action = LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_MUTABLE)
}
fusedLocationProviderClient?.requestLocationUpdates(
mLocationRequest,
locationUpdatePendingIntent
)
LocationUpdatesBroadcastReceiver
class LocationUpdatesBroadcastReceiver: BroadcastReceiver() {
private val TAG = "LocationUpdatesBroadcas";
override fun onReceive(context: Context?, intent: Intent?) {
Log.d(TAG, "onReceive() context:$context, intent:$intent")
if (intent?.action == ACTION_PROCESS_UPDATES) {
// Checks for location availability changes.
LocationAvailability.extractLocationAvailability(intent)?.let { locationAvailability ->
if (!locationAvailability.isLocationAvailable) {
Log.d(TAG, "Location services are no longer available!")
}
}
LocationResult.extractResult(intent)?.let { locationResult ->
Log.e(TAG, "onReceive: lastLocation ${locationResult.locations.size}", )
if(locationResult.lastLocation != null){
ShareLocationToAutomaticWorker.INSTANCE?.reciveLocation(locationResult.lastLocation!!)
}
}
}
}
companion object {
const val ACTION_PROCESS_UPDATES =
"com.commuteoptm.bcos.background_services.action." +
"PROCESS_UPDATES"
}
interface UpdatedLocation{
fun onLocationUpdate(location: Location)
}
}
ShareLocationToAutomaticWorker
class ShareLocationToAutomaticWorker(var updateLocation: LocationUpdatesBroadcastReceiver.UpdatedLocation){
private val TAG = "LocationUpdatesBroadcas"
fun reciveLocation( location:Location){
Log.e(TAG, "reciveLocation:--- ${location} " )
updateLocation.onLocationUpdate(location)
}
companion object {
var INSTANCE: ShareLocationToAutomaticWorker? = null
fun getInstance(updateLocation: LocationUpdatesBroadcastReceiver.UpdatedLocation): ShareLocationToAutomaticWorker {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: ShareLocationToAutomaticWorker(
updateLocation )
.also { INSTANCE = it }
}
}
}
}
Related
i wrote a class for requesting permissions for reading/writing storage i think this is not working correctly, everytime i open my app settings will be opened and ask user for granting permission (permission alrady granted)
since we know that we have permissions for read/write we can create directory or do nothing
` private lateinit var context : Context
private lateinit var activity: Activity
private val READ_PERMISSION_CODE = 1
fun requestPermissions(activity : Activity, context: Context) {
this.activity = activity
this.context = context
activity.askForPermissions(
Permission.ACCESS_COARSE_LOCATION,
Permission.READ_EXTERNAL_STORAGE,
Permission.WRITE_EXTERNAL_STORAGE
){ result->
if ( result.isAllGranted(Permission.WRITE_EXTERNAL_STORAGE, Permission.READ_EXTERNAL_STORAGE)) {
createNewDirectory()
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (!result.isAllGranted(Permission.READ_EXTERNAL_STORAGE) && !result.isAllGranted(Permission.WRITE_EXTERNAL_STORAGE)) {
requestLegacyPermission()
}
}
}
}
}
private fun requestLegacyPermission() {
val intent : Intent
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
intent = Intent(ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.setData(Uri.parse("package:${BuildConfig.APPLICATION_ID}"))
}else{
intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
}
activity.startActivityForResult(intent, READ_PERMISSION_CODE)
}
private fun createNewDirectory() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val path = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
val newDirectory = File(path, Constants.baseDirectory)
if (!newDirectory.exists()) {
newDirectory.mkdirs()
}
} else {
val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val newDirectory = File(path, Constants.baseDirectory)
if (!newDirectory.exists()) {
newDirectory.mkdirs()
}
}
}`
I'm facing following Errors/Exceptions when receiving data from one App to another App in Kotlin (Android):
Class Not Found When Unmarshalling: com.example.Parcelable.User
mExtras=Bundle[{)}]
mParcelledData=null
mMap=size 0
Also, In MainActivtiy: if(intent.hasExtra("USER")) statement giving error and displaying Toast message written in Else statement.
Please help me how I can resolve these exceptions ? Thanks a Lot!
(SendingApp File) -> MainActivity.kt
btnSendData1.setOnClickListener {
val intent = Intent(Intent.ACTION_SEND).setType("text/plain")
intent.component =
ComponentName.unflattenFromString("com.example.receive")
val args = Bundle()
args.putParcelable(USER_KEY, User("Ahmer", "28"))
intent.putExtras(args)
Log.d("Ahmer", args.toString())
startActivity(Intent.createChooser(intent, "Share to..."))
}
(ReceivingApp File) -> MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var textView: TextView
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.tv_username)
when (intent?.action) {
Intent.ACTION_SEND -> {
if ("text/plain" == intent.type) {
handleSendText(intent)
} else {
Toast.makeText(this, "No Text", Toast.LENGTH_SHORT).show()
}
}
}
}
#SuppressLint("SetTextI18n")
private fun handleSendText(intent: Intent) {
if (intent.extras != null) {
if (intent.hasExtra("USER")) {
Log.d("Intent", intent.toString())
val bundle = intent.extras
val user = bundle?.getParcelable<User>("USER") as User
textView.text = "${user.name}${user.age}"
} else {
Toast.makeText(this, "Has Extras Null", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, "Extras Null", Toast.LENGTH_SHORT).show()
}
}
}
(ReceivingApp File) -> User.kt
package com.example.receive
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
#Parcelize
data class User(
var name: String,
var age: String
) : Parcelable
I am making an SOS application that, when pressing the power button three times, should make a call to a pre-defined number and send an SOS message to the specified numbers, the problem is that it sees that the power button is pressed and sends a message but does not make a call over the lock.
MyReceiver:
class MyReceiver(activity: Activity?) : BroadcastReceiver() {
private var activity: Activity? = null
var t1 = System.currentTimeMillis()
var t2 = System.currentTimeMillis()
var thread: Thread? = null
override fun onReceive(context: Context, intent: Intent) {
Log.v("onReceive", "Power button is pressed.")
val a = countPowerOff + 1
if (intent.action == Intent.ACTION_SCREEN_OFF) {
countPowerOff++
if (countPowerOff == 1) t1 = System.currentTimeMillis()
if (countPowerOff == 3) t2 = System.currentTimeMillis()
} else {
if (intent.action == Intent.ACTION_SCREEN_ON) {
if (countPowerOff == 3) {
Toast.makeText(context, "Clicked to call", Toast.LENGTH_LONG)
.show()
}
if (countPowerOff == 3 && t2 - t1 <= 5000) {
thread = Thread(Task())
thread!!.start()
}
if (countPowerOff == 3) {
countPowerOff = 0
}
}
}
}
internal inner class Task : Runnable {
override fun run() {
(activity as MainActivity).callFragmentMethod()
}
}
companion object {
var countPowerOff = 0
var starttime = 0
}
init {
this.activity = activity
}
}
Method for making a call and sending a message:
fun sendSos() {
val manager = context!!.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
sosMassage =
"I need help, I'm at Latitude: ${currentLocation.latitude} and Longitude ${currentLocation.longitude} https://www.google.com/maps/search/?api=1&query=${currentLocation.latitude},${currentLocation.longitude}"
}
threadCall = Thread(callSOS())
threadCall!!.start()
threadMessage = Thread(sendSOS())
threadMessage!!.start()
}
internal inner class callSOS : Runnable {
override fun run() {
checkForCallSmsPermission()
val intent = Intent(Intent.ACTION_CALL)
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
val callPreferences = prefs.getString("call_preference", "1")
if (callPreferences == "1") {
intent.setData(Uri.parse("tel:103"))
} else {
intent.setData(Uri.parse("tel:${prefs.getString("contact_text_1", "103")}"))
}
startActivity(intent)
}
}
internal inner class sendSOS : Runnable {
override fun run() {
checkForSmsPermission()
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
val sms = SmsManager.getDefault()
val textPreferences = prefs.getString("text_preference", "1")
val primaryContact = prefs.getString("contact_text_1", "0701217070")
sms.sendTextMessage(
primaryContact,
null,
sosMassage,
null,
null
)
if (textPreferences == "2") {
for (i in 2..3) {
val contactNumber = prefs.getString("contact_text_$i", null)
if (contactNumber != null) {
sms.sendTextMessage(
contactNumber,
null,
sosMassage,
null,
null
)
}
}
}
}
}
I am building an application which shows overlay over another application like a app locker do. but its not like it. I want to show a dialog on an application which exists on database every time i open the application. I have made a service which continuously checks for which application is in foreground using a timer task set at delay and period of 1sec.
The problem is that it shows the dialog only one time on every application due to one statement inside the timer task run function. How can i make it work so that it always shows dialog on the application. Any alternative or optimizations?
import android.app.ActivityManager
import android.app.Dialog
import android.app.Service
import android.app.usage.UsageStats
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Build
import android.os.IBinder
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.widget.ImageView
import androidx.annotation.RequiresApi
import app.privvio.android.policysplash.PolicyDialog
import app.privvio.android.preference.GetFirebaseAllApps
import java.util.*
import kotlin.collections.ArrayList
class AppCheckServices : Service() {
private var context: Context? = null
var imageView: ImageView? = null
private var windowManager: WindowManager? = null
private var dialog: Dialog? = null
var dbPackageName: ArrayList<String> = GetFirebaseAllApps().getAppsUploaded()
override fun onCreate() {
super.onCreate()
context = applicationContext
/** Timer started for long time **/
timer = Timer("AppCheckServices")
timer!!.scheduleAtFixedRate(updateTask, 1000L, 1000L)
/** Window Manager posts a Blank Image
* view with transparent background which blocks the view
* and the policy dialog is shown over it
* **/
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
imageView = ImageView(this)
imageView!!.visibility = View.GONE
val LAYOUT_FLAG: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
LAYOUT_FLAG,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT)
params.gravity = Gravity.TOP or Gravity.CENTER
params.x = applicationContext.resources.displayMetrics.widthPixels / 2
params.y = applicationContext.resources.displayMetrics.heightPixels / 2
windowManager!!.addView(imageView, params)
}
private val updateTask: TimerTask = object : TimerTask() {
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
override fun run() {
currentApp = ForegroundApps
if (dbPackageName.contains(ForegroundApps)) {
Log.d("DBAppFRGRND", "true : The application is in foreground.")
if (imageView != null) {
imageView!!.post {
if (currentApp != previousApp) {
showPolicyDialog()
previousApp = currentApp
} else {
Log.d("AppCheckService", "currentApp matches previous App")
}
}
}
} else {
Log.d("DBAppFRGRND", "false : The application is not uploaded to the server. ")
if (imageView != null) {
imageView!!.post {
hidePolicyDialog();
}
}
}
}
}
/** This section tells whether an application is in foreground or background */
val ForegroundApps: String?
#RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
get() {
var mpackageName: String? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
val usm = this.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val time = System.currentTimeMillis()
val appList: List<UsageStats>? = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, time)
if (appList != null && appList.isNotEmpty()) {
val sortedMap = TreeMap<Long, UsageStats>()
for (usageStats in appList) {
sortedMap[usageStats.lastTimeUsed] = usageStats
}
mpackageName = sortedMap.takeIf { it.isNotEmpty() }?.lastEntry()?.value?.packageName
Log.d(TAG, "isEmpty No : $mpackageName")
} else {
Log.d(TAG, "isEmpty Yes")
mpackageName = ""
}
} else {
val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
#Suppress("DEPRECATION") //The deprecated method is used for devices running an API lower than LOLLIPOP
mpackageName = am.getRunningTasks(5)[0].topActivity?.packageName
Log.d(TAG, "isEmpty No : $mpackageName")
}
return mpackageName
}
fun showPolicyDialog() {
if (context == null) context = applicationContext
PolicyDialog(context!!).showDialog(context!!)
}
fun hidePolicyDialog() {
previousApp = ""
try {
if (dialog != null) {
if (dialog!!.isShowing) {
dialog!!.dismiss()
}
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
/** We want this service to continue running until it is explicitly
* stopped, so return sticky.
*/
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
// timer!!.cancel()
// timer = null
if (imageView != null) {
windowManager!!.removeView(imageView)
}
try {
if (dialog != null) {
if (dialog!!.isShowing) {
dialog!!.dismiss()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
companion object {
const val TAG = "AppCheckServices"
var currentApp: String? = ""
var previousApp: String? = ""
var timer: Timer? = null
}
}
Is it possible to use Android Bundle to create and putString() in Activity then getString() in Service on button click?
If not what can i do?
Example
MainActivity.kt
val bundle = Bundle()
bundle.putString("MyString", "Message")
val mesg = Message.obtain(null, MyService.SEND_MESSAGE_FLAG)
mesg.obj = bundle
try {
myService!!.send(mesg)
} catch (e: RemoteException) {
}
Service
override fun handleMessage(msg: Message) {
when (msg.what) {
SEND_MESSAGE_FLAG -> {
val data = msg.data
val dataString = data.getString("MyString")
println(dataString)
val mesg = Message.obtain(null, SEND_MESSAGE_FLAG)
mesg.obj = dataString
try {
msg.replyTo.send(mesg)
} catch (e: RemoteException) {
Log.i(TAG, "Error: " + e.message)
}
}
}
super.handleMessage(msg)
}
You can add static method in your service:
companion object {
private const val EXTRA_KEY_MY_STR = "EXTRA_KEY_MY_STR"
fun startMyService(context: Context?, myStr: String?) {
if (context != null) {
val intent = Intent(context, MyService::class.java)
intent.putExtra(EXTRA_KEY_MY_STR, myStr)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
}
}
then call it from your activity: MyService.startMyService(this, "MyString")
and then get string in your onHandleIntent(): val myStr = intent?.extras?.getString(EXTRA_KEY_MY_STR)