Merge branch 'app-hiding' into 'master'

Implement App Hiding

See merge request fdroid/fdroidclient!629
This commit is contained in:
Hans-Christoph Steiner 2018-01-29 19:18:09 +00:00
commit f83094d9fe
20 changed files with 755 additions and 11 deletions

View File

@ -279,6 +279,8 @@
android:name=".AppUpdateStatusService" android:name=".AppUpdateStatusService"
android:exported="false" /> android:exported="false" />
<!-- Warning: Please add all new services to HidingManager -->
<activity <activity
android:name=".views.main.MainActivity" android:name=".views.main.MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
@ -552,6 +554,18 @@
android:name=".views.panic.ExitActivity" android:name=".views.panic.ExitActivity"
android:theme="@android:style/Theme.NoDisplay"/> android:theme="@android:style/Theme.NoDisplay"/>
<activity
android:name=".views.hiding.CalculatorActivity"
android:enabled="false"
android:icon="@mipmap/ic_calculator_launcher"
android:label="@string/hiding_calculator"
android:theme="@style/AppThemeLight">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application> </application>
</manifest> </manifest>

View File

@ -62,6 +62,7 @@ import org.fdroid.fdroid.installer.ApkFileProvider;
import org.fdroid.fdroid.installer.InstallHistoryService; import org.fdroid.fdroid.installer.InstallHistoryService;
import org.fdroid.fdroid.net.ImageLoaderForUIL; import org.fdroid.fdroid.net.ImageLoaderForUIL;
import org.fdroid.fdroid.net.WifiStateChangeService; import org.fdroid.fdroid.net.WifiStateChangeService;
import org.fdroid.fdroid.views.hiding.HidingManager;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
@ -286,7 +287,7 @@ public class FDroidApp extends Application {
Languages.setLanguage(this); Languages.setLanguage(this);
ACRA.init(this); ACRA.init(this);
if (isAcraProcess()) { if (isAcraProcess() || HidingManager.isHidden(this)) {
return; return;
} }

View File

@ -42,6 +42,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
Resources res = context.getResources(); Resources res = context.getResources();
defaultPreventScreenshots = res.getBoolean(R.bool.defaultPreventScreenshots); defaultPreventScreenshots = res.getBoolean(R.bool.defaultPreventScreenshots);
defaultPanicExit = res.getBoolean(R.bool.defaultPanicExit); defaultPanicExit = res.getBoolean(R.bool.defaultPanicExit);
defaultHideOnLongPressSearch = res.getBoolean(R.bool.defaultHideOnLongPressSearch);
} }
public static final String PREF_UPD_INTERVAL = "updateInterval"; public static final String PREF_UPD_INTERVAL = "updateInterval";
@ -73,6 +74,7 @@ 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_HIDE_ON_LONG_PRESS_SEARCH = "hideOnLongPressSearch";
private static final boolean DEFAULT_ROOTED = true; private static final boolean DEFAULT_ROOTED = true;
private static final boolean DEFAULT_HIDE_ANTI_FEATURE_APPS = false; private static final boolean DEFAULT_HIDE_ANTI_FEATURE_APPS = false;
@ -94,6 +96,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
private static final boolean DEFAULT_POST_PRIVILEGED_INSTALL = false; private static final boolean DEFAULT_POST_PRIVILEGED_INSTALL = false;
private final boolean defaultPreventScreenshots; private final boolean defaultPreventScreenshots;
private final boolean defaultPanicExit; private final boolean defaultPanicExit;
private final boolean defaultHideOnLongPressSearch;
public enum Theme { public enum Theme {
light, light,
@ -335,6 +338,10 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
return preferences.getBoolean(PREF_PANIC_HIDE, false); return preferences.getBoolean(PREF_PANIC_HIDE, false);
} }
public boolean hideOnLongPressSearch() {
return preferences.getBoolean(PREF_HIDE_ON_LONG_PRESS_SEARCH, defaultHideOnLongPressSearch);
}
/** /**
* This is cached as it is called several times inside app list adapters. * This is cached as it is called several times inside app list adapters.
* Providing it here means the shared preferences file only needs to be * Providing it here means the shared preferences file only needs to be

View File

@ -85,6 +85,8 @@ public class InstallManagerService extends Service {
private LocalBroadcastManager localBroadcastManager; private LocalBroadcastManager localBroadcastManager;
private AppUpdateStatusManager appUpdateStatusManager; private AppUpdateStatusManager appUpdateStatusManager;
private BroadcastReceiver broadcastReceiver;
private boolean running = false;
/** /**
* This service does not use binding, so no need to implement this method * This service does not use binding, so no need to implement this method
@ -101,9 +103,10 @@ public class InstallManagerService extends Service {
localBroadcastManager = LocalBroadcastManager.getInstance(this); localBroadcastManager = LocalBroadcastManager.getInstance(this);
appUpdateStatusManager = AppUpdateStatusManager.getInstance(this); appUpdateStatusManager = AppUpdateStatusManager.getInstance(this);
BroadcastReceiver br = new BroadcastReceiver() { broadcastReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (intent.getData() == null) return;
String packageName = intent.getData().getSchemeSpecificPart(); String packageName = intent.getData().getSchemeSpecificPart();
for (AppUpdateStatusManager.AppUpdateStatus status : appUpdateStatusManager.getByPackageName(packageName)) { for (AppUpdateStatusManager.AppUpdateStatus status : appUpdateStatusManager.getByPackageName(packageName)) {
appUpdateStatusManager.updateApk(status.getUniqueKey(), AppUpdateStatusManager.Status.Installed, null); appUpdateStatusManager.updateApk(status.getUniqueKey(), AppUpdateStatusManager.Status.Installed, null);
@ -113,7 +116,15 @@ public class InstallManagerService extends Service {
IntentFilter intentFilter = new IntentFilter(); IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addDataScheme("package"); intentFilter.addDataScheme("package");
registerReceiver(br, intentFilter); registerReceiver(broadcastReceiver, intentFilter);
running = true;
}
@Override
public void onDestroy() {
running = false;
unregisterReceiver(broadcastReceiver);
super.onDestroy();
} }
@Override @Override
@ -218,6 +229,10 @@ public class InstallManagerService extends Service {
final BroadcastReceiver downloadReceiver = new BroadcastReceiver() { final BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!running) {
localBroadcastManager.unregisterReceiver(this);
return;
}
String action = intent.getAction(); String action = intent.getAction();
if (Downloader.ACTION_STARTED.equals(action)) { if (Downloader.ACTION_STARTED.equals(action)) {
Utils.debugLog(TAG, action + " " + intent); Utils.debugLog(TAG, action + " " + intent);
@ -273,6 +288,10 @@ public class InstallManagerService extends Service {
BroadcastReceiver downloadReceiver = new BroadcastReceiver() { BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!running) {
localBroadcastManager.unregisterReceiver(this);
return;
}
Uri downloadUri = intent.getData(); Uri downloadUri = intent.getData();
String urlString = downloadUri.toString(); String urlString = downloadUri.toString();
long repoId = intent.getLongExtra(Downloader.EXTRA_REPO_ID, 0); long repoId = intent.getLongExtra(Downloader.EXTRA_REPO_ID, 0);
@ -351,6 +370,10 @@ public class InstallManagerService extends Service {
BroadcastReceiver installReceiver = new BroadcastReceiver() { BroadcastReceiver installReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!running) {
localBroadcastManager.unregisterReceiver(this);
return;
}
String downloadUrl = intent.getDataString(); String downloadUrl = intent.getDataString();
Apk apk; Apk apk;
switch (intent.getAction()) { switch (intent.getAction()) {

View File

@ -0,0 +1,152 @@
package org.fdroid.fdroid.views.hiding;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import org.fdroid.fdroid.R;
import java.util.regex.Pattern;
/**
* A very hacky calculator which is barely functional.
* It is just meant to pass a very casual inspection.
*/
public class CalculatorActivity extends AppCompatActivity {
// binary operators
private static final String TIMES = "×";
private static final String DIVIDED = "÷";
private static final String PLUS = "+";
private static final String MINUS = "-";
// unary operators
private static final String PERCENT = "%";
private @Nullable String lastOp;
// views
private TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calculator);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
textView = (TextView) findViewById(R.id.textView);
}
public void ce(View view) {
// clear display
textView.setText(null);
}
public void c(View view) {
// clear last character
if (textView.length() > 0) {
String text = textView.getText().toString();
textView.setText(text.substring(0, text.length() - 1));
}
}
public void number(View view) {
String number = ((Button) view).getText().toString();
String newNumber = String.format("%s%s", textView.getText(), number);
// FIXME don't allow multiple commas
String pin = String.valueOf(HidingManager.getUnhidePin(this));
if (newNumber.equals(pin)) {
// correct PIN was entered, show app launcher again
HidingManager.show(this);
}
textView.setText(newNumber);
}
public void op(View view) {
String text = textView.getText().toString();
if (text.length() == 0) {
return;
} else if (containsBinaryOperator(String.valueOf(text.charAt(text.length() - 1)))) {
// last character was already binary operator, ignore
return;
}
String op = ((Button) view).getText().toString();
if (containsBinaryOperator(op)) {
// remember binary operator
lastOp = op;
// add binary operator to display
textView.setText(String.format("%s%s", text, op));
} else if (op.equals(PERCENT)) {
double result;
try {
result = Double.valueOf(eval(text));
} catch (NumberFormatException e) {
result = 0;
}
textView.setText(toString(result / 100));
} else if ("=".equals(op)) {
textView.setText(eval(text));
} else {
Toast.makeText(this, "Error: Unknown Operation", Toast.LENGTH_SHORT).show();
}
}
private String eval(String s) {
if (lastOp != null && s.contains(lastOp)) {
// remember and reset binary operator
String op = lastOp;
lastOp = null;
// extract binary operation
String[] parts = s.split(Pattern.quote(op));
double left;
double right;
try {
left = Double.valueOf(parts[0]);
right = Double.valueOf(parts[1]);
} catch (NumberFormatException e) {
return "";
}
// evaluate binary operation
switch (op) {
case PLUS:
return toString(left + right);
case MINUS:
return toString(left - right);
case TIMES:
return toString(left * right);
case DIVIDED:
if (right == 0) return "";
return toString(left / right);
default:
Toast.makeText(this, "Error: Unknown Operation", Toast.LENGTH_SHORT).show();
return s;
}
} else {
return s;
}
}
private boolean containsBinaryOperator(String s) {
return s.contains(TIMES) || s.contains(DIVIDED) || s.contains(PLUS) || s.contains(MINUS);
}
private String toString(double d) {
String s = String.valueOf(d);
if (s.length() > 2 && s.endsWith(".0")) {
return s.substring(0, s.length() - 2);
}
return s;
}
}

View File

@ -0,0 +1,123 @@
package org.fdroid.fdroid.views.hiding;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v7.app.AlertDialog;
import org.fdroid.fdroid.AppUpdateStatusService;
import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.CleanCacheService;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.data.InstalledAppProviderService;
import org.fdroid.fdroid.installer.InstallHistoryService;
import org.fdroid.fdroid.installer.InstallManagerService;
import org.fdroid.fdroid.installer.InstallerService;
import org.fdroid.fdroid.localrepo.CacheSwapAppsService;
import org.fdroid.fdroid.localrepo.SwapService;
import org.fdroid.fdroid.net.DownloaderService;
import org.fdroid.fdroid.net.WifiStateChangeService;
import org.fdroid.fdroid.views.main.MainActivity;
/**
* This class is encapsulating all methods related to hiding the app from the launcher
* and restoring it.
*
* It can tell you whether the app is hidden, what the PIN to restore is
* and show confirmation dialogs before hiding.
*/
public class HidingManager {
private static final ComponentName LAUNCHER_NAME =
new ComponentName(BuildConfig.APPLICATION_ID, MainActivity.class.getName());
private static final ComponentName CALCULATOR_NAME =
new ComponentName(BuildConfig.APPLICATION_ID, CalculatorActivity.class.getName());
public static int getUnhidePin(Context context) {
return context.getResources().getInteger(R.integer.unhidePin);
}
public static boolean isHidden(Context context) {
PackageManager pm = context.getPackageManager();
int state = pm.getComponentEnabledSetting(LAUNCHER_NAME);
return state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
public static void showHideDialog(final Context context) {
String appName = context.getString(R.string.app_name);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getString(R.string.hiding_dialog_title, appName));
builder.setMessage(context.getString(R.string.hiding_dialog_message, appName,
HidingManager.getUnhidePin(context), context.getString(R.string.hiding_calculator)));
builder.setPositiveButton(context.getString(R.string.panic_hide_title, appName),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
hide(context);
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
});
builder.setView(R.layout.dialog_app_hiding);
builder.create().show();
}
public static void hide(Context context) {
stopServices(context);
removeNotifications(context);
PackageManager pm = context.getPackageManager();
// hide launcher icon
pm.setComponentEnabledSetting(LAUNCHER_NAME,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
// show calculator icon
pm.setComponentEnabledSetting(CALCULATOR_NAME,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
0); // please kill app (faster and safer, because it also stops services)
}
public static void show(Context context) {
PackageManager pm = context.getPackageManager();
// show launcher icon
pm.setComponentEnabledSetting(LAUNCHER_NAME,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
// hide calculator icon
pm.setComponentEnabledSetting(CALCULATOR_NAME,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
0); // please kill app (faster)
}
private static void removeNotifications(Context context) {
NotificationManagerCompat nm = NotificationManagerCompat.from(context);
nm.cancelAll();
}
/**
* Stops all running services, so nothing can pop up and reveal F-Droid's existence on the system
*/
private static void stopServices(Context context) {
context.stopService(new Intent(context, UpdateService.class));
context.stopService(new Intent(context, DownloaderService.class));
context.stopService(new Intent(context, InstallerService.class));
context.stopService(new Intent(context, CleanCacheService.class));
context.stopService(new Intent(context, WifiStateChangeService.class));
context.stopService(new Intent(context, SwapService.class));
context.stopService(new Intent(context, InstallManagerService.class));
context.stopService(new Intent(context, InstallHistoryService.class));
context.stopService(new Intent(context, CacheSwapAppsService.class));
context.stopService(new Intent(context, InstalledAppProviderService.class));
context.stopService(new Intent(context, AppUpdateStatusService.class));
}
}

View File

@ -15,6 +15,7 @@ import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.data.CategoryProvider; import org.fdroid.fdroid.data.CategoryProvider;
@ -22,6 +23,7 @@ import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.views.apps.AppListActivity; import org.fdroid.fdroid.views.apps.AppListActivity;
import org.fdroid.fdroid.views.categories.CategoryAdapter; import org.fdroid.fdroid.views.categories.CategoryAdapter;
import org.fdroid.fdroid.views.categories.CategoryController; import org.fdroid.fdroid.views.categories.CategoryController;
import org.fdroid.fdroid.views.hiding.HidingManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -73,6 +75,17 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
activity.startActivity(new Intent(activity, AppListActivity.class)); activity.startActivity(new Intent(activity, AppListActivity.class));
} }
}); });
searchFab.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if (Preferences.get().hideOnLongPressSearch()) {
HidingManager.showHideDialog(activity);
return true;
} else {
return false;
}
}
});
activity.getSupportLoaderManager().restartLoader(LOADER_ID, null, this); activity.getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
} }

View File

@ -14,6 +14,8 @@ import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
@ -21,6 +23,7 @@ import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.Schema; import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.views.apps.AppListActivity; import org.fdroid.fdroid.views.apps.AppListActivity;
import org.fdroid.fdroid.views.hiding.HidingManager;
import org.fdroid.fdroid.views.whatsnew.WhatsNewAdapter; import org.fdroid.fdroid.views.whatsnew.WhatsNewAdapter;
import java.util.Date; import java.util.Date;
@ -71,6 +74,17 @@ class WhatsNewViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
activity.startActivity(new Intent(activity, AppListActivity.class)); activity.startActivity(new Intent(activity, AppListActivity.class));
} }
}); });
searchFab.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if (Preferences.get().hideOnLongPressSearch()) {
HidingManager.showHideDialog(activity);
return true;
} else {
return false;
}
}
});
activity.getSupportLoaderManager().initLoader(LOADER_ID, null, this); activity.getSupportLoaderManager().initLoader(LOADER_ID, null, this);
} }

View File

@ -13,7 +13,6 @@ import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.preference.PreferenceFragment; import android.support.v4.preference.PreferenceFragment;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
@ -21,6 +20,7 @@ import android.text.TextUtils;
import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.views.hiding.HidingManager;
import java.util.ArrayList; import java.util.ArrayList;
@ -79,10 +79,6 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
return true; return true;
} }
}); });
// TODO implement app hiding
PreferenceCategory category = (PreferenceCategory) findPreference("pref_panic_destructive_actions");
category.removePreference(prefHide);
} }
@Override @Override
@ -100,9 +96,8 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// enable "exit" if "hiding" gets enabled
if (key.equals(PREF_HIDE) && sharedPreferences.getBoolean(PREF_HIDE, false)) { if (key.equals(PREF_HIDE) && sharedPreferences.getBoolean(PREF_HIDE, false)) {
prefExit.setChecked(true); 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(PREF_EXIT) && !sharedPreferences.getBoolean(PREF_EXIT, true)) {
@ -217,4 +212,35 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
return packageName; return packageName;
} }
private void showHideConfirmationDialog() {
String appName = getString(R.string.app_name);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(R.string.panic_hide_warning_title);
builder.setMessage(getString(R.string.panic_hide_warning_message, appName,
HidingManager.getUnhidePin(getContext()), getString(R.string.hiding_calculator)));
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// enable "exit" if "hiding" gets enabled
prefExit.setChecked(true);
// dismiss, but not cancel dialog
dialogInterface.dismiss();
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
prefHide.setChecked(false);
}
});
builder.setView(R.layout.dialog_app_hiding);
builder.create().show();
}
} }

View File

@ -7,6 +7,7 @@ import android.support.v7.app.AppCompatActivity;
import android.util.Log; import android.util.Log;
import org.fdroid.fdroid.Preferences; 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;
@ -36,7 +37,7 @@ public class PanicResponderActivity extends AppCompatActivity {
// Performing destructive panic responses // Performing destructive panic responses
if (preferences.panicHide()) { if (preferences.panicHide()) {
Log.i(TAG, "Hiding app..."); Log.i(TAG, "Hiding app...");
// TODO HidingManager.hide(this);
} }
} }

View File

@ -0,0 +1,292 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".views.hiding.CalculatorActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:popupTheme="?attr/actionBarPopupTheme"
app:theme="?attr/actionBarTheme" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:gravity="bottom|end"
android:padding="5dp"
android:textAlignment="textEnd"
android:textSize="22sp"
android:typeface="monospace"
app:layout_constraintBottom_toTopOf="@+id/ce"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
tools:text="1337+42" />
<Button
android:id="@+id/times"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="op"
android:text="×"
app:layout_constraintBottom_toTopOf="@+id/seven"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/divided"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="op"
android:text="÷"
app:layout_constraintBottom_toTopOf="@+id/eight"
app:layout_constraintStart_toEndOf="@+id/times"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/c"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="c"
android:text="C"
app:layout_constraintBottom_toTopOf="@+id/nine"
app:layout_constraintStart_toEndOf="@+id/divided"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/seven"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="7"
app:layout_constraintBottom_toTopOf="@+id/four"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/eight"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="8"
app:layout_constraintBottom_toTopOf="@+id/five"
app:layout_constraintStart_toEndOf="@+id/seven"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/nine"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="9"
app:layout_constraintBottom_toTopOf="@+id/six"
app:layout_constraintStart_toEndOf="@+id/eight"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/ce"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:onClick="ce"
android:text="CE"
app:layout_constraintBottom_toTopOf="@+id/plus"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/nine"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/four"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="4"
app:layout_constraintBottom_toTopOf="@+id/one"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/five"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="5"
app:layout_constraintBottom_toTopOf="@+id/two"
app:layout_constraintStart_toEndOf="@+id/four"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/six"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="6"
app:layout_constraintBottom_toTopOf="@+id/three"
app:layout_constraintStart_toEndOf="@+id/five"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/plus"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:onClick="op"
android:text="+"
app:layout_constraintBottom_toTopOf="@+id/minus"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/six"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/minus"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:onClick="op"
android:text="-"
app:layout_constraintBottom_toTopOf="@+id/equals"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/three"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/one"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="1"
app:layout_constraintBottom_toTopOf="@+id/zero"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/two"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="2"
app:layout_constraintBottom_toTopOf="@+id/comma"
app:layout_constraintStart_toEndOf="@+id/one"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/three"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="3"
app:layout_constraintBottom_toTopOf="@+id/percent"
app:layout_constraintStart_toEndOf="@+id/two"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/zero"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/comma"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="number"
android:text="."
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/zero"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/percent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:onClick="op"
android:text="%"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/comma"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/equals"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:onClick="op"
android:text="="
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/three"
app:layout_constraintTop_toTopOf="@+id/three"
tools:ignore="HardcodedText" />
</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/launcherIcon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginLeft="24dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/hiding_calculator"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_calculator_launcher" />
<TextView
android:id="@+id/appName"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:gravity="center_vertical"
android:text="@string/hiding_calculator"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@+id/launcherIcon"
app:layout_constraintStart_toEndOf="@+id/launcherIcon"
app:layout_constraintTop_toTopOf="@+id/launcherIcon" />
<TextView
android:id="@+id/pin"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:gravity="center_vertical"
android:text="@integer/unhidePin"
android:textColor="@color/red"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@+id/launcherIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/appName"
app:layout_constraintTop_toTopOf="@+id/launcherIcon" />
<TextView
android:id="@+id/warningText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="@string/hiding_dialog_warning"
android:textColor="?android:textColorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/launcherIcon"
app:layout_constraintTop_toBottomOf="@+id/launcherIcon" />
</android.support.constraint.ConstraintLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -17,5 +17,8 @@
<!-- Default Preferences --> <!-- Default Preferences -->
<bool name="defaultPreventScreenshots">false</bool> <bool name="defaultPreventScreenshots">false</bool>
<bool name="defaultPanicExit">true</bool> <bool name="defaultPanicExit">true</bool>
<bool name="defaultHideOnLongPressSearch">false</bool>
<integer name="unhidePin">1337</integer>
</resources> </resources>

View File

@ -259,6 +259,15 @@ This often occurs with apps installed via Google Play or other sources, if they
<string name="panic_destructive_actions">Destructive Actions</string> <string name="panic_destructive_actions">Destructive Actions</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_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="hiding_calculator">Calculator</string>
<string name="hiding_dialog_title">Hide %s Now</string>
<string name="hiding_dialog_message">Are you sure you want to remove %1$s from the launcher? Only typing \"%2$d\" in the fake %3$s app can restore it.</string>
<string name="hiding_dialog_warning">Warning: Any app shortcut on the home screen will also be removed and needs to be re-added manually.</string>
<string name="hide_on_long_search_press_title">Hide with search button</string>
<string name="hide_on_long_search_press_summary">Long pressing the search button will hide the app</string>
<!-- <!--
status_download takes four parameters: status_download takes four parameters:

View File

@ -100,6 +100,12 @@
android:summary="@string/preventScreenshots_summary" android:summary="@string/preventScreenshots_summary"
android:title="@string/preventScreenshots_title"/> android:title="@string/preventScreenshots_title"/>
<CheckBoxPreference
android:defaultValue="@bool/defaultHideOnLongPressSearch"
android:key="hideOnLongPressSearch"
android:summary="@string/hide_on_long_search_press_summary"
android:title="@string/hide_on_long_search_press_title"/>
<PreferenceScreen <PreferenceScreen
android:summary="@string/panic_settings_summary" android:summary="@string/panic_settings_summary"
android:title="@string/panic_settings"> android:title="@string/panic_settings">