set up Panic preference to choose apps to uninstall on trigger

This commit is contained in:
Hans-Christoph Steiner 2019-05-28 16:33:27 +02:00
parent 3812331166
commit 515b5b08f0
13 changed files with 383 additions and 10 deletions

View File

@ -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"

View File

@ -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,15 +21,18 @@ 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 {
@ -39,6 +43,7 @@ public class PanicPreferencesFragment extends PreferenceFragment
private ListPreference prefApp; private ListPreference prefApp;
private CheckBoxPreference prefExit; private CheckBoxPreference prefExit;
private CheckBoxPreference prefHide; private CheckBoxPreference prefHide;
private PreferenceCategory categoryAppsToUninstall;
@Override @Override
public void onCreatePreferences(Bundle bundle, String s) { public void onCreatePreferences(Bundle bundle, String s) {
@ -49,6 +54,7 @@ public class PanicPreferencesFragment extends PreferenceFragment
prefApp = (ListPreference) findPreference(PREF_APP); prefApp = (ListPreference) findPreference(PREF_APP);
prefHide = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_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)));
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
@ -86,6 +92,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 Preference(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
@ -156,6 +204,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 +212,7 @@ 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);
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);

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,144 @@
/*
* 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.AppProvider;
import org.fdroid.fdroid.data.Schema;
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,
AppProvider.getInstalledUri(),
Schema.AppMetadataTable.Cols.ALL,
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);
}
}

View 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>

View File

@ -28,4 +28,10 @@
</android.support.v7.preference.PreferenceCategory> </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.PreferenceScreen> </android.support.v7.preference.PreferenceScreen>

View File

@ -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,8 @@ 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_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";
@ -515,6 +519,22 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
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".

View File

@ -22,15 +22,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 +45,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 +65,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 +100,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 +113,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 +154,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);
@ -220,9 +230,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 updatesAdapter The adapter. Can be used for refreshing the adapter with adapter.refreshStatuses().
* *
* @param app The app that was swiped away
* @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 +338,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 +555,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);
}
};
} }

View File

@ -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;
}
} }

View File

@ -11,14 +11,14 @@ import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.Schema; 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);
} }

View File

@ -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>

View File

@ -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,9 +324,12 @@ 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>