diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index eae2723ad..70cecf9cb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -109,6 +109,15 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/apk_file_provider" />
+
+
+
+
diff --git a/app/src/main/java/org/fdroid/fdroid/AppDetails.java b/app/src/main/java/org/fdroid/fdroid/AppDetails.java
index f433ae184..fd7030dff 100644
--- a/app/src/main/java/org/fdroid/fdroid/AppDetails.java
+++ b/app/src/main/java/org/fdroid/fdroid/AppDetails.java
@@ -862,7 +862,7 @@ public class AppDetails extends AppCompatActivity {
return true;
case UNINSTALL:
- uninstallApk(app.packageName);
+ uninstallApk();
return true;
case IGNOREALL:
@@ -942,7 +942,7 @@ public class AppDetails extends AppCompatActivity {
private void initiateInstall(Apk apk) {
Installer installer = InstallerFactory.create(this, apk);
- Intent intent = installer.getPermissionScreen(apk);
+ Intent intent = installer.getPermissionScreen();
if (intent != null) {
// permission screen required
Utils.debugLog(TAG, "permission screen required");
@@ -959,9 +959,13 @@ public class AppDetails extends AppCompatActivity {
InstallManagerService.queue(this, app, apk);
}
- private void uninstallApk(String packageName) {
- Installer installer = InstallerFactory.create(this, null);
- Intent intent = installer.getUninstallScreen(packageName);
+ /**
+ * Queue for uninstall based on the instance variable {@link #app}
+ */
+ private void uninstallApk() {
+ Apk apk = app.installedApk;
+ Installer installer = InstallerFactory.create(this, apk);
+ Intent intent = installer.getUninstallScreen();
if (intent != null) {
// uninstall screen required
Utils.debugLog(TAG, "screen screen required");
@@ -975,7 +979,7 @@ public class AppDetails extends AppCompatActivity {
private void startUninstall() {
localBroadcastManager.registerReceiver(uninstallReceiver,
Installer.getUninstallIntentFilter(app.packageName));
- InstallerService.uninstall(context, app.packageName);
+ InstallerService.uninstall(context, app.installedApk);
}
private void launchApk(String packageName) {
@@ -1630,7 +1634,7 @@ public class AppDetails extends AppCompatActivity {
// If "launchable", launch
activity.launchApk(app.packageName);
} else {
- activity.uninstallApk(app.packageName);
+ activity.uninstallApk();
}
} else if (app.suggestedVersionCode > 0) {
// If not installed, install
@@ -1658,10 +1662,6 @@ public class AppDetails extends AppCompatActivity {
appDetails = (AppDetails) activity;
}
- void remove() {
- appDetails.uninstallApk(appDetails.getApp().packageName);
- }
-
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// A bit of a hack, but we can't add the header view in setupSummaryHeader(),
@@ -1689,7 +1689,7 @@ public class AppDetails extends AppCompatActivity {
App app = appDetails.getApp();
final Apk apk = appDetails.getApks().getItem(position - l.getHeaderViewsCount());
if (app.installedVersionCode == apk.versionCode) {
- remove();
+ appDetails.uninstallApk();
} else if (app.installedVersionCode > apk.versionCode) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.installDowngrade);
diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java
index 610779db5..c4a9369c2 100644
--- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java
+++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java
@@ -56,6 +56,7 @@ import org.fdroid.fdroid.compat.PRNGFixes;
import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.InstalledAppProviderService;
import org.fdroid.fdroid.data.Repo;
+import org.fdroid.fdroid.installer.InstallHistoryService;
import org.fdroid.fdroid.net.IconDownloader;
import org.fdroid.fdroid.net.WifiStateChangeService;
@@ -299,6 +300,21 @@ public class FDroidApp extends Application {
});
configureTor(Preferences.get().isTorEnabled());
+
+ if (Preferences.get().isKeepingInstallHistory()) {
+ InstallHistoryService.register(this);
+ }
+
+ String packageName = getString(R.string.install_history_reader_packageName);
+ String unset = getString(R.string.install_history_reader_packageName_UNSET);
+ if (!TextUtils.equals(packageName, unset)) {
+ int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ if (Build.VERSION.SDK_INT >= 19) {
+ modeFlags |= Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
+ }
+ grantUriPermission(packageName, InstallHistoryService.LOG_URI, modeFlags);
+ }
}
@TargetApi(18)
diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java
index 1973d4dd0..7a5b91bed 100644
--- a/app/src/main/java/org/fdroid/fdroid/Preferences.java
+++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java
@@ -55,6 +55,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
public static final String PREF_IGN_TOUCH = "ignoreTouchscreen";
public static final String PREF_KEEP_CACHE_TIME = "keepCacheFor";
public static final String PREF_UNSTABLE_UPDATES = "unstableUpdates";
+ public static final String PREF_KEEP_INSTALL_HISTORY = "keepInstallHistory";
public static final String PREF_EXPERT = "expert";
public static final String PREF_PRIVILEGED_INSTALLER = "privilegedInstaller";
public static final String PREF_UNINSTALL_PRIVILEGED_APP = "uninstallPrivilegedApp";
@@ -75,6 +76,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
//private static final boolean DEFAULT_LOCAL_REPO_BONJOUR = true;
private static final long DEFAULT_KEEP_CACHE_TIME = TimeUnit.DAYS.toMillis(1);
private static final boolean DEFAULT_UNSTABLE_UPDATES = false;
+ private static final boolean DEFAULT_KEEP_INSTALL_HISTORY = false;
//private static final boolean DEFAULT_LOCAL_REPO_HTTPS = false;
private static final boolean DEFAULT_INCOMP_VER = false;
private static final boolean DEFAULT_EXPERT = false;
@@ -184,6 +186,10 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
return preferences.getBoolean(PREF_UNSTABLE_UPDATES, DEFAULT_UNSTABLE_UPDATES);
}
+ public boolean isKeepingInstallHistory() {
+ return preferences.getBoolean(PREF_KEEP_INSTALL_HISTORY, DEFAULT_KEEP_INSTALL_HISTORY);
+ }
+
public boolean showIncompatibleVersions() {
return preferences.getBoolean(PREF_INCOMP_VER, DEFAULT_INCOMP_VER);
}
diff --git a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java
index 9d30651e1..c4acc9a76 100644
--- a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java
+++ b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java
@@ -483,7 +483,9 @@ public class RepoUpdater {
}
if (repoPushRequest.versionCode == null
|| repoPushRequest.versionCode == packageInfo.versionCode) {
- InstallerService.uninstall(context, packageName);
+ Apk apk = ApkProvider.Helper.find(context, repoPushRequest.packageName,
+ packageInfo.versionCode);
+ InstallerService.uninstall(context, apk);
} else {
Utils.debugLog(TAG, "ignoring request based on versionCode:" + repoPushRequest);
}
diff --git a/app/src/main/java/org/fdroid/fdroid/compat/FileCompat.java b/app/src/main/java/org/fdroid/fdroid/compat/FileCompat.java
index 5612d6d05..6ad56b4c3 100644
--- a/app/src/main/java/org/fdroid/fdroid/compat/FileCompat.java
+++ b/app/src/main/java/org/fdroid/fdroid/compat/FileCompat.java
@@ -66,7 +66,8 @@ public class FileCompat {
dest.getAbsolutePath(),
};
try {
- Utils.debugLog(TAG, "Executing command: " + commands[0] + " " + commands[1] + " " + commands[2]);
+ Utils.debugLog(TAG, "Executing command: " + commands[0] + " " + commands[1]
+ + " " + commands[2] + " " + commands[3]);
Process proc = Runtime.getRuntime().exec(commands);
Utils.consumeStream(proc.getInputStream());
Utils.consumeStream(proc.getErrorStream());
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java
index cf440ae96..e35ad48ae 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2016 Dominik Schürmann
+ * Copyright (C) 2016 Blue Jay Wireless
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,11 +26,8 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
-import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Apk;
-import java.io.File;
-
/**
* The default installer of F-Droid. It uses the normal Intents APIs of Android
* to install apks. Its main inner workings are encapsulated in DefaultInstallerActivity.
@@ -39,21 +37,19 @@ import java.io.File;
*/
public class DefaultInstaller extends Installer {
- private static final String TAG = "DefaultInstaller";
+ public static final String TAG = "DefaultInstaller";
- DefaultInstaller(Context context) {
- super(context);
+ DefaultInstaller(Context context, Apk apk) {
+ super(context, apk);
}
@Override
- protected void installPackageInternal(Uri localApkUri, Uri downloadUri, Apk apk) {
- sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_STARTED);
-
- Utils.debugLog(TAG, "DefaultInstaller uri: " + localApkUri + " file: " + new File(localApkUri.getPath()));
+ protected void installPackageInternal(Uri localApkUri, Uri downloadUri) {
Intent installIntent = new Intent(context, DefaultInstallerActivity.class);
installIntent.setAction(DefaultInstallerActivity.ACTION_INSTALL_PACKAGE);
installIntent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
+ installIntent.putExtra(Installer.EXTRA_APK, apk);
installIntent.setData(localApkUri);
PendingIntent installPendingIntent = PendingIntent.getActivity(
@@ -67,21 +63,19 @@ public class DefaultInstaller extends Installer {
}
@Override
- protected void uninstallPackage(String packageName) {
- sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
+ protected void uninstallPackage() {
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_STARTED);
Intent uninstallIntent = new Intent(context, DefaultInstallerActivity.class);
uninstallIntent.setAction(DefaultInstallerActivity.ACTION_UNINSTALL_PACKAGE);
- uninstallIntent.putExtra(
- DefaultInstallerActivity.EXTRA_UNINSTALL_PACKAGE_NAME, packageName);
+ uninstallIntent.putExtra(Installer.EXTRA_APK, apk);
PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
context.getApplicationContext(),
- packageName.hashCode(),
+ apk.packageName.hashCode(),
uninstallIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
- sendBroadcastUninstall(packageName,
- Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
}
@Override
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java
index 8c7769f41..9c53800f2 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014-2016 Dominik Schürmann
+ * Copyright (C) 2016 Blue Jay Wireless
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,23 +32,21 @@ import android.support.v4.app.FragmentActivity;
import android.util.Log;
import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.data.Apk;
/**
* A transparent activity as a wrapper around Android's PackageInstaller Intents
*/
public class DefaultInstallerActivity extends FragmentActivity {
- private static final String TAG = "AndroidInstallerAct";
+ private static final String TAG = "DefaultInstallerActivit";
static final String ACTION_INSTALL_PACKAGE = "org.fdroid.fdroid.installer.DefaultInstaller.action.INSTALL_PACKAGE";
static final String ACTION_UNINSTALL_PACKAGE = "org.fdroid.fdroid.installer.DefaultInstaller.action.UNINSTALL_PACKAGE";
- static final String EXTRA_UNINSTALL_PACKAGE_NAME = "org.fdroid.fdroid.installer.DefaultInstaller.extra.UNINSTALL_PACKAGE_NAME";
-
private static final int REQUEST_CODE_INSTALL = 0;
private static final int REQUEST_CODE_UNINSTALL = 1;
private Uri downloadUri;
- private String uninstallPackageName;
// for the broadcasts
private DefaultInstaller installer;
@@ -56,18 +55,16 @@ public class DefaultInstallerActivity extends FragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- installer = new DefaultInstaller(this);
-
Intent intent = getIntent();
String action = intent.getAction();
+ Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
+ installer = new DefaultInstaller(this, apk);
if (ACTION_INSTALL_PACKAGE.equals(action)) {
Uri localApkUri = intent.getData();
downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
installPackage(localApkUri);
} else if (ACTION_UNINSTALL_PACKAGE.equals(action)) {
- uninstallPackageName = intent.getStringExtra(EXTRA_UNINSTALL_PACKAGE_NAME);
-
- uninstallPackage(uninstallPackageName);
+ uninstallPackage(apk.packageName);
} else {
throw new IllegalStateException("Intent action not specified!");
}
@@ -125,7 +122,6 @@ public class DefaultInstallerActivity extends FragmentActivity {
"This Android rom does not support ACTION_INSTALL_PACKAGE!");
finish();
}
- installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_STARTED);
}
private void uninstallPackage(String packageName) {
@@ -134,7 +130,7 @@ public class DefaultInstallerActivity extends FragmentActivity {
getPackageManager().getPackageInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "NameNotFoundException", e);
- installer.sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_INTERRUPTED,
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED,
"Package that is scheduled for uninstall is not installed!");
finish();
return;
@@ -155,7 +151,7 @@ public class DefaultInstallerActivity extends FragmentActivity {
startActivityForResult(intent, REQUEST_CODE_UNINSTALL);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "ActivityNotFoundException", e);
- installer.sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_INTERRUPTED,
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED,
"This Android rom does not support ACTION_UNINSTALL_PACKAGE!");
finish();
}
@@ -197,25 +193,21 @@ public class DefaultInstallerActivity extends FragmentActivity {
case REQUEST_CODE_UNINSTALL:
// resultCode is always 0 on Android < 4.0.
if (Build.VERSION.SDK_INT < 14) {
- installer.sendBroadcastUninstall(uninstallPackageName,
- Installer.ACTION_UNINSTALL_COMPLETE);
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
break;
}
switch (resultCode) {
case Activity.RESULT_OK:
- installer.sendBroadcastUninstall(uninstallPackageName,
- Installer.ACTION_UNINSTALL_COMPLETE);
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
break;
case Activity.RESULT_CANCELED:
- installer.sendBroadcastUninstall(uninstallPackageName,
- Installer.ACTION_UNINSTALL_INTERRUPTED);
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
break;
case Activity.RESULT_FIRST_USER:
default:
// AOSP UninstallAppProgress returns RESULT_FIRST_USER on error
- installer.sendBroadcastUninstall(uninstallPackageName,
- Installer.ACTION_UNINSTALL_INTERRUPTED,
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED,
getString(R.string.uninstall_error_unknown));
break;
}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java
index d40137c2d..3d0b11623 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2016 Dominik Schürmann
*
* This program is free software; you can redistribute it and/or
@@ -39,12 +40,12 @@ import java.io.File;
*/
public class ExtensionInstaller extends Installer {
- ExtensionInstaller(Context context) {
- super(context);
+ ExtensionInstaller(Context context, Apk apk) {
+ super(context, apk);
}
@Override
- protected void installPackageInternal(Uri localApkUri, Uri downloadUri, Apk apk) {
+ protected void installPackageInternal(Uri localApkUri, Uri downloadUri) {
// extension must be signed with the same public key as main F-Droid
// NOTE: Disabled for debug builds to be able to test official extension from repo
ApkSignatureVerifier signatureVerifier = new ApkSignatureVerifier(context);
@@ -71,23 +72,22 @@ public class ExtensionInstaller extends Installer {
}
@Override
- protected void uninstallPackage(String packageName) {
- sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
+ protected void uninstallPackage() {
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_STARTED);
Intent uninstallIntent = new Intent(context, InstallExtensionDialogActivity.class);
uninstallIntent.setAction(InstallExtensionDialogActivity.ACTION_UNINSTALL);
PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
context.getApplicationContext(),
- packageName.hashCode(),
+ apk.packageName.hashCode(),
uninstallIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
- sendBroadcastUninstall(packageName,
- Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
// don't use broadcasts for the rest of this special installer
- sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_COMPLETE);
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
}
@Override
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallHistoryService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallHistoryService.java
new file mode 100644
index 000000000..6d930c850
--- /dev/null
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallHistoryService.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 Blue Jay Wireless
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+package org.fdroid.fdroid.installer;
+
+import android.app.IntentService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Process;
+import android.support.v4.content.LocalBroadcastManager;
+import android.text.TextUtils;
+
+import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.data.Apk;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Saves all activity of installs and uninstalls to the database for later use, like
+ * displaying in some kind of history viewer or reporting to a "popularity contest"
+ * app tracker.
+ */
+public class InstallHistoryService extends IntentService {
+ public static final String TAG = "InstallHistoryService";
+
+ public static final Uri LOG_URI = Uri.parse("content://org.fdroid.fdroid.installer/install_history/all");
+
+ private static BroadcastReceiver broadcastReceiver;
+
+ public static void register(Context context) {
+ if (broadcastReceiver != null) {
+ return; // already registered
+ }
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addDataScheme("http");
+ intentFilter.addDataScheme("https");
+ intentFilter.addDataScheme("package");
+ intentFilter.addAction(Installer.ACTION_INSTALL_STARTED);
+ intentFilter.addAction(Installer.ACTION_INSTALL_COMPLETE);
+ intentFilter.addAction(Installer.ACTION_INSTALL_INTERRUPTED);
+ intentFilter.addAction(Installer.ACTION_INSTALL_USER_INTERACTION);
+ intentFilter.addAction(Installer.ACTION_UNINSTALL_STARTED);
+ intentFilter.addAction(Installer.ACTION_UNINSTALL_COMPLETE);
+ intentFilter.addAction(Installer.ACTION_UNINSTALL_INTERRUPTED);
+ intentFilter.addAction(Installer.ACTION_UNINSTALL_USER_INTERACTION);
+
+ broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ queue(context, intent);
+ }
+ };
+ LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
+ localBroadcastManager.registerReceiver(broadcastReceiver, intentFilter);
+ }
+
+ public static void unregister(Context context) {
+ LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
+ localBroadcastManager.unregisterReceiver(broadcastReceiver);
+ broadcastReceiver = null;
+ }
+
+ public static void queue(Context context, Intent intent) {
+ Utils.debugLog(TAG, "queue " + intent);
+ intent.setClass(context, InstallHistoryService.class);
+ context.startService(intent);
+ }
+
+ public InstallHistoryService() {
+ super("InstallHistoryService");
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ Utils.debugLog(TAG, "onHandleIntent " + intent);
+ if (intent == null) {
+ return;
+ }
+
+ Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
+ long timestamp = System.currentTimeMillis();
+ Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
+ String packageName = apk.packageName;
+ int versionCode = apk.versionCode;
+
+ List values = new ArrayList<>(4);
+ values.add(String.valueOf(timestamp));
+ values.add(packageName);
+ values.add(String.valueOf(versionCode));
+ values.add(intent.getAction());
+
+ File installHistoryDir = new File(getCacheDir(), "install_history");
+ installHistoryDir.mkdir();
+ File logFile = new File(installHistoryDir, "all");
+ FileWriter fw = null;
+ PrintWriter out = null;
+ try {
+ fw = new FileWriter(logFile, true);
+ out = new PrintWriter(fw);
+ out.println(TextUtils.join(",", values));
+ } catch (IOException e) {
+ Utils.debugLog(TAG, e.getMessage());
+ } finally {
+ Utils.closeQuietly(out);
+ Utils.closeQuietly(fw);
+ }
+ }
+}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
index 1a21bd9db..2e3564507 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
@@ -5,6 +5,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -22,6 +23,7 @@ import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.compat.PackageManagerCompat;
import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.App;
+import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderService;
@@ -235,6 +237,7 @@ public class InstallManagerService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
String downloadUrl = intent.getDataString();
+ Apk apk;
switch (intent.getAction()) {
case Installer.ACTION_INSTALL_STARTED:
// nothing to do
@@ -247,12 +250,17 @@ public class InstallManagerService extends Service {
localBroadcastManager.unregisterReceiver(this);
break;
case Installer.ACTION_INSTALL_INTERRUPTED:
+ apk = intent.getParcelableExtra(Installer.EXTRA_APK);
String errorMessage =
intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE);
// show notification if app details is not visible
if (!TextUtils.isEmpty(errorMessage)) {
App app = getAppFromActive(downloadUrl);
+ if (app == null) {
+ ContentResolver resolver = context.getContentResolver();
+ app = AppProvider.Helper.findByPackageName(resolver, apk.packageName);
+ }
// show notification if app details is not visible
if (app != null && AppDetails.isAppVisible(app.packageName)) {
cancelNotification(downloadUrl);
@@ -264,15 +272,15 @@ public class InstallManagerService extends Service {
localBroadcastManager.unregisterReceiver(this);
break;
case Installer.ACTION_INSTALL_USER_INTERACTION:
+ apk = intent.getParcelableExtra(Installer.EXTRA_APK);
PendingIntent installPendingIntent =
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
- Apk apkUserInteraction = getApkFromActive(downloadUrl);
// show notification if app details is not visible
- if (AppDetails.isAppVisible(apkUserInteraction.packageName)) {
+ if (AppDetails.isAppVisible(apk.packageName)) {
cancelNotification(downloadUrl);
} else {
- notifyDownloadComplete(apkUserInteraction, downloadUrl, installPendingIntent);
+ notifyDownloadComplete(apk, downloadUrl, installPendingIntent);
}
break;
@@ -335,10 +343,14 @@ public class InstallManagerService extends Service {
title = String.format(getString(R.string.tap_to_update_format),
pm.getApplicationLabel(pm.getApplicationInfo(apk.packageName, 0)));
} catch (PackageManager.NameNotFoundException e) {
- // TODO use packageName to fetch App instance from database if not cached
String name = getAppName(apk);
if (TextUtils.isEmpty(name) || name.equals(new App().name)) {
- return; // do not have a name to display, so leave notification as is
+ ContentResolver resolver = getContentResolver();
+ App app = AppProvider.Helper.findByPackageName(resolver, apk.packageName);
+ if (app == null || TextUtils.isEmpty(app.name)) {
+ return; // do not have a name to display, so leave notification as is
+ }
+ name = app.name;
}
title = String.format(getString(R.string.tap_to_install_format), name);
}
@@ -450,10 +462,13 @@ public class InstallManagerService extends Service {
*/
public static void queue(Context context, App app, Apk apk) {
String urlString = apk.getUrl();
+ Uri downloadUri = Uri.parse(urlString);
+ Installer.sendBroadcastInstall(context, downloadUri, Installer.ACTION_INSTALL_STARTED, apk,
+ null, null);
Utils.debugLog(TAG, "queue " + app.packageName + " " + apk.versionCode + " from " + urlString);
Intent intent = new Intent(context, InstallManagerService.class);
intent.setAction(ACTION_INSTALL);
- intent.setData(Uri.parse(urlString));
+ intent.setData(downloadUri);
intent.putExtra(EXTRA_APP, app);
intent.putExtra(EXTRA_APK, apk);
context.startService(intent);
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
index 8df65cd4c..ce1216a0b 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2016 Dominik Schürmann
*
* This program is free software; you can redistribute it and/or
@@ -42,11 +43,11 @@ import java.io.IOException;
* Handles the actual install process. Subclasses implement the details.
*/
public abstract class Installer {
- final Context context;
- private final LocalBroadcastManager localBroadcastManager;
-
private static final String TAG = "Installer";
+ final Context context;
+ final Apk apk;
+
public static final String ACTION_INSTALL_STARTED = "org.fdroid.fdroid.installer.Installer.action.INSTALL_STARTED";
public static final String ACTION_INSTALL_COMPLETE = "org.fdroid.fdroid.installer.Installer.action.INSTALL_COMPLETE";
public static final String ACTION_INSTALL_INTERRUPTED = "org.fdroid.fdroid.installer.Installer.action.INSTALL_INTERRUPTED";
@@ -67,29 +68,31 @@ public abstract class Installer {
*/
static final String EXTRA_DOWNLOAD_URI = "org.fdroid.fdroid.installer.Installer.extra.DOWNLOAD_URI";
public static final String EXTRA_APK = "org.fdroid.fdroid.installer.Installer.extra.APK";
- public static final String EXTRA_PACKAGE_NAME = "org.fdroid.fdroid.installer.Installer.extra.PACKAGE_NAME";
public static final String EXTRA_USER_INTERACTION_PI = "org.fdroid.fdroid.installer.Installer.extra.USER_INTERACTION_PI";
public static final String EXTRA_ERROR_MESSAGE = "org.fdroid.fdroid.net.installer.Installer.extra.ERROR_MESSAGE";
- Installer(Context context) {
+ /**
+ * @param apk must be included so that all the phases of the install process
+ * can get all the data about the app, even after F-Droid was killed
+ */
+ Installer(Context context, Apk apk) {
this.context = context;
- localBroadcastManager = LocalBroadcastManager.getInstance(context);
+ this.apk = apk;
}
/**
* Returns permission screen for given apk.
*
- * @param apk instance of Apk
* @return Intent with Activity to show required permissions.
* Returns null if Installer handles that on itself, e.g., with DefaultInstaller,
* or if no new permissions have been introduced during an update
*/
- public Intent getPermissionScreen(Apk apk) {
+ public Intent getPermissionScreen() {
if (!isUnattended()) {
return null;
}
- int count = newPermissionCount(apk);
+ int count = newPermissionCount();
if (count == 0) {
// no permission screen needed!
return null;
@@ -101,7 +104,7 @@ public abstract class Installer {
return intent;
}
- private int newPermissionCount(Apk apk) {
+ private int newPermissionCount() {
boolean supportsRuntimePermissions = apk.targetSdkVersion >= 23;
if (supportsRuntimePermissions) {
return 0;
@@ -125,69 +128,69 @@ public abstract class Installer {
* Returns an Intent to start a dialog wrapped in an activity
* for uninstall confirmation.
*
- * @param packageName packageName of app to uninstall
* @return Intent with activity for uninstall confirmation
* Returns null if Installer handles that on itself, e.g.,
* with DefaultInstaller.
*/
- public Intent getUninstallScreen(String packageName) {
+ public Intent getUninstallScreen() {
if (!isUnattended()) {
return null;
}
Intent intent = new Intent(context, UninstallDialogActivity.class);
- intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Installer.EXTRA_APK, apk);
return intent;
}
void sendBroadcastInstall(Uri downloadUri, String action, PendingIntent pendingIntent) {
- sendBroadcastInstall(downloadUri, action, pendingIntent, null);
+ sendBroadcastInstall(context, downloadUri, action, apk, pendingIntent, null);
}
void sendBroadcastInstall(Uri downloadUri, String action) {
- sendBroadcastInstall(downloadUri, action, null, null);
+ sendBroadcastInstall(context, downloadUri, action, apk, null, null);
}
void sendBroadcastInstall(Uri downloadUri, String action, String errorMessage) {
- sendBroadcastInstall(downloadUri, action, null, errorMessage);
+ sendBroadcastInstall(context, downloadUri, action, apk, null, errorMessage);
}
- void sendBroadcastInstall(Uri downloadUri, String action,
- PendingIntent pendingIntent, String errorMessage) {
+ static void sendBroadcastInstall(Context context,
+ Uri downloadUri, String action, Apk apk,
+ PendingIntent pendingIntent, String errorMessage) {
Intent intent = new Intent(action);
intent.setData(downloadUri);
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
+ intent.putExtra(Installer.EXTRA_APK, apk);
if (!TextUtils.isEmpty(errorMessage)) {
intent.putExtra(Installer.EXTRA_ERROR_MESSAGE, errorMessage);
}
- localBroadcastManager.sendBroadcast(intent);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
- void sendBroadcastUninstall(String packageName, String action, String errorMessage) {
- sendBroadcastUninstall(packageName, action, null, errorMessage);
+ void sendBroadcastUninstall(String action, String errorMessage) {
+ sendBroadcastUninstall(action, null, errorMessage);
}
- void sendBroadcastUninstall(String packageName, String action) {
- sendBroadcastUninstall(packageName, action, null, null);
+ void sendBroadcastUninstall(String action) {
+ sendBroadcastUninstall(action, null, null);
}
- void sendBroadcastUninstall(String packageName, String action, PendingIntent pendingIntent) {
- sendBroadcastUninstall(packageName, action, pendingIntent, null);
+ void sendBroadcastUninstall(String action, PendingIntent pendingIntent) {
+ sendBroadcastUninstall(action, pendingIntent, null);
}
- void sendBroadcastUninstall(String packageName, String action,
- PendingIntent pendingIntent, String errorMessage) {
- Uri uri = Uri.fromParts("package", packageName, null);
+ void sendBroadcastUninstall(String action, PendingIntent pendingIntent, String errorMessage) {
+ Uri uri = Uri.fromParts("package", apk.packageName, null);
Intent intent = new Intent(action);
intent.setData(uri); // for broadcast filtering
- intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Installer.EXTRA_APK, apk);
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
if (!TextUtils.isEmpty(errorMessage)) {
intent.putExtra(Installer.EXTRA_ERROR_MESSAGE, errorMessage);
}
- localBroadcastManager.sendBroadcast(intent);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
/**
@@ -223,9 +226,8 @@ public abstract class Installer {
* @param localApkUri points to the local copy of the APK to be installed
* @param downloadUri serves as the unique ID for all actions related to the
* installation of that specific APK
- * @param apk apk object of the app that should be installed
*/
- public void installPackage(Uri localApkUri, Uri downloadUri, Apk apk) {
+ public void installPackage(Uri localApkUri, Uri downloadUri) {
try {
// verify that permissions of the apk file match the ones from the apk object
ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk);
@@ -242,8 +244,8 @@ public abstract class Installer {
if (isUnattended()) {
Log.e(TAG, e.getMessage(), e);
Log.e(TAG, "Falling back to AOSP DefaultInstaller!");
- DefaultInstaller defaultInstaller = new DefaultInstaller(context);
- defaultInstaller.installPackageInternal(localApkUri, downloadUri, apk);
+ DefaultInstaller defaultInstaller = new DefaultInstaller(context, apk);
+ defaultInstaller.installPackageInternal(localApkUri, downloadUri);
return;
}
}
@@ -260,17 +262,16 @@ public abstract class Installer {
return;
}
- installPackageInternal(sanitizedUri, downloadUri, apk);
+ installPackageInternal(sanitizedUri, downloadUri);
}
- protected abstract void installPackageInternal(Uri localApkUri, Uri downloadUri, Apk apk);
+ protected abstract void installPackageInternal(Uri localApkUri, Uri downloadUri);
/**
- * Uninstall app
- *
- * @param packageName package name of the app that should be uninstalled
+ * Uninstall app as defined by {@link Installer#apk} in
+ * {@link Installer#Installer(Context, Apk)}
*/
- protected abstract void uninstallPackage(String packageName);
+ protected abstract void uninstallPackage();
/**
* This {@link Installer} instance is capable of "unattended" install and
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java
index 3c19ae42c..47c9bc74c 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2016 Dominik Schürmann
*
* This program is free software; you can redistribute it and/or
@@ -20,6 +21,7 @@
package org.fdroid.fdroid.installer;
import android.content.Context;
+import android.text.TextUtils;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Apk;
@@ -34,22 +36,23 @@ public class InstallerFactory {
* case to install the "F-Droid Privileged Extension" ExtensionInstaller.
*
* @param context current {@link Context}
- * @param apk apk to be installed. Required to select the ExtensionInstaller.
- * If this is null, the ExtensionInstaller will never be returned.
+ * @param apk to be installed, always required.
* @return instance of an Installer
*/
public static Installer create(Context context, Apk apk) {
- Installer installer;
+ if (apk == null || TextUtils.isEmpty(apk.packageName)) {
+ throw new IllegalArgumentException("packageName must not be empty!");
+ }
- if (apk != null
- && apk.packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) {
+ Installer installer;
+ if (apk.packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) {
// special case for "F-Droid Privileged Extension"
- installer = new ExtensionInstaller(context);
+ installer = new ExtensionInstaller(context, apk);
} else if (PrivilegedInstaller.isDefault(context)) {
Utils.debugLog(TAG, "privileged extension correctly installed -> PrivilegedInstaller");
- installer = new PrivilegedInstaller(context);
+ installer = new PrivilegedInstaller(context, apk);
} else {
- installer = new DefaultInstaller(context);
+ installer = new DefaultInstaller(context, apk);
}
return installer;
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java
index 4e3b3ad9f..3784c788c 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2016 Dominik Schürmann
*
* This program is free software; you can redistribute it and/or
@@ -63,10 +64,9 @@ public class InstallerService extends IntentService {
if (ACTION_INSTALL.equals(intent.getAction())) {
Uri uri = intent.getData();
Uri downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
- installer.installPackage(uri, downloadUri, apk);
+ installer.installPackage(uri, downloadUri);
} else if (ACTION_UNINSTALL.equals(intent.getAction())) {
- String packageName = intent.getStringExtra(Installer.EXTRA_PACKAGE_NAME);
- installer.uninstallPackage(packageName);
+ installer.uninstallPackage();
}
}
@@ -90,13 +90,13 @@ public class InstallerService extends IntentService {
/**
* Uninstall an app
*
- * @param context this app's {@link Context}
- * @param packageName package name of the app that will be uninstalled
+ * @param context this app's {@link Context}
+ * @param apk {@link Apk} instance of the app that will be uninstalled
*/
- public static void uninstall(Context context, String packageName) {
+ public static void uninstall(Context context, Apk apk) {
Intent intent = new Intent(context, InstallerService.class);
intent.setAction(ACTION_UNINSTALL);
- intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Installer.EXTRA_APK, apk);
context.startService(intent);
}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
index 8649f729b..1ecd62789 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2014-2016 Dominik Schürmann
* Copyright (C) 2015 Daniel Martí
*
@@ -255,8 +256,8 @@ public class PrivilegedInstaller extends Installer {
"device owner has marked the package as uninstallable.");
}
- public PrivilegedInstaller(Context context) {
- super(context);
+ public PrivilegedInstaller(Context context, Apk apk) {
+ super(context, apk);
}
public static boolean isExtensionInstalled(Context context) {
@@ -306,9 +307,7 @@ public class PrivilegedInstaller extends Installer {
}
@Override
- protected void installPackageInternal(final Uri localApkUri, final Uri downloadUri, Apk apk) {
- sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_STARTED);
-
+ protected void installPackageInternal(final Uri localApkUri, final Uri downloadUri) {
ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service);
@@ -354,8 +353,8 @@ public class PrivilegedInstaller extends Installer {
}
@Override
- protected void uninstallPackage(final String packageName) {
- sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
+ protected void uninstallPackage() {
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_STARTED);
ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -365,9 +364,9 @@ public class PrivilegedInstaller extends Installer {
@Override
public void handleResult(String packageName, int returnCode) throws RemoteException {
if (returnCode == DELETE_SUCCEEDED) {
- sendBroadcastUninstall(packageName, ACTION_UNINSTALL_COMPLETE);
+ sendBroadcastUninstall(ACTION_UNINSTALL_COMPLETE);
} else {
- sendBroadcastUninstall(packageName, ACTION_UNINSTALL_INTERRUPTED,
+ sendBroadcastUninstall(ACTION_UNINSTALL_INTERRUPTED,
"Error " + returnCode + ": "
+ UNINSTALL_RETURN_CODES.get(returnCode));
}
@@ -377,15 +376,15 @@ public class PrivilegedInstaller extends Installer {
try {
boolean hasPermissions = privService.hasPrivilegedPermissions();
if (!hasPermissions) {
- sendBroadcastUninstall(packageName, ACTION_UNINSTALL_INTERRUPTED,
+ sendBroadcastUninstall(ACTION_UNINSTALL_INTERRUPTED,
context.getString(R.string.system_install_denied_permissions));
return;
}
- privService.deletePackage(packageName, 0, callback);
+ privService.deletePackage(apk.packageName, 0, callback);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
- sendBroadcastUninstall(packageName, ACTION_UNINSTALL_INTERRUPTED,
+ sendBroadcastUninstall(ACTION_UNINSTALL_INTERRUPTED,
"connecting to privileged service failed");
}
}
diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java
index fa2380719..2538cefb8 100644
--- a/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java
+++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java
@@ -32,6 +32,7 @@ import android.view.ContextThemeWrapper;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.installer.Installer;
/**
@@ -48,14 +49,15 @@ public class UninstallDialogActivity extends FragmentActivity {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
- final String packageName = intent.getStringExtra(Installer.EXTRA_PACKAGE_NAME);
+ final Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
PackageManager pm = getPackageManager();
ApplicationInfo appInfo;
try {
//noinspection WrongConstant (lint is actually wrong here!)
- appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+ appInfo = pm.getApplicationInfo(apk.packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Failed to get ApplicationInfo for uninstalling");
}
@@ -86,7 +88,7 @@ public class UninstallDialogActivity extends FragmentActivity {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent data = new Intent();
- data.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
+ data.putExtra(Installer.EXTRA_APK, apk);
setResult(Activity.RESULT_OK, intent);
finish();
}
diff --git a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
index 4304b87c6..c1355b5ad 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
@@ -19,6 +19,7 @@ import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.PreferencesActivity;
import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.installer.InstallHistoryService;
import org.fdroid.fdroid.installer.PrivilegedInstaller;
import info.guardianproject.netcipher.NetCipher;
@@ -193,6 +194,14 @@ public class PreferencesFragment extends PreferenceFragment
}
break;
+ case Preferences.PREF_KEEP_INSTALL_HISTORY:
+ CheckBoxPreference p = (CheckBoxPreference) findPreference(key);
+ if (p.isChecked()) {
+ InstallHistoryService.register(getContext());
+ } else {
+ InstallHistoryService.unregister(getContext());
+ }
+ break;
}
}
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 00e6b1e2d..cb5f3c688 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -7,7 +7,7 @@
https://gitlab.com/fdroid/fdroidclient
team@f-droid.org
GNU General Public License version\u00A03 or later
-
+
transition_app_item_icon
https://
@@ -15,6 +15,11 @@
%1$s on F-Droid
+
+ 1-THIS MEANS NO APP IS GRANTED ACCESS!
+ @string/install_history_reader_packageName_UNSET
+
- 0
- 1
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a3a30b88f..d23295a97 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -14,6 +14,8 @@
Updates
Unstable updates
Suggest updates to unstable versions
+ Keep install history
+ Store a log of all installs and uninstalls inside F-Droid
Other
Automatic update interval
diff --git a/app/src/main/res/xml/install_history_file_provider.xml b/app/src/main/res/xml/install_history_file_provider.xml
new file mode 100644
index 000000000..06e42c762
--- /dev/null
+++ b/app/src/main/res/xml/install_history_file_provider.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index aa80594bd..96ae03aaa 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -91,6 +91,12 @@
android:summary="@string/unstable_updates_summary"
android:defaultValue="false"
android:dependency="expert" />
+