Merge branch '1258-panic-kit' into 'master'

Basic PanicKit Support with one default action

Closes #1258

See merge request fdroid/fdroidclient!621
This commit is contained in:
Hans-Christoph Steiner 2017-12-14 16:46:30 +00:00
commit a170b054dd
12 changed files with 476 additions and 2 deletions

View File

@ -41,6 +41,7 @@ dependencies {
compile 'eu.chainfire:libsuperuser:1.0.0.201602271131' compile 'eu.chainfire:libsuperuser:1.0.0.201602271131'
compile 'cc.mvdan.accesspoint:library:0.2.0' compile 'cc.mvdan.accesspoint:library:0.2.0'
compile 'info.guardianproject.netcipher:netcipher:2.0.0-alpha1' compile 'info.guardianproject.netcipher:netcipher:2.0.0-alpha1'
compile "info.guardianproject.panic:panic:0.5"
compile 'commons-io:commons-io:2.5' compile 'commons-io:commons-io:2.5'
compile 'commons-net:commons-net:3.5' compile 'commons-net:commons-net:3.5'
compile 'org.openhab.jmdns:jmdns:3.4.2' compile 'org.openhab.jmdns:jmdns:3.4.2'
@ -142,6 +143,7 @@ if (!hasProperty('sourceDeps')) {
'com.nostra13.universalimageloader:universal-image-loader:dbd5197ffec3a8317533190870a7c00ff3750dd6a31241448c6a5522d51b65b4', 'com.nostra13.universalimageloader:universal-image-loader:dbd5197ffec3a8317533190870a7c00ff3750dd6a31241448c6a5522d51b65b4',
'eu.chainfire:libsuperuser:018344ff19ee94d252c14b4a503ee8b519184db473a5af83513f5837c413b128', 'eu.chainfire:libsuperuser:018344ff19ee94d252c14b4a503ee8b519184db473a5af83513f5837c413b128',
'info.guardianproject.netcipher:netcipher:eeeb5d0d95ccfe176b4296cbd71a9a24c6efb0bab5c4025a8c6bc36abdddfc75', 'info.guardianproject.netcipher:netcipher:eeeb5d0d95ccfe176b4296cbd71a9a24c6efb0bab5c4025a8c6bc36abdddfc75',
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'io.reactivex:rxandroid:35c1a90f8c1f499db3c1f3d608e1f191ac8afddb10c02dd91ef04c03a0a4bcda', 'io.reactivex:rxandroid:35c1a90f8c1f499db3c1f3d608e1f191ac8afddb10c02dd91ef04c03a0a4bcda',
'io.reactivex:rxjava:2c162afd78eba217cdfee78b60e85d3bfb667db61e12bc95e3cf2ddc5beeadf6', 'io.reactivex:rxjava:2c162afd78eba217cdfee78b60e85d3bfb667db61e12bc95e3cf2ddc5beeadf6',
'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce', 'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce',

View File

@ -532,6 +532,33 @@
<activity android:name=".AboutActivity" android:theme="@style/Theme.AppCompat.Light.Dialog" /> <activity android:name=".AboutActivity" android:theme="@style/Theme.AppCompat.Light.Dialog" />
<activity android:name=".installer.FileInstallerActivity" android:theme="@style/AppThemeTransparent" /> <activity android:name=".installer.FileInstallerActivity" android:theme="@style/AppThemeTransparent" />
<activity
android:name=".views.panic.PanicPreferencesActivity"
android:label="@string/panic_settings"
android:parentActivityName=".views.main.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.main.MainActivity"/>
<intent-filter>
<action android:name="info.guardianproject.panic.action.CONNECT"/>
<action android:name="info.guardianproject.panic.action.DISCONNECT"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".views.panic.PanicResponderActivity"
android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay">
<!-- this can never have launchMode singleTask or singleInstance! -->
<intent-filter>
<action android:name="info.guardianproject.panic.action.TRIGGER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".views.panic.ExitActivity"
android:theme="@android:style/Theme.NoDisplay"/>
</application> </application>
</manifest> </manifest>

View File

@ -2,6 +2,7 @@ package org.fdroid.fdroid;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Build; import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
@ -38,8 +39,9 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
.putString(PREF_LOCAL_REPO_NAME, getDefaultLocalRepoName()) .putString(PREF_LOCAL_REPO_NAME, getDefaultLocalRepoName())
.apply(); .apply();
} }
defaultPreventScreenshots = Resources res = context.getResources();
context.getResources().getBoolean(R.bool.defaultPreventScreenshots); defaultPreventScreenshots = res.getBoolean(R.bool.defaultPreventScreenshots);
defaultPanicExit = res.getBoolean(R.bool.defaultPanicExit);
} }
public static final String PREF_UPD_INTERVAL = "updateInterval"; public static final String PREF_UPD_INTERVAL = "updateInterval";
@ -69,6 +71,8 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
public static final String PREF_POST_PRIVILEGED_INSTALL = "postPrivilegedInstall"; public static final String PREF_POST_PRIVILEGED_INSTALL = "postPrivilegedInstall";
public static final String PREF_TRIED_EMPTY_UPDATE = "triedEmptyUpdate"; public static final String PREF_TRIED_EMPTY_UPDATE = "triedEmptyUpdate";
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_HIDE = "pref_panic_hide";
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;
@ -89,6 +93,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
private static final boolean DEFAULT_FORCE_OLD_INDEX = false; private static final boolean DEFAULT_FORCE_OLD_INDEX = false;
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;
public enum Theme { public enum Theme {
light, light,
@ -322,6 +327,14 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
return preferences.getBoolean(PREF_PREVENT_SCREENSHOTS, defaultPreventScreenshots); return preferences.getBoolean(PREF_PREVENT_SCREENSHOTS, defaultPreventScreenshots);
} }
public boolean panicExit() {
return preferences.getBoolean(PREF_PANIC_EXIT, defaultPanicExit);
}
public boolean panicHide() {
return preferences.getBoolean(PREF_PANIC_HIDE, false);
}
/** /**
* 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

@ -0,0 +1,41 @@
package org.fdroid.fdroid.views.panic;
import android.support.v7.app.AppCompatActivity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
public class ExitActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 21) {
finishAndRemoveTask();
} else {
finish();
}
System.exit(0);
}
public static void exitAndRemoveFromRecentApps(final AppCompatActivity activity) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(activity, ExitActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_ANIMATION);
activity.startActivity(intent);
}
});
}
}

View File

@ -0,0 +1,38 @@
package org.fdroid.fdroid.views.panic;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
public class PanicPreferencesActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle bundle) {
((FDroidApp) getApplication()).applyTheme(this);
super.onCreate(bundle);
setContentView(R.layout.activity_panic_settings);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayShowHomeEnabled(true);
ab.setDisplayHomeAsUpEnabled(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@ -0,0 +1,220 @@
package org.fdroid.fdroid.views.panic;
import android.app.Activity;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.support.annotation.Nullable;
import android.support.v4.preference.PreferenceFragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import java.util.ArrayList;
import info.guardianproject.panic.Panic;
import info.guardianproject.panic.PanicResponder;
public class PanicPreferencesFragment extends PreferenceFragment 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_HIDE = Preferences.PREF_PANIC_HIDE;
private PackageManager pm;
private ListPreference prefApp;
private CheckBoxPreference prefExit;
private CheckBoxPreference prefHide;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_panic);
pm = getActivity().getPackageManager();
prefExit = (CheckBoxPreference) findPreference(PREF_EXIT);
prefApp = (ListPreference) findPreference(PREF_APP);
prefHide = (CheckBoxPreference) findPreference(PREF_HIDE);
prefHide.setTitle(getString(R.string.panic_hide_title, getString(R.string.app_name)));
if (PanicResponder.checkForDisconnectIntent(getActivity())) {
// the necessary action should have been performed by the check already
getActivity().finish();
return;
}
String connectIntentSender = PanicResponder.getConnectIntentSender(getActivity());
// if there's a connecting app and it is not the old one
if (!TextUtils.isEmpty(connectIntentSender) && !TextUtils.equals(connectIntentSender, PanicResponder
.getTriggerPackageName(getActivity()))) {
// Show dialog allowing the user to opt-in
showOptInDialog();
}
prefApp.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String packageName = (String) newValue;
PanicResponder.setTriggerPackageName(getActivity(), packageName);
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
prefHide.setChecked(false);
prefHide.setEnabled(false);
getActivity().setResult(Activity.RESULT_CANCELED);
} else {
prefHide.setEnabled(true);
}
showPanicApp(packageName);
return true;
}
});
// TODO implement app hiding
PreferenceCategory category = (PreferenceCategory) findPreference("pref_panic_destructive_actions");
category.removePreference(prefHide);
}
@Override
public void onStart() {
super.onStart();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
}
@Override
public void onStop() {
super.onStop();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// enable "exit" if "hiding" gets enabled
if (key.equals(PREF_HIDE) && sharedPreferences.getBoolean(PREF_HIDE, false)) {
prefExit.setChecked(true);
}
// disable "hiding" if "exit" gets disabled
if (key.equals(PREF_EXIT) && !sharedPreferences.getBoolean(PREF_EXIT, true)) {
prefHide.setChecked(false);
}
}
private void showPanicApp(String packageName) {
// Fill list of available panic apps
ArrayList<CharSequence> entries = new ArrayList<>();
ArrayList<CharSequence> entryValues = new ArrayList<>();
entries.add(0, getString(R.string.panic_app_setting_none));
entryValues.add(0, Panic.PACKAGE_NAME_NONE);
for (ResolveInfo resolveInfo : PanicResponder.resolveTriggerApps(pm)) {
if (resolveInfo.activityInfo == null) continue;
entries.add(resolveInfo.activityInfo.loadLabel(pm));
entryValues.add(resolveInfo.activityInfo.packageName);
}
prefApp.setEntries(entries.toArray(new CharSequence[entries.size()]));
prefApp.setEntryValues(entryValues.toArray(new CharSequence[entryValues.size()]));
prefApp.setDefaultValue(Panic.PACKAGE_NAME_NONE);
if (entries.size() <= 1) {
// bring the user to Ripple if no other panic apps are available
prefApp.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=info.guardianproject.ripple"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(intent);
}
return true;
}
});
}
if (TextUtils.isEmpty(packageName) || packageName.equals(Panic.PACKAGE_NAME_NONE)) {
// no panic app set
prefApp.setValue(Panic.PACKAGE_NAME_NONE);
prefApp.setSummary(getString(R.string.panic_app_setting_summary));
if (Build.VERSION.SDK_INT >= 11) {
prefApp.setIcon(null); // otherwise re-setting view resource doesn't work
prefApp.setIcon(R.drawable.ic_cancel);
}
// disable destructive panic actions
prefHide.setEnabled(false);
} else {
// try to display connected panic app
try {
prefApp.setValue(packageName);
prefApp.setSummary(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)));
if (Build.VERSION.SDK_INT >= 11) {
prefApp.setIcon(pm.getApplicationIcon(packageName));
}
prefHide.setEnabled(true);
} catch (PackageManager.NameNotFoundException e) {
// revert back to no app, just to be safe
PanicResponder.setTriggerPackageName(getActivity(), Panic.PACKAGE_NAME_NONE);
showPanicApp(Panic.PACKAGE_NAME_NONE);
}
}
}
private void showOptInDialog() {
DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
PanicResponder.setTriggerPackageName(getActivity());
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
getActivity().setResult(Activity.RESULT_OK);
}
};
DialogInterface.OnClickListener cancelListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(getString(R.string.panic_app_dialog_title));
CharSequence app = getString(R.string.panic_app_unknown_app);
String packageName = getCallingPackageName();
if (packageName != null) {
try {
app = pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
String text = String.format(getString(R.string.panic_app_dialog_message), app);
builder.setMessage(text);
builder.setNegativeButton(R.string.allow, okListener);
builder.setPositiveButton(R.string.cancel, cancelListener);
builder.show();
}
@Nullable
private String getCallingPackageName() {
ComponentName componentName = getActivity().getCallingActivity();
String packageName = null;
if (componentName != null) {
packageName = componentName.getPackageName();
}
return packageName;
}
}

View File

@ -0,0 +1,53 @@
package org.fdroid.fdroid.views.panic;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import org.fdroid.fdroid.Preferences;
import info.guardianproject.panic.Panic;
import info.guardianproject.panic.PanicResponder;
public class PanicResponderActivity extends AppCompatActivity {
private static final String TAG = PanicResponderActivity.class.getSimpleName();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent == null || !Panic.isTriggerIntent(intent)) {
finish();
return;
}
// received intent from panic app
Log.i(TAG, "Received Panic Trigger...");
Preferences preferences = Preferences.get();
if (PanicResponder.receivedTriggerFromConnectedApp(this)) {
Log.i(TAG, "Panic Trigger came from connected app");
// Performing destructive panic responses
if (preferences.panicHide()) {
Log.i(TAG, "Hiding app...");
// TODO
}
}
// exit and clear, if not deactivated
if (preferences.panicExit()) {
ExitActivity.exitAndRemoveFromRecentApps(this);
if (Build.VERSION.SDK_INT >= 21) {
finishAndRemoveTask();
}
}
finish();
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:popupTheme="?attr/actionBarPopupTheme"
app:theme="?attr/actionBarTheme"/>
<fragment
android:id="@+id/fragment_container"
class="org.fdroid.fdroid.views.panic.PanicPreferencesFragment"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@ -16,5 +16,6 @@
<!-- Default Preferences --> <!-- Default Preferences -->
<bool name="defaultPreventScreenshots">false</bool> <bool name="defaultPreventScreenshots">false</bool>
<bool name="defaultPanicExit">true</bool>
</resources> </resources>

View File

@ -244,6 +244,22 @@ This often occurs with apps installed via Google Play or other sources, if they
<string name="preventScreenshots_title">Prevent Screenshots</string> <string name="preventScreenshots_title">Prevent Screenshots</string>
<string name="preventScreenshots_summary">Blocks screenshots from being taken and hides app content from recent apps screen</string> <string name="preventScreenshots_summary">Blocks screenshots from being taken and hides app content from recent apps screen</string>
<string name="panic_app_setting_title">Panic Button App</string>
<string name="panic_app_unknown_app">an unknown app</string>
<string name="panic_app_setting_summary">No app has been set</string>
<string name="panic_app_setting_none">None</string>
<string name="panic_app_dialog_title">Confirm Panic App</string>
<string name="panic_app_dialog_message">Are you sure that you want to allow %1$s to trigger destructive panic button actions?</string>
<string name="allow">Allow</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_exit_title">Exit App</string>
<string name="panic_exit_summary">App will be closed </string>
<string name="panic_destructive_actions">Destructive Actions</string>
<string name="panic_hide_title">Hide %s</string>
<string name="panic_hide_summary">App will hide itself</string>
<!-- <!--
status_download takes four parameters: status_download takes four parameters:
- Repository (url) - Repository (url)

View File

@ -100,6 +100,15 @@
android:summary="@string/preventScreenshots_summary" android:summary="@string/preventScreenshots_summary"
android:title="@string/preventScreenshots_title"/> android:title="@string/preventScreenshots_title"/>
<PreferenceScreen
android:summary="@string/panic_settings_summary"
android:title="@string/panic_settings">
<intent
android:action="android.intent.action.MAIN"
android:targetClass="org.fdroid.fdroid.views.panic.PanicPreferencesActivity"
android:targetPackage="@string/applicationId"/>
</PreferenceScreen>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/other" <PreferenceCategory android:title="@string/other"

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<CheckBoxPreference
android:defaultValue="@bool/defaultPanicExit"
android:key="pref_panic_exit"
android:summary="@string/panic_exit_summary"
android:title="@string/panic_exit_title"/>
<PreferenceCategory
android:key="pref_panic_destructive_actions"
android:title="@string/panic_destructive_actions">
<ListPreference
android:key="pref_panic_app"
android:summary="@string/panic_app_setting_summary"
android:title="@string/panic_app_setting_title"
tools:icon="@drawable/ic_cancel"/>
<CheckBoxPreference
android:defaultValue="false"
android:enabled="false"
android:key="pref_panic_hide"
android:summary="@string/panic_hide_summary"
android:title="@string/panic_hide_title"/>
</PreferenceCategory>
</PreferenceScreen>