fdroid-client/app/src/main/java/org/fdroid/fdroid/installer/FileInstallerActivity.java

203 lines
7.9 KiB
Java

package org.fdroid.fdroid.installer;
import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextThemeWrapper;
import android.widget.Toast;
import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.net.DownloaderService;
import java.io.File;
import java.io.IOException;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
public class FileInstallerActivity extends FragmentActivity {
private static final String TAG = "FileInstallerActivity";
private static final int MY_PERMISSIONS_REQUEST_STORAGE = 1;
static final String ACTION_INSTALL_FILE
= "org.fdroid.fdroid.installer.FileInstaller.action.INSTALL_PACKAGE";
static final String ACTION_UNINSTALL_FILE
= "org.fdroid.fdroid.installer.FileInstaller.action.UNINSTALL_PACKAGE";
private FileInstallerActivity activity;
// for the broadcasts
private FileInstaller installer;
private Apk apk;
private Uri localApkUri;
/**
* @see InstallManagerService
*/
private Uri canonicalUri;
private int act = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity = this;
Intent intent = getIntent();
String action = intent.getAction();
localApkUri = intent.getData();
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
installer = new FileInstaller(this, apk);
if (ACTION_INSTALL_FILE.equals(action)) {
canonicalUri = Uri.parse(intent.getStringExtra(DownloaderService.EXTRA_CANONICAL_URL));
if (hasStoragePermission()) {
installPackage(localApkUri, canonicalUri, apk);
} else {
requestPermission();
act = 1;
}
} else if (ACTION_UNINSTALL_FILE.equals(action)) {
canonicalUri = null;
if (hasStoragePermission()) {
uninstallPackage(apk);
} else {
requestPermission();
act = 2;
}
} else {
throw new IllegalStateException("Intent action not specified!");
}
}
private boolean hasStoragePermission() {
return ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
if (!hasStoragePermission()) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showDialog();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_STORAGE);
}
}
}
private void showDialog() {
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
final AlertDialog.Builder builder = new AlertDialog.Builder(theme);
builder.setMessage(R.string.app_permission_storage)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_STORAGE);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (act == 1) {
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
} else if (act == 2) {
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
}
finish();
}
})
.create().show();
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_STORAGE:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (act == 1) {
installPackage(localApkUri, canonicalUri, apk);
} else if (act == 2) {
uninstallPackage(apk);
}
} else {
if (act == 1) {
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
} else if (act == 2) {
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
}
}
finish();
}
}
private void installPackage(Uri localApkUri, Uri canonicalUri, Apk apk) {
Utils.debugLog(TAG, "Installing: " + localApkUri.getPath());
File path = apk.getInstalledMediaFile(activity.getApplicationContext());
path.getParentFile().mkdirs();
try {
FileUtils.copyFile(new File(localApkUri.getPath()), path);
} catch (IOException e) {
Utils.debugLog(TAG, "Failed to copy: " + e.getMessage());
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
}
if (apk.isMediaInstalled(activity.getApplicationContext())) { // Copying worked
Utils.debugLog(TAG, "Copying worked: " + localApkUri.getPath());
if (!postInstall(canonicalUri, apk, path)) {
Toast.makeText(this, String.format(this.getString(R.string.app_installed_media), path.toString()),
Toast.LENGTH_LONG).show();
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_COMPLETE);
}
} else {
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
}
finish();
}
/**
* Run any file-type-specific processes after the file has been copied into place.
*
* @return whether this handles sending the {@link Installer#ACTION_INSTALL_COMPLETE}
* broadcast.
*/
private boolean postInstall(Uri canonicalUri, Apk apk, File path) {
if (path.getName().endsWith(".obf") || path.getName().endsWith(".obf.zip")) {
ObfInstallerService.install(this, canonicalUri, apk, path);
return true;
}
return false;
}
private void uninstallPackage(Apk apk) {
if (apk.isMediaInstalled(activity.getApplicationContext())) {
File file = apk.getInstalledMediaFile(activity.getApplicationContext());
if (!file.delete()) {
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
return;
}
}
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
finish();
}
}