Merge branch 'panic-app-wipe' into 'master'
panic app and repo wipe See merge request fdroid/fdroidclient!827
This commit is contained in:
commit
ca81518f74
@ -148,7 +148,7 @@ dependencies {
|
|||||||
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
implementation 'com.google.zxing:core:3.3.3'
|
implementation 'com.google.zxing:core:3.3.3'
|
||||||
implementation 'info.guardianproject.netcipher:netcipher:2.0.0-beta1'
|
implementation 'info.guardianproject.netcipher:netcipher:2.0.0-beta1'
|
||||||
implementation 'info.guardianproject.panic:panic:0.5'
|
implementation 'info.guardianproject.panic:panic:1.0'
|
||||||
implementation 'commons-io:commons-io:2.6'
|
implementation 'commons-io:commons-io:2.6'
|
||||||
implementation 'commons-net:commons-net:3.6'
|
implementation 'commons-net:commons-net:3.6'
|
||||||
implementation 'ch.acra:acra:4.9.1'
|
implementation 'ch.acra:acra:4.9.1'
|
||||||
|
@ -104,6 +104,10 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".views.panic.SelectInstalledAppsActivity"
|
||||||
|
android:parentActivityName=".views.panic.PanicPreferencesActivity"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".views.panic.PanicResponderActivity"
|
android:name=".views.panic.PanicResponderActivity"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package org.fdroid.fdroid.views.panic;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.support.v7.preference.CheckBoxPreference;
|
||||||
|
import android.support.v7.preference.PreferenceViewHolder;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
|
||||||
|
public class DestructiveCheckBoxPreference extends CheckBoxPreference {
|
||||||
|
public DestructiveCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestructiveCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestructiveCheckBoxPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestructiveCheckBoxPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
if (!holder.itemView.isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Resources resources = getContext().getResources();
|
||||||
|
if (FDroidApp.isAppThemeLight()) {
|
||||||
|
holder.itemView.setBackgroundColor(resources.getColor(R.color.panic_destructive_light));
|
||||||
|
} else {
|
||||||
|
holder.itemView.setBackgroundColor(resources.getColor(R.color.panic_destructive_dark));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package org.fdroid.fdroid.views.panic;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.preference.Preference;
|
||||||
|
import android.support.v7.preference.PreferenceViewHolder;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
|
||||||
|
public class DestructivePreference extends Preference {
|
||||||
|
public DestructivePreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestructivePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestructivePreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestructivePreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
if (FDroidApp.isAppThemeLight()) {
|
||||||
|
holder.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.panic_destructive_light));
|
||||||
|
} else {
|
||||||
|
holder.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.panic_destructive_dark));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.LightingColorFilter;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -20,37 +21,42 @@ import android.support.v7.app.AlertDialog;
|
|||||||
import android.support.v7.preference.CheckBoxPreference;
|
import android.support.v7.preference.CheckBoxPreference;
|
||||||
import android.support.v7.preference.ListPreference;
|
import android.support.v7.preference.ListPreference;
|
||||||
import android.support.v7.preference.Preference;
|
import android.support.v7.preference.Preference;
|
||||||
|
import android.support.v7.preference.PreferenceCategory;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import info.guardianproject.panic.Panic;
|
import info.guardianproject.panic.Panic;
|
||||||
import info.guardianproject.panic.PanicResponder;
|
import info.guardianproject.panic.PanicResponder;
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||||
import org.fdroid.fdroid.views.hiding.HidingManager;
|
import org.fdroid.fdroid.views.hiding.HidingManager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class PanicPreferencesFragment extends PreferenceFragment
|
public class PanicPreferencesFragment extends PreferenceFragment
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
private static final String PREF_EXIT = Preferences.PREF_PANIC_EXIT;
|
|
||||||
private static final String PREF_APP = "pref_panic_app";
|
private static final String PREF_APP = "pref_panic_app";
|
||||||
private static final String PREF_HIDE = Preferences.PREF_PANIC_HIDE;
|
|
||||||
|
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private ListPreference prefApp;
|
private ListPreference prefApp;
|
||||||
private CheckBoxPreference prefExit;
|
private CheckBoxPreference prefExit;
|
||||||
private CheckBoxPreference prefHide;
|
private CheckBoxPreference prefHide;
|
||||||
|
private CheckBoxPreference prefResetRepos;
|
||||||
|
private PreferenceCategory categoryAppsToUninstall;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle bundle, String s) {
|
public void onCreatePreferences(Bundle bundle, String s) {
|
||||||
addPreferencesFromResource(R.xml.preferences_panic);
|
addPreferencesFromResource(R.xml.preferences_panic);
|
||||||
|
|
||||||
pm = getActivity().getPackageManager();
|
pm = getActivity().getPackageManager();
|
||||||
prefExit = (CheckBoxPreference) findPreference(PREF_EXIT);
|
prefExit = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_EXIT);
|
||||||
prefApp = (ListPreference) findPreference(PREF_APP);
|
prefApp = (ListPreference) findPreference(PREF_APP);
|
||||||
prefHide = (CheckBoxPreference) findPreference(PREF_HIDE);
|
prefHide = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_HIDE);
|
||||||
prefHide.setTitle(getString(R.string.panic_hide_title, getString(R.string.app_name)));
|
prefHide.setTitle(getString(R.string.panic_hide_title, getString(R.string.app_name)));
|
||||||
|
prefResetRepos = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_RESET_REPOS);
|
||||||
|
categoryAppsToUninstall = (PreferenceCategory) findPreference("pref_panic_apps_to_uninstall");
|
||||||
|
|
||||||
if (PanicResponder.checkForDisconnectIntent(getActivity())) {
|
if (PanicResponder.checkForDisconnectIntent(getActivity())) {
|
||||||
// the necessary action should have been performed by the check already
|
// the necessary action should have been performed by the check already
|
||||||
@ -73,9 +79,12 @@ public class PanicPreferencesFragment extends PreferenceFragment
|
|||||||
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
||||||
prefHide.setChecked(false);
|
prefHide.setChecked(false);
|
||||||
prefHide.setEnabled(false);
|
prefHide.setEnabled(false);
|
||||||
|
prefResetRepos.setChecked(false);
|
||||||
|
prefResetRepos.setEnabled(false);
|
||||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
getActivity().setResult(Activity.RESULT_CANCELED);
|
||||||
} else {
|
} else {
|
||||||
prefHide.setEnabled(true);
|
prefHide.setEnabled(true);
|
||||||
|
prefResetRepos.setEnabled(true);
|
||||||
}
|
}
|
||||||
showPanicApp(packageName);
|
showPanicApp(packageName);
|
||||||
return true;
|
return true;
|
||||||
@ -88,6 +97,48 @@ public class PanicPreferencesFragment extends PreferenceFragment
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||||
|
|
||||||
|
if (!PrivilegedInstaller.isDefault(getActivity())) {
|
||||||
|
getPreferenceScreen().removePreference(categoryAppsToUninstall);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showWipeList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showWipeList() {
|
||||||
|
Intent intent = new Intent(getActivity(), SelectInstalledAppsActivity.class);
|
||||||
|
intent.setAction(Intent.ACTION_MAIN);
|
||||||
|
Set<String> wipeSet = Preferences.get().getPanicWipeSet();
|
||||||
|
categoryAppsToUninstall.removeAll();
|
||||||
|
if (Panic.PACKAGE_NAME_NONE.equals(prefApp.getValue())) {
|
||||||
|
categoryAppsToUninstall.setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
categoryAppsToUninstall.setEnabled(true);
|
||||||
|
if (wipeSet.size() > 0) {
|
||||||
|
for (String packageName : wipeSet) {
|
||||||
|
Preference preference = new DestructivePreference(getActivity());
|
||||||
|
preference.setSingleLineTitle(true);
|
||||||
|
preference.setIntent(intent);
|
||||||
|
categoryAppsToUninstall.addPreference(preference);
|
||||||
|
try {
|
||||||
|
preference.setTitle(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)));
|
||||||
|
preference.setIcon(pm.getApplicationIcon(packageName));
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
preference.setTitle(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Preference preference = new Preference(getActivity());
|
||||||
|
preference.setIntent(intent);
|
||||||
|
Drawable icon = getResources().getDrawable(R.drawable.ic_add_circle_outline_white);
|
||||||
|
icon.setColorFilter(new LightingColorFilter(0, getResources().getColor(R.color.swap_light_grey_icon)));
|
||||||
|
preference.setSingleLineTitle(true);
|
||||||
|
preference.setTitle(R.string.panic_add_apps_to_uninstall);
|
||||||
|
preference.setIcon(icon);
|
||||||
|
categoryAppsToUninstall.addPreference(preference);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -98,11 +149,13 @@ public class PanicPreferencesFragment extends PreferenceFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
if (key.equals(PREF_HIDE) && sharedPreferences.getBoolean(PREF_HIDE, false)) {
|
if (key.equals(Preferences.PREF_PANIC_HIDE)
|
||||||
|
&& sharedPreferences.getBoolean(Preferences.PREF_PANIC_HIDE, false)) {
|
||||||
showHideConfirmationDialog();
|
showHideConfirmationDialog();
|
||||||
}
|
}
|
||||||
// disable "hiding" if "exit" gets disabled
|
// disable "hiding" if "exit" gets disabled
|
||||||
if (key.equals(PREF_EXIT) && !sharedPreferences.getBoolean(PREF_EXIT, true)) {
|
if (key.equals(Preferences.PREF_PANIC_EXIT)
|
||||||
|
&& !sharedPreferences.getBoolean(Preferences.PREF_PANIC_EXIT, true)) {
|
||||||
prefHide.setChecked(false);
|
prefHide.setChecked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,6 +209,7 @@ public class PanicPreferencesFragment extends PreferenceFragment
|
|||||||
|
|
||||||
// disable destructive panic actions
|
// disable destructive panic actions
|
||||||
prefHide.setEnabled(false);
|
prefHide.setEnabled(false);
|
||||||
|
showWipeList();
|
||||||
} else {
|
} else {
|
||||||
// try to display connected panic app
|
// try to display connected panic app
|
||||||
try {
|
try {
|
||||||
@ -163,6 +217,8 @@ public class PanicPreferencesFragment extends PreferenceFragment
|
|||||||
prefApp.setSummary(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)));
|
prefApp.setSummary(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)));
|
||||||
prefApp.setIcon(pm.getApplicationIcon(packageName));
|
prefApp.setIcon(pm.getApplicationIcon(packageName));
|
||||||
prefHide.setEnabled(true);
|
prefHide.setEnabled(true);
|
||||||
|
prefResetRepos.setEnabled(true);
|
||||||
|
showWipeList();
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
// revert back to no app, just to be safe
|
// revert back to no app, just to be safe
|
||||||
PanicResponder.setTriggerPackageName(getActivity(), Panic.PACKAGE_NAME_NONE);
|
PanicResponder.setTriggerPackageName(getActivity(), Panic.PACKAGE_NAME_NONE);
|
||||||
@ -243,6 +299,7 @@ public class PanicPreferencesFragment extends PreferenceFragment
|
|||||||
@Override
|
@Override
|
||||||
public void onCancel(DialogInterface dialogInterface) {
|
public void onCancel(DialogInterface dialogInterface) {
|
||||||
prefHide.setChecked(false);
|
prefHide.setChecked(false);
|
||||||
|
prefResetRepos.setChecked(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setView(R.layout.dialog_app_hiding);
|
builder.setView(R.layout.dialog_app_hiding);
|
||||||
|
@ -1,17 +1,48 @@
|
|||||||
package org.fdroid.fdroid.views.panic;
|
package org.fdroid.fdroid.views.panic;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
import org.fdroid.fdroid.views.hiding.HidingManager;
|
|
||||||
|
|
||||||
import info.guardianproject.panic.Panic;
|
import info.guardianproject.panic.Panic;
|
||||||
import info.guardianproject.panic.PanicResponder;
|
import info.guardianproject.panic.PanicResponder;
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
|
import org.fdroid.fdroid.data.DBHelper;
|
||||||
|
import org.fdroid.fdroid.data.InstalledApp;
|
||||||
|
import org.fdroid.fdroid.data.InstalledAppProvider;
|
||||||
|
import org.fdroid.fdroid.data.Repo;
|
||||||
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
|
import org.fdroid.fdroid.data.Schema;
|
||||||
|
import org.fdroid.fdroid.installer.Installer;
|
||||||
|
import org.fdroid.fdroid.installer.InstallerService;
|
||||||
|
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||||
|
import org.fdroid.fdroid.views.hiding.HidingManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link Activity} is purely to run events in response to a panic trigger.
|
||||||
|
* It needs to be an {@code Activity} rather than a {@link android.app.Service}
|
||||||
|
* so that it can fetch some of the required information about what sent the
|
||||||
|
* {@link Intent}. This is therefore an {@code Activity} without any UI, which
|
||||||
|
* is a special case in Android. All the code must be in
|
||||||
|
* {@link #onCreate(Bundle)} and {@link #finish()} must be called at the end of
|
||||||
|
* that method.
|
||||||
|
*
|
||||||
|
* @see PanicResponder#receivedTriggerFromConnectedApp(Activity)
|
||||||
|
*/
|
||||||
public class PanicResponderActivity extends AppCompatActivity {
|
public class PanicResponderActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private static final String TAG = PanicResponderActivity.class.getSimpleName();
|
private static final String TAG = PanicResponderActivity.class.getSimpleName();
|
||||||
@ -21,7 +52,7 @@ public class PanicResponderActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
if (intent == null || !Panic.isTriggerIntent(intent)) {
|
if (!Panic.isTriggerIntent(intent)) {
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -29,12 +60,71 @@ public class PanicResponderActivity extends AppCompatActivity {
|
|||||||
// received intent from panic app
|
// received intent from panic app
|
||||||
Log.i(TAG, "Received Panic Trigger...");
|
Log.i(TAG, "Received Panic Trigger...");
|
||||||
|
|
||||||
Preferences preferences = Preferences.get();
|
final Preferences preferences = Preferences.get();
|
||||||
|
|
||||||
if (PanicResponder.receivedTriggerFromConnectedApp(this)) {
|
boolean receivedTriggerFromConnectedApp = PanicResponder.receivedTriggerFromConnectedApp(this);
|
||||||
Log.i(TAG, "Panic Trigger came from connected app");
|
final boolean runningAppUninstalls = PrivilegedInstaller.isDefault(this);
|
||||||
|
|
||||||
// Performing destructive panic responses
|
ArrayList<String> wipeList = new ArrayList<>(preferences.getPanicWipeSet());
|
||||||
|
preferences.setPanicWipeSet(Collections.<String>emptySet());
|
||||||
|
preferences.setPanicTmpSelectedSet(Collections.<String>emptySet());
|
||||||
|
|
||||||
|
if (receivedTriggerFromConnectedApp && runningAppUninstalls && wipeList.size() > 0) {
|
||||||
|
|
||||||
|
// if this app (e.g. F-Droid) is to be deleted, do it last
|
||||||
|
if (wipeList.contains(getPackageName())) {
|
||||||
|
wipeList.remove(getPackageName());
|
||||||
|
wipeList.add(getPackageName());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Context context = this;
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
|
||||||
|
final String lastToUninstall = wipeList.get(wipeList.size() - 1);
|
||||||
|
final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
switch ((intent.getAction())) {
|
||||||
|
case Installer.ACTION_UNINSTALL_INTERRUPTED:
|
||||||
|
case Installer.ACTION_UNINSTALL_COMPLETE:
|
||||||
|
latch.countDown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
lbm.registerReceiver(receiver, Installer.getUninstallIntentFilter(lastToUninstall));
|
||||||
|
|
||||||
|
for (String packageName : wipeList) {
|
||||||
|
InstalledApp installedApp = InstalledAppProvider.Helper.findByPackageName(context, packageName);
|
||||||
|
InstallerService.uninstall(context, new Apk(installedApp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for apps to uninstall before triggering final responses
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
latch.await(10, TimeUnit.MINUTES);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
lbm.unregisterReceiver(receiver);
|
||||||
|
if (preferences.panicResetRepos()) {
|
||||||
|
resetRepos(context);
|
||||||
|
}
|
||||||
|
if (preferences.panicHide()) {
|
||||||
|
HidingManager.hide(context);
|
||||||
|
}
|
||||||
|
if (preferences.panicExit()) {
|
||||||
|
exitAndClear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
} else if (receivedTriggerFromConnectedApp) {
|
||||||
|
if (preferences.panicResetRepos()) {
|
||||||
|
resetRepos(this);
|
||||||
|
}
|
||||||
|
// Performing destructive panic response
|
||||||
if (preferences.panicHide()) {
|
if (preferences.panicHide()) {
|
||||||
Log.i(TAG, "Hiding app...");
|
Log.i(TAG, "Hiding app...");
|
||||||
HidingManager.hide(this);
|
HidingManager.hide(this);
|
||||||
@ -42,13 +132,43 @@ public class PanicResponderActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// exit and clear, if not deactivated
|
// exit and clear, if not deactivated
|
||||||
if (preferences.panicExit()) {
|
if (!runningAppUninstalls && preferences.panicExit()) {
|
||||||
|
exitAndClear();
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void resetRepos(Context context) {
|
||||||
|
HashSet<String> enabledAddresses = new HashSet<>();
|
||||||
|
HashSet<String> disabledAddresses = new HashSet<>();
|
||||||
|
String[] defaultReposItems = DBHelper.loadInitialRepos(context).toArray(new String[0]);
|
||||||
|
for (int i = 1; i < defaultReposItems.length; i += DBHelper.REPO_XML_ITEM_COUNT) {
|
||||||
|
if ("1".equals(defaultReposItems[i + 3])) {
|
||||||
|
enabledAddresses.add(defaultReposItems[i]);
|
||||||
|
} else {
|
||||||
|
disabledAddresses.add(defaultReposItems[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Repo> repos = RepoProvider.Helper.all(context);
|
||||||
|
for (Repo repo : repos) {
|
||||||
|
ContentValues values = new ContentValues(1);
|
||||||
|
if (enabledAddresses.contains(repo.address)) {
|
||||||
|
values.put(Schema.RepoTable.Cols.IN_USE, true);
|
||||||
|
RepoProvider.Helper.update(context, repo, values);
|
||||||
|
} else if (disabledAddresses.contains(repo.address)) {
|
||||||
|
values.put(Schema.RepoTable.Cols.IN_USE, false);
|
||||||
|
RepoProvider.Helper.update(context, repo, values);
|
||||||
|
} else {
|
||||||
|
RepoProvider.Helper.remove(context, repo.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exitAndClear() {
|
||||||
ExitActivity.exitAndRemoveFromRecentApps(this);
|
ExitActivity.exitAndRemoveFromRecentApps(this);
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
finishAndRemoveTask();
|
finishAndRemoveTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package org.fdroid.fdroid.views.panic;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.views.installed.InstalledAppListAdapter;
|
||||||
|
import org.fdroid.fdroid.views.installed.InstalledAppListItemController;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class SelectInstalledAppListAdapter extends InstalledAppListAdapter {
|
||||||
|
private final Set<String> selectedApps;
|
||||||
|
|
||||||
|
SelectInstalledAppListAdapter(Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
Preferences prefs = Preferences.get();
|
||||||
|
selectedApps = prefs.getPanicWipeSet();
|
||||||
|
prefs.setPanicTmpSelectedSet(selectedApps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public InstalledAppListItemController onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = activity.getLayoutInflater().inflate(R.layout.installed_app_list_item, parent, false);
|
||||||
|
return new SelectInstalledAppListItemController(activity, view, selectedApps);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package org.fdroid.fdroid.views.panic;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.View;
|
||||||
|
import org.fdroid.fdroid.AppUpdateStatusManager;
|
||||||
|
import org.fdroid.fdroid.data.App;
|
||||||
|
import org.fdroid.fdroid.views.apps.AppListItemState;
|
||||||
|
import org.fdroid.fdroid.views.installed.InstalledAppListItemController;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the currently installed apps as a selectable list.
|
||||||
|
*/
|
||||||
|
public class SelectInstalledAppListItemController extends InstalledAppListItemController {
|
||||||
|
|
||||||
|
private final Set<String> selectedApps;
|
||||||
|
|
||||||
|
public SelectInstalledAppListItemController(Activity activity, View itemView, Set<String> selectedApps) {
|
||||||
|
super(activity, itemView);
|
||||||
|
this.selectedApps = selectedApps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected AppListItemState getCurrentViewState(
|
||||||
|
@NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
|
||||||
|
return new AppListItemState(app).setCheckBoxStatus(selectedApps.contains(app.packageName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActionButtonPressed(App app) {
|
||||||
|
super.onActionButtonPressed(app);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* 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.views.panic;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.graphics.LightingColorFilter;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.LoaderManager;
|
||||||
|
import android.support.v4.content.CursorLoader;
|
||||||
|
import android.support.v4.content.Loader;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.data.InstalledAppProvider;
|
||||||
|
import org.fdroid.fdroid.views.installed.InstalledAppListAdapter;
|
||||||
|
|
||||||
|
public class SelectInstalledAppsActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
|
private InstalledAppListAdapter adapter;
|
||||||
|
private RecyclerView appList;
|
||||||
|
private TextView emptyState;
|
||||||
|
private int checkId;
|
||||||
|
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
((FDroidApp) getApplication()).applyTheme(this);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.installed_apps_layout);
|
||||||
|
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
toolbar.setTitle(getString(R.string.panic_add_apps_to_uninstall));
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
adapter = new SelectInstalledAppListAdapter(this);
|
||||||
|
|
||||||
|
appList = findViewById(R.id.app_list);
|
||||||
|
appList.setHasFixedSize(true);
|
||||||
|
appList.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
appList.setAdapter(adapter);
|
||||||
|
|
||||||
|
emptyState = findViewById(R.id.empty_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
prefs = Preferences.get();
|
||||||
|
|
||||||
|
// Starts a new or restarts an existing Loader in this manager
|
||||||
|
getSupportLoaderManager().restartLoader(0, null, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
|
return new CursorLoader(this, InstalledAppProvider.getAllAppsUri(), null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
||||||
|
adapter.setApps(cursor);
|
||||||
|
|
||||||
|
if (adapter.getItemCount() == 0) {
|
||||||
|
appList.setVisibility(View.GONE);
|
||||||
|
emptyState.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
appList.setVisibility(View.VISIBLE);
|
||||||
|
emptyState.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||||
|
adapter.setApps(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
MenuItem menuItem = menu.add(R.string.menu_select_for_wipe);
|
||||||
|
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||||
|
checkId = menuItem.getItemId();
|
||||||
|
if (FDroidApp.isAppThemeLight()) {
|
||||||
|
Resources resources = getResources();
|
||||||
|
Drawable icon = resources.getDrawable(R.drawable.check);
|
||||||
|
icon.setColorFilter(new LightingColorFilter(0xffffffff, resources.getColor(android.R.color.white)));
|
||||||
|
menuItem.setIcon(icon);
|
||||||
|
} else {
|
||||||
|
menuItem.setIcon(R.drawable.check);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ApplySharedPref")
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
finish();
|
||||||
|
if (item.getItemId() == checkId) {
|
||||||
|
prefs.setPanicWipeSet(prefs.getPanicTmpSelectedSet());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,6 @@ import org.fdroid.fdroid.R;
|
|||||||
import org.fdroid.fdroid.data.InstalledAppProvider;
|
import org.fdroid.fdroid.data.InstalledAppProvider;
|
||||||
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
|
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
|
||||||
import org.fdroid.fdroid.localrepo.LocalRepoService;
|
import org.fdroid.fdroid.localrepo.LocalRepoService;
|
||||||
import org.fdroid.fdroid.localrepo.SwapService;
|
|
||||||
import org.fdroid.fdroid.localrepo.SwapView;
|
import org.fdroid.fdroid.localrepo.SwapView;
|
||||||
|
|
||||||
public class SelectAppsView extends SwapView implements LoaderManager.LoaderCallbacks<Cursor> {
|
public class SelectAppsView extends SwapView implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
@ -61,7 +60,7 @@ public class SelectAppsView extends SwapView implements LoaderManager.LoaderCall
|
|||||||
listView = findViewById(R.id.list);
|
listView = findViewById(R.id.list);
|
||||||
adapter = new AppListAdapter(listView, getContext(),
|
adapter = new AppListAdapter(listView, getContext(),
|
||||||
getContext().getContentResolver().query(InstalledAppProvider.getContentUri(),
|
getContext().getContentResolver().query(InstalledAppProvider.getContentUri(),
|
||||||
InstalledAppTable.Cols.ALL, null, null, null));
|
null, null, null, null));
|
||||||
|
|
||||||
listView.setAdapter(adapter);
|
listView.setAdapter(adapter);
|
||||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||||
@ -97,13 +96,7 @@ public class SelectAppsView extends SwapView implements LoaderManager.LoaderCall
|
|||||||
} else {
|
} else {
|
||||||
uri = InstalledAppProvider.getSearchUri(currentFilterString);
|
uri = InstalledAppProvider.getSearchUri(currentFilterString);
|
||||||
}
|
}
|
||||||
return new CursorLoader(
|
return new CursorLoader(getActivity(), uri, null, null, null, null);
|
||||||
getActivity(),
|
|
||||||
uri,
|
|
||||||
InstalledAppTable.Cols.ALL,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
InstalledAppTable.Cols.APPLICATION_LABEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
8
app/src/full/res/drawable/check.xml
Normal file
8
app/src/full/res/drawable/check.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!-- drawable/check.xml -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||||
|
</vector>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources>
|
||||||
|
|
||||||
<color name="swap_incompatible">#ff7900</color>
|
<color name="swap_incompatible">#ff7900</color>
|
||||||
<color name="swap_light_blue">#27aae1</color>
|
<color name="swap_light_blue">#27aae1</color>
|
||||||
@ -10,4 +10,6 @@
|
|||||||
<color name="swap_grey_icon">#4a4a4a</color>
|
<color name="swap_grey_icon">#4a4a4a</color>
|
||||||
<color name="swap_light_grey_icon">#bbbbbb</color>
|
<color name="swap_light_grey_icon">#bbbbbb</color>
|
||||||
|
|
||||||
|
<color name="panic_destructive_light">#fff0f0</color>
|
||||||
|
<color name="panic_destructive_dark">#330000</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,13 +19,26 @@
|
|||||||
android:title="@string/panic_app_setting_title"
|
android:title="@string/panic_app_setting_title"
|
||||||
tools:icon="@drawable/ic_cancel"/>
|
tools:icon="@drawable/ic_cancel"/>
|
||||||
|
|
||||||
<android.support.v7.preference.CheckBoxPreference
|
<org.fdroid.fdroid.views.panic.DestructiveCheckBoxPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
android:key="pref_panic_hide"
|
android:key="pref_panic_hide"
|
||||||
android:summary="@string/panic_hide_summary"
|
android:summary="@string/panic_hide_summary"
|
||||||
android:title="@string/panic_hide_title"/>
|
android:title="@string/panic_hide_title"/>
|
||||||
|
|
||||||
|
<org.fdroid.fdroid.views.panic.DestructiveCheckBoxPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="pref_panic_reset_repos"
|
||||||
|
android:summary="@string/panic_reset_repos_summary"
|
||||||
|
android:title="@string/panic_reset_repos_title"/>
|
||||||
|
|
||||||
|
</android.support.v7.preference.PreferenceCategory>
|
||||||
|
|
||||||
|
<android.support.v7.preference.PreferenceCategory
|
||||||
|
android:key="pref_panic_apps_to_uninstall"
|
||||||
|
android:title="@string/panic_apps_to_uninstall">
|
||||||
|
|
||||||
</android.support.v7.preference.PreferenceCategory>
|
</android.support.v7.preference.PreferenceCategory>
|
||||||
|
|
||||||
</android.support.v7.preference.PreferenceScreen>
|
</android.support.v7.preference.PreferenceScreen>
|
@ -35,10 +35,12 @@ import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
|||||||
import org.fdroid.fdroid.net.ConnectivityMonitorService;
|
import org.fdroid.fdroid.net.ConnectivityMonitorService;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,6 +109,9 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
|||||||
public static final String PREF_PREVENT_SCREENSHOTS = "preventScreenshots";
|
public static final String PREF_PREVENT_SCREENSHOTS = "preventScreenshots";
|
||||||
public static final String PREF_PANIC_EXIT = "pref_panic_exit";
|
public static final String PREF_PANIC_EXIT = "pref_panic_exit";
|
||||||
public static final String PREF_PANIC_HIDE = "pref_panic_hide";
|
public static final String PREF_PANIC_HIDE = "pref_panic_hide";
|
||||||
|
public static final String PREF_PANIC_RESET_REPOS = "pref_panic_reset_repos";
|
||||||
|
public static final String PREF_PANIC_WIPE_SET = "panicWipeSet";
|
||||||
|
public static final String PREF_PANIC_TMP_SELECTED_SET = "panicTmpSelectedSet";
|
||||||
public static final String PREF_HIDE_ON_LONG_PRESS_SEARCH = "hideOnLongPressSearch";
|
public static final String PREF_HIDE_ON_LONG_PRESS_SEARCH = "hideOnLongPressSearch";
|
||||||
public static final String PREF_HIDE_ALL_NOTIFICATIONS = "hideAllNotifications";
|
public static final String PREF_HIDE_ALL_NOTIFICATIONS = "hideAllNotifications";
|
||||||
public static final String PREF_SEND_VERSION_AND_UUID_TO_SERVERS = "sendVersionAndUUIDToServers";
|
public static final String PREF_SEND_VERSION_AND_UUID_TO_SERVERS = "sendVersionAndUUIDToServers";
|
||||||
@ -511,10 +516,30 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
|||||||
return preferences.getBoolean(PREF_PANIC_HIDE, IGNORED_B);
|
return preferences.getBoolean(PREF_PANIC_HIDE, IGNORED_B);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean panicResetRepos() {
|
||||||
|
return preferences.getBoolean(PREF_PANIC_RESET_REPOS, IGNORED_B);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hideOnLongPressSearch() {
|
public boolean hideOnLongPressSearch() {
|
||||||
return preferences.getBoolean(PREF_HIDE_ON_LONG_PRESS_SEARCH, IGNORED_B);
|
return preferences.getBoolean(PREF_HIDE_ON_LONG_PRESS_SEARCH, IGNORED_B);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getPanicTmpSelectedSet() {
|
||||||
|
return preferences.getStringSet(Preferences.PREF_PANIC_TMP_SELECTED_SET, Collections.<String>emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPanicTmpSelectedSet(Set<String> selectedSet) {
|
||||||
|
preferences.edit().putStringSet(Preferences.PREF_PANIC_TMP_SELECTED_SET, selectedSet).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getPanicWipeSet() {
|
||||||
|
return preferences.getStringSet(Preferences.PREF_PANIC_WIPE_SET, Collections.<String>emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPanicWipeSet(Set<String> selectedSet) {
|
||||||
|
preferences.edit().putStringSet(Preferences.PREF_PANIC_WIPE_SET, selectedSet).apply();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preference for whitelabel builds that are meant to be entirely controlled
|
* Preference for whitelabel builds that are meant to be entirely controlled
|
||||||
* by the server, without user interaction, e.g. "appliances".
|
* by the server, without user interaction, e.g. "appliances".
|
||||||
|
@ -86,25 +86,38 @@ public class ApkProvider extends FDroidProvider {
|
|||||||
* <li>If installed, limit to apks signed by the same signer as the installed apk.</li>
|
* <li>If installed, limit to apks signed by the same signer as the installed apk.</li>
|
||||||
* <li>Otherwise, limit to apks signed by the "preferred" signer (see {@link App#preferredSigner}).</li>
|
* <li>Otherwise, limit to apks signed by the "preferred" signer (see {@link App#preferredSigner}).</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
* If all else fails, try to return some {@link Apk} that will install something,
|
||||||
|
* rather than returning a null and triggering a {@link NullPointerException}.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public static Apk findSuggestedApk(Context context, App app) {
|
public static Apk findSuggestedApk(Context context, App app) {
|
||||||
return findApkFromAnyRepo(context, app.packageName, app.suggestedVersionCode,
|
String mostAppropriateSignature = app.getMostAppropriateSignature();
|
||||||
app.getMostAppropriateSignature());
|
Apk apk = findApkFromAnyRepo(context, app.packageName, app.suggestedVersionCode,
|
||||||
|
mostAppropriateSignature);
|
||||||
|
if (apk == null && (mostAppropriateSignature == null || !app.isInstalled(context))) {
|
||||||
|
List<Apk> apks = findByPackageName(context, app.packageName);
|
||||||
|
for (Apk availableApk : apks) {
|
||||||
|
if (availableApk.sig.equals(mostAppropriateSignature)) {
|
||||||
|
apk = availableApk;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (apk == null && apks.size() > 0) {
|
||||||
|
apk = apks.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apk;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode) {
|
public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode) {
|
||||||
return findApkFromAnyRepo(context, packageName, versionCode, null, Cols.ALL);
|
return findApkFromAnyRepo(context, packageName, versionCode, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode,
|
public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode,
|
||||||
String signature) {
|
String signature) {
|
||||||
return findApkFromAnyRepo(context, packageName, versionCode, signature, Cols.ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode,
|
|
||||||
@Nullable String signature, String[] projection) {
|
|
||||||
final Uri uri = getApkFromAnyRepoUri(packageName, versionCode, signature);
|
final Uri uri = getApkFromAnyRepoUri(packageName, versionCode, signature);
|
||||||
return findByUri(context, uri, projection);
|
return findByUri(context, uri, Cols.ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Apk findByUri(Context context, Uri uri, String[] projection) {
|
public static Apk findByUri(Context context, Uri uri, String[] projection) {
|
||||||
|
@ -15,9 +15,12 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
|
import org.fdroid.fdroid.data.Schema.AppMetadataTable;
|
||||||
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
|
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
|
||||||
import org.fdroid.fdroid.data.Schema.InstalledAppTable.Cols;
|
import org.fdroid.fdroid.data.Schema.InstalledAppTable.Cols;
|
||||||
|
import org.fdroid.fdroid.data.Schema.PackageTable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -28,17 +31,33 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
|
|
||||||
public static class Helper {
|
public static class Helper {
|
||||||
|
|
||||||
|
public static App[] all(Context context) {
|
||||||
|
ArrayList<App> appList = new ArrayList<>();
|
||||||
|
Cursor cursor = context.getContentResolver().query(InstalledAppProvider.getAllAppsUri(),
|
||||||
|
null, null, null, null);
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.getCount() > 0) {
|
||||||
|
cursor.moveToFirst();
|
||||||
|
while (!cursor.isAfterLast()) {
|
||||||
|
appList.add(new App(cursor));
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return appList.toArray(new App[0]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The keys are the package names, and their corresponding values are
|
* @return The keys are the package names, and their corresponding values are
|
||||||
* the {@link PackageInfo#lastUpdateTime last update time} in milliseconds.
|
* the {@link PackageInfo#lastUpdateTime last update time} in milliseconds.
|
||||||
*/
|
*/
|
||||||
public static Map<String, Long> all(Context context) {
|
public static Map<String, Long> lastUpdateTimes(Context context) {
|
||||||
|
|
||||||
Map<String, Long> cachedInfo = new HashMap<>();
|
Map<String, Long> cachedInfo = new HashMap<>();
|
||||||
|
|
||||||
final Uri uri = InstalledAppProvider.getContentUri();
|
final Uri uri = InstalledAppProvider.getContentUri();
|
||||||
final String[] projection = Cols.ALL;
|
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||||
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
if (cursor.getCount() > 0) {
|
if (cursor.getCount() > 0) {
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
@ -58,7 +77,7 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static InstalledApp findByPackageName(Context context, String packageName) {
|
public static InstalledApp findByPackageName(Context context, String packageName) {
|
||||||
Cursor cursor = context.getContentResolver().query(getAppUri(packageName), Cols.ALL, null, null, null);
|
Cursor cursor = context.getContentResolver().query(getAppUri(packageName), null, null, null, null);
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -80,6 +99,8 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
|
|
||||||
private static final String PATH_SEARCH = "search";
|
private static final String PATH_SEARCH = "search";
|
||||||
private static final int CODE_SEARCH = CODE_SINGLE + 1;
|
private static final int CODE_SEARCH = CODE_SINGLE + 1;
|
||||||
|
private static final String PATH_ALL_APPS = "allApps";
|
||||||
|
private static final int CODE_ALL_APPS = CODE_SEARCH + 1;
|
||||||
|
|
||||||
private static final UriMatcher MATCHER = new UriMatcher(-1);
|
private static final UriMatcher MATCHER = new UriMatcher(-1);
|
||||||
|
|
||||||
@ -100,6 +121,7 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
static {
|
static {
|
||||||
MATCHER.addURI(getAuthority(), null, CODE_LIST);
|
MATCHER.addURI(getAuthority(), null, CODE_LIST);
|
||||||
MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*", CODE_SEARCH);
|
MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*", CODE_SEARCH);
|
||||||
|
MATCHER.addURI(getAuthority(), PATH_ALL_APPS, CODE_ALL_APPS);
|
||||||
MATCHER.addURI(getAuthority(), "*", CODE_SINGLE);
|
MATCHER.addURI(getAuthority(), "*", CODE_SINGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +129,10 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
return Uri.parse("content://" + getAuthority());
|
return Uri.parse("content://" + getAuthority());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Uri getAllAppsUri() {
|
||||||
|
return getContentUri().buildUpon().appendPath(PATH_ALL_APPS).build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link Uri} that points to a specific installed app
|
* @return the {@link Uri} that points to a specific installed app
|
||||||
*/
|
*/
|
||||||
@ -229,6 +255,7 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QuerySelection selection = new QuerySelection(customSelection, selectionArgs);
|
QuerySelection selection = new QuerySelection(customSelection, selectionArgs);
|
||||||
|
QueryBuilder query = null;
|
||||||
switch (MATCHER.match(uri)) {
|
switch (MATCHER.match(uri)) {
|
||||||
case CODE_LIST:
|
case CODE_LIST:
|
||||||
selection = selectNotSystemSignature(selection);
|
selection = selectNotSystemSignature(selection);
|
||||||
@ -242,16 +269,31 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
selection = selection.add(querySearch(uri.getLastPathSegment()));
|
selection = selection.add(querySearch(uri.getLastPathSegment()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CODE_ALL_APPS:
|
||||||
|
selection = selectNotSystemSignature(selection);
|
||||||
|
query = new QueryBuilder();
|
||||||
|
query.addField(Cols._ID);
|
||||||
|
query.appendField(Cols.APPLICATION_LABEL, null, Schema.AppMetadataTable.Cols.NAME);
|
||||||
|
query.appendField(Cols.VERSION_CODE, null, AppMetadataTable.Cols.UPSTREAM_VERSION_CODE);
|
||||||
|
query.appendField(Cols.VERSION_NAME, null, AppMetadataTable.Cols.UPSTREAM_VERSION_NAME);
|
||||||
|
query.appendField(PackageTable.Cols.PACKAGE_NAME, PackageTable.NAME,
|
||||||
|
AppMetadataTable.Cols.Package.PACKAGE_NAME);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
String message = "Invalid URI for installed app content provider: " + uri;
|
String message = "Invalid URI for installed app content provider: " + uri;
|
||||||
Log.e(TAG, message);
|
Log.e(TAG, message);
|
||||||
throw new UnsupportedOperationException(message);
|
throw new UnsupportedOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryBuilder query = new QueryBuilder();
|
if (query != null) { // NOPMD
|
||||||
|
// the fields are already setup above
|
||||||
|
} else if (projection == null || projection.length == 0) {
|
||||||
|
query = new QueryBuilder();
|
||||||
|
query.addFields(Cols.ALL);
|
||||||
|
} else {
|
||||||
|
query = new QueryBuilder();
|
||||||
query.addFields(projection);
|
query.addFields(projection);
|
||||||
if (projection.length == 0) {
|
|
||||||
query.addField(Cols._ID);
|
|
||||||
}
|
}
|
||||||
query.addSelection(selection);
|
query.addSelection(selection);
|
||||||
query.addOrderBy(sortOrder);
|
query.addOrderBy(sortOrder);
|
||||||
@ -279,6 +321,12 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Cols.Package#NAME} is not included in the database here, because
|
||||||
|
* it is included only in the {@link PackageTable}, since there are large
|
||||||
|
* cross-table queries needed to handle the complexity of multiple repos
|
||||||
|
* potentially serving the same apps.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ public class InstalledAppProviderService extends JobIntentService {
|
|||||||
*/
|
*/
|
||||||
public static void compareToPackageManager(Context context) {
|
public static void compareToPackageManager(Context context) {
|
||||||
Utils.debugLog(TAG, "Comparing package manager to our installed app cache.");
|
Utils.debugLog(TAG, "Comparing package manager to our installed app cache.");
|
||||||
Map<String, Long> cachedInfo = InstalledAppProvider.Helper.all(context);
|
Map<String, Long> cachedInfo = InstalledAppProvider.Helper.lastUpdateTimes(context);
|
||||||
|
|
||||||
List<PackageInfo> packageInfoList = context.getPackageManager()
|
List<PackageInfo> packageInfoList = context.getPackageManager()
|
||||||
.getInstalledPackages(PackageManager.GET_SIGNATURES);
|
.getInstalledPackages(PackageManager.GET_SIGNATURES);
|
||||||
|
@ -7,6 +7,7 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@ -22,15 +23,15 @@ import android.text.TextUtils;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewOutlineProvider;
|
import android.view.ViewOutlineProvider;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
|
||||||
import org.fdroid.fdroid.AppUpdateStatusManager;
|
import org.fdroid.fdroid.AppUpdateStatusManager;
|
||||||
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
|
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
@ -45,6 +46,7 @@ import org.fdroid.fdroid.views.updates.UpdatesAdapter;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supports the following layouts:
|
* Supports the following layouts:
|
||||||
@ -64,6 +66,8 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
private static final String TAG = "AppListItemController";
|
private static final String TAG = "AppListItemController";
|
||||||
|
|
||||||
|
private static Preferences prefs;
|
||||||
|
|
||||||
protected final Activity activity;
|
protected final Activity activity;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -97,6 +101,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private final Button secondaryButton;
|
private final Button secondaryButton;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final CheckBox checkBox;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private App currentApp;
|
private App currentApp;
|
||||||
|
|
||||||
@ -107,6 +114,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
public AppListItemController(final Activity activity, View itemView) {
|
public AppListItemController(final Activity activity, View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
|
if (prefs == null) {
|
||||||
|
prefs = Preferences.get();
|
||||||
|
}
|
||||||
|
|
||||||
installButton = (ImageView) itemView.findViewById(R.id.install);
|
installButton = (ImageView) itemView.findViewById(R.id.install);
|
||||||
if (installButton != null) {
|
if (installButton != null) {
|
||||||
@ -145,6 +155,7 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
cancelButton = (ImageButton) itemView.findViewById(R.id.cancel_button);
|
cancelButton = (ImageButton) itemView.findViewById(R.id.cancel_button);
|
||||||
actionButton = (Button) itemView.findViewById(R.id.action_button);
|
actionButton = (Button) itemView.findViewById(R.id.action_button);
|
||||||
secondaryButton = (Button) itemView.findViewById(R.id.secondary_button);
|
secondaryButton = (Button) itemView.findViewById(R.id.secondary_button);
|
||||||
|
checkBox = itemView.findViewById(R.id.checkbox);
|
||||||
|
|
||||||
if (actionButton != null) {
|
if (actionButton != null) {
|
||||||
actionButton.setEnabled(true);
|
actionButton.setEnabled(true);
|
||||||
@ -176,7 +187,15 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
public void bindModel(@NonNull App app) {
|
public void bindModel(@NonNull App app) {
|
||||||
currentApp = app;
|
currentApp = app;
|
||||||
|
|
||||||
|
if (app.iconUrl == null) {
|
||||||
|
try {
|
||||||
|
icon.setImageDrawable(activity.getPackageManager().getApplicationIcon(app.packageName));
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
} else {
|
||||||
ImageLoader.getInstance().displayImage(app.iconUrl, icon, Utils.getRepoAppDisplayImageOptions());
|
ImageLoader.getInstance().displayImage(app.iconUrl, icon, Utils.getRepoAppDisplayImageOptions());
|
||||||
|
}
|
||||||
|
|
||||||
// Figures out the current install/update/download/etc status for the app we are viewing.
|
// Figures out the current install/update/download/etc status for the app we are viewing.
|
||||||
// Then, asks the view to update itself to reflect this status.
|
// Then, asks the view to update itself to reflect this status.
|
||||||
@ -220,9 +239,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Override to respond to the user swiping an app to dismiss it from the list.
|
* Override to respond to the user swiping an app to dismiss it from the list.
|
||||||
|
*
|
||||||
* @param app The app that was swiped away
|
* @param app The app that was swiped away
|
||||||
* @param updatesAdapter The adapter. Can be used for refreshing the adapter with adapter.refreshStatuses().
|
* @param updatesAdapter The adapter. Can be used for refreshing the adapter with adapter.refreshStatuses().
|
||||||
*
|
|
||||||
* @see #canDismiss() This must also be overridden and should return true.
|
* @see #canDismiss() This must also be overridden and should return true.
|
||||||
*/
|
*/
|
||||||
protected void onDismissApp(@NonNull App app, UpdatesAdapter updatesAdapter) {
|
protected void onDismissApp(@NonNull App app, UpdatesAdapter updatesAdapter) {
|
||||||
@ -328,6 +347,18 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
secondaryStatus.setText(statusText);
|
secondaryStatus.setText(statusText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (checkBox != null) {
|
||||||
|
if (viewState.shouldShowCheckBox()) {
|
||||||
|
itemView.setOnClickListener(selectInstalledAppListener);
|
||||||
|
checkBox.setChecked(viewState.isCheckBoxChecked());
|
||||||
|
checkBox.setVisibility(View.VISIBLE);
|
||||||
|
status.setVisibility(View.GONE);
|
||||||
|
secondaryStatus.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
checkBox.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -533,4 +564,18 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
InstallManagerService.cancel(activity, currentStatus.getCanonicalUrl());
|
InstallManagerService.cancel(activity, currentStatus.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final View.OnClickListener selectInstalledAppListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Set<String> wipeSet = prefs.getPanicTmpSelectedSet();
|
||||||
|
checkBox.toggle();
|
||||||
|
if (checkBox.isChecked()) {
|
||||||
|
wipeSet.add(currentApp.packageName);
|
||||||
|
} else {
|
||||||
|
wipeSet.remove(currentApp.packageName);
|
||||||
|
}
|
||||||
|
prefs.setPanicTmpSelectedSet(wipeSet);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@ package org.fdroid.fdroid.views.apps;
|
|||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.data.App;
|
import org.fdroid.fdroid.data.App;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dumb model which is used to specify what should/should not be shown in an {@link AppListItemController}.
|
* A dumb model which is used to specify what should/should not be shown in an {@link AppListItemController}.
|
||||||
|
*
|
||||||
* @see AppListItemController and its subclasses.
|
* @see AppListItemController and its subclasses.
|
||||||
*/
|
*/
|
||||||
public class AppListItemState {
|
public class AppListItemState {
|
||||||
@ -20,6 +20,8 @@ public class AppListItemState {
|
|||||||
private int progressCurrent = -1;
|
private int progressCurrent = -1;
|
||||||
private int progressMax = -1;
|
private int progressMax = -1;
|
||||||
private boolean showInstallButton;
|
private boolean showInstallButton;
|
||||||
|
private boolean showCheckBox;
|
||||||
|
private boolean isCheckBoxChecked;
|
||||||
|
|
||||||
public AppListItemState(@NonNull App app) {
|
public AppListItemState(@NonNull App app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
@ -63,6 +65,9 @@ public class AppListItemState {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public CharSequence getMainText() {
|
public CharSequence getMainText() {
|
||||||
|
if (showCheckBox) {
|
||||||
|
return app.name;
|
||||||
|
}
|
||||||
return mainText != null
|
return mainText != null
|
||||||
? mainText
|
? mainText
|
||||||
: Utils.formatAppNameAndSummary(app.name, app.summary);
|
: Utils.formatAppNameAndSummary(app.name, app.summary);
|
||||||
@ -113,4 +118,23 @@ public class AppListItemState {
|
|||||||
public CharSequence getSecondaryStatusText() {
|
public CharSequence getSecondaryStatusText() {
|
||||||
return secondaryStatusText;
|
return secondaryStatusText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean shouldShowCheckBox() {
|
||||||
|
return showCheckBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCheckBoxChecked() {
|
||||||
|
return isCheckBoxChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the {@link android.widget.CheckBox} display and set the on/off status
|
||||||
|
* e.g. {@link android.widget.CheckBox#isChecked()}
|
||||||
|
*/
|
||||||
|
public AppListItemState setCheckBoxStatus(boolean checked) {
|
||||||
|
this.showCheckBox = true;
|
||||||
|
this.isCheckBoxChecked = checked;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package org.fdroid.fdroid.views.installed;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@ -9,16 +10,15 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.data.App;
|
import org.fdroid.fdroid.data.App;
|
||||||
import org.fdroid.fdroid.data.Schema;
|
|
||||||
|
|
||||||
class InstalledAppListAdapter extends RecyclerView.Adapter<InstalledAppListItemController> {
|
public class InstalledAppListAdapter extends RecyclerView.Adapter<InstalledAppListItemController> {
|
||||||
|
|
||||||
private final Activity activity;
|
protected final Activity activity;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Cursor cursor;
|
private Cursor cursor;
|
||||||
|
|
||||||
InstalledAppListAdapter(Activity activity) {
|
protected InstalledAppListAdapter(Activity activity) {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
}
|
}
|
||||||
@ -30,7 +30,8 @@ class InstalledAppListAdapter extends RecyclerView.Adapter<InstalledAppListItemC
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.moveToPosition(position);
|
cursor.moveToPosition(position);
|
||||||
return cursor.getLong(cursor.getColumnIndex(Schema.AppMetadataTable.Cols.ROW_ID));
|
// TODO this should be based on Schema.InstalledAppProvider.Cols._ID
|
||||||
|
return cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -71,4 +71,14 @@
|
|||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp" />
|
android:layout_marginLeft="8dp" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/icon"
|
||||||
|
android:clickable="false"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
@ -206,6 +206,7 @@ This often occurs with apps installed via Google Play or other sources, if they
|
|||||||
<string name="menu_install">Install</string>
|
<string name="menu_install">Install</string>
|
||||||
<!-- This is a button label, it must be the right size, or the layout gets messed up. It should ideally be 10-20 characters. -->
|
<!-- This is a button label, it must be the right size, or the layout gets messed up. It should ideally be 10-20 characters. -->
|
||||||
<string name="menu_uninstall">Uninstall</string>
|
<string name="menu_uninstall">Uninstall</string>
|
||||||
|
<string name="menu_select_for_wipe">Select for wipe</string>
|
||||||
<string name="menu_ignore_all">Ignore All Updates</string>
|
<string name="menu_ignore_all">Ignore All Updates</string>
|
||||||
<string name="menu_ignore_this">Ignore This Update</string>
|
<string name="menu_ignore_this">Ignore This Update</string>
|
||||||
<string name="menu_website">Website</string>
|
<string name="menu_website">Website</string>
|
||||||
@ -323,13 +324,18 @@ This often occurs with apps installed via Google Play or other sources, if they
|
|||||||
<!-- "panic button" is a well known concept in English, it is good to find a similar metaphor in your language -->
|
<!-- "panic button" is a well known concept in English, it is good to find a similar metaphor in your language -->
|
||||||
<string name="panic_settings">Panic button settings</string>
|
<string name="panic_settings">Panic button settings</string>
|
||||||
<string name="panic_settings_summary">Actions to be taken in case of emergency</string>
|
<string name="panic_settings_summary">Actions to be taken in case of emergency</string>
|
||||||
<string name="panic_exit_title">Exit App</string>
|
<string name="panic_exit_title">Exit app</string>
|
||||||
<string name="panic_exit_summary">App will be closed </string>
|
<string name="panic_exit_summary">This app will be closed</string>
|
||||||
<string name="panic_destructive_actions">Destructive Actions</string>
|
<string name="panic_destructive_actions">Destructive Actions</string>
|
||||||
|
<string name="panic_will_be_wiped">Will be uninstalled and all data deleted</string>
|
||||||
|
<string name="panic_apps_to_uninstall">Apps to be uninstalled and all data wiped</string>
|
||||||
|
<string name="panic_add_apps_to_uninstall">Add apps to be uninstalled and wiped</string>
|
||||||
<string name="panic_hide_title">Hide %s</string>
|
<string name="panic_hide_title">Hide %s</string>
|
||||||
<string name="panic_hide_summary">App will hide itself</string>
|
<string name="panic_hide_summary">App will hide itself</string>
|
||||||
<string name="panic_hide_warning_title">Remember how to restore</string>
|
<string name="panic_hide_warning_title">Remember how to restore</string>
|
||||||
<string name="panic_hide_warning_message">In a panic event, this will remove %1$s from the launcher. Only typing \"%2$d\" in the fake %3$s app can restore it.</string>
|
<string name="panic_hide_warning_message">In a panic event, this will remove %1$s from the launcher. Only typing \"%2$d\" in the fake %3$s app can restore it.</string>
|
||||||
|
<string name="panic_reset_repos_title">Reset repos</string>
|
||||||
|
<string name="panic_reset_repos_summary">Force the repo setup back to defaults</string>
|
||||||
|
|
||||||
<!-- The name of a fake calculator app used as a disguise -->
|
<!-- The name of a fake calculator app used as a disguise -->
|
||||||
<string name="hiding_calculator">Calculator</string>
|
<string name="hiding_calculator">Calculator</string>
|
||||||
|
@ -392,23 +392,6 @@ public class ApkProviderTest extends FDroidProviderTest {
|
|||||||
assertEquals("xxxxyyyy", apk.hash);
|
assertEquals("xxxxyyyy", apk.hash);
|
||||||
assertEquals("a hash type", apk.hashType);
|
assertEquals("a hash type", apk.hashType);
|
||||||
|
|
||||||
String[] projection = {
|
|
||||||
Cols.Package.PACKAGE_NAME,
|
|
||||||
Cols.HASH,
|
|
||||||
};
|
|
||||||
|
|
||||||
Apk apkLessFields = ApkProvider.Helper.findApkFromAnyRepo(context, "com.example", 11, null, projection);
|
|
||||||
|
|
||||||
assertNotNull(apkLessFields);
|
|
||||||
|
|
||||||
assertEquals("com.example", apkLessFields.packageName);
|
|
||||||
assertEquals("xxxxyyyy", apkLessFields.hash);
|
|
||||||
|
|
||||||
// Didn't ask for these fields, so should be their default values...
|
|
||||||
assertNull(apkLessFields.hashType);
|
|
||||||
assertNull(apkLessFields.versionName);
|
|
||||||
assertEquals(0, apkLessFields.versionCode);
|
|
||||||
|
|
||||||
Apk notFound = ApkProvider.Helper.findApkFromAnyRepo(context, "com.doesnt.exist", 1000);
|
Apk notFound = ApkProvider.Helper.findApkFromAnyRepo(context, "com.doesnt.exist", 1000);
|
||||||
assertNull(notFound);
|
assertNull(notFound);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.fdroid.fdroid.data;
|
package org.fdroid.fdroid.data;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
import org.fdroid.fdroid.TestUtils;
|
import org.fdroid.fdroid.TestUtils;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@ -26,4 +27,10 @@ public abstract class FDroidProviderTest {
|
|||||||
DBHelper.clearDbHelperSingleton();
|
DBHelper.clearDbHelperSingleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Repo setEnabled(Repo repo, boolean enabled) {
|
||||||
|
ContentValues enable = new ContentValues(1);
|
||||||
|
enable.put(Schema.RepoTable.Cols.IN_USE, enabled);
|
||||||
|
RepoProvider.Helper.update(context, repo, enable);
|
||||||
|
return RepoProvider.Helper.findByAddress(context, repo.address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import java.util.Map;
|
|||||||
import static org.fdroid.fdroid.Assert.assertIsInstalledVersionInDb;
|
import static org.fdroid.fdroid.Assert.assertIsInstalledVersionInDb;
|
||||||
import static org.fdroid.fdroid.Assert.assertResultCount;
|
import static org.fdroid.fdroid.Assert.assertResultCount;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void insertSingleApp() {
|
public void insertSingleApp() {
|
||||||
Map<String, Long> foundBefore = InstalledAppProvider.Helper.all(RuntimeEnvironment.application);
|
Map<String, Long> foundBefore = InstalledAppProvider.Helper.lastUpdateTimes(RuntimeEnvironment.application);
|
||||||
assertEquals(foundBefore.size(), 0);
|
assertEquals(foundBefore.size(), 0);
|
||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
@ -49,7 +50,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
|
|||||||
values.put(Cols.SIGNATURE, "000111222333444555666777888999aaabbbcccdddeeefff");
|
values.put(Cols.SIGNATURE, "000111222333444555666777888999aaabbbcccdddeeefff");
|
||||||
contentResolver.insert(InstalledAppProvider.getContentUri(), values);
|
contentResolver.insert(InstalledAppProvider.getContentUri(), values);
|
||||||
|
|
||||||
Map<String, Long> foundAfter = InstalledAppProvider.Helper.all(RuntimeEnvironment.application);
|
Map<String, Long> foundAfter = InstalledAppProvider.Helper.lastUpdateTimes(RuntimeEnvironment.application);
|
||||||
assertEquals(1, foundAfter.size());
|
assertEquals(1, foundAfter.size());
|
||||||
assertEquals(100000000L, foundAfter.get("org.example.test-app").longValue());
|
assertEquals(100000000L, foundAfter.get("org.example.test-app").longValue());
|
||||||
|
|
||||||
@ -71,6 +72,39 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
|
|||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHelperAll() {
|
||||||
|
final String packageName0 = "com.0";
|
||||||
|
final String packageName1 = "com.1";
|
||||||
|
final String packageName2 = "com.2";
|
||||||
|
|
||||||
|
App[] apps = InstalledAppProvider.Helper.all(context);
|
||||||
|
assertEquals(0, apps.length);
|
||||||
|
|
||||||
|
insertInstalledApp(packageName0, 0, "v0");
|
||||||
|
insertInstalledApp(packageName1, 1, "v1");
|
||||||
|
insertInstalledApp(packageName2, 2, "v2");
|
||||||
|
|
||||||
|
assertResultCount(contentResolver, 3, InstalledAppProvider.getContentUri());
|
||||||
|
assertResultCount(contentResolver, 3, InstalledAppProvider.getAllAppsUri());
|
||||||
|
assertIsInstalledVersionInDb(contentResolver, packageName0, 0, "v0");
|
||||||
|
assertIsInstalledVersionInDb(contentResolver, packageName1, 1, "v1");
|
||||||
|
assertIsInstalledVersionInDb(contentResolver, packageName2, 2, "v2");
|
||||||
|
|
||||||
|
apps = InstalledAppProvider.Helper.all(context);
|
||||||
|
assertEquals(3, apps.length);
|
||||||
|
assertEquals(packageName0, apps[0].packageName);
|
||||||
|
assertEquals("v0", apps[0].upstreamVersionName);
|
||||||
|
assertEquals(0, apps[0].upstreamVersionCode);
|
||||||
|
assertEquals(packageName1, apps[1].packageName);
|
||||||
|
assertEquals("v1", apps[1].upstreamVersionName);
|
||||||
|
assertEquals(1, apps[1].upstreamVersionCode);
|
||||||
|
assertEquals(packageName2, apps[2].packageName);
|
||||||
|
assertEquals("v2", apps[2].upstreamVersionName);
|
||||||
|
assertEquals(2, apps[2].upstreamVersionCode);
|
||||||
|
assertNotEquals(packageName0, apps[2].packageName);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsert() {
|
public void testInsert() {
|
||||||
|
|
||||||
@ -84,6 +118,9 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
|
|||||||
assertIsInstalledVersionInDb(contentResolver, "com.example.com1", 1, "v1");
|
assertIsInstalledVersionInDb(contentResolver, "com.example.com1", 1, "v1");
|
||||||
assertIsInstalledVersionInDb(contentResolver, "com.example.com2", 2, "v2");
|
assertIsInstalledVersionInDb(contentResolver, "com.example.com2", 2, "v2");
|
||||||
assertIsInstalledVersionInDb(contentResolver, "com.example.com3", 3, "v3");
|
assertIsInstalledVersionInDb(contentResolver, "com.example.com3", 3, "v3");
|
||||||
|
|
||||||
|
App[] apps = InstalledAppProvider.Helper.all(context);
|
||||||
|
assertEquals(3, apps.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -77,13 +77,6 @@ public class RepoProviderTest extends FDroidProviderTest {
|
|||||||
assertEquals(0, RepoProvider.Helper.countEnabledRepos(context));
|
assertEquals(0, RepoProvider.Helper.countEnabledRepos(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Repo setEnabled(Repo repo, boolean enabled) {
|
|
||||||
ContentValues enable = new ContentValues(1);
|
|
||||||
enable.put(RepoTable.Cols.IN_USE, enabled);
|
|
||||||
RepoProvider.Helper.update(context, repo, enable);
|
|
||||||
return RepoProvider.Helper.findByAddress(context, repo.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lastUpdated() {
|
public void lastUpdated() {
|
||||||
assertNull(RepoProvider.Helper.lastUpdate(context));
|
assertNull(RepoProvider.Helper.lastUpdate(context));
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package org.fdroid.fdroid.updater;
|
package org.fdroid.fdroid.updater;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
@ -66,7 +66,7 @@ public class AppIconsTest extends MultiIndexUpdaterTest {
|
|||||||
|
|
||||||
private void assertIconUrl(String expectedUrl) {
|
private void assertIconUrl(String expectedUrl) {
|
||||||
App app = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(),
|
App app = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(),
|
||||||
"org.adaway", new String[] {Schema.AppMetadataTable.Cols.ICON_URL});
|
"org.adaway", new String[]{Schema.AppMetadataTable.Cols.ICON_URL});
|
||||||
|
|
||||||
assertEquals(app.iconUrl, expectedUrl);
|
assertEquals(app.iconUrl, expectedUrl);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package org.fdroid.fdroid.updater;
|
package org.fdroid.fdroid.updater;
|
||||||
|
|
||||||
import org.fdroid.fdroid.BuildConfig;
|
import org.fdroid.fdroid.BuildConfig;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.fdroid.fdroid.updater;
|
package org.fdroid.fdroid.updater;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import org.fdroid.fdroid.BuildConfig;
|
import org.fdroid.fdroid.BuildConfig;
|
||||||
import org.fdroid.fdroid.IndexUpdater;
|
import org.fdroid.fdroid.IndexUpdater;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
@ -35,12 +34,6 @@ public class Issue763MultiRepo extends MultiIndexUpdaterTest {
|
|||||||
antoxRepo = createRepo("Tox", "https://pkg.tox.chat/fdroid/repo", context, antoxCert);
|
antoxRepo = createRepo("Tox", "https://pkg.tox.chat/fdroid/repo", context, antoxCert);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(Repo repo, boolean enabled) {
|
|
||||||
ContentValues values = new ContentValues(1);
|
|
||||||
values.put(Schema.RepoTable.Cols.IN_USE, enabled ? 1 : 0);
|
|
||||||
RepoProvider.Helper.update(context, repo, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void antoxRepo() throws IndexUpdater.UpdateException {
|
public void antoxRepo() throws IndexUpdater.UpdateException {
|
||||||
assertAntoxEmpty();
|
assertAntoxEmpty();
|
||||||
@ -111,7 +104,7 @@ public class Issue763MultiRepo extends MultiIndexUpdaterTest {
|
|||||||
private void assertAntoxExists() {
|
private void assertAntoxExists() {
|
||||||
String packageName = "chat.tox.antox";
|
String packageName = "chat.tox.antox";
|
||||||
List<Apk> actualApksAfterUpdate = ApkProvider.Helper.findByRepo(context, antoxRepo, Schema.ApkTable.Cols.ALL);
|
List<Apk> actualApksAfterUpdate = ApkProvider.Helper.findByRepo(context, antoxRepo, Schema.ApkTable.Cols.ALL);
|
||||||
int[] expectedVersions = new int[] {15421};
|
int[] expectedVersions = new int[]{15421};
|
||||||
|
|
||||||
assertApp(packageName, expectedVersions);
|
assertApp(packageName, expectedVersions);
|
||||||
assertApksExist(actualApksAfterUpdate, packageName, expectedVersions);
|
assertApksExist(actualApksAfterUpdate, packageName, expectedVersions);
|
||||||
@ -121,17 +114,17 @@ public class Issue763MultiRepo extends MultiIndexUpdaterTest {
|
|||||||
List<Apk> actualApksAfterUpdate = ApkProvider.Helper.findByRepo(context, microGRepo, Schema.ApkTable.Cols.ALL);
|
List<Apk> actualApksAfterUpdate = ApkProvider.Helper.findByRepo(context, microGRepo, Schema.ApkTable.Cols.ALL);
|
||||||
|
|
||||||
String vendingPackage = "com.android.vending";
|
String vendingPackage = "com.android.vending";
|
||||||
int[] expectedVendingVersions = new int[] {1};
|
int[] expectedVendingVersions = new int[]{1};
|
||||||
assertApp(vendingPackage, expectedVendingVersions);
|
assertApp(vendingPackage, expectedVendingVersions);
|
||||||
assertApksExist(actualApksAfterUpdate, vendingPackage, expectedVendingVersions);
|
assertApksExist(actualApksAfterUpdate, vendingPackage, expectedVendingVersions);
|
||||||
|
|
||||||
String gmsPackage = "com.google.android.gms";
|
String gmsPackage = "com.google.android.gms";
|
||||||
int[] expectedGmsVersions = new int[] {11059462, 10545451, 10545440, 10087438, 10087435, 9258259, 8492252, };
|
int[] expectedGmsVersions = new int[]{11059462, 10545451, 10545440, 10087438, 10087435, 9258259, 8492252};
|
||||||
assertApp(gmsPackage, expectedGmsVersions);
|
assertApp(gmsPackage, expectedGmsVersions);
|
||||||
assertApksExist(actualApksAfterUpdate, gmsPackage, expectedGmsVersions);
|
assertApksExist(actualApksAfterUpdate, gmsPackage, expectedGmsVersions);
|
||||||
|
|
||||||
String gsfPackage = "com.google.android.gsf";
|
String gsfPackage = "com.google.android.gsf";
|
||||||
int[] expectedGsfVersions = new int[] {8};
|
int[] expectedGsfVersions = new int[]{8};
|
||||||
assertApp(gsfPackage, expectedGsfVersions);
|
assertApp(gsfPackage, expectedGsfVersions);
|
||||||
assertApksExist(actualApksAfterUpdate, gsfPackage, expectedGsfVersions);
|
assertApksExist(actualApksAfterUpdate, gsfPackage, expectedGsfVersions);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
|
|
||||||
package org.fdroid.fdroid.updater;
|
package org.fdroid.fdroid.updater;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import org.fdroid.fdroid.IndexV1Updater;
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
import org.fdroid.fdroid.IndexUpdater;
|
import org.fdroid.fdroid.IndexUpdater;
|
||||||
import org.fdroid.fdroid.IndexUpdater.UpdateException;
|
import org.fdroid.fdroid.IndexUpdater.UpdateException;
|
||||||
|
import org.fdroid.fdroid.IndexV1Updater;
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.TestUtils;
|
import org.fdroid.fdroid.TestUtils;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
import org.fdroid.fdroid.data.ApkProvider;
|
import org.fdroid.fdroid.data.ApkProvider;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package org.fdroid.fdroid.updater;
|
package org.fdroid.fdroid.updater;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@ -41,7 +40,8 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest {
|
|||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@StringDef({"Conflicting", "Normal"})
|
@StringDef({"Conflicting", "Normal"})
|
||||||
public @interface RepoIdentifier { }
|
public @interface RepoIdentifier {
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void appsRemovedFromRepo() throws IndexUpdater.UpdateException {
|
public void appsRemovedFromRepo() throws IndexUpdater.UpdateException {
|
||||||
@ -310,7 +310,9 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest {
|
|||||||
assertAdAwayMetadata(adaway, id);
|
assertAdAwayMetadata(adaway, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) */
|
/**
|
||||||
|
* @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String)
|
||||||
|
*/
|
||||||
private void assertAdAwayMetadata(App adaway, @RepoIdentifier String id) {
|
private void assertAdAwayMetadata(App adaway, @RepoIdentifier String id) {
|
||||||
assertNotNull(adaway);
|
assertNotNull(adaway);
|
||||||
assertEquals(String.format("AdAway", id),
|
assertEquals(String.format("AdAway", id),
|
||||||
@ -338,7 +340,9 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest {
|
|||||||
assertAdbMetadata(adb, id);
|
assertAdbMetadata(adb, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) */
|
/**
|
||||||
|
* @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String)
|
||||||
|
*/
|
||||||
private void assertAdbMetadata(App adb, @RepoIdentifier String id) {
|
private void assertAdbMetadata(App adb, @RepoIdentifier String id) {
|
||||||
assertNotNull(adb);
|
assertNotNull(adb);
|
||||||
assertEquals("adbWireless", adb.name);
|
assertEquals("adbWireless", adb.name);
|
||||||
@ -355,7 +359,9 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest {
|
|||||||
assertCalendarMetadata(calendar, id);
|
assertCalendarMetadata(calendar, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) */
|
/**
|
||||||
|
* @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String)
|
||||||
|
*/
|
||||||
private void assertCalendarMetadata(App calendar, @RepoIdentifier String id) {
|
private void assertCalendarMetadata(App calendar, @RepoIdentifier String id) {
|
||||||
assertNotNull(calendar);
|
assertNotNull(calendar);
|
||||||
assertEquals("Add to calendar",
|
assertEquals("Add to calendar",
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package org.fdroid.fdroid.views.panic;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.data.DBHelper;
|
||||||
|
import org.fdroid.fdroid.data.FDroidProviderTest;
|
||||||
|
import org.fdroid.fdroid.data.Repo;
|
||||||
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
|
import org.fdroid.fdroid.data.RepoProviderTest;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class PanicResponderActivityTest extends FDroidProviderTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link DBHelper} class populates the default repos when it first creates a database.
|
||||||
|
* The names/URLs/signing certificates for these repos are all hard coded in the source/res.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void defaultRepos() {
|
||||||
|
List<Repo> defaultRepos = RepoProvider.Helper.all(context);
|
||||||
|
assertEquals(defaultRepos.size(), 4); // based on app/src/main/res/default_repo.xml
|
||||||
|
|
||||||
|
Repo gpRepo = RepoProvider.Helper.findByAddress(context, "https://guardianproject.info/fdroid/repo");
|
||||||
|
setEnabled(gpRepo, true);
|
||||||
|
assertEquals(2, RepoProvider.Helper.countEnabledRepos(context));
|
||||||
|
|
||||||
|
PanicResponderActivity.resetRepos(context);
|
||||||
|
assertEquals(1, RepoProvider.Helper.countEnabledRepos(context));
|
||||||
|
defaultRepos = RepoProvider.Helper.all(context);
|
||||||
|
assertEquals(4, defaultRepos.size());
|
||||||
|
|
||||||
|
RepoProviderTest.insertRepo(
|
||||||
|
context,
|
||||||
|
"https://mock-repo-1.example.com/fdroid/repo",
|
||||||
|
"Just a made up repo",
|
||||||
|
"ABCDEF1234567890",
|
||||||
|
"Mock Repo 1"
|
||||||
|
);
|
||||||
|
defaultRepos = RepoProvider.Helper.all(context);
|
||||||
|
assertEquals(5, defaultRepos.size());
|
||||||
|
assertEquals(2, RepoProvider.Helper.countEnabledRepos(context));
|
||||||
|
|
||||||
|
PanicResponderActivity.resetRepos(context);
|
||||||
|
defaultRepos = RepoProvider.Helper.all(context);
|
||||||
|
assertEquals(4, defaultRepos.size());
|
||||||
|
assertEquals(1, RepoProvider.Helper.countEnabledRepos(context));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user