How to get Application Installation failed event in app - java

I have been using Action_View to install apk using following code
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(Uri.fromFile(new File(location + "myAPK.apk")),
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
It opens the Install Prompt window in device. Now user can install or cancel the installation process.
I am only interested when user click the install package but installation failed due to some reason may be corrupt apk or mismatched signed apk etc.
How can i capture the event when installation failed.. Can i get the result from ACTION_INSTALL_PACKAGE
I have gone through reading System Broadcast Messages but all are used for either Pacakge added or replaced.
Any Clue ?

Quoting the documentation for ACTION_INSTALL_PACKAGE:
Output: If EXTRA_RETURN_RESULT, returns whether the install succeeded.
Quoting the documentation for EXTRA_RETURN_RESULT:
Used as a boolean extra field with ACTION_INSTALL_PACKAGE or ACTION_UNINSTALL_PACKAGE. Specifies that the installer UI should return to the application the result code of the install/uninstall. The returned result code will be RESULT_OK on success or RESULT_FIRST_USER on failure.
So, add EXTRA_RETURN_RESULT to your Intent, with a value of true, and use startActivityForResult().

Launch the Intent with startActivityForResult:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(Uri.fromFile(new File(location + "myAPK.apk")),
"application/vnd.android.package-archive");
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, MY_CONSTANT);
Then analyse the result
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ...
}
}

I only want to add my own considerations and experiences. I had encountered the same problem.
If you want to get the result the best way is that suggested by Nano and CommonsWare.
I want to highlight that if your apk is internally located you may face the problem "parsing the package error". I want remember you that you need the right permission in the manifest <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />.
But I noticed that is also needed add Intent.FLAG_GRANT_READ_URI_PERMISSION.
So the complete example code could be like this mine:
Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", currentFile);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(fileUri,"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, APK_INSTALL_CODE);
Then I would also remember you that this mechanism is all asynchronous, so if you need to know the result to move on, you need to wait it.
I don't know if this is the best way to do but it works:
Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", currentFile);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(fileUri,"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, APK_INSTALL_CODE);
while (WAIT_APK_INSTALL) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == APK_INSTALL_CODE) {
if (resultCode == 1) {
WAIT_APK_INSTALL = false;
//SUCCESS
}
else {
//FAILED
//maybe it's needed crash the app like me. In my case the installation was necessary
Intent intent = new Intent(getBaseContext(), ErrorActivity.class);
intent.putExtra(GlobalStrings.INTENT_EXTRA_MESSAGE, getString(R.string.apk_installation_failed));
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
startActivity(intent);
System.exit(1); // kill off the crashed app
}
}
}

Related

Detect when installation has finished in Intent using ACTION_INSTALL_PACKAGE

I have a launcher type application from which user can install different apps. It is working completely fine but the apk's from different apps just keep stacking up and taking too much space.
Is there a way that I can detect that application installation has completed when using new Intent(Intent.ACTION_INSTALL_PACKAGE) and then I could delete the previously downloaded apk... after the installation has completed.
Here is some code:
Intent openDownloadIntent = getOpenDownloadedApkIntent(context, file);
try {
context.startActivity(openDownloadIntent);
} catch (ActivityNotFoundException e) {
// TODO
}
private static Intent getOpenDownloadedApkIntent(Context context, File file) {
// The type of intent to use to open the downloaded apk changed in Android N (7.0)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri path = FileProvider.getUriForFile(context,
context.getApplicationContext().getPackageName() + ".utils.DownloadedFileProvider",
file);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(path);
return intent;
} else {
Uri uri = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
return intent;
}
}

Getting result from startActivityForResult inside BroadcastReciever, calling ACTION_INSTALL_PACKAGE Intent

I am creating an app from which the user can download other apps and install them. Right now it works fine, but It does not delete the apk after the installation. I have tried using BroadcastRecievers but they do not seem to understand when the application is already installed.
At the moment I am trying to startActivityForResult and once that is finished, delete the apk from Files.
public class Updater {
private static BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Logger.d("Download completed.");
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(), downloadApp.getDownloadKey());
Logger.i("Opening: " + file.getAbsolutePath());
Intent openDownloadIntent = getOpenDownloadedApkIntent(context, file);
RelativeLayout progressView = progressViewReference.get();
if (progressView!= null) {
progressView.setVisibility(View.GONE);
}
try {
((Activity) context).startActivityForResult(openDownloadIntent, getResultCode());
} catch (ActivityNotFoundException e) {
// TODO: more robust error handling (show dialog or something)
Logger.e("Exception when launching download intent, message:" + e.getMessage());
}
}
};
private static Intent getOpenDownloadedApkIntent(Context context, File file) {
// The type of intent to use to open the downloaded apk changed in Android N (7.0)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri path = FileProvider.getUriForFile(context,
context.getApplicationContext().getPackageName() + ".utils.DownloadedFileProvider",
file);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(path);
return intent;
} else {
Uri uri = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
return intent;
}
}
}
So I have the Updater class where the 2 methods are (see my code above). And this class is being called by an adapter, so there is nowhere to put the onActivityResult. I tried placing it in the Activity that calls the Adapter, which calls the Updater class but it does not reach there even with the EXTRA_RETURN_RESULT
So my question is.. how can I call onActivityResult here once the installation is finished.
this is pseudo code for you activity
class DownloadActivity extends AppCompatActivity {
// some of your code here
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
// in here you are checking if requestCode is equal to Intent.ACTION_INSTALL_PACKAGE
if(requestCode == Intent.ACTION_INSTALL_PACKAGE and resultCode == ResultCode.OK) {
// Start activity for deleting file
startActivityForResult(Intente.ACTION_DELETE);
}
if(requestCode == Intent.ACTION_DELETE and resultCode == ResultCode.OK) {
// Handling actions after deleted
}
}
}
OnActivityResult is called by android, when action is ended. You do not have to call this method only inside this method you define what have to be done.
Where those onDownloadComplete and getOpenDownloadedApkIntent are defined and called?

How to get file path for pdf in android Marshmallow

I tried to get path from
Intent intent = new Intent();
intent.setType("application/pdf");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Complete action using"), 2);
But I did't get path from Marshamallow, I can get path from lollipop.
How I can get file path from internal storage?
I think you will have to use file provider for that.
Refer this.
https://developer.android.com/reference/android/support/v4/content/FileProvider
When you start an activity using startActivityForResult, the calling activity will get the results (if any) in onActivityResult. You should override that method in your calling activity...
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
}
This will method will get called as soon as the results are returned from the activity started by startActivityForResult. You can use the requestCode parameter to match (or filter out) the results you are expecting. In your case, you should check that requestCode is equal to 2 which is the value you provided in startActivityForResult.
If the requestCode is equal to 2, then you know that you need to process a PDF file. The intent parameter "could" have the path of that PDF file...
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
if(requestCode == 2 && data != null){
//This is the uri to the file (which could be null)
Uri uri = data.getData();
if(uri != null){
Log.i("LOG_TAG", String.format("uri path = %s", uri));
}
}
}
Now, to browse and select files you will do it the following way (Android Kitkat and above...I think)...
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
startActivityForResult(intent, SELECT_FILE_REQUEST_CODE);
Then you receive the selected PDF uri as mentioned above

Get address from file explorer Android

I'm making a program that asks the user to find a file which could be stored on the sdcard or the internal card of the phone. I know that in order to open the default file explorer in the phone I must use this code:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("file/*");
startActivity(intent);
However, my problem is related with the communication with the File Explorer, how can I get the address of the file the user selected.
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("file/*");
startActivityForResult(intent, YOUR_REQUEST_CODE);
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case YOUR_REQUEST_CODE:
//get the uri from data's extras
break;
}
//do whatever you want with uri
} else {
Toast.makeText(this, "Wrong result", Toast.LENGTH_SHORT).show();
}
}

Error loading image from presistent Uri

The program flow is that the user selects an image from the gallery and it is displayed in an image view. This works fine. At the same time, the Uri is written to a database.
Later, when the program is run again or that activity is displayed again, the Uri is retrieved from the database and the image is displayed again. This second showing of the image is what causes the exception:
requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS
I added the permission to the manifest and the exception still occurs.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
The same procedure attempts to display both images, and it works when it comes from the picker, but fails when it comes from the presistent Uri from the database.
Picker Intent
try {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Select Picture"),
PICK_IMAGE);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
"Error",
Toast.LENGTH_LONG).show();
Log.e(e.getClass().getName(), e.getMessage(), e);
}
With the result from the picker intent I use
Uri selectedImageUri = data.getData();
With the database read I use. localImage is a String.
Uri.parse(localImage)
I read this bug report https://issues.apache.org/jira/browse/CB-5398, but I'm not sure how to use it to resolve this situation. The code below from another thread seems to attempt to resolve a similar issue with permissions and the photo picker, but in my case I'm not picking again, I just need to display the image from the Uri.
I guess it is a matter of resetting permissions. I'm not really sure. I have the permissions in the manifest. Perhaps I need different code to display an image from a Uri not obtained from the picker? Maybe I need to target a different API? My build.gradle says targetSdkVersion 21, minSdkVersion 15. These were the defaults. I'm in Android Studio 1.0.2.
I'm stumped. Any help is appreciated.
public static final String KITKAT_VALUE = 1002;
Intent intent;
if (Build.VERSION.SDK_INT < 19){
intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
startActivityForResult(intent, KITKAT_VALUE);
} else {
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
startActivityForResult(intent, KITKAT_VALUE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == KITKAT_VALUE ) {
if (resultCode == Activity.RESULT_OK) {
// do something here
}
}
}
At the same time, the Uri is written to a database.
That's not going to work prior to API Level 19, and then only if you are using the Storage Access Framework (ACTION_OPEN_DOCUMENT) and take the offered persistent permissions. Given your existing code, the temporary permissions that you are granted for that Uri will expire, at the latest when your process is terminated.
I added the permission to the manifest and the exception still occurs.
You cannot hold MANAGE_DOCUMENTS, unless you are signed by the signing key that signed the firmware.

Categories

Resources