Detecting location settings change from quick settings - java

I'm currently developing an app that requires the Android's location service to always be active.
Right now I'm able to detect if the user activates or deactivates the location service when he resumes the app (in the onResume() event ).
The problem is that, if the user deactivates the location service throught the quick settings menu, I am not able to detect the event.
How can I know when the user activates / deactivates the device's location through the quick settings?
Thank you.
My code:
...
BroadcastReceiver mGpsSwitchStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null && intent.getAction().matches("android.location.PROVIDERS_CHANGED")) {
// Make an action or refresh an already managed state.
Log.d("vtApp", "CHANGED");
}
}
};
currentActivity.registerReceiver(mGpsSwitchStateReceiver, new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION));
References I followed:
How to trigger broadcast receiver when gps is turn on/off?
Android Quick Settings notifications?
Changing Location Settings

I had the same problem.
this worked for me
intentFilter.addAction(android.location.LocationManager.PROVIDERS_CHANGED_ACTION);
not to confuse it with
Intent.ACTION_PROVIDER_CHANGED

Related

What may cause an app to not detect properly a home button press soemtimes?

I've an app which on a home button press it performs a certain action, most of the time the press is detected and the action is successfully performed.
But sometimes the press is not detected for no apparent reason. The press is detected via this receiver:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
[...]
if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {
mListener.onHomePressed();
[...]
}
}
}
}
}
Why could such a thing happen? Is there any way so if it gets pressed it always fires that onReceive?
Why could such a thing happen?
You will not receive that broadcast if your app is in the background and you registered the receiver via registerReceiver(). Also, I do not know if there is a requirement for all devices to send that broadcast. Plus, there are devices that do not have a HOME button (e.g., Chrome OS).
Is there any way so if it gets pressed it always fires that onReceive?
Not that I am aware of.
I would focus far more on removing this functionality. Your app is more likely to be compatible with more devices that way.

How to restart app and clear all data with Button [duplicate]

My current Android Application needs to call
ActivityManager.clearApplicationUserData()
to simulate the user clearing App storage
Which works fine.
A side effect of calling clearApplicationUserData() is that the App is (understandably) closed.
Which gives a poor user experience.
I am having difficulty in restarting my Application once I have called clearApplicationUserData().
I have tried using startActivity, Alarm Manager with Pending Intent, Foreground/Background service.
Nothing works.
Is it impossible to restart an Android App having called clearApplicationUserData()?
(1st answer: this answer only works on limited situations. it's not a complete answer)
public boolean clearApplicationUserData ()
Description
Return: true if the application successfully requested that the application's data be erased; false otherwise.
As the reference website stated, we have a returnee before the application is being closed. so, we are going to use this returnee in order to restart the app.
if(ActivityManager.clearApplicationUserData)
{
doRestart = true;
}
when Activity onDestroy() and onStop() are called restart app.
#Override
protected void onDestroy() {
super.onDestroy();
if(doRestart){
Intent intent = new Intent(this, Activity.class);
this.startActivity(intent);
}
}
#Override
protected void onStop() {
super.onStop();
if(doRestart){
Intent intent = new Intent(this, Activity.class);
this.startActivity(intent);
}
}
We put restart action in both onDestroy() and onStop() in order to make sure the app will be restarted again.
And also, I think it's a good idea to force stop activity before OS stops it.
if(ActivityManager.clearApplicationUserData)
{
doRestart = true;
finish(); <= i mean this
}
it's because it makes sure that onDestroy() and onStop() will be invoked.
My suggestion might sound trivial but, have you consider not calling ActivityManager.clearApplicationUserData()?
Here what the docs says about this method:
Permits an application to erase its own data from disk. This is
equivalent to the user choosing to clear the app's data from within
the device settings UI. It erases all dynamic data associated with the
app -- its private data and data in its private area on external
storage -- but does not remove the installed application itself, nor
any OBB files.
So in order to mimic this behavior you just need to clear you internal and external storage directories. No permissions are needed to access any of those.
(2nd answer: I need much more contribution on it)
After 8 hours of researching in Android OS and Android Developers Website in order to find a solution to restart activity when clearApplicationUserData is invoked. Finally, I would be able to find a nice/hacking solution.
This solution looks like Zidane dribble :)
Let's introduce the solution. at first, clearApplicationUserData clears all the clues of the application when is invoked like tasks, notifications, alarms and etc. therefore, explicit Activity calling is impossible.
implicit way is the only possible way of calling activity.
After a couple of tests I found that application manifest registered intent-filters wouldn't be removed and they are able to listen for incoming system broadcasts.
Approximately, 98% of system broadcasts wouldn't be received by cleared application and that 2% remained might not be broadcasted very soon.
so what to do? hmmm? come on man I must find a solution ...
bingo, ** I must trigger something in order to system broadcast it** <= looks hacking :)
so I decide to choose WIFI_STATE_CHANGED because
Easy Access permission
System Broadcasts it on delay <= this makes sure that the app is closed
before broadcast
manifest.xml
<receiver
android:name=".PackageDataClearedReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
MainActivity.java
public class MainActivity extends AppCompatActivity {
ActivityManager am;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatButton btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new Thread(new Runnable() {
#Override
public void run() {
am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if (am != null) {
ExecutorService pool = Executors.newFixedThreadPool(2);
final Collection<Future> futures = new HashSet<Future>();
futures.add(pool.submit(new Runnable() {
#Override
public void run() {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(true);
wifiManager.setWifiEnabled(false);
am.clearApplicationUserData();
}
}));
for (Future future : futures) {
future.isDone();
}
}
}
}).start();
}
});
}
}
demo
keep in mind, it's just A minimum viable product that needs to be developed more in order to make it work perfectly.
Clearing the app data on the device through the API clearApplicationUserData() resets the app as if it were just installed. As you have found, any alarms and broadcasts registered with your app are also cleared. The most efficient way to keep your app in the foreground would be to clear the data yourself, as others have pointed out, rather than using the API. Here is an example: https://stackoverflow.com/a/9073473/949224
However, if you are determined to use the API (which does guarantee all data is cleared) and the app is force-stopped, I have a suggestion:
Create a small companion app that can be launched just prior to you clearing your app data. The companion app simply re-launches your app, possibly after a short time-out.
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.testrelaunchapp");
if (launchIntent != null) {
startActivity(launchIntent);//null pointer check in case package name was not found
} else {
Log.w( TAG, "Unable to resolve launch activity of relauncher companion app");
}
((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE))
.clearApplicationUserData();
The companion app itself needs to close afterwards, and ideally should be hidden from the Activity Stack etc..
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.yourmainapp");
if (launchIntent != null) {
Handler handler = new Handler(getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.i( TAG, "About to act on launchIntent");
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(launchIntent);
finish();
System.exit(0);
}
}, 1000);
}
}
I have seen this work with Android 6.0, but no guarantees it would be versatile and work across the board. There would be more to do to make the companion app UI-less if desired and to be hidden from the phone's launcher. You would also probably want to bundle the APK as a file within your own app and install it upon first run, which would need the user to enable installation from "Unknown sources" (non-Play store). It can be done through intents to the right System settings, if needed, but users would need a good explanation why this is needed.
So, as I was saying, the simpler approach is to clear the data and app permissions yourself.

Android: BroadcastReceiver: UNINSTALL_PACKAGE intent

I need to detect when my app is being uninstalled. For that, I've seen logcat sends out UNINSTALL_PACKAGE intent, and I simply added it to existing Broadcast Receiver I have. But it just doesn't catch it, while other intent I'm listening to, TIME_TICK, works perfectly. Why?
Code currently used:
private IntentFilter intentFilter;
static {
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_TICK);
intentFilter.addAction(Intent.ACTION_UNINSTALL_PACKAGE);
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_UNINSTALL_PACKAGE) {
Log.e("App", "Uninstalling");
} else if (action.equals(Intent.ACTION_TIME_TICK){
// this works
}
}
};
I need to detect when my app is being uninstalled
There is no supported way to do this, for obvious security reasons.
But it just doesn't catch it
ACTION_UNINSTALL_PACKAGE is an activity action. Nothing on an Android device should be sending it as a broadcast. Hence, you cannot listen to it via a BroadcastReceiver.
I 've seen logcat sends out UNINSTALL_PACKAGE intent, and I simply added it to existing Broadcast Reciever I have. But it just doesn't catch it, while other intent I'm listening to, TIME_TICK, works perfectly. Why?
Whenever your application is being uninstalled, Android OS kills all the components associated with that application as well free memory & resources. Uninstall broadcast receiver intent was to listen uninstall event of others app not for your app.
If you want to capture uninstall event of your app then there is no concept google provides, however you can do by observing data change in your file system where you need to keep monitoring changes in file system.
This solution is not going to work for all phones & different versions of Android.
Can't you just listen for android.intent.action.PACKAGE_REMOVED with your IntentFilter when you uninstall using ACTION_DELETE or ACTION_UNINSTALL_PACKAGE?

Prevent app from running on two instances

I just recently added the capability of my app to check for updates on our local server (this app is not published in the Google Play store. It's going to be used internally and there is no internet connection where it will be used. Don't ask. That's how it is :) ). I keep track of updates by checking a certain table in SQL Server and if my app's version is lower than what is indicated in this table, I download the new APK from an internal website (LAN only) then install the APK. I also have another application in the device that listens for PACKAGE_ADDED broadcasts. I can capture the broadcast successfully.
The problem is, after installation, the broadcast receiver starts the app by calling the following.
public class PackageInstalledBroadcastReceiver extends BroadcastReceiver {
private final String MY_PACKAGE_NAME = "com.company.packagename";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
String packageName = intent.getData().getSchemeSpecificPart();
if (packageName.equalsIgnoreCase(MY_PACKAGE_NAME)) {
Intent i = new Intent();
i.setClassName(MY_PACKAGE_NAME, MY_PACKAGE_NAME + ".LoginActivity");
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
}
}
But it takes a long time for the app to start. The user might think that nothing is happening, so he/she can start the app manually by clicking the app's icon. If the user clicks the app's icon, the app starts immediately. After a while, the activity that the broadcast receiver started is also opened. So now, I end up with two instances of my app in the same activity (LoginActivity). I can say that this is the case, because if I press the Back key on the device from the LoginActivity, I still end up in another LoginActivity and if I press the Back key again, I end up on the device's desktop.
You have two problem in your question:
The fist, why your BroadcastReceiver take a long time to start your activity.
It have not enough information for stoving this.
The second your want to make your activity have a single instance.
Android provide a way to do that:
Step one: Visit your application androidmanifest file
Step two: Find your activity declaration.
Step there: Add the following property android:launchMode = "singleInstance"
The reference here:
P/s: If you could provide my some more information of your fist problem. Please create a new question. Hope you resolve it.

Check if downloads are active

So I'm using Download Manager to download multiple files in my app. I need these files to finish downloading before starting a certain activity. How can I check if there are active downloads, so I can tell the user to wait until the downloads are finished. And then, when they are finished, I need to make a button visible. I've googled this, even tried some code myself(blindly) and nothing works. If somebody can nudge me in the right direction I'd be grateful.
Use query() to inquire about downloads. When you call enqueue(), the return value is an ID for the download. You can query by status as well:
Cursor c = downloadManager.query(new DownloadManager.Query()
.setFilterByStatus(DownloadManager.STATUS_PAUSED
| DownloadManager.STATUS_PENDING
| DownloadManager.STATUS_RUNNING));
To be notified when a download is finished, register a BroadcastReceiver for ACTION_DOWNLOAD_COMPLETE:
BroadcastReceiver onComplete = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do something
}
};
registerReceiver(onComplete, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
Note that you should also listen for the ACTION_NOTIFICATION_CLICKED broadcast to know when a user has clicked the notification for a running download.

Categories

Resources