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.
Related
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.
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
I have a service which controls my mediaplayer object and when i close my app, a notification is still shown to control playback.
Now when a song is done playing i want update the UI in my activity and i did this with a broadcastreceiver, but this only works when my app is visible and not in the background/closed. (unregistered broadcastreceiver in onPause)
But how do i keep listening for these events when my application is not visible and when the user opens my application again it has the updated UI (new song).
Service
#Override
public void onCompletion(MediaPlayer mp) {
Log.d(TAG, "OnCompletion called!");
Intent broadCastReceiverIntentUpdateSong = new Intent(Constants.ACTIONS.BROADCAST_UPDATE_SONG);
sendBroadcast(broadCastReceiverIntentUpdateSong);
}
When your app starts, it should ask the Service for the current state of the player and show that.
While the app is running and in the foreground, it can listen for the broadcast events and update the UI (or its own internal state) accordingly.
When your app goes to the background, it doesn't need to do anything. When it comes again to the foreground (in onResume()) it can again ask the Servicefor the current state of the player.
You can have the Activity bind to the Service and use AIDL to get the current state OR you can just call startService() with an Intent that contains an ACTION or an EXTRA that indicates that you want to know the current state, and the Service can ract to that by sending a broadcast Intent containing the current state, which your Activity can listen for.
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?
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.