mirror of https://github.com/beemdevelopment/Aegis
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
9.2 KiB
Java
278 lines
9.2 KiB
Java
package com.beemdevelopment.aegis.ui;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.app.Activity;
|
|
import android.content.Intent;
|
|
import android.content.res.Configuration;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.WindowManager;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.CallSuper;
|
|
import androidx.annotation.ColorInt;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
import androidx.appcompat.view.ActionMode;
|
|
import androidx.core.view.ViewPropertyAnimatorCompat;
|
|
|
|
import com.beemdevelopment.aegis.Preferences;
|
|
import com.beemdevelopment.aegis.R;
|
|
import com.beemdevelopment.aegis.Theme;
|
|
import com.beemdevelopment.aegis.ThemeMap;
|
|
import com.beemdevelopment.aegis.icons.IconPackManager;
|
|
import com.beemdevelopment.aegis.vault.VaultManager;
|
|
import com.beemdevelopment.aegis.vault.VaultRepositoryException;
|
|
import com.google.android.material.color.DynamicColors;
|
|
import com.google.android.material.color.MaterialColors;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import dagger.hilt.InstallIn;
|
|
import dagger.hilt.android.AndroidEntryPoint;
|
|
import dagger.hilt.android.EarlyEntryPoint;
|
|
import dagger.hilt.android.EarlyEntryPoints;
|
|
import dagger.hilt.components.SingletonComponent;
|
|
|
|
@AndroidEntryPoint
|
|
public abstract class AegisActivity extends AppCompatActivity implements VaultManager.LockListener {
|
|
protected Preferences _prefs;
|
|
|
|
@Inject
|
|
protected VaultManager _vaultManager;
|
|
|
|
@Inject
|
|
protected IconPackManager _iconPackManager;
|
|
|
|
private ActionModeStatusGuardHack _statusGuardHack;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
// set the theme and locale before creating the activity
|
|
_prefs = EarlyEntryPoints.get(getApplicationContext(), PrefEntryPoint.class).getPreferences();
|
|
onSetTheme();
|
|
setLocale(_prefs.getLocale());
|
|
super.onCreate(savedInstanceState);
|
|
|
|
_statusGuardHack = new ActionModeStatusGuardHack();
|
|
|
|
// set FLAG_SECURE on the window of every AegisActivity
|
|
if (_prefs.isSecureScreenEnabled()) {
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
|
}
|
|
|
|
// register a callback to listen for lock events
|
|
_vaultManager.registerLockListener(this);
|
|
}
|
|
|
|
@Override
|
|
@CallSuper
|
|
protected void onDestroy() {
|
|
_vaultManager.unregisterLockListener(this);
|
|
super.onDestroy();
|
|
}
|
|
|
|
@CallSuper
|
|
@Override
|
|
protected void onResume() {
|
|
super.onResume();
|
|
_vaultManager.setBlockAutoLock(false);
|
|
}
|
|
|
|
@SuppressLint("SoonBlockedPrivateApi")
|
|
@SuppressWarnings("JavaReflectionMemberAccess")
|
|
@Override
|
|
public void onLocked(boolean userInitiated) {
|
|
setResult(RESULT_CANCELED, null);
|
|
|
|
try {
|
|
// Call a private overload of the finish() method to prevent the app
|
|
// from disappearing from the recent apps menu
|
|
Method method = Activity.class.getDeclaredMethod("finish", int.class);
|
|
method.setAccessible(true);
|
|
method.invoke(this, 2); // FINISH_TASK_WITH_ACTIVITY = 2
|
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
|
// On recent Android versions, the overload of the finish() method
|
|
// used above is no longer accessible
|
|
finishAndRemoveTask();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the activity is expected to set its theme.
|
|
*/
|
|
protected void onSetTheme() {
|
|
setTheme(ThemeMap.DEFAULT);
|
|
}
|
|
|
|
/**
|
|
* Sets the theme of the activity. The actual style that is set is picked from the
|
|
* given map, based on the theme configured by the user.
|
|
*/
|
|
protected void setTheme(Map<Theme, Integer> themeMap) {
|
|
int theme = themeMap.get(getConfiguredTheme());
|
|
setTheme(theme);
|
|
|
|
if (_prefs.isDynamicColorsEnabled()) {
|
|
DynamicColors.applyToActivityIfAvailable(this);
|
|
}
|
|
}
|
|
|
|
protected Theme getConfiguredTheme() {
|
|
Theme theme = _prefs.getCurrentTheme();
|
|
|
|
if (theme == Theme.SYSTEM || theme == Theme.SYSTEM_AMOLED) {
|
|
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
|
if (currentNightMode == Configuration.UI_MODE_NIGHT_YES) {
|
|
theme = theme == Theme.SYSTEM_AMOLED ? Theme.AMOLED : Theme.DARK;
|
|
} else {
|
|
theme = Theme.LIGHT;
|
|
}
|
|
}
|
|
|
|
return theme;
|
|
}
|
|
|
|
protected void setLocale(Locale locale) {
|
|
Locale.setDefault(locale);
|
|
|
|
Configuration config = new Configuration();
|
|
config.locale = locale;
|
|
|
|
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
|
|
}
|
|
|
|
protected boolean saveVault() {
|
|
try {
|
|
_vaultManager.save();
|
|
return true;
|
|
} catch (VaultRepositoryException e) {
|
|
Toast.makeText(this, getString(R.string.saving_error), Toast.LENGTH_LONG).show();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected boolean saveAndBackupVault() {
|
|
try {
|
|
_vaultManager.saveAndBackup();
|
|
return true;
|
|
} catch (VaultRepositoryException e) {
|
|
Toast.makeText(this, getString(R.string.saving_error), Toast.LENGTH_LONG).show();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes this activity if it has become an orphan (isOrphan() == true) and launches MainActivity.
|
|
* @param savedInstanceState the bundle passed to onCreate.
|
|
* @return whether to abort onCreate.
|
|
*/
|
|
protected boolean abortIfOrphan(Bundle savedInstanceState) {
|
|
if (savedInstanceState == null || !isOrphan()) {
|
|
return false;
|
|
}
|
|
|
|
Intent intent = new Intent(this, MainActivity.class);
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
startActivity(intent);
|
|
finish();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onSupportActionModeStarted(@NonNull ActionMode mode) {
|
|
super.onSupportActionModeStarted(mode);
|
|
_statusGuardHack.apply(View.VISIBLE);
|
|
}
|
|
|
|
@Override
|
|
public void onSupportActionModeFinished(@NonNull ActionMode mode) {
|
|
super.onSupportActionModeFinished(mode);
|
|
_statusGuardHack.apply(View.GONE);
|
|
}
|
|
|
|
/**
|
|
* When starting/finishing an action mode, forcefully cancel the fade in/out animation and
|
|
* set the status bar color. This requires the abc_decor_view_status_guard colors to be set
|
|
* to transparent.
|
|
*
|
|
* This should fix any inconsistencies between the color of the action bar and the status bar
|
|
* when an action mode is active.
|
|
*/
|
|
private class ActionModeStatusGuardHack {
|
|
private Field _fadeAnimField;
|
|
private Field _actionModeViewField;
|
|
|
|
@ColorInt
|
|
private final int _statusBarColor;
|
|
|
|
private ActionModeStatusGuardHack() {
|
|
_statusBarColor = getWindow().getStatusBarColor();
|
|
|
|
try {
|
|
_fadeAnimField = getDelegate().getClass().getDeclaredField("mFadeAnim");
|
|
_fadeAnimField.setAccessible(true);
|
|
_actionModeViewField = getDelegate().getClass().getDeclaredField("mActionModeView");
|
|
_actionModeViewField.setAccessible(true);
|
|
} catch (NoSuchFieldException ignored) {
|
|
}
|
|
}
|
|
|
|
private void apply(int visibility) {
|
|
if (_fadeAnimField == null || _actionModeViewField == null) {
|
|
return;
|
|
}
|
|
|
|
ViewPropertyAnimatorCompat fadeAnim;
|
|
ViewGroup actionModeView;
|
|
try {
|
|
fadeAnim = (ViewPropertyAnimatorCompat) _fadeAnimField.get(getDelegate());
|
|
actionModeView = (ViewGroup) _actionModeViewField.get(getDelegate());
|
|
} catch (IllegalAccessException e) {
|
|
return;
|
|
}
|
|
|
|
if (fadeAnim == null || actionModeView == null) {
|
|
return;
|
|
}
|
|
|
|
fadeAnim.cancel();
|
|
|
|
actionModeView.setVisibility(visibility);
|
|
actionModeView.setAlpha(visibility == View.VISIBLE ? 1f : 0f);
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
int statusBarColor = visibility == View.VISIBLE
|
|
? MaterialColors.getColor(actionModeView, com.google.android.material.R.attr.colorSurfaceContainer)
|
|
: _statusBarColor;
|
|
getWindow().setStatusBarColor(statusBarColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reports whether this Activity instance has become an orphan. This can happen if
|
|
* the vault was killed/locked by an external trigger while the Activity was still open.
|
|
*/
|
|
private boolean isOrphan() {
|
|
return !(this instanceof MainActivity)
|
|
&& !(this instanceof AuthActivity)
|
|
&& !(this instanceof IntroActivity)
|
|
&& !_vaultManager.isVaultLoaded();
|
|
}
|
|
|
|
@EarlyEntryPoint
|
|
@InstallIn(SingletonComponent.class)
|
|
public interface PrefEntryPoint {
|
|
Preferences getPreferences();
|
|
}
|
|
}
|