Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to use fingerprint to unfreeze applications #45

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ android {

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.biometric:biometric:1.0.0-alpha03'
implementation 'androidx.legacy:legacy-support-core-ui:1.0.0'
implementation 'androidx.fragment:fragment:1.0.0'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.preference:preference:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
implementation 'com.google.android.material:material:1.0.0'
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

<uses-feature android:name="android.software.device_admin" android:required="true"/>
<uses-feature android:name="android.software.managed_users" android:required="true"/>
<uses-feature android:name="android.hardware.fingerprint" android:required="false"/>
<!--<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>-->

<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Expand Down Expand Up @@ -40,7 +42,7 @@
<!-- Needed because only cross-profile Activity intents are supported -->
<activity android:name=".ui.DummyActivity"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="net.typeblog.shelter.action.FINALIZE_PROVISION" />
<action android:name="net.typeblog.shelter.action.START_SERVICE" />
Expand Down
55 changes: 50 additions & 5 deletions app/src/main/java/net/typeblog/shelter/ui/DummyActivity.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.typeblog.shelter.ui;

import android.Manifest;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Intent;
Expand All @@ -17,6 +16,8 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.biometric.BiometricPrompt;

import net.typeblog.shelter.R;
import net.typeblog.shelter.ShelterApplication;
Expand All @@ -35,13 +36,15 @@
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

// DummyActivity does nothing about presenting any UI
// It is a wrapper over various different operations
// that might be required to perform across user profiles
// which is only possible through Intents that are in
// the crossProfileIntentFilter
public class DummyActivity extends Activity {
public class DummyActivity extends AppCompatActivity {
public static final String FINALIZE_PROVISION = "net.typeblog.shelter.action.FINALIZE_PROVISION";
public static final String START_SERVICE = "net.typeblog.shelter.action.START_SERVICE";
public static final String TRY_START_SERVICE = "net.typeblog.shelter.action.TRY_START_SERVICE";
Expand Down Expand Up @@ -73,7 +76,7 @@ public class DummyActivity extends Activity {
UNFREEZE_AND_LAUNCH);

private static final int REQUEST_INSTALL_PACKAGE = 1;
private static final int REQUEST_PERMISSION_EXTERNAL_STORAGE= 2;
private static final int REQUEST_PERMISSION_EXTERNAL_STORAGE = 2;

// A state variable to record the last time DummyActivity was informed that someone
// in the same process needs to call an action without signature
Expand Down Expand Up @@ -151,7 +154,14 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
} else if (FINALIZE_PROVISION.equals(intent.getAction())) {
actionFinalizeProvision();
} else if (UNFREEZE_AND_LAUNCH.equals(intent.getAction()) || PUBLIC_UNFREEZE_AND_LAUNCH.equals(intent.getAction())) {
actionUnfreezeAndLaunch();
if ((SettingsManager.getInstance().getAutoFreezeServiceEnabled() && SettingsManager.getInstance().getFingerprintAuthEnabled())) {
//We check to see if the app has already been authorized on the main profile
if (!intent.getBooleanExtra("authorized", false))
auth();
else
actionUnfreezeAndLaunch();
} else
actionUnfreezeAndLaunch();
} else if (PUBLIC_FREEZE_ALL.equals(intent.getAction())) {
actionPublicFreezeAll();
} else if (FREEZE_ALL_IN_LIST.equals(intent.getAction())) {
Expand Down Expand Up @@ -317,7 +327,7 @@ private void actionUnfreezeAndLaunch() {
intent.putExtra("shouldFreeze",
SettingsManager.getInstance().getAutoFreezeServiceEnabled() &&
LocalStorageManager.getInstance()
.stringListContains(LocalStorageManager.PREF_AUTO_FREEZE_LIST_WORK_PROFILE, packageName));
.stringListContains(LocalStorageManager.PREF_AUTO_FREEZE_LIST_WORK_PROFILE, packageName));
if (getIntent().hasExtra("linkedPackages")) {
// Multiple apps should be unfrozen here
String[] packages = getIntent().getStringExtra("linkedPackages").split(",");
Expand All @@ -333,6 +343,9 @@ private void actionUnfreezeAndLaunch() {
intent.putExtra("linkedPackages", packages);
intent.putExtra("linkedPackagesShouldFreeze", packagesShouldFreeze);
}

//We use this to for the fingerprint authentication to check if the app has already been authorized on the main profile
intent.putExtra("authorized", true);
Utility.transferIntentToProfile(this, intent);
startActivity(intent);
finish();
Expand Down Expand Up @@ -460,4 +473,36 @@ private void actionSynchronizePreference() {
SettingsManager.getInstance().applyAll();
finish();
}

private void auth() {
Executor executor = Executors.newSingleThreadExecutor();

final BiometricPrompt biometricPrompt = new BiometricPrompt(this, executor, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
// user clicked negative button
}
finish();
}

@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
actionUnfreezeAndLaunch();
}

@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
//Called when a biometric is valid but not recognized.
}
});
final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.settings_fingerprint_auth_service))
.setNegativeButtonText(getString(R.string.fingerprint_auth_negative))
.build();
biometricPrompt.authenticate(promptInfo);
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/net/typeblog/shelter/ui/SettingsFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import net.typeblog.shelter.R;
import net.typeblog.shelter.services.IShelterService;
import net.typeblog.shelter.util.BiometricUtils;
import net.typeblog.shelter.util.SettingsManager;
import net.typeblog.shelter.util.Utility;

Expand All @@ -31,6 +32,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
private static final String SETTINGS_AUTO_FREEZE_SERVICE = "settings_auto_freeze_service";
private static final String SETTINGS_AUTO_FREEZE_DELAY = "settings_auto_freeze_delay";
private static final String SETTINGS_SKIP_FOREGROUND = "settings_dont_freeze_foreground";
private static final String SETTINGS_FINGERPRINT_AUTH = "settings_fingerprint_auth_service";

private SettingsManager mManager = SettingsManager.getInstance();
private IShelterService mServiceWork = null;
Expand All @@ -39,6 +41,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
private CheckBoxPreference mPrefCameraProxy = null;
private CheckBoxPreference mPrefAutoFreezeService = null;
private CheckBoxPreference mPrefSkipForeground = null;
private CheckBoxPreference mPrefFingerprintAuth = null;

private Preference mPrefAutoFreezeDelay = null;

Expand Down Expand Up @@ -83,6 +86,10 @@ public void onCreatePreferences(Bundle bundle, String s) {
mPrefSkipForeground = (CheckBoxPreference) findPreference(SETTINGS_SKIP_FOREGROUND);
mPrefSkipForeground.setChecked(mManager.getSkipForegroundEnabled());
mPrefSkipForeground.setOnPreferenceChangeListener(this);

mPrefFingerprintAuth = (CheckBoxPreference) findPreference(SETTINGS_FINGERPRINT_AUTH);
mPrefFingerprintAuth.setChecked(mManager.getFingerprintAuthEnabled());
mPrefFingerprintAuth.setOnPreferenceChangeListener(this);
}

@Override
Expand Down Expand Up @@ -144,6 +151,13 @@ public boolean onPreferenceChange(Preference preference, Object newState) {
.show();
return false;
}
} else if (preference == mPrefFingerprintAuth) {
if ((BiometricUtils.isPermissionGranted(getContext()) || BiometricUtils.isBiometricPromptEnabled(getContext())) &&
BiometricUtils.isHardwareSupported(getContext()) && BiometricUtils.isFingerprintAvailable(getContext()))
mManager.setFingerprintAuthEnabled((boolean) newState);
else
mManager.setFingerprintAuthEnabled(false);
return true;
} else {
return false;
}
Expand Down
41 changes: 41 additions & 0 deletions app/src/main/java/net/typeblog/shelter/util/BiometricUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package net.typeblog.shelter.util;

import android.Manifest;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.core.app.ActivityCompat;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;

public class BiometricUtils {

public static boolean isBiometricPromptEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
return ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_BIOMETRIC) == PackageManager.PERMISSION_GRANTED;
return false;
}

/* Condition 1: Check if the device has fingerprint sensors. */
public static boolean isHardwareSupported(Context context) {
FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(context);
return fingerprintManager.isHardwareDetected();
}

/* Condition 2: Fingerprint authentication can be matched with a
* registered fingerprint of the user. So we need to perform this check
* in order to enable fingerprint authentication*/
public static boolean isFingerprintAvailable(Context context) {
FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(context);
return fingerprintManager.hasEnrolledFingerprints();
}

/* Condition 3: Check if the permission has been added to
* the app. This permission will be granted as soon as the user
* installs the app on their device.*/
public static boolean isPermissionGranted(Context context) {
return ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) ==
PackageManager.PERMISSION_GRANTED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class LocalStorageManager {
public static final String PREF_AUTO_FREEZE_SERVICE = "auto_freeze_service";
public static final String PREF_DONT_FREEZE_FOREGROUND = "dont_freeze_foreground";
public static final String PREF_AUTO_FREEZE_DELAY = "auto_freeze_delay";
public static final String PREF_FINGERPRINT_AUTH = "fingerprint_auth";
public static final String PREF_CAMERA_PROXY = "camera_proxy";

private static final String LIST_DIVIDER = ",";
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/net/typeblog/shelter/util/SettingsManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,15 @@ public void setSkipForegroundEnabled(boolean enabled) {
public boolean getSkipForegroundEnabled() {
return mStorage.getBoolean(LocalStorageManager.PREF_DONT_FREEZE_FOREGROUND);
}

// Set the enabled state of "Fingerprint Authentication"
// This does NOT need to be synchronized nor applied across profile
public void setFingerprintAuthEnabled(boolean enabled) {
mStorage.setBoolean(LocalStorageManager.PREF_FINGERPRINT_AUTH, enabled);
}

// Get the enabled state of "Fingerprint Authentication"
public boolean getFingerprintAuthEnabled() {
return mStorage.getBoolean(LocalStorageManager.PREF_FINGERPRINT_AUTH);
}
}
7 changes: 7 additions & 0 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,11 @@
<string name="settings_dont_freeze_foreground_desc">لا تقم بتجميد التطبيقات المفتوحه (مع نشاط مرئي) عند قفل الشاشة. يمكن أن يكون هذا مفيدًا للتطبيقات مثل مشغل الموسيقى ، ولكن ستحتاج إلى تجميدها يدويًا من خلال \"اختصار تجميد دفعة\" بعد ذلك.</string>
<string name="continue_anyway">اكمل على أي حال</string>
<string name="request_usage_stats">يحتاج العازل إلى إذن إحصائيات الاستخدام للقيام بذلك. الرجاء تمكين الإذن لكلي التطبيقين بعد الضغط على "موافق". سيؤدي عدم القيام بذلك إلى عدم عمل هذه الميزة بشكل صحيح.</string>
<string name="allow_cross_profile_widgets">السماح للأدوات في الملف الرئيسي</string>
<string name="show_all">إظهار كل التطبيقات</string>
<string name="show_all_warning">قد يتسبب التلاعب بالتطبيقات المخفية من القائمة في حدوث أعطال وكل أنواع السلوك غير المتوقع. على الرغم من ذلك ، يمكن أن تكون هذه الميزة مفيدة عندما لا يقوم ROM المخصص بتمكين جميع تطبيقات النظام الضرورية في الملف العمل بشكل افتراضي. إذا تابعت ، فهذا علي مسؤليتك الخاصه.</string>
<string name="settings_auto_freeze_delay">تأخير التجميد التلقائي</string>
<string name="settings_fingerprint_auth_service">بصمة الإصبع</string>
<string name="settings_fingerprint_auth_service_desc">استخدم بصمتك لالغاء تجميد و بدأ التطبيقات</string>
<string name="fingerprint_auth_negative">الغاء</string>
</resources>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
<string name="settings_auto_freeze_delay">Auto Freeze Delay</string>
<string name="settings_dont_freeze_foreground">Skip Foreground Apps</string>
<string name="settings_dont_freeze_foreground_desc">Do NOT freeze foreground apps (with visible activity) when you lock your screen. This can be useful for apps like music players, but you\'ll need to manually freeze them through \"Batch Freeze Shortcut\" afterwards.</string>
<string name="settings_fingerprint_auth_service">Fingerprint Authentication</string>
<string name="settings_fingerprint_auth_service_desc">Use your fingerprint to unfreeze and launch apps</string>
<string name="fingerprint_auth_negative">Cancel</string>
<string name="settings_about">About</string>
<string name="settings_version">Version</string>
<string name="settings_source_code">Source Code</string>
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/xml/preferences_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
android:title="@string/settings_dont_freeze_foreground"
android:summary="@string/settings_dont_freeze_foreground_desc" />

<androidx.preference.CheckBoxPreference
android:defaultValue="false"
android:dependency="settings_auto_freeze_service"
android:key="settings_fingerprint_auth_service"
android:summary="@string/settings_fingerprint_auth_service_desc"
android:title="@string/settings_fingerprint_auth_service" />
</androidx.preference.PreferenceCategory>

<androidx.preference.PreferenceCategory
Expand Down