Compare commits

...
23 Commits
Author SHA1 Message Date
gdlbo 44fa23020e fixed double pin 2023-09-25 18:19:05 +03:00
gdlbo 4cdcf9aee0 Update R.java 2023-09-25 16:37:31 +03:00
gdlbo 63c5262444 conflicts fixes 2023-09-25 15:45:33 +03:00
gdlbo 9074e80478 Merge branch 'main' into feat/app_pin 2023-09-25 15:26:21 +03:00
gdlbo 13fab86b83 Merge branch 'main' into feat/app_pin 2023-03-25 22:08:20 +03:00
k1zn 5ada5d74d2 /ru/ add time localization 2023-03-12 19:25:06 +05:00
k1zn aa1802c371 /ru/ localization 2023-03-12 19:17:19 +05:00
aliveoutside 0cf1c6ecba Merge remote-tracking branch 'origin/feat/app_pin' into feat/app_pin 2023-03-12 13:38:48 +03:00
gdlbo f051420ff9 upgraded pin-code settings 2023-03-07 19:56:53 +03:00
gdlbo 3902a40e30 Update R.java 2023-03-07 19:16:38 +03:00
aliveoutside 0c97f15088 pincode code cleanup 2023-03-05 17:46:39 +03:00
aliveoutside 4cf112921b open keyboard automatically if fingerprint prompt was dismissed 2023-03-05 16:49:58 +03:00
aliveoutside 6e31b26af0 remove pincode time setting and set the default timer to 24 hrs 2023-03-05 16:15:32 +03:00
gdlbo 6f22b5dd03 pin number limits 2023-03-03 20:22:05 +03:00
gdlbo 9ee1bd0f79 Update UnlockActivity.java 2023-03-03 16:39:06 +03:00
gdlbo 56b11bc43c fixed size of pin 2023-03-03 16:22:44 +03:00
gdlbo a476d490a6 added something
idk lol lmao dead
2023-03-02 19:15:12 +03:00
gdlbo 74b50fb9c2 reset pin 2023-03-02 13:08:47 +03:00
gdlbo ddaedd0ba0 Improved screen locking system + improved lock display 2023-03-02 12:53:46 +03:00
gdlbo 729342085a compiling fix 2023-03-02 12:07:56 +03:00
gdlbo bb20178dc9 Merge branch 'main' into feat/app_pin 2023-03-02 11:59:41 +03:00
aliveoutside 306cee1fd4 Merge branch 'main' into feat/app_pin 2023-02-20 21:10:07 +03:00
aliveoutside 02e8732e9f init PIN Lock 2023-02-20 21:02:28 +03:00
50 changed files with 2818 additions and 9 deletions
+2 -1
View File
@@ -43,4 +43,5 @@
-keep class com.vtosters.lite.** { *; }
-keep class com.vk.** { *; }
-keep class bruhcollective.itaysonlab.libvkx.client.** { *; }
-keep class bruhcollective.itaysonlab.libvkx.client.** { *; }
-keep class com.guardanis.applock.** { *; }
@@ -0,0 +1,342 @@
package com.guardanis.applock;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import com.vtosters.lite.R;
import com.guardanis.applock.activities.UnlockActivity;
import com.guardanis.applock.services.FingerprintLockService;
import com.guardanis.applock.services.FingerprintLockService.AuthenticationDelegate;
import com.guardanis.applock.services.LockService;
import com.guardanis.applock.services.PINLockService;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import ru.vtosters.lite.utils.AndroidUtils;
public class AppLock {
public static final int REQUEST_CODE_UNLOCK = 9371;
public static final int REQUEST_CODE_LOCK_CREATION = 9372;
public static final int REQUEST_CODE_FINGERPRINT_PERMISSION = 9373;
public static final int ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE = 1;
public static final int ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED = 2;
public static final int ERROR_CODE_FINGERPRINTS_EMPTY = 3;
public static final int ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED = 4;
public static final int ERROR_CODE_SDK_VERSION_MINIMUM = 5;
private static final String PREFS = "pin__preferences";
private static final String PREF_UNLOCK_FAILURE_TIME = "pin__unlock_failure_time";
private static final String PREF_UNLOCK_SUCCESS_TIME = "pin__unlock_success_time";
private static AppLock instance;
protected Context context;
protected HashMap<Class<? extends LockService>, LockService> lockServices = new HashMap<>();
protected int unlockAttemptsCount = 1;
protected AppLock(Context context) {
this.context = context.getApplicationContext();
this.lockServices.put(PINLockService.class, new PINLockService());
this.lockServices.put(FingerprintLockService.class, new FingerprintLockService());
}
public static AppLock getInstance(Context context) {
if (instance == null)
instance = new AppLock(context);
return instance;
}
/**
* @return true if the user has enrolled in either PIN or Fingerprint locking
*/
public static boolean isEnrolled(Context context) {
AppLock helper = getInstance(context);
for (LockService service : helper.lockServices.values()) {
if (service.isEnrolled(context))
return true;
}
return false;
}
public static void setAuthenticationRequired(Context context) {
AppLock helper = getInstance(context);
helper.getPreferences()
.edit()
.putLong(PREF_UNLOCK_SUCCESS_TIME, 0)
.commit();
}
/**
* @return true if the user is enrolled in locking and the last successful unlock happened more than the default lock duration ago
*/
public static boolean isUnlockRequired(Context context) {
int minutes = Integer.parseInt(getPinCodeTime(context));
if (minutes < 1) {
minutes = 1;
}
return isUnlockRequired(context, TimeUnit.MINUTES.toMillis(minutes));
}
public static String getPinCodeTime(Context context) {
return context.getString(AndroidUtils.getIdentifier("applock__activity_lock_reenable_minutes", "integer"));
}
public static String getPinCodeLimits(Context context) {
return context.getString(AndroidUtils.getIdentifier("applock__input_pin_item_count", "integer"));
}
/**
* @return true if the user is enrolled in locking and the last successful unlock happened more than lastSuccessValidMs ago
*/
public static boolean isUnlockRequired(Context context, long lastSuccessValidMs) {
return isEnrolled(context) && lastSuccessValidMs < System.currentTimeMillis() - getUnlockSuccessTime(context);
}
protected static long getUnlockSuccessTime(Context context) {
return AppLock.getInstance(context)
.getPreferences()
.getLong(PREF_UNLOCK_SUCCESS_TIME, 0);
}
public static void onActivityResumed(Activity activity) {
if (isUnlockRequired(activity)) {
boolean unlockActivityReturnAllowed = activity.getResources()
.getBoolean(AndroidUtils.getIdentifier("applock__unlock_activity_return_allowed", "bool"));
Intent intent = new Intent(activity, UnlockActivity.class)
.putExtra(UnlockActivity.INTENT_ALLOW_UNLOCKED_EXIT, unlockActivityReturnAllowed);
activity.startActivityForResult(intent, REQUEST_CODE_UNLOCK);
}
}
/**
* Check if an action-based unlock is required and navigates to the UnlockActivity if true.
*
* @return true if unlock is required.
*/
public static boolean unlockIfRequired(Activity activity) {
if (isUnlockRequired(activity)) {
Intent intent = new Intent(activity, UnlockActivity.class)
.putExtra(UnlockActivity.INTENT_ALLOW_UNLOCKED_EXIT, true);
activity.startActivityForResult(intent, REQUEST_CODE_UNLOCK);
return true;
} else
return false;
}
public void attemptFingerprintUnlock(final UnlockDelegate eventListener) {
if (handleFailureBlocking(eventListener))
return;
AuthenticationDelegate delegate = new AuthenticationDelegate() {
@Override
public void onResolutionRequired(int errorCode) {
// eventListener.onResolutionRequired(errorCode);
eventListener.requestOpenKeyboard();
}
@Override
public void onAuthenticationHelp(int code, CharSequence message) {
eventListener.onAuthenticationHelp(code, String.valueOf(message));
}
@Override
public void onAuthenticating(CancellationSignal cancellationSignal) {
// Handled internally
}
@Override
public void onAuthenticationSuccess(@NonNull FingerprintManagerCompat.AuthenticationResult result) {
onUnlockSuccessful(eventListener);
}
@Override
public void onAuthenticationFailed(String message) {
Log.w(getClass().getSimpleName(), "Fingerprint authentication failed: " + message);
}
};
getLockService(FingerprintLockService.class)
.authenticate(context, delegate);
}
public void attemptPINUnlock(String pin, final UnlockDelegate eventListener) {
if (handleFailureBlocking(eventListener))
return;
PINLockService.AuthenticationDelegate delegate = new PINLockService.AuthenticationDelegate() {
@Override
public void onNoPIN() {
onUnlockFailed(context.getString(AndroidUtils.getIdentifier("applock__unlock_error_no_matching_pin_found", "string")));
}
@Override
public void onPINDoesNotMatch() {
onUnlockFailed(context.getString(AndroidUtils.getIdentifier("applock__unlock_error_match_failed", "string")));
}
@Override
public void onPINMatches() {
onUnlockSuccessful(eventListener);
}
private void onUnlockFailed(String message) {
handleUnlockFailure(message, eventListener);
}
};
getLockService(PINLockService.class)
.authenticate(context, pin, delegate);
}
/**
* @return true if failure blocking is enabled
*/
private boolean handleFailureBlocking(final UnlockDelegate eventListener) {
if (isUnlockFailureBlockEnabled()) {
unlockAttemptsCount++;
if (getFailureDelayMs() < System.currentTimeMillis() - getUnlockFailureBlockStart())
resetUnlockFailure();
else {
String message = String.format(
context.getString(AndroidUtils.getIdentifier("applock__unlock_error_retry_limit_exceeded", "string")),
formatTimeRemaining());
if (eventListener != null)
eventListener.onFailureLimitExceeded(message);
return true;
}
}
return false;
}
protected void handleUnlockFailure(String message, UnlockDelegate eventListener) {
this.unlockAttemptsCount++;
if (eventListener != null)
eventListener.onFailureLimitExceeded(message);
if (context.getResources().getInteger(AndroidUtils.getIdentifier("applock__max_retry_count", "integer")) < unlockAttemptsCount)
onFailureExceedsLimit();
}
public SharedPreferences getPreferences() {
return context.getSharedPreferences(PREFS, 0);
}
protected void onFailureExceedsLimit() {
getPreferences()
.edit()
.putLong(PREF_UNLOCK_FAILURE_TIME, System.currentTimeMillis())
.commit();
}
public boolean isUnlockFailureBlockEnabled() {
return context.getResources().getInteger(AndroidUtils.getIdentifier("applock__max_retry_count", "integer")) < unlockAttemptsCount
|| System.currentTimeMillis() - getUnlockFailureBlockStart() < getFailureDelayMs();
}
protected long getUnlockFailureBlockStart() {
return getPreferences()
.getLong(PREF_UNLOCK_FAILURE_TIME, 0);
}
protected void onUnlockSuccessful(UnlockDelegate eventListener) {
getPreferences()
.edit()
.putLong(PREF_UNLOCK_SUCCESS_TIME, System.currentTimeMillis())
.commit();
resetUnlockFailure();
if (eventListener != null)
eventListener.onUnlockSuccessful();
}
/**
* Reset the last login time to require the user to re-authenticate with
* AppLock the next time they are eligible to unlock.
*/
public void setAuthenticationRequired() {
getPreferences()
.edit()
.putLong(PREF_UNLOCK_SUCCESS_TIME, 0)
.commit();
}
protected void resetUnlockFailure() {
unlockAttemptsCount = 1;
getPreferences()
.edit()
.putLong(PREF_UNLOCK_FAILURE_TIME, 0)
.commit();
}
protected String formatTimeRemaining() {
long millis = getFailureDelayMs() - (System.currentTimeMillis() - getUnlockFailureBlockStart());
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis));
if (TimeUnit.MILLISECONDS.toMinutes(millis) < 1)
return String.format(AndroidUtils.getString(R.string.n_seconds), seconds);
else
return String.format(AndroidUtils.getString(R.string.n_mins_secs), TimeUnit.MILLISECONDS.toMinutes(millis), seconds);
}
protected long getFailureDelayMs() {
return TimeUnit.MINUTES.toMillis(context.getResources()
.getInteger(AndroidUtils.getIdentifier("applock__failure_retry_delay", "integer")));
}
/**
* This will remove all PIN and/or Fingerprint enrollment data.
* Users will need to re-enroll in AppLock after this call.
*/
public void invalidateEnrollments() {
resetUnlockFailure();
setAuthenticationRequired();
for (LockService service : lockServices.values())
service.invalidateEnrollments(context);
}
public void cancelPendingAuthentications() {
for (LockService service : lockServices.values())
service.cancelPendingAuthentications(context);
}
public <T extends LockService> T getLockService(Class<T> named) {
return (T) lockServices.get(named);
}
public interface UnlockDelegate {
void onUnlockSuccessful();
void onResolutionRequired(int errorCode);
void onAuthenticationHelp(int code, String message);
void onFailureLimitExceeded(String message);
void requestOpenKeyboard();
}
}
@@ -0,0 +1,58 @@
package com.guardanis.applock.activities;
import static ru.vtosters.hooks.other.ThemesUtils.getBackgroundContent;
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
import static ru.vtosters.hooks.other.ThemesUtils.isDarkTheme;
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.WindowInsetsController;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.guardanis.applock.views.LockCreationViewController;
public class LockCreationActivity extends AppCompatActivity implements LockCreationViewController.Delegate {
protected LockCreationViewController viewController;
@Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(getIdentifier("applock__activity_lock_creation", "layout"));
findViewById(getIdentifier("applock__activity_lock_creation", "id")).setBackgroundColor(getBackgroundContent());
((TextView) findViewById(getIdentifier("pin__description", "id"))).setTextColor(getTextAttr());
this.viewController = new LockCreationViewController(this, findViewById(getIdentifier("pin__container", "id")));
this.viewController.setDelegate(this);
this.viewController.setupRootFlow();
this.getWindow().setStatusBarColor(getBackgroundContent());
this.getWindow().setNavigationBarColor(getBackgroundContent());
if (!isDarkTheme()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this.getWindow().getInsetsController().setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
} else {
this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
}
}
@Override
public void onLockCreated() {
setResult(Activity.RESULT_OK);
finish();
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
super.onPointerCaptureChanged(hasCapture);
}
}
@@ -0,0 +1,31 @@
package com.guardanis.applock.activities;
import android.app.Activity;
import android.content.Intent;
import androidx.appcompat.app.AppCompatActivity;
import com.guardanis.applock.AppLock;
public class LockableAppCompatActivity extends AppCompatActivity {
@Override
protected void onPostResume() {
super.onPostResume();
AppLock.onActivityResumed(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AppLock.REQUEST_CODE_UNLOCK && resultCode == Activity.RESULT_CANCELED)
handleUnlockActivityReturnFailure();
}
protected void handleUnlockActivityReturnFailure() {
setResult(Activity.RESULT_CANCELED);
finish();
}
}
@@ -0,0 +1,88 @@
package com.guardanis.applock.activities;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowInsetsController;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.guardanis.applock.views.UnlockViewController;
import ru.vtosters.lite.utils.AndroidUtils;
import static ru.vtosters.hooks.other.ThemesUtils.getBackgroundContent;
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
import static ru.vtosters.hooks.other.ThemesUtils.isDarkTheme;
public class UnlockActivity extends AppCompatActivity implements UnlockViewController.Delegate {
public static final String INTENT_ALLOW_UNLOCKED_EXIT = "pin__allow_activity_exit"; // false by default
protected UnlockViewController viewController;
@Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(AndroidUtils.getIdentifier("applock__activity_unlock", "layout"));
findViewById(AndroidUtils.getIdentifier("applock__activity_unlock", "id")).setBackgroundColor(getBackgroundContent());
((TextView) findViewById(AndroidUtils.getIdentifier("pin__description", "id"))).setTextColor(getTextAttr());
this.viewController = new UnlockViewController(this, findViewById(AndroidUtils.getIdentifier("pin__container", "id")));
this.viewController.setAutoAuthorizationEnabled(false); // Disable auto authorization so fingerprint doesn't crash onResume
this.viewController.setDelegate(this);
this.viewController.setupRootFlow();
this.viewController.setAutoAuthorizationEnabled(true);
this.getWindow().setStatusBarColor(getBackgroundContent());
this.getWindow().setNavigationBarColor(getBackgroundContent());
if (!isDarkTheme()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this.getWindow().getInsetsController().setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
} else {
this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
}
}
@Override
public void onUnlockSuccessful() {
setResult(Activity.RESULT_OK);
finish();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (event.getRepeatCount() == 0) handleBackPressed();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onBackPressed() {
handleBackPressed();
}
protected void handleBackPressed() {
if (!getIntent().getBooleanExtra(INTENT_ALLOW_UNLOCKED_EXIT, false)) {
return;
}
setResult(Activity.RESULT_CANCELED);
finish();
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
super.onPointerCaptureChanged(hasCapture);
}
}
@@ -0,0 +1,104 @@
package com.guardanis.applock.dialogs;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.util.Log;
import android.view.View;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDialog;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.views.AppLockViewController;
import java.lang.ref.WeakReference;
public abstract class AppLockDialogBuilder<ALVC extends AppLockViewController> {
protected WeakReference<Activity> activity;
protected ALVC viewController;
protected WeakReference<AppCompatDialog> dialog = new WeakReference<AppCompatDialog>(null);
protected int layoutResId;
public AppLockDialogBuilder(Activity activity, int layoutResId) {
this.activity = new WeakReference<>(activity);
this.layoutResId = layoutResId;
}
public Dialog show() {
if (viewController != null)
throw new RuntimeException("You can only call show() once per AppLockDialogBuilder instance.");
final Activity activity = this.activity.get();
if (activity == null)
return null;
View parent = activity.getLayoutInflater()
.inflate(layoutResId, null, false);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setView(parent);
builder.setOnCancelListener(dialogInterface -> handleCanceled());
builder.setOnDismissListener(dialogInterface -> handleDismissed());
this.viewController = buildViewControllerInstance(parent);
this.viewController.setupRootFlow();
AppCompatDialog dialog = builder.show();
this.dialog = new WeakReference<>(dialog);
return dialog;
}
protected abstract ALVC buildViewControllerInstance(View parent);
protected void handleCanceled() {
dismissDialog();
}
protected void handleDismissed() {
this.dialog = new WeakReference<>(null);
this.viewController.unregisterReceivers();
Activity activity = this.activity.get();
if (activity == null)
return;
AppLock.getInstance(activity)
.cancelPendingAuthentications();
}
protected void dismissDialog() {
try {
AppCompatDialog dialog = this.dialog.get();
if (dialog == null)
return;
// Attempt to prevent stupid fucking uncatchable system error "View not Attached to Window" when dialog dismissed
Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();
if (context instanceof Activity) {
if (!((Activity) context).isFinishing()) {
if (!((Activity) context).isDestroyed())
dialog.dismiss();
}
} else
dialog.dismiss();
} catch (Throwable e) {
Log.e("AppLockDialogBuilder", "View not Attached to Window???", e);
}
this.dialog = new WeakReference<>(null);
}
public ALVC getViewController() {
return viewController;
}
}
@@ -0,0 +1,60 @@
package com.guardanis.applock.dialogs;
import android.app.Activity;
import android.view.View;
import com.guardanis.applock.views.LockCreationViewController;
import ru.vtosters.lite.utils.AndroidUtils;
public class LockCreationDialogBuilder extends AppLockDialogBuilder<LockCreationViewController> implements LockCreationViewController.Delegate {
protected Runnable lockCreatedCallback;
protected Runnable canceledCallback;
public LockCreationDialogBuilder(Activity activity) {
super(activity, AndroidUtils.getIdentifier("applock__lock_creation", "layout"));
}
/**
* Set a Runnable to be triggered when a user has successfully enrolled in locking.
*/
public LockCreationDialogBuilder onLockCreated(Runnable lockCreatedCallback) {
this.lockCreatedCallback = lockCreatedCallback;
return this;
}
/**
* Set a Runnable to be triggered when a user has canceled enrollment.
*/
public LockCreationDialogBuilder onCanceled(Runnable canceledCallback) {
this.canceledCallback = canceledCallback;
return this;
}
@Override
protected LockCreationViewController buildViewControllerInstance(View parent) {
LockCreationViewController controller = new LockCreationViewController(activity.get(), parent);
controller.setDelegate(this);
return controller;
}
@Override
public void onLockCreated() {
dismissDialog();
if (lockCreatedCallback != null)
lockCreatedCallback.run();
}
@Override
protected void handleCanceled() {
super.handleCanceled();
if (canceledCallback != null)
canceledCallback.run();
}
}
@@ -0,0 +1,110 @@
package com.guardanis.applock.dialogs;
import android.app.Activity;
import android.app.Dialog;
import android.view.View;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.views.UnlockViewController;
import ru.vtosters.lite.utils.AndroidUtils;
public class UnlockDialogBuilder extends AppLockDialogBuilder<UnlockViewController> implements UnlockViewController.Delegate {
protected Runnable unlockCallback;
protected Runnable canceledCallback;
public UnlockDialogBuilder(Activity activity) {
super(activity, AndroidUtils.getIdentifier("applock__unlock", "layout"));
}
/**
* Set a Runnable to be triggered when a user has successfully unlocked.
*/
public UnlockDialogBuilder onUnlocked(Runnable unlockCallback) {
this.unlockCallback = unlockCallback;
return this;
}
/**
* Set a Runnable to be triggered when a user has canceled unlocking.
*/
public UnlockDialogBuilder onCanceled(Runnable canceledCallback) {
this.canceledCallback = canceledCallback;
return this;
}
@Override
protected UnlockViewController buildViewControllerInstance(View parent) {
UnlockViewController controller = new UnlockViewController(activity.get(), parent);
controller.setDelegate(this);
return controller;
}
@Override
public void onUnlockSuccessful() {
dismissDialog();
triggerUnlockCallback();
}
@Override
protected void handleCanceled() {
super.handleCanceled();
if (canceledCallback != null)
canceledCallback.run();
}
protected void triggerUnlockCallback() {
if (unlockCallback != null)
unlockCallback.run();
}
/**
* Check if the user is actually enrolled in locking before opening the unlock dialog.
* If the user is enrolled, show() will be called. Otherwise, this function will trigger this
* instance's UnlockDialogBuilder.unlockCallback immediately.
*
* @return Dialog if show() is called
*/
public Dialog showIfEnrolledOrSuccess() {
Activity activity = this.activity.get();
if (activity == null)
return null;
if (!AppLock.isEnrolled(activity)) {
triggerUnlockCallback();
return null;
}
return show();
}
/**
* Check if the user is both enrolled in locking and that unlocking is required before
* opening the unlock dialog. If the user is enrolled, show() will be called. Otherwise,
* this function will trigger this instance's UnlockDialogBuilder.unlockCallback immediately.
*
* @param longValidDurationMs the milliseconds a user's last unlock is valid for
* @return Dialog if show() is called
*/
public Dialog showIfRequiredOrSuccess(long longValidDurationMs) {
Activity activity = this.activity.get();
if (activity == null)
return null;
if (!AppLock.isUnlockRequired(activity, longValidDurationMs)) {
triggerUnlockCallback();
return null;
}
return show();
}
}
@@ -0,0 +1,116 @@
package com.guardanis.applock.pin;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import com.guardanis.applock.AppLock;
import ru.vtosters.lite.utils.AndroidUtils;
import java.lang.ref.WeakReference;
interface InputTextListener {
void onTextEntered(int count);
}
public class PINInputController implements TextView.OnEditorActionListener, InputTextListener {
private final WeakReference<PINInputView> inputView;
private WeakReference<InputEventListener> eventListener = new WeakReference<>(null);
/**
* If you want to actually receive events after this, make sure you call setInputEventListener
*/
public PINInputController(PINInputView inputView) {
this.inputView = new WeakReference<>(inputView);
inputView.setOnEditorActionListener(this);
inputView.setInputTextListener(this);
}
public PINInputController ensureKeyboardVisible() {
final PINInputView inputView = this.inputView.get();
if (inputView == null)
return this;
inputView.postDelayed(inputView::ensureKeyboardVisible, 300);
return this;
}
public PINInputController setPasswordCharactersEnabled(boolean passwordCharacterEnabled) {
this.inputView.get().setPasswordCharactersEnabled(passwordCharacterEnabled);
return this;
}
public PINInputController setInputNumbersCount(int inputNumbersCount) {
this.inputView.get().setInputViewsCount(inputNumbersCount);
return this;
}
public PINInputController setInputEventListener(InputEventListener eventListener) {
this.eventListener = new WeakReference<>(eventListener);
return this;
}
@Override
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
PINInputView inputView = this.inputView.get();
if (inputView == null)
return false;
if (isSoftKeyboardFinishedAction(textView, i, keyEvent)) {
InputEventListener eventListener = this.eventListener.get();
if (eventListener != null)
eventListener.onInputEntered(inputView.getText());
inputView.reset();
return true;
}
return false;
}
private boolean isSoftKeyboardFinishedAction(TextView view, int action, KeyEvent event) {
// Some devices return null event on editor actions for Enter Button (thanks, Samsung)
if (!(event == null || event.getAction() == KeyEvent.ACTION_DOWN))
return false;
// Enter clicked on Bluetooth Keyboard (action is zero on hardware keyboards)
if (action == EditorInfo.IME_ACTION_UNSPECIFIED && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)
return true;
return action == EditorInfo.IME_ACTION_DONE
|| action == EditorInfo.IME_ACTION_GO
|| action == EditorInfo.IME_ACTION_SEND;
}
public boolean matchesRequiredPINLength(String input) {
final PINInputView inputView = this.inputView.get();
if (inputView == null)
return false;
return inputView.matchesRequiredPINLength(input);
}
@Override
public void onTextEntered(int count) {
if (count == Integer.parseInt(AppLock.getPinCodeLimits(AndroidUtils.getGlobalContext()))) {
var inputView = this.inputView.get();
InputEventListener eventListener = this.eventListener.get();
if (eventListener != null)
eventListener.onInputEntered(inputView.getText());
inputView.reset();
}
}
public interface InputEventListener {
void onInputEntered(String input);
}
}
@@ -0,0 +1,232 @@
package com.guardanis.applock.pin;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.WeakHashMap;
import com.guardanis.applock.AppLock;
import ru.vtosters.hooks.other.ThemesUtils;
import ru.vtosters.lite.utils.AndroidUtils;
public class PINInputView extends LinearLayout implements TextWatcher {
private final WeakHashMap<PINItemView, PINItemAnimator> animators = new WeakHashMap<PINItemView, PINItemAnimator>();
private Paint itemTextPaint;
private Paint itemBackgroundPaint;
private int inputViewsCount = 10;
private PINItemView[] pinItemViews;
private EditText editText;
private String lastText = "";
private boolean passwordCharactersEnabled = true;
private String passwordCharacter;
private InputTextListener listener;
public PINInputView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
private void init(AttributeSet attrs) {
setWillNotDraw(false);
setupEditText();
itemTextPaint = new Paint();
itemTextPaint.setColor(ThemesUtils.getAccentColor());
itemBackgroundPaint = new Paint();
itemBackgroundPaint.setColor(ThemesUtils.getMutedAccentColor());
inputViewsCount = Integer.parseInt(AppLock.getPinCodeLimits(AndroidUtils.getGlobalContext()));
passwordCharacter = getResources().getString(AndroidUtils.getIdentifier("applock__password_char", "string"));
}
protected void setupEditText() {
removeAllViews();
this.editText = inflateFakeEditText();
this.editText.addTextChangedListener(this);
addView(editText);
}
protected EditText inflateFakeEditText() {
return (EditText) LayoutInflater.from(getContext())
.inflate(AndroidUtils.getIdentifier("applock__fake_edit_text", "layout"), this, false);
}
public PINInputView setInputViewsCount(int inputViewsCount) {
this.inputViewsCount = inputViewsCount;
return reset();
}
public PINInputView reset() {
this.lastText = "";
this.editText.setText("");
if (pinItemViews != null)
animateLastOut();
return this;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
ensureKeyboardVisible();
return true;
}
return super.onTouchEvent(event);
}
public void ensureKeyboardVisible() {
editText.requestFocus();
((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
.showSoftInput(editText, 0);
}
@Override
public void onDraw(Canvas canvas) {
if (pinItemViews == null || pinItemViews.length != inputViewsCount)
setupItemViews(canvas);
String text = editText.getText().toString();
for (int i = 0; i < pinItemViews.length; i++) {
String textItem = i < text.length()
? (passwordCharactersEnabled ? passwordCharacter : text.substring(i, i + 1))
: "";
pinItemViews[i].draw(canvas, textItem);
}
}
private void setupItemViews(Canvas canvas) {
pinItemViews = new PINItemView[inputViewsCount];
int cellWidth = canvas.getWidth() / inputViewsCount;
int largestRadius = Math.min(cellWidth / 2, canvas.getHeight() / 2);
int smallestRadius = (int) (largestRadius * Float.parseFloat(getResources().getString(AndroidUtils.getIdentifier("applock__empty_item_min_size_percent", "string"))));
int[] minMaxRadius = new int[]{smallestRadius, largestRadius};
itemTextPaint.setTextSize((int) (largestRadius * .85));
for (int i = 0; i < pinItemViews.length; i++) {
float[] positionInCanvas = getPositionInCanvas(canvas, i, cellWidth);
pinItemViews[i] = new PINItemView(positionInCanvas, minMaxRadius, itemTextPaint, itemBackgroundPaint);
}
}
private float[] getPositionInCanvas(Canvas canvas, int position, int cellWidth) {
return new float[]{
(cellWidth * position) + (cellWidth / 2),
canvas.getHeight() / 2
};
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (editable == null || lastText == null)
return;
String text = editable.toString();
if (inputViewsCount < text.length()) {
editText.setText(lastText);
editText.setSelection(editText.getText().length());
} else if (text.length() < lastText.length()) {
animateLastOut();
this.lastText = text;
} else if (lastText.length() < text.length()) {
animateLastIn();
this.lastText = text;
}
if (listener != null) {
listener.onTextEntered(text.length());
}
}
public void setInputTextListener(InputTextListener listener) {
this.listener = listener;
}
private void animateLastOut() {
int startingIndex = editText.getText().toString().length(); // One after current length, only happens on backspace
for (int i = pinItemViews.length - 1; startingIndex <= i; i--)
if (!pinItemViews[i].isAnimatedOut())
animate(pinItemViews[i], PINItemAnimator.ItemAnimationDirection.OUT);
}
private void animateLastIn() {
PINItemView item = pinItemViews[editText.getText().toString().length() - 1];
animate(item, PINItemAnimator.ItemAnimationDirection.IN);
}
private void animate(PINItemView view, PINItemAnimator.ItemAnimationDirection direction) {
cancelPreviousAnimation(view);
view.setAnimationDirection(direction);
PINItemAnimator animator = new PINItemAnimator(this, view, direction);
animators.put(view, animator);
animator.start();
}
private void cancelPreviousAnimation(PINItemView view) {
PINItemAnimator animator = animators.get(view);
try {
animator.cancel();
animators.put(view, null);
} catch (Exception e) {
e.printStackTrace();
}
}
public String getText() {
return editText.getText().toString();
}
public void setOnEditorActionListener(TextView.OnEditorActionListener actionListener) {
editText.setOnEditorActionListener(actionListener);
}
public void setPasswordCharactersEnabled(boolean passwordCharactersEnabled) {
this.passwordCharactersEnabled = passwordCharactersEnabled;
}
public void setPasswordCharacter(String passwordCharacter) {
this.passwordCharacter = passwordCharacter;
}
public boolean matchesRequiredPINLength(String input) {
return input.length() == inputViewsCount;
}
}
@@ -0,0 +1,92 @@
package com.guardanis.applock.pin;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import java.lang.ref.WeakReference;
import ru.vtosters.lite.utils.AndroidUtils;
public class PINItemAnimator extends Thread {
private static final int ANIMATION_DURATION = 150;
private static final int UPDATE_RATE = 25;
private final WeakReference<PINInputView> inputView;
private final PINItemView itemView;
private final ItemAnimationDirection animationDirection;
private final Interpolator itemInterpolator = new AccelerateDecelerateInterpolator();
private final float minSizePercent;
private long startTime;
private boolean canceled = false;
public PINItemAnimator(PINInputView inputView, PINItemView itemView, ItemAnimationDirection animationDirection) {
this.inputView = new WeakReference<>(inputView);
this.itemView = itemView;
this.animationDirection = animationDirection;
this.minSizePercent = Float.parseFloat(inputView.getResources().getString(AndroidUtils.getIdentifier("applock__empty_item_min_size_percent", "string")));
}
@Override
public void run() {
this.startTime = System.currentTimeMillis();
try {
if (animationDirection == ItemAnimationDirection.IN)
animateIn();
else
animateOut();
} catch (Exception e) {
e.printStackTrace();
}
}
private void animateIn() throws Exception {
float percent = minSizePercent;
while (percent < 1 && !canceled) {
percent = Math.min(minSizePercent + calculatePercentComplete(), 1);
updateView(percent);
Thread.sleep(UPDATE_RATE);
}
}
private void animateOut() throws Exception {
float percent = 1 - calculatePercentComplete();
while (minSizePercent < percent && !canceled) {
percent = Math.max(1 - calculatePercentComplete(), minSizePercent);
updateView(percent);
Thread.sleep(UPDATE_RATE);
}
}
protected float calculatePercentComplete() {
float completed = ((float) (System.currentTimeMillis() - startTime)) / ANIMATION_DURATION;
return itemInterpolator.getInterpolation(completed);
}
private void updateView(final float percent) {
final PINInputView inputView = this.inputView.get();
if (inputView == null)
return;
inputView.post(() -> {
itemView.onAnimationUpdate(percent);
inputView.invalidate();
});
}
public void cancel() {
this.canceled = true;
}
public enum ItemAnimationDirection {
IN, OUT
}
}
@@ -0,0 +1,61 @@
package com.guardanis.applock.pin;
import android.graphics.Canvas;
import android.graphics.Paint;
public class PINItemView {
private final float[] position;
private final int intendedRadius;
private int currentRadius;
private Paint backgroundPaint;
private float[] textPosition;
private Paint textPaint;
private PINItemAnimator.ItemAnimationDirection animationDirection = PINItemAnimator.ItemAnimationDirection.IN;
public PINItemView(float[] position, int[] minMaxRadius, Paint baseTextPaint, Paint baseBackgroundPaint) {
this.position = position;
this.intendedRadius = minMaxRadius[1];
this.currentRadius = minMaxRadius[0];
setupPaints(baseTextPaint, baseBackgroundPaint);
}
private void setupPaints(Paint baseTextPaint, Paint baseBackgroundPaint) {
this.textPaint = new Paint();
this.textPaint.setTextAlign(Paint.Align.CENTER);
this.textPaint.setAntiAlias(true);
this.textPaint.setColor(baseTextPaint.getColor());
this.textPaint.setTextSize(baseTextPaint.getTextSize());
this.textPosition = new float[]{
position[0],
position[1] - ((textPaint.descent() + textPaint.ascent()) / 2)
};
this.backgroundPaint = new Paint();
this.backgroundPaint.setTextAlign(Paint.Align.CENTER);
this.backgroundPaint.setAntiAlias(true);
this.backgroundPaint.setColor(baseBackgroundPaint.getColor());
}
public void draw(Canvas canvas, String textValue) {
canvas.drawCircle(position[0], position[1], currentRadius, backgroundPaint);
canvas.drawText(textValue, textPosition[0], textPosition[1], textPaint);
}
public void setAnimationDirection(PINItemAnimator.ItemAnimationDirection animationDirection) {
this.animationDirection = animationDirection;
}
public void onAnimationUpdate(float percentCompleted) {
this.currentRadius = (int) (intendedRadius * percentCompleted);
this.textPaint.setAlpha((int) (255 * percentCompleted));
}
public boolean isAnimatedOut() {
return animationDirection == PINItemAnimator.ItemAnimationDirection.OUT;
}
}
@@ -0,0 +1,169 @@
package com.guardanis.applock.services;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.content.ContextCompat;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.utils.CipherGenerator;
import javax.crypto.Cipher;
import ru.vtosters.lite.utils.AndroidUtils;
public class FingerprintLockService extends LockService {
private static final String PREF_ENROLLMENT_ALLOWED = "pin__fingerprint_enrollment_allowed";
protected CancellationSignal fingerprintCancellationSignal;
@Override
public boolean isEnrollmentEligible(Context context) {
return Build.VERSION_CODES.M <= Build.VERSION.SDK_INT && context.getResources().getBoolean(AndroidUtils.getIdentifier("applock__fingerprint_service_enabled", "bool")) && isHardwarePresent(context);
}
public void enroll(Context context, AuthenticationDelegate delegate) {
authenticate(context, false, delegate);
}
public void authenticate(Context context, AuthenticationDelegate delegate) {
authenticate(context, true, delegate);
}
protected void authenticate(Context context, boolean localEnrollmentRequired, AuthenticationDelegate delegate) {
int errorCode = getRequiredResolutionErrorCode(context, localEnrollmentRequired);
if (-1 < errorCode) {
delegate.onResolutionRequired(errorCode);
return;
}
// Should be handled by getRequiredResolutionErrorCode(Context, boolean)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
attemptFingerprintManagerAuthentication(context, delegate);
}
/**
* @return the resolvable error code or -1 if there are no issues requiring a resolution
*/
protected int getRequiredResolutionErrorCode(Context context, boolean localEnrollmentRequired) {
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return AppLock.ERROR_CODE_SDK_VERSION_MINIMUM;
if (localEnrollmentRequired && !isEnrolled(context))
return AppLock.ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED;
if (!isHardwarePresent(context)) return AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE;
if (!manager.hasEnrolledFingerprints()) return AppLock.ERROR_CODE_FINGERPRINTS_EMPTY;
if (ContextCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED)
return AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED;
return -1;
}
protected void attemptFingerprintManagerAuthentication(final Context context, final AuthenticationDelegate delegate) {
this.fingerprintCancellationSignal = new CancellationSignal();
var callback = new FingerprintManagerCompat.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
super.onAuthenticationError(errMsgId, errString);
delegate.onResolutionRequired(AndroidUtils.getIdentifier("applock__fingerprint_error_unknown", "string"));
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
super.onAuthenticationHelp(helpMsgId, helpString);
delegate.onAuthenticationHelp(helpMsgId, helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
notifyEnrolled(context);
delegate.onAuthenticationSuccess(result);
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
}
};
delegate.onAuthenticating(fingerprintCancellationSignal);
try {
Cipher cipher = generateAuthCipher(context, false, 0);
FingerprintManagerCompat.CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher);
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
manager.authenticate(cryptoObject, 0, fingerprintCancellationSignal, callback, null);
} catch (Exception e) {
e.printStackTrace();
delegate.onResolutionRequired(AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE);
}
}
public boolean isHardwarePresent(Context context) {
return FingerprintManagerCompat.from(context).isHardwareDetected();
}
@Override
public boolean isEnrolled(Context context) {
return AppLock.getInstance(context).getPreferences().getBoolean(PREF_ENROLLMENT_ALLOWED, false);
}
protected void notifyEnrolled(Context context) {
AppLock.getInstance(context).getPreferences().edit().putBoolean(PREF_ENROLLMENT_ALLOWED, true).commit();
}
@SuppressLint("NewApi")
protected Cipher generateAuthCipher(Context context, boolean forceRegenerate, int attempts) throws Exception {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
throw new RuntimeException("generateAuthCipher() not supported before Api 23");
return new CipherGenerator().generateAuthCipher(context, forceRegenerate, attempts);
}
@Override
public void invalidateEnrollments(Context context) {
AppLock.getInstance(context).getPreferences().edit().putBoolean(PREF_ENROLLMENT_ALLOWED, false).commit();
}
@Override
public void cancelPendingAuthentications(Context context) {
if (fingerprintCancellationSignal != null) {
this.fingerprintCancellationSignal.cancel();
this.fingerprintCancellationSignal = null;
}
}
public interface AuthenticationDelegate {
void onResolutionRequired(int errorCode);
void onAuthenticationHelp(int code, CharSequence message);
void onAuthenticating(CancellationSignal cancellationSignal);
void onAuthenticationSuccess(FingerprintManagerCompat.AuthenticationResult result);
void onAuthenticationFailed(String message);
}
}
@@ -0,0 +1,14 @@
package com.guardanis.applock.services;
import android.content.Context;
public abstract class LockService {
public abstract boolean isEnrolled(Context context);
public abstract boolean isEnrollmentEligible(Context context);
public abstract void cancelPendingAuthentications(Context context);
public abstract void invalidateEnrollments(Context context);
}
@@ -0,0 +1,74 @@
package com.guardanis.applock.services;
import android.content.Context;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.utils.CryptoUtils;
public class PINLockService extends LockService {
private static final String PREF_SAVED_LOCKED_PASSWORD = "pin__saved_locked_password";
@Override
public boolean isEnrollmentEligible(Context context) {
// I couldn't find a good reason to allow the disabling of the PIN as an option.
return true;
}
public void authenticate(Context context, String pin, AuthenticationDelegate eventListener) {
if (!isEnrolled(context)) {
eventListener.onNoPIN();
return;
}
if (!getEnrolledPIN(context).equals(CryptoUtils.encryptSha1(pin))) {
eventListener.onPINDoesNotMatch();
return;
}
eventListener.onPINMatches();
}
@Override
public boolean isEnrolled(Context context) {
return AppLock.getInstance(context)
.getPreferences()
.getString(PREF_SAVED_LOCKED_PASSWORD, null) != null;
}
private String getEnrolledPIN(Context context) {
return AppLock.getInstance(context)
.getPreferences()
.getString(PREF_SAVED_LOCKED_PASSWORD, null);
}
public void enroll(Context context, String pin) {
AppLock.getInstance(context)
.getPreferences()
.edit()
.putString(PREF_SAVED_LOCKED_PASSWORD, CryptoUtils.encryptSha1(pin))
.commit();
}
@Override
public void invalidateEnrollments(Context context) {
AppLock.getInstance(context)
.getPreferences()
.edit()
.remove(PREF_SAVED_LOCKED_PASSWORD)
.commit();
}
@Override
public void cancelPendingAuthentications(Context context) {
// There are none
}
public interface AuthenticationDelegate {
void onNoPIN();
void onPINDoesNotMatch();
void onPINMatches();
}
}
@@ -0,0 +1,69 @@
package com.guardanis.applock.utils;
import android.content.Context;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import androidx.annotation.RequiresApi;
import java.security.KeyStore;
import java.security.UnrecoverableKeyException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import ru.vtosters.lite.utils.AndroidUtils;
@RequiresApi(api = Build.VERSION_CODES.M)
public class CipherGenerator {
private static final String KEYSTORE_NAME = "AndroidKeyStore";
public Cipher generateAuthCipher(Context context, boolean forceRegenerate, int attempts) throws Exception {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
throw new RuntimeException("generateAuthCipher() not supported before Api 23");
String alias = context.getString(AndroidUtils.getIdentifier("applock__fingerprint_alias", "string"));
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME);
keyStore.load(null);
if (forceRegenerate || !keyStore.containsAlias(alias)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
keyGenerator.init(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
}
String cipherFormat = String.format(
"%s/%s/%s",
KeyProperties.KEY_ALGORITHM_AES,
KeyProperties.BLOCK_MODE_CBC,
KeyProperties.ENCRYPTION_PADDING_PKCS7);
try {
Cipher cipher = Cipher.getInstance(cipherFormat);
cipher.init(Cipher.ENCRYPT_MODE, keyStore.getKey(alias, null));
return cipher;
} catch (KeyPermanentlyInvalidatedException e) {
e.printStackTrace();
if (1 < attempts)
return generateAuthCipher(context, true, attempts + 1);
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
if (1 < attempts)
return generateAuthCipher(context, true, attempts + 1);
}
return null;
}
}
@@ -0,0 +1,40 @@
package com.guardanis.applock.utils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class CryptoUtils {
public static String encryptSha1(String text) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(text.getBytes(StandardCharsets.UTF_8), 0, text.length());
return convertToHex(md.digest());
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private static String convertToHex(byte[] data) {
StringBuilder buf = new StringBuilder();
for (byte b : data) {
int half = (b >>> 4) & 0x0F;
int twoHalves = 0;
do {
buf.append(half <= 9
? (char) ('0' + half)
: (char) ('a' + half - 10));
half = b & 0x0F;
}
while (twoHalves++ < 1);
}
return buf.toString();
}
}
@@ -0,0 +1,85 @@
package com.guardanis.applock.utils;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import java.lang.ref.WeakReference;
public class LifeCycleUtils {
public static AppLockActivityLifeCycleCallbacks attach(Activity activity, AppLockActivityLifeCycleCallbacks.Delegate delegate) {
AppLockActivityLifeCycleCallbacks callbacks = new AppLockActivityLifeCycleCallbacks(activity, delegate);
activity.getApplication()
.registerActivityLifecycleCallbacks(callbacks);
return callbacks;
}
public static class AppLockActivityLifeCycleCallbacks implements Application.ActivityLifecycleCallbacks {
protected WeakReference<Activity> openedActivity;
protected WeakReference<Delegate> delegate;
public AppLockActivityLifeCycleCallbacks(Activity activity, Delegate delegate) {
this.openedActivity = new WeakReference<>(activity);
this.delegate = new WeakReference<>(delegate);
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityResumed(Activity activity) {
Activity opened = this.openedActivity.get();
Delegate delegate = this.delegate.get();
if (opened == null || delegate == null || opened != activity)
return;
delegate.onActivityResumed();
}
@Override
public void onActivityPaused(Activity activity) {
Activity opened = this.openedActivity.get();
Delegate delegate = this.delegate.get();
if (opened == null || delegate == null || opened != activity)
return;
delegate.onActivityPaused();
}
@Override
public void onActivityDestroyed(Activity activity) {
Activity opened = this.openedActivity.get();
if (opened == null || opened != activity)
return;
opened.getApplication()
.unregisterActivityLifecycleCallbacks(this);
}
public interface Delegate {
void onActivityResumed();
void onActivityPaused();
}
}
}
@@ -0,0 +1,184 @@
package com.guardanis.applock.views;
import android.Manifest;
import android.app.Activity;
import android.app.Application;
import android.content.Intent;
import android.view.View;
import android.widget.TextView;
import androidx.core.app.ActivityCompat;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.pin.PINInputController;
import com.guardanis.applock.pin.PINInputView;
import com.guardanis.applock.utils.LifeCycleUtils;
import java.lang.ref.WeakReference;
import ru.vtosters.lite.utils.AndroidUtils;
public abstract class AppLockViewController implements LifeCycleUtils.AppLockActivityLifeCycleCallbacks.Delegate {
protected PINInputController pinInputController;
protected WeakReference<Activity> activity;
protected WeakReference<View> parent;
protected WeakReference<PINInputView> pinInputView;
protected WeakReference<TextView> descriptionView;
protected WeakReference<View> actionSettings;
protected boolean autoAuthorizationEnabled = true;
protected Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
public AppLockViewController(Activity activity, View parent) {
this.activity = new WeakReference<>(activity);
this.parent = new WeakReference<>(parent);
this.descriptionView = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__description", "id")));
this.actionSettings = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__action_settings", "id")));
this.pinInputView = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__input_view", "id")));
int inputViewsCount = Integer.parseInt(AppLock.getPinCodeLimits(activity));
boolean passwordCharsEnabled = parent.getResources()
.getBoolean(AndroidUtils.getIdentifier("applock__item_password_chars_enabled", "bool"));
this.pinInputController = new PINInputController(pinInputView.get())
.setInputNumbersCount(inputViewsCount)
.setPasswordCharactersEnabled(passwordCharsEnabled);
this.activityLifecycleCallbacks = LifeCycleUtils.attach(activity, this);
}
public abstract void setupRootFlow();
public void setDescription(int descriptionResId) {
final TextView descriptionView = this.descriptionView.get();
if (descriptionView == null)
return;
descriptionView.setText(descriptionResId);
}
public void setDescription(String description) {
final TextView descriptionView = this.descriptionView.get();
if (descriptionView == null)
return;
descriptionView.setText(description);
}
protected <T extends View> void hide(WeakReference<T> weakView) {
final T view = weakView.get();
if (view == null)
return;
view.setVisibility(View.GONE);
}
protected <T extends View> void show(WeakReference<T> weakView) {
final T view = weakView.get();
if (view == null)
return;
view.setVisibility(View.VISIBLE);
}
public PINInputController getPINInputController() {
return pinInputController;
}
public View getParent() {
return parent.get();
}
public void setAutoAuthorizationEnabled(boolean autoAuthorizationEnabled) {
this.autoAuthorizationEnabled = autoAuthorizationEnabled;
}
public void updateActionSettings(final int errorCode) {
View actionSettings = this.actionSettings.get();
if (actionSettings == null)
return;
if (getSettingsIntent(errorCode) == null) {
actionSettings.setVisibility(View.GONE);
return;
}
actionSettings.setVisibility(View.VISIBLE);
actionSettings.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
handleActionSettingsClicked(errorCode);
}
});
}
protected abstract void handleActionSettingsClicked(int errorCode);
protected void handleInitialErrorPrompt(int errorCode) {
Activity activity = this.activity.get();
if (activity == null)
return;
if (errorCode == AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED) {
ActivityCompat.requestPermissions(
activity,
new String[]{Manifest.permission.USE_FINGERPRINT},
AppLock.REQUEST_CODE_FINGERPRINT_PERMISSION);
}
}
protected int getDescriptionResIdForError(int errorCode) {
return switch (errorCode) {
case AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED ->
AndroidUtils.getIdentifier("applock__fingerprint_error_permission", "string");
case AppLock.ERROR_CODE_FINGERPRINTS_EMPTY ->
AndroidUtils.getIdentifier("applock__fingerprint_error_none", "string");
case AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE ->
AndroidUtils.getIdentifier("applock__fingerprint_error_hardware", "string");
case AppLock.ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED ->
AndroidUtils.getIdentifier("applock__fingerprint_error_not_enrolled", "string");
default -> AndroidUtils.getIdentifier("applock__fingerprint_error_unknown", "string");
};
}
/**
* @return an Intent directed towards the correct system setting for the error, or null if there is none.
*/
public Intent getSettingsIntent(int errorCode) {
return switch (errorCode) {
case AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED, AppLock.ERROR_CODE_FINGERPRINTS_EMPTY, AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE ->
new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS);
default -> null;
};
}
public void unregisterReceivers() {
Activity activity = this.activity.get();
if (activity == null)
return;
activity.getApplication()
.unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks);
}
@Override
public void onActivityResumed() {
}
@Override
public void onActivityPaused() {
}
}
@@ -0,0 +1,253 @@
package com.guardanis.applock.views;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.view.View;
import androidx.core.content.ContextCompat;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.pin.PINInputController;
import com.guardanis.applock.services.FingerprintLockService;
import com.guardanis.applock.services.PINLockService;
import java.lang.ref.WeakReference;
import ru.vtosters.lite.utils.AndroidUtils;
public class LockCreationViewController extends AppLockViewController
implements PINInputController.InputEventListener, FingerprintLockService.AuthenticationDelegate {
protected DisplayVariant displayVariant = DisplayVariant.NONE;
protected WeakReference<Delegate> delegate;
protected WeakReference<View> chooserParent;
protected String pinFirst;
public LockCreationViewController(Activity activity, View parent) {
super(activity, parent);
this.chooserParent = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__create_chooser_items", "id")));
}
public LockCreationViewController setDelegate(Delegate delegate) {
this.delegate = new WeakReference<>(delegate);
return this;
}
@Override
public void setupRootFlow() {
Activity activity = this.activity.get();
if (activity == null)
return;
setupPINCreation();
}
protected void setupPINCreation() {
this.displayVariant = DisplayVariant.PIN_CREATION;
hide(chooserParent);
hide(actionSettings);
show(pinInputView);
setDescription(AndroidUtils.getIdentifier("applock__description_create_pin", "string"));
pinInputController.ensureKeyboardVisible();
pinInputController.setInputEventListener(this);
}
protected void setupPINConfirmation() {
this.displayVariant = DisplayVariant.PIN_CONFIRMATION;
hide(chooserParent);
hide(actionSettings);
show(pinInputView);
setDescription(AndroidUtils.getIdentifier("applock__description_confirm", "string"));
pinInputController.ensureKeyboardVisible();
pinInputController.setInputEventListener(this);
}
@Override
public void onInputEntered(String input) {
switch (displayVariant) {
case PIN_CREATION -> {
if (!pinInputController.matchesRequiredPINLength(input)) {
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
return;
}
this.pinFirst = input;
setupPINConfirmation();
}
case PIN_CONFIRMATION -> {
if (!pinInputController.matchesRequiredPINLength(input)) {
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
return;
}
if (!input.equals(pinFirst)) {
this.pinFirst = null;
setupPINCreation();
setDescription(AndroidUtils.getIdentifier("applock__description_create_pin_reattempt", "string"));
return;
}
createPINLock(input);
}
default -> {
}
}
}
protected void createPINLock(String input) {
Activity activity = this.activity.get();
if (activity == null)
return;
AppLock.getInstance(activity)
.getLockService(PINLockService.class)
.enroll(activity, input);
var fingerprintLockService = AppLock.getInstance(activity).getLockService(FingerprintLockService.class);
if (fingerprintLockService.isEnrollmentEligible(activity)) {
setupFingerprintAuthentication();
} else {
handleLockCreated();
}
}
protected void setupFingerprintAuthentication() {
this.displayVariant = DisplayVariant.FINGERPRINT_AUTHENTICATION;
hide(pinInputView);
hide(chooserParent);
hide(actionSettings);
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint", "string"));
if (autoAuthorizationEnabled)
attemptFingerprintAuthentication();
}
protected void attemptFingerprintAuthentication() {
Activity activity = this.activity.get();
if (activity == null)
return;
AppLock.getInstance(activity)
.getLockService(FingerprintLockService.class)
.enroll(activity, this);
}
@Override
public void onResolutionRequired(int errorCode) {
setDescription(getDescriptionResIdForError(errorCode));
updateActionSettings(errorCode);
handleInitialErrorPrompt(errorCode);
}
@Override
public void onAuthenticationHelp(int code, CharSequence message) {
Activity activity = this.activity.get();
if (activity == null)
return;
String unformattedHelpMessage = activity.getString(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint_help", "string"));
String formatted = String.format(unformattedHelpMessage, message);
setDescription(formatted);
}
@Override
public void onAuthenticating(CancellationSignal cancellationSignal) {
}
@Override
public void onAuthenticationSuccess(FingerprintManagerCompat.AuthenticationResult result) {
handleLockCreated();
}
@Override
public void onAuthenticationFailed(String message) {
setDescription(message);
}
protected void handleLockCreated() {
this.displayVariant = DisplayVariant.NONE;
Delegate delegate = this.delegate.get();
if (delegate != null)
delegate.onLockCreated();
}
@Override
public void onActivityPaused() {
Activity activity = this.activity.get();
if (activity == null)
return;
AppLock.getInstance(activity)
.cancelPendingAuthentications();
if (displayVariant == DisplayVariant.FINGERPRINT_AUTHENTICATION)
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint_paused", "string"));
}
@Override
public void onActivityResumed() {
Activity activity = this.activity.get();
if (activity == null || displayVariant != DisplayVariant.FINGERPRINT_AUTHENTICATION)
return;
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
setDescription(AndroidUtils.getIdentifier("applock__fingerprint_error_permission_multiple", "string"));
updateActionSettings(AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED);
return;
}
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint", "string"));
hide(actionSettings);
attemptFingerprintAuthentication();
}
@Override
protected void handleActionSettingsClicked(int errorCode) {
Activity activity = this.activity.get();
Intent intent = getSettingsIntent(errorCode);
if (activity == null || intent == null)
return;
activity.startActivity(intent);
}
public enum DisplayVariant {
NONE,
PIN_CREATION,
PIN_CONFIRMATION,
FINGERPRINT_AUTHENTICATION
}
public interface Delegate {
void onLockCreated();
}
}
@@ -0,0 +1,186 @@
package com.guardanis.applock.views;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.view.View;
import androidx.core.content.ContextCompat;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.pin.PINInputController;
import com.guardanis.applock.services.FingerprintLockService;
import java.lang.ref.WeakReference;
import ru.vtosters.lite.utils.AndroidUtils;
public class UnlockViewController extends AppLockViewController implements AppLock.UnlockDelegate, PINInputController.InputEventListener {
protected DisplayVariant displayVariant = DisplayVariant.PIN_UNLOCK;
protected WeakReference<Delegate> delegate;
public UnlockViewController(Activity activity, View parent) {
super(activity, parent);
}
public UnlockViewController setDelegate(Delegate delegate) {
this.delegate = new WeakReference<Delegate>(delegate);
return this;
}
@Override
public void setupRootFlow() {
View parent = this.parent.get();
if (parent == null)
return;
setupPINUnlock();
}
protected void setupPINUnlock() {
this.displayVariant = DisplayVariant.PIN_UNLOCK;
hide(actionSettings);
show(pinInputView);
setDescription(AndroidUtils.getIdentifier("applock__description_unlock_pin", "string"));
requestOpenKeyboard();
pinInputController.setInputEventListener(this);
FingerprintLockService fingerprintService = AppLock.getInstance(this.parent.get().getContext())
.getLockService(FingerprintLockService.class);
if (fingerprintService.isEnrolled(parent.get().getContext()))
attemptFingerprintAuthentication();
}
@Override
public void onInputEntered(String input) {
if (!pinInputController.matchesRequiredPINLength(input)) {
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
return;
}
attemptPINUnlock(input);
}
protected void attemptPINUnlock(String input) {
Activity activity = this.activity.get();
if (activity == null)
return;
AppLock.getInstance(activity)
.attemptPINUnlock(input, this);
}
protected void attemptFingerprintAuthentication() {
Activity activity = this.activity.get();
if (activity == null)
return;
AppLock.getInstance(activity)
.attemptFingerprintUnlock(this);
}
@Override
public void onUnlockSuccessful() {
this.displayVariant = DisplayVariant.NONE;
Delegate delegate = this.delegate.get();
if (delegate != null)
delegate.onUnlockSuccessful();
}
@Override
public void onResolutionRequired(int errorCode) {
setDescription(getDescriptionResIdForError(errorCode));
updateActionSettings(errorCode);
handleInitialErrorPrompt(errorCode);
}
@Override
public void onAuthenticationHelp(int code, String message) {
Activity activity = this.activity.get();
if (activity == null)
return;
String unformattedHelpMessage = activity.getString(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint_help", "string"));
String formatted = String.format(unformattedHelpMessage, message);
setDescription(formatted);
}
@Override
public void onFailureLimitExceeded(String message) {
setDescription(message);
}
@Override
public void requestOpenKeyboard() {
pinInputController.ensureKeyboardVisible();
}
@Override
public void onActivityPaused() {
Activity activity = this.activity.get();
if (activity == null)
return;
AppLock.getInstance(activity)
.cancelPendingAuthentications();
if (displayVariant == DisplayVariant.FINGERPRINT_AUTHENTICATION)
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint_paused", "string"));
}
@Override
public void onActivityResumed() {
Activity activity = this.activity.get();
if (activity == null || displayVariant != DisplayVariant.FINGERPRINT_AUTHENTICATION)
return;
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
setDescription(AndroidUtils.getIdentifier("applock__fingerprint_error_permission_multiple", "string"));
updateActionSettings(AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED);
return;
}
setDescription(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint", "string"));
hide(actionSettings);
attemptFingerprintAuthentication();
}
@Override
protected void handleActionSettingsClicked(int errorCode) {
Activity activity = this.activity.get();
Intent intent = getSettingsIntent(errorCode);
if (activity == null || intent == null)
return;
activity.startActivity(intent);
}
public enum DisplayVariant {
NONE,
PIN_UNLOCK,
FINGERPRINT_AUTHENTICATION
}
public interface Delegate {
void onUnlockSuccessful();
}
}
@@ -1,10 +1,13 @@
package com.vk.api.audio;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.vk.api.base.ApiRequest;
import com.vk.dto.music.MusicTrack;
import com.vk.log.L;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.Genius;
@@ -12,6 +15,9 @@ import ru.vtosters.lite.utils.AndroidUtils;
import java.util.ArrayList;
import ru.vtosters.lite.music.Genius;
import ru.vtosters.lite.utils.AndroidUtils;
public class AudioGetLyrics extends ApiRequest<AudioGetLyrics.a> {
private final MusicTrack musicTrack;
+87 -6
View File
@@ -2140,6 +2140,8 @@ public final class R {
public static final int im_ic_msgdl = 0x7f040774; // 2130970484
public static final int im_ic_msgreload = 0x7f040775; // 2130970485
public static final int im_ic_stats = 0x7f040776; // 2130970486
public static final int pinBackgroundColor = 0x7f040777; // 2130970487
public static final int pinTextColor = 0x7f040778; // 2130970488
}
public static final class bool {
@@ -2160,6 +2162,9 @@ public final class R {
public static final int spb_default_reversed = 0x7f05000e; // 2131034126
public static final int vk_is_tablet = 0x7f05000f; // 2131034127
public static final int com_samsung_android_icon_container_has_icon_container = 0x7f050010; // 2131034128
public static final int applock__fingerprint_service_enabled = 0x7f050011; // 2131034129
public static final int applock__item_password_chars_enabled = 0x7f050012; // 2131034130
public static final int applock__unlock_activity_return_allowed = 0x7f050013; // 2131034131
}
public static final class color {
@@ -2968,6 +2973,13 @@ public final class R {
public static final int yellow_light = 0x7f060323; // 2131100451
public static final int yellow_sunflower = 0x7f060324; // 2131100452
public static final int iclauncher = 0x7f060325; // 2131100453
public static final int applock__action_settings = 0x7f060327; // 2131100455
public static final int applock__activity_background = 0x7f060328; // 2131100456
public static final int applock__create_choose_option_bg = 0x7f060329; // 2131100457
public static final int applock__create_choose_option_bg_pressed = 0x7f06032a; // 2131100458
public static final int applock__description_text = 0x7f06032b; // 2131100459
public static final int applock__item_background = 0x7f06032c; // 2131100460
public static final int applock__item_text = 0x7f06032d; // 2131100461
public static final int abc_color_highlight_material = 0x7f060004; // 2131099652
public static final int alpha05 = 0x7f060326; // 2131100454
}
@@ -3821,6 +3833,21 @@ public final class R {
public static final int wrap_content = 0x7f07035b; // 2131166043
public static final int year_label_height = 0x7f07035c; // 2131166044
public static final int year_label_text_size = 0x7f07035d; // 2131166045
public static final int applock__action_settings = 0x7f07035e; // 2131166046
public static final int applock__action_settings_margin_top = 0x7f07035f; // 2131166047
public static final int applock__action_settings_padding = 0x7f070360; // 2131166048
public static final int applock__activity_padding = 0x7f070361; // 2131166049
public static final int applock__chooser_image_size = 0x7f070362; // 2131166050
public static final int applock__chooser_parent_padding_vertical = 0x7f070363; // 2131166051
public static final int applock__chooser_text_padding_top = 0x7f070364; // 2131166052
public static final int applock__content_padding_horizontal = 0x7f070365; // 2131166053
public static final int applock__content_padding_vertical = 0x7f070366; // 2131166054
public static final int applock__description_padding_bottom = 0x7f070367; // 2131166055
public static final int applock__description_text = 0x7f070368; // 2131166056
public static final int applock__fingerprint_scan_image_margin_top = 0x7f070369; // 2131166057
public static final int applock__fingerprint_scan_image_size = 0x7f07036a; // 2131166058
public static final int applock__item_margin_top = 0x7f07036b; // 2131166059
public static final int applock__item_size = 0x7f07036c; // 2131166060
public static final int vkim_article_blocked_bottom_margin = 0x7f070329; // 2131165993
public static final int vkim_article_blocked_ic_margin = 0x7f07032a; // 2131165994
public static final int vkim_article_blocked_ic_size = 0x7f07032b; // 2131165995
@@ -4999,6 +5026,9 @@ public final class R {
public static final int custom_thumb_selector = 0x7f080be9; // 2131233769
public static final int custom_track_selector = 0x7f080bea; // 2131233770
public static final int splash = 0x7f080bec; // 2131233772
public static final int applock__chooser_option_bg = 0x7f080bee; // 2131233774
public static final int applock__fingerprint = 0x7f080bef; // 2131233775
public static final int applock__pin = 0x7f080bf0; // 2131233776
public static final int abc_control_background_material = 0x7f080032; // 2131230770
public static final int ic_action_close = 0x7f080322; // 2131231522
public static final int ic_camera_outline_28_new = 0x7f0803a0; // 2131231648
@@ -11062,6 +11092,12 @@ public final class R {
public static final int color_preview_border = 0x7f0a1024; // 2131365924
public static final int account_backup_view = 0x7f0a1025; // 2131365925
public static final int copy_image = 0x7f0a1026; // 2131365926
public static final int applock__activity_lock_creation = 0x7f0a1027; // 2131365927
public static final int applock__activity_unlock = 0x7f0a1028; // 2131365928
public static final int pin__action_settings = 0x7f0a1029; // 2131365929
public static final int pin__container = 0x7f0a102a; // 2131365930
public static final int pin__description = 0x7f0a102b; // 2131365931
public static final int pin__input_view = 0x7f0a102c; // 2131365932
}
public static final class integer {
@@ -11108,6 +11144,10 @@ public final class R {
public static final int status_bar_notification_info_maxnum = 0x7f0b002b; // 2131427371
public static final int vkim_activity_animation_duration = 0x7f0b002c; // 2131427372
public static final int vkim_playing_drawable_rect_count = 0x7f0b002d; // 2131427373
public static final int applock__activity_lock_reenable_minutes = 0x7f0b002e; // 2131427374
public static final int applock__failure_retry_delay = 0x7f0b002f; // 2131427375
public static final int applock__input_pin_item_count = 0x7f0b0030; // 2131427376
public static final int applock__max_retry_count = 0x7f0b0031; // 2131427377
public static final int emoji_size_in_sprite = 0x7f0b0012; // 2131427346
public static final int s1dip = 0x7f0b0027; // 2131427367
public static final int s2dip = 0x7f0b0028; // 2131427368
@@ -12883,6 +12923,11 @@ public final class R {
public static final int wppreview = 0x7f0d06df; // 2131560159
public static final int write_bar_i = 0x7f0d06e0; // 2131560160
public static final int color_picker_item = 0x7f0d06e1; // 2131560161
public static final int applock__activity_lock_creation = 0x7f0d06e2; // 2131560162
public static final int applock__activity_unlock = 0x7f0d06e3; // 2131560163
public static final int applock__fake_edit_text = 0x7f0d06e4; // 2131560164
public static final int applock__lock_creation = 0x7f0d06e5; // 2131560165
public static final int applock__unlock = 0x7f0d06e6; // 2131560166
}
public static final class menu {
@@ -19658,12 +19703,43 @@ public final class R {
public static final int vkx_integration_enabled_info = 0x7f12188a; // 2131892362
public static final int whitelisted_ad_groups_stories_title = 0x7f12188b; // 2131892363
public static final int writebar_ios_title = 0x7f12188c; // 2131892364
public static final int cache_size = 0x7f12188d; // 2131892365
public static final int cache_size_summ = 0x7f12188e; // 2131892366
public static final int clear_all_cache = 0x7f12188f; // 2131892367
public static final int invalidate_theme_cache_each_update_title = 0x7f121890; // 2131892368
public static final int invertFilters = 0x7f121891; // 2131892369
public static final int invertFilters_summ = 0x7f121892; // 2131892370
public static final int applock__action_settings = 0x7f12188d; // 2131892365
public static final int applock__create_chooser_option_fingerprint = 0x7f12188e; // 2131892366
public static final int applock__create_chooser_option_pin = 0x7f12188f; // 2131892367
public static final int applock__description_chooser = 0x7f121890; // 2131892368
public static final int applock__description_confirm = 0x7f121891; // 2131892369
public static final int applock__description_create_fingerprint = 0x7f121892; // 2131892370
public static final int applock__description_create_fingerprint_paused = 0x7f121893; // 2131892371
public static final int applock__description_create_pin = 0x7f121894; // 2131892372
public static final int applock__description_create_pin_reattempt = 0x7f121895; // 2131892373
public static final int applock__description_unlock_fingerprint = 0x7f121896; // 2131892374
public static final int applock__description_unlock_fingerprint_help = 0x7f121897; // 2131892375
public static final int applock__description_unlock_pin = 0x7f121898; // 2131892376
public static final int applock__empty_item_min_size_percent = 0x7f121899; // 2131892377
public static final int applock__fingerprint_alias = 0x7f12189a; // 2131892378
public static final int applock__fingerprint_error_hardware = 0x7f12189b; // 2131892379
public static final int applock__fingerprint_error_none = 0x7f12189c; // 2131892380
public static final int applock__fingerprint_error_not_enrolled = 0x7f12189d; // 2131892381
public static final int applock__fingerprint_error_permission = 0x7f12189e; // 2131892382
public static final int applock__fingerprint_error_permission_multiple = 0x7f12189f; // 2131892383
public static final int applock__fingerprint_error_unknown = 0x7f1218a0; // 2131892384
public static final int applock__fingerprint_error_unrecognized = 0x7f1218a1; // 2131892385
public static final int applock__password_char = 0x7f1218a2; // 2131892386
public static final int applock__toast_unlock_required = 0x7f1218a3; // 2131892387
public static final int applock__unlock_error_insufficient_selection = 0x7f1218a4; // 2131892388
public static final int applock__unlock_error_match_failed = 0x7f1218a5; // 2131892389
public static final int applock__unlock_error_no_matching_pin_found = 0x7f1218a6; // 2131892390
public static final int applock__unlock_error_retry_limit_exceeded = 0x7f1218a7; // 2131892391
public static final int cache_size = 0x7f1218a8; // 2131892392
public static final int cache_size_summ = 0x7f1218a9; // 2131892393
public static final int clear_all_cache = 0x7f1218aa; // 2131892394
public static final int invalidate_theme_cache_each_update_title = 0x7f1218ab; // 2131892395
public static final int invertFilters = 0x7f1218ac; // 2131892396
public static final int invertFilters_summ = 0x7f1218ad; // 2131892397
public static final int invertcached_summ = 0x7f1218ae; // 2131892398
public static final int invertcached_title = 0x7f1218af; // 2131892399
public static final int pin_not_set = 0x7f1218b0; // 2131892400
public static final int pin_reset = 0x7f1218b1; // 2131892401
}
public static final class style {
@@ -20799,6 +20875,11 @@ public final class R {
public static final int VkAmoledTheme = 0x7f130472; // 2131952754
public static final int VkMilkAmoledStyle = 0x7f130473; // 2131952755
public static final int VkMilkAmoledTheme = 0x7f130474; // 2131952756
public static final int applock__action_settings = 0x7f130487; // 2131952775
public static final int applock__content_parent = 0x7f130488; // 2131952776
public static final int applock__description = 0x7f130489; // 2131952777
public static final int applock__input_fingerprint_view = 0x7f13048a; // 2131952778
public static final int applock__input_pin_view = 0x7f13048b; // 2131952779
public static final int Base_V23_Theme_AppCompat = 0x7f13009d; // 2131951773
public static final int Base_V23_Theme_AppCompat_Light = 0x7f13009e; // 2131951774
public static final int Platform_V25_AppCompat = 0x7f13017f; // 2131951999
@@ -5,6 +5,8 @@ import android.content.Intent;
import android.os.Build;
import b.h.g.k.VKProgressDialog;
import com.aefyr.tsg.g2.TelegramStickersService;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.activities.UnlockActivity;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
@@ -67,6 +69,13 @@ public class MainActivityInjector {
TelegramStickersService.getInstance(activity);
}
public static void applock(Activity activity) {
if (AppLock.isUnlockRequired(activity)) {
var intent = new Intent(activity, UnlockActivity.class);
activity.startActivity(intent);
}
}
private static void updateBinsAndTmpArchive(Activity activity) {
final VKProgressDialog dialog = new VKProgressDialog(activity);
@@ -2,10 +2,13 @@ package ru.vtosters.hooks.other;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.StrictMode;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.activities.UnlockActivity;
import com.vk.medianative.MediaImageEncoder;
import com.vk.medianative.MediaNative;
import com.vtosters.lite.data.Users;
@@ -33,7 +36,15 @@ public class Preferences {
VTVerifications.load(application);
LifecycleUtils.registerActivities(application);
if (AppLock.isEnrolled(AndroidUtils.getGlobalContext())) {
AppLock.setAuthenticationRequired(AndroidUtils.getGlobalContext());
}
AnalyticsHelper.start(application);
if (AppLock.isEnrolled(AndroidUtils.getGlobalContext())) {
AppLock.setAuthenticationRequired(AndroidUtils.getGlobalContext());
}
} // VK Init
public static String getBuildName() {
@@ -9,6 +9,8 @@ import okhttp3.Request;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.vtosters.lite.R;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AndroidUtils;
@@ -2,6 +2,7 @@ package ru.vtosters.lite.ui;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -334,6 +335,52 @@ public class PreferenceFragmentUtils {
});
}
public static Preference addEditTextValPreference(
PreferenceScreen screen,
String key,
CharSequence title,
CharSequence defval,
onPreferenceTextValueChangeListener editTextPrefChangeListener
) {
return addPreference(screen, key, title, null, null, preference -> {
LinearLayout linearLayout = new LinearLayout(screen.getContext());
final EditText editText = new EditText(screen.getContext());
editText.setText(PreferenceManager.getDefaultSharedPreferences(screen.getContext()).getString(key, ""));
editText.setHint(title);
editText.setTextColor(ThemesUtils.getTextAttr());
editText.setHintTextColor(ThemesUtils.getSTextAttr());
editText.setText(defval);
editText.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
editText.setTransformationMethod(null);
linearLayout.addView(editText);
editText.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
ViewGroup.MarginLayoutParams margin = ((ViewGroup.MarginLayoutParams) editText.getLayoutParams());
margin.setMargins(AndroidUtils.dp2px(20f), 0, AndroidUtils.dp2px(20f), 0);
editText.setLayoutParams(margin);
new VkAlertDialog.Builder(screen.getContext())
.setTitle(title)
.setView(linearLayout)
.setPositiveButton("OK", (dialog, which) -> {
boolean change = editTextPrefChangeListener.onChanged(preference, editText.getText().toString());
if (!change)
return;
PreferenceManager.getDefaultSharedPreferences(screen.getContext())
.edit()
.putString(key, editText.getText().toString())
.apply();
})
.show();
return false;
});
}
public interface onPreferenceTextValueChangeListener {
boolean onChanged(Preference preference, String newValue);
}
@@ -4,6 +4,11 @@ import android.app.Activity;
import android.content.*;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.view.ContextThemeWrapper;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.activities.LockCreationActivity;
import com.vk.auth.api.VKAccount;
import com.vk.core.util.ToastUtils;
import com.vk.pushes.PushSubscriber;
@@ -73,6 +78,22 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
return true;
});
findPreference("pin_setup").setOnPreferenceClickListener(preference -> {
var intent = new Intent(this.getActivity(), LockCreationActivity.class);
startActivityForResult(intent, AppLock.REQUEST_CODE_LOCK_CREATION);
return true;
});
findPreference("pin_reset").setOnPreferenceClickListener(preference -> {
if (AppLock.isEnrolled(requireContext())) {
AppLock.getInstance(requireContext()).invalidateEnrollments();
AndroidUtils.sendToast(AndroidUtils.getString("pin_reset"));
} else {
AndroidUtils.sendToast(AndroidUtils.getString("pin_not_set"));
}
return true;
});
findPreference("tokencopy").setOnPreferenceClickListener(preference -> {
copyText(AccountManagerUtils.getUserToken());
@@ -218,7 +218,7 @@ public class ThemesFragment extends TrackedMaterialPreferenceToolbarFragment {
requireActivity().runOnUiThread(() -> {
dialog.dismiss();
new VkAlertDialog.Builder(requireContext())
.setTitle(AndroidUtils.getString("error"))
.setTitle(AndroidUtils.getString(R.string.error))
.setMessage(AndroidUtils.getString("error_applying_accent") + ":\n" + e)
.setPositiveButton("OK", null)
.show();
@@ -7,6 +7,10 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import com.guardanis.applock.AppLock;
import com.guardanis.applock.activities.UnlockActivity;
import com.vk.core.fragments.FragmentImpl;
import com.vk.navigation.Navigator;
import com.vtosters.lite.MainActivity;
import java.util.ArrayList;
@@ -42,6 +46,11 @@ public class LifecycleUtils {
@Override // android.app.Application.ActivityLifecycleCallbacks
public void onActivityCreated(Activity activity, Bundle bundle) {
activities.add(activity);
if (AppLock.isUnlockRequired(activity) && activity.getClass() != MainActivity.class) {
var intent = new Intent(activity, UnlockActivity.class);
activity.startActivity(intent);
}
}
@Override // android.app.Application.ActivityLifecycleCallbacks
@@ -52,7 +61,7 @@ public class LifecycleUtils {
}
public static Activity getCurrentActivity() {
return activities.size() > 0 ? activities.get(activities.size() - 1) : new MainActivity();
return !activities.isEmpty() ? activities.get(activities.size() - 1) : new MainActivity();
}
public static void restartApplication() {
+9
View File
@@ -16,6 +16,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
@@ -197,6 +198,14 @@
android:name="ru.vtosters.lite.ui.activities.VKAdminTokenActivity"
android:launchMode="singleTop">
</activity>
<activity
android:name="com.guardanis.applock.activities.LockCreationActivity"
android:launchMode="singleTop">
</activity>
<activity
android:name="com.guardanis.applock.activities.UnlockActivity"
android:launchMode="singleTop">
</activity>
<activity
android:name="com.vtosters.lite.MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/applock__create_choose_option_bg_pressed" android:state_selected="true"/>
<item android:drawable="@color/applock__create_choose_option_bg_pressed" android:state_pressed="true"/>
<item android:drawable="@color/applock__create_choose_option_bg" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/applock__activity_lock_creation"
android:orientation="vertical"
android:padding="@dimen/applock__activity_padding">
<include
android:id="@+id/pin__container"
layout="@layout/applock__lock_creation" />
</LinearLayout>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/applock__activity_unlock"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/applock__activity_padding">
<include
android:id="@+id/pin__container"
layout="@layout/applock__unlock" />
</LinearLayout>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="1dp"
android:layout_height="1dp"
android:background="@android:color/transparent"
android:cursorVisible="false"
android:imeOptions="flagNoExtractUi|actionDone"
android:inputType="number"
android:textColor="@android:color/transparent"/>
@@ -0,0 +1,20 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:applock="http://schemas.android.com/apk/res-auto"
style="@style/applock__content_parent"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true">
<TextView
android:id="@+id/pin__description"
style="@style/applock__description" />
<com.guardanis.applock.pin.PINInputView
android:id="@+id/pin__input_view"
style="@style/applock__input_pin_view"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/pin__action_settings"
style="@style/applock__action_settings"/>
</LinearLayout>
+20
View File
@@ -0,0 +1,20 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:applock="http://schemas.android.com/apk/res-auto"
style="@style/applock__content_parent"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true">
<TextView
android:id="@+id/pin__description"
style="@style/applock__description"/>
<com.guardanis.applock.pin.PINInputView
android:id="@+id/pin__input_view"
style="@style/applock__input_pin_view"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/pin__action_settings"
style="@style/applock__action_settings"/>
</LinearLayout>
+31
View File
@@ -6364,6 +6364,37 @@
<string name="device_info_copied">Информация об устройстве скопирована</string>
<string name="app_open_by_default_settings">Для нормальной работы приложения необходимо разрешить приложению открытие ссылок по умолчанию</string>
<string name="applock__description_chooser">Защитить это приложение с помощью PIN-кода или отпечатка пальцев?</string>
<string name="applock__description_create_pin">Создайте PIN-код для защиты этого приложения</string>
<string name="applock__description_create_pin_reattempt">Неверный PIN-код. Попробуйте еще раз</string>
<string name="applock__description_create_fingerprint">Используйте свой отпечаток пальца, чтобы защитить это приложение</string>
<string name="applock__description_create_fingerprint_paused">Разблокировка по биометрии отменена. Попробуйте снова</string>
<string name="applock__description_confirm">Введите PIN-код для подтверждения</string>
<string name="applock__description_unlock_pin">Введите свой PIN-код для разблокировки</string>
<string name="applock__description_unlock_fingerprint">Приложите палец к сканеру</string>
<string name="applock__description_unlock_fingerprint_help">Проблема при распознавании отпечатка пальца: %1$s</string>
<string name="applock__create_chooser_option_pin">PIN</string>
<string name="applock__create_chooser_option_fingerprint">Отпечаток пальца</string>
<string name="applock__unlock_error_no_matching_pin_found">Не существует сохраненного PIN-кода для разблокировки</string>
<string name="applock__unlock_error_match_failed">Неверный PIN-код!</string>
<string name="applock__unlock_error_insufficient_selection">Неправильное количество цифр! Пожалуйста, попробуйте еще раз</string>
<string name="applock__unlock_error_retry_limit_exceeded">Слишком много неудачных попыток. Попробуйте снова через %1$s</string>
<string name="applock__toast_unlock_required">Вы не можете этого сделать</string>
<string name="applock__password_char">*</string>
<string name="applock__action_settings">Открыть системные настройки</string>
<string name="applock__fingerprint_alias">AppLock</string>
<string name="applock__fingerprint_error_not_enrolled">Вы не настроили разблокировку по отпечатку пальца</string>
<string name="applock__fingerprint_error_unrecognized">Ваш отпечаток пальца не был распознан</string>
<string name="applock__fingerprint_error_unknown">Произошло что-то странное</string>
<string name="applock__fingerprint_error_none">Отпечатки пальцев не найдены! Добавьте их в системных настройках</string>
<string name="applock__fingerprint_error_hardware">Отсутствует сканер отпечатков пальцев</string>
<string name="applock__fingerprint_error_permission">Требуется разрешение на использование отпечатков пальцев</string>
<string name="applock__fingerprint_error_permission_multiple">Разрешение на работу с биометрией не было получено. Выдайте его приложению в системных настройках</string>
<item name="applock__empty_item_min_size_percent" format="float" type="string">0.2</item>
<string name="pin_not_set">PIN-код не установлен</string>
<string name="pin_reset">PIN-код был успешно сброшен</string>
<string name="failed_to_get_text">Ошибка получения текста</string>
<string name="error_no_text">Ошибка получения текста, возможно текст не найден</string>
<string name="applying_accent">Применение акцента</string>
+4
View File
@@ -2511,4 +2511,8 @@
<attr name="im_ic_msgreload" format="reference" />
<attr name="im_ic_msgdl" format="reference" />
<attr name="im_ic_stats" format="reference" />
<declare-styleable name="PINInputView">
<attr name="pinTextColor" format="reference|color"/>
<attr name="pinBackgroundColor" format="reference|color"/>
</declare-styleable>
</resources>
+3
View File
@@ -17,4 +17,7 @@
<bool name="spb_default_reversed">false</bool>
<bool name="vk_is_tablet">false</bool>
<bool name="com_samsung_android_icon_container_has_icon_container">true</bool>
<bool name="applock__unlock_activity_return_allowed">false</bool>
<bool name="applock__item_password_chars_enabled">true</bool>
<bool name="applock__fingerprint_service_enabled">true</bool>
</resources>
+11
View File
@@ -686,4 +686,15 @@
<color name="yellow_light">#ffffd54f</color>
<color name="yellow_sunflower">#fffece00</color>
<color name="iclauncher">#fffcfcfc</color>
<color name="applock__activity_background">#FAFAFA</color>
<color name="applock__description_text">#7F8C8D</color>
<color name="applock__item_background">#3C9ADF</color>
<color name="applock__item_text">#FAFAFA</color>
<color name="applock__create_choose_option_bg">#00FFFFFF</color>
<color name="applock__create_choose_option_bg_pressed">#11000000</color>
<color name="applock__action_settings">#95A5A6</color>
</resources>
+16
View File
@@ -855,4 +855,20 @@
<item name="wrap_content" type="dimen">-2</item>
<dimen name="year_label_height">64.0dip</dimen>
<dimen name="year_label_text_size">22.0dip</dimen>
<dimen name="applock__activity_padding">24dip</dimen>
<dimen name="applock__content_padding_horizontal">24dip</dimen>
<dimen name="applock__content_padding_vertical">36dip</dimen>
<dimen name="applock__item_margin_top">20dip</dimen>
<dimen name="applock__item_size">50dip</dimen>
<dimen name="applock__fingerprint_scan_image_margin_top">35dip</dimen>
<dimen name="applock__fingerprint_scan_image_size">65dip</dimen>
<dimen name="applock__chooser_image_size">50dip</dimen>
<dimen name="applock__chooser_parent_padding_vertical">24dip</dimen>
<dimen name="applock__chooser_text_padding_top">12dip</dimen>
<dimen name="applock__description_text">18dip</dimen>
<dimen name="applock__description_padding_bottom">20dip</dimen>
<dimen name="applock__action_settings">14dip</dimen>
<dimen name="applock__action_settings_padding">12dip</dimen>
<dimen name="applock__action_settings_margin_top">24dip</dimen>
</resources>
+4
View File
@@ -3919,6 +3919,10 @@
<item name="download_mp3" type="id" />
<item name="remove_from_cache_vkx" type="id" />
<item name="add_to_cache_vkx" type="id" />
<item name="pin__description" type="id" />
<item name="pin__action_settings" type="id" />
<item name="pin__input_view" type="id" />
<item name="pin__container" type="id"/>
<item name="account_backup_view" type="id" />
<item name="copy_image" type="id" />
</resources>
+5
View File
@@ -43,4 +43,9 @@
<integer name="status_bar_notification_info_maxnum">999</integer>
<integer name="vkim_activity_animation_duration">200</integer>
<integer name="vkim_playing_drawable_rect_count">4</integer>
<integer name="applock__input_pin_item_count">4</integer>
<integer name="applock__max_retry_count">5</integer>
<integer name="applock__failure_retry_delay">5</integer>
<integer name="applock__activity_lock_reenable_minutes">1440</integer>
</resources>
+31
View File
@@ -6502,6 +6502,37 @@ May look like a fake leaving the online"</string>
<string name="device_info_copied">Device info copied</string>
<string name="app_open_by_default_settings">You need to allow the app to open links by default for the app to work properly</string>
<string name="applock__description_chooser">Secure this application with a PIN or your fingerprints?</string>
<string name="applock__description_create_pin">Create a PIN to secure this application</string>
<string name="applock__description_create_pin_reattempt">PINs didn\'t match. Try again</string>
<string name="applock__description_create_fingerprint">Scan your fingerprint to secure this application</string>
<string name="applock__description_create_fingerprint_paused">Fingerprint authentication canceled. Reload this content to restart</string>
<string name="applock__description_confirm">Re-enter your PIN to confirm</string>
<string name="applock__description_unlock_pin">Enter your PIN to unlock</string>
<string name="applock__description_unlock_fingerprint">Scan your fingerprint to unlock</string>
<string name="applock__description_unlock_fingerprint_help">We\'re having trouble recognizing your fingerprint: %1$s</string>
<string name="applock__create_chooser_option_pin">PIN</string>
<string name="applock__create_chooser_option_fingerprint">Fingerprint</string>
<string name="applock__unlock_error_no_matching_pin_found">There is no saved PIN to unlock</string>
<string name="applock__unlock_error_match_failed">The PINs don\'t match!</string>
<string name="applock__unlock_error_insufficient_selection">Wrong number of digits! Please try again</string>
<string name="applock__unlock_error_retry_limit_exceeded">You failed too many times. You may try again in %1$s</string>
<string name="applock__toast_unlock_required">You can\'t do that</string>
<string name="applock__password_char">*</string>
<string name="applock__action_settings">Open System Settings</string>
<string name="applock__fingerprint_alias">AppLock</string>
<string name="applock__fingerprint_error_not_enrolled">You haven\'t setup fingerprint unlocking</string>
<string name="applock__fingerprint_error_unrecognized">Your fingerprint wasn\'t recognized</string>
<string name="applock__fingerprint_error_unknown">Something weird happened</string>
<string name="applock__fingerprint_error_none">No fingerprints found! Enable them in your system settings</string>
<string name="applock__fingerprint_error_hardware">Missing fingerprint hardware?</string>
<string name="applock__fingerprint_error_permission">Fingerprint permission required</string>
<string name="applock__fingerprint_error_permission_multiple">Fingerprint permission denied. Please enable fingerprint support in your system settings</string>
<item name="applock__empty_item_min_size_percent" format="float" type="string">0.2</item>
<string name="pin_not_set">PIN not set</string>
<string name="pin_reset">PIN has been successfully reset</string>
<string name="failed_to_get_text">Failed to get text</string>
<string name="error_no_text">Error receiving text, possibly no text found</string>
<string name="applying_accent">Applying accent</string>
+43
View File
@@ -10262,4 +10262,47 @@
<item name="android:shadowDy">-1.0</item>
<item name="android:shadowRadius">0.25</item>
</style>
<style name="applock__content_parent">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:orientation">vertical</item>
<item name="android:paddingLeft">@dimen/applock__content_padding_horizontal</item>
<item name="android:paddingTop">@dimen/applock__content_padding_vertical</item>
<item name="android:paddingRight">@dimen/applock__content_padding_horizontal</item>
<item name="android:paddingBottom">@dimen/applock__content_padding_vertical</item>
</style>
<style name="applock__description">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center</item>
<item name="android:text">These is some kind of instructions you should follow.</item>
<item name="android:textColor">?text_primary</item>
<item name="android:textSize">@dimen/applock__description_text</item>
</style>
<style name="applock__input_pin_view">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/applock__item_size</item>
<item name="android:layout_marginTop">@dimen/applock__item_margin_top</item>
</style>
<style name="applock__input_fingerprint_view">
<item name="android:layout_width">@dimen/applock__fingerprint_scan_image_size</item>
<item name="android:layout_height">@dimen/applock__fingerprint_scan_image_size</item>
<item name="android:layout_marginTop">@dimen/applock__fingerprint_scan_image_margin_top</item>
</style>
<style name="applock__action_settings">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">@dimen/applock__action_settings_margin_top</item>
<item name="android:gravity">center</item>
<item name="android:padding">@dimen/applock__action_settings_padding</item>
<item name="android:text">@string/applock__action_settings</item>
<item name="android:textColor">?text_primary</item>
<item name="android:textSize">@dimen/applock__action_settings</item>
</style>
</resources>
+8
View File
@@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="PIN-код">
<Preference
android:key="pin_setup"
android:title="Установить PIN"/>
<Preference
android:key="pin_reset"
android:title="Сбросить PIN"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/other_fix_app">
<Preference
android:key="firebasefix"
@@ -546,6 +546,8 @@
invoke-virtual {p0, p1, v0}, Landroidx/fragment/app/FragmentActivity;->startActivityForResult(Landroid/content/Intent;I)V
:goto_1
invoke-static {p0}, Lru/vtosters/hooks/MainActivityInjector;->applock(Landroid/app/Activity;)V
return-void
.end method