Merge branch 'Fox2Code:master' into master
commit
bf9fadb6df
@ -0,0 +1,19 @@
|
||||
package com.fox2code.mmm.background;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.fox2code.mmm.MainApplication;
|
||||
|
||||
public class BackgroundBootListener extends BroadcastReceiver {
|
||||
private static final String BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!BOOT_COMPLETED.equals(intent.getAction())) return;
|
||||
if (!MainApplication.isBackgroundUpdateCheckEnabled()) return;
|
||||
BackgroundUpdateChecker.onMainActivityCreate(context);
|
||||
BackgroundUpdateChecker.doCheck(context);
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package com.fox2code.mmm.background;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationChannelCompat;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.work.Constraints;
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.NetworkType;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
|
||||
import com.fox2code.mmm.MainActivity;
|
||||
import com.fox2code.mmm.MainApplication;
|
||||
import com.fox2code.mmm.R;
|
||||
import com.fox2code.mmm.manager.LocalModuleInfo;
|
||||
import com.fox2code.mmm.manager.ModuleManager;
|
||||
import com.fox2code.mmm.repo.RepoManager;
|
||||
import com.fox2code.mmm.repo.RepoModule;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BackgroundUpdateChecker extends Worker {
|
||||
private static boolean easterEggActive = false;
|
||||
public static final String NOTIFICATION_CHANNEL_ID = "background_update";
|
||||
public static final int NOTIFICATION_ID = 1;
|
||||
|
||||
public BackgroundUpdateChecker(@NonNull Context context,
|
||||
@NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
if (!NotificationManagerCompat.from(this.getApplicationContext()).areNotificationsEnabled()
|
||||
|| !MainApplication.isBackgroundUpdateCheckEnabled()) return Result.success();
|
||||
|
||||
doCheck(this.getApplicationContext());
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
static void doCheck(Context context) {
|
||||
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
|
||||
RepoManager.getINSTANCE().update(null);
|
||||
ModuleManager.getINSTANCE().scan();
|
||||
ModuleManager.getINSTANCE().scan();
|
||||
int moduleUpdateCount = 0;
|
||||
for (LocalModuleInfo localModuleInfo :
|
||||
ModuleManager.getINSTANCE().getModules().values()) {
|
||||
RepoModule repoModule = RepoManager.getINSTANCE()
|
||||
.getModules().get(localModuleInfo.id);
|
||||
localModuleInfo.checkModuleUpdate();
|
||||
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode) {
|
||||
moduleUpdateCount++;
|
||||
} else if (repoModule != null &&
|
||||
repoModule.moduleInfo.versionCode > localModuleInfo.versionCode) {
|
||||
moduleUpdateCount++;
|
||||
}
|
||||
}
|
||||
if (moduleUpdateCount != 0) {
|
||||
postNotification(context, moduleUpdateCount);
|
||||
}
|
||||
}
|
||||
|
||||
public static void postNotification(Context context, int updateCount) {
|
||||
if (!easterEggActive) easterEggActive = new Random().nextInt(100) <= updateCount;
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(
|
||||
context, NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(context.getString(easterEggActive ?
|
||||
R.string.notification_update_title_easter_egg :
|
||||
R.string.notification_update_title)
|
||||
.replace("%i", String.valueOf(updateCount)))
|
||||
.setContentText(context.getString(R.string.notification_update_subtitle))
|
||||
.setSmallIcon(R.drawable.ic_baseline_extension_24)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setContentIntent(PendingIntent.getActivity(context, 0,
|
||||
new Intent(context, MainActivity.class).setFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
|
||||
PendingIntent.FLAG_IMMUTABLE : 0)).setAutoCancel(true);
|
||||
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
public static void onMainActivityCreate(Context context) {
|
||||
NotificationManagerCompat notificationManagerCompat =
|
||||
NotificationManagerCompat.from(context);
|
||||
notificationManagerCompat.createNotificationChannel(
|
||||
new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID,
|
||||
NotificationManagerCompat.IMPORTANCE_HIGH).setShowBadge(true)
|
||||
.setName(context.getString(R.string.notification_update_pref)).build());
|
||||
notificationManagerCompat.cancel(BackgroundUpdateChecker.NOTIFICATION_ID);
|
||||
BackgroundUpdateChecker.easterEggActive = false;
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork("background_checker",
|
||||
ExistingPeriodicWorkPolicy.REPLACE, new PeriodicWorkRequest.Builder(
|
||||
BackgroundUpdateChecker.class, 6, TimeUnit.HOURS)
|
||||
.setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true)
|
||||
.setRequiredNetworkType(NetworkType.UNMETERED).build()).build());
|
||||
}
|
||||
|
||||
public static void onMainActivityResume(Context context) {
|
||||
NotificationManagerCompat.from(context).cancel(
|
||||
BackgroundUpdateChecker.NOTIFICATION_ID);
|
||||
BackgroundUpdateChecker.easterEggActive = false;
|
||||
}
|
||||
}
|
@ -1,587 +0,0 @@
|
||||
package com.fox2code.mmm.compat;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.Dimension;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.fox2code.mmm.Constants;
|
||||
import com.fox2code.mmm.R;
|
||||
import com.kieronquinn.monetcompat.app.MonetCompatActivity;
|
||||
import com.kieronquinn.monetcompat.extensions.views.ViewExtensions_RecyclerViewKt;
|
||||
import com.kieronquinn.monetcompat.extensions.views.ViewExtensions_ScrollViewKt;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import rikka.insets.WindowInsetsHelper;
|
||||
import rikka.layoutinflater.view.LayoutInflaterFactory;
|
||||
|
||||
/**
|
||||
* I will probably outsource this to a separate library later
|
||||
*/
|
||||
public class CompatActivity extends AppCompatActivity {
|
||||
public static final int INTENT_ACTIVITY_REQUEST_CODE = 0x01000000;
|
||||
private static final String TAG = "CompatActivity";
|
||||
public static final CompatActivity.OnBackPressedCallback DISABLE_BACK_BUTTON =
|
||||
new CompatActivity.OnBackPressedCallback() {
|
||||
@Override
|
||||
public boolean onBackPressed(CompatActivity compatActivity) {
|
||||
compatActivity.setOnBackPressedCallback(this);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
final WeakReference<CompatActivity> selfReference;
|
||||
private final CompatConfigHelper compatConfigHelper = new CompatConfigHelper(this);
|
||||
private CompatActivity.OnActivityResultCallback onActivityResultCallback;
|
||||
private CompatActivity.OnBackPressedCallback onBackPressedCallback;
|
||||
private MenuItem.OnMenuItemClickListener menuClickListener;
|
||||
private CharSequence menuContentDescription;
|
||||
@StyleRes
|
||||
private int setThemeDynamic = 0;
|
||||
private boolean onCreateCalledOnce = false;
|
||||
private boolean onCreateCalled = false;
|
||||
private boolean isRefreshUi = false;
|
||||
private boolean hasHardwareNavBar;
|
||||
private int drawableResId;
|
||||
private MenuItem menuItem;
|
||||
// CompatConfigHelper
|
||||
private boolean forceEnglish;
|
||||
private Boolean nightModeOverride;
|
||||
|
||||
public CompatActivity() {
|
||||
this.selfReference = new WeakReference<>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
if (!this.onCreateCalled) {
|
||||
this.getLayoutInflater().setFactory2(new LayoutInflaterFactory(this.getDelegate())
|
||||
.addOnViewCreatedListeners(WindowInsetsHelper.Companion.getLISTENER(),
|
||||
(view, parent, name, context, attrs) -> {
|
||||
if (view instanceof RecyclerView) {
|
||||
ViewExtensions_RecyclerViewKt.enableStretchOverscroll(
|
||||
(RecyclerView) view, null);
|
||||
} else if (view instanceof NestedScrollView) {
|
||||
ViewExtensions_ScrollViewKt.enableStretchOverscroll(
|
||||
(NestedScrollView) view, null);
|
||||
}
|
||||
}));
|
||||
this.hasHardwareNavBar = this.hasHardwareNavBar0();
|
||||
this.onCreateCalledOnce = true;
|
||||
}
|
||||
Application application = this.getApplication();
|
||||
if (application instanceof ApplicationCallbacks) {
|
||||
((ApplicationCallbacks) application).onCreateCompatActivity(this);
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
this.onCreateCalled = true;
|
||||
this.checkResourcesOverrides(
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
this.hasHardwareNavBar = this.hasHardwareNavBar0();
|
||||
super.onResume();
|
||||
this.refreshUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
this.onActivityResultCallback = null;
|
||||
boolean fadeOut = this.onCreateCalled && this.getIntent()
|
||||
.getBooleanExtra(Constants.EXTRA_FADE_OUT, false);
|
||||
super.finish();
|
||||
if (fadeOut) {
|
||||
super.overridePendingTransition(
|
||||
android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
public void refreshUI() {
|
||||
// Avoid recursive calls
|
||||
if (this.isRefreshUi || !this.onCreateCalled) return;
|
||||
Application application = this.getApplication();
|
||||
if (application instanceof ApplicationCallbacks) {
|
||||
this.isRefreshUi = true;
|
||||
try {
|
||||
((ApplicationCallbacks) application)
|
||||
.onRefreshUI(this);
|
||||
} finally {
|
||||
this.isRefreshUi = false;
|
||||
}
|
||||
this.checkResourcesOverrides(
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
}
|
||||
|
||||
public final void forceBackPressed() {
|
||||
if (!this.isFinishing())
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (this.isFinishing()) return;
|
||||
OnBackPressedCallback onBackPressedCallback = this.onBackPressedCallback;
|
||||
this.onBackPressedCallback = null;
|
||||
if (onBackPressedCallback == null ||
|
||||
!onBackPressedCallback.onBackPressed(this)) {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
|
||||
androidx.appcompat.app.ActionBar compatActionBar;
|
||||
try {
|
||||
compatActionBar = this.getSupportActionBar();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to call getSupportActionBar", e);
|
||||
compatActionBar = null; // Allow fallback to builtin actionBar.
|
||||
}
|
||||
if (compatActionBar != null) {
|
||||
compatActionBar.setDisplayHomeAsUpEnabled(showHomeAsUp);
|
||||
} else {
|
||||
android.app.ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null)
|
||||
actionBar.setDisplayHomeAsUpEnabled(showHomeAsUp);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideActionBar() {
|
||||
androidx.appcompat.app.ActionBar compatActionBar;
|
||||
try {
|
||||
compatActionBar = this.getSupportActionBar();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to call getSupportActionBar", e);
|
||||
compatActionBar = null; // Allow fallback to builtin actionBar.
|
||||
}
|
||||
if (compatActionBar != null) {
|
||||
compatActionBar.hide();
|
||||
} else {
|
||||
android.app.ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null)
|
||||
actionBar.hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void showActionBar() {
|
||||
androidx.appcompat.app.ActionBar compatActionBar;
|
||||
try {
|
||||
compatActionBar = this.getSupportActionBar();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to call getSupportActionBar", e);
|
||||
compatActionBar = null; // Allow fallback to builtin actionBar.
|
||||
}
|
||||
if (compatActionBar != null) {
|
||||
compatActionBar.show();
|
||||
} else {
|
||||
android.app.ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null)
|
||||
actionBar.show();
|
||||
}
|
||||
}
|
||||
|
||||
public View getActionBarView() {
|
||||
androidx.appcompat.app.ActionBar compatActionBar;
|
||||
try {
|
||||
compatActionBar = this.getSupportActionBar();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to call getSupportActionBar", e);
|
||||
compatActionBar = null; // Allow fallback to builtin actionBar.
|
||||
}
|
||||
if (compatActionBar != null) {
|
||||
return compatActionBar.getCustomView();
|
||||
} else {
|
||||
android.app.ActionBar actionBar = this.getActionBar();
|
||||
return actionBar != null ? actionBar.getCustomView() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@Dimension
|
||||
@Px
|
||||
public int getActionBarHeight() {
|
||||
androidx.appcompat.app.ActionBar compatActionBar;
|
||||
try {
|
||||
compatActionBar = this.getSupportActionBar();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to call getSupportActionBar", e);
|
||||
compatActionBar = null; // Allow fallback to builtin actionBar.
|
||||
}
|
||||
View customView = null;
|
||||
if (compatActionBar != null) {
|
||||
return compatActionBar.isShowing() || ((customView =
|
||||
compatActionBar.getCustomView()) != null &&
|
||||
customView.getVisibility() == View.VISIBLE) ?
|
||||
Math.max(customView == null ? 0 : customView.getHeight(),
|
||||
compatActionBar.getHeight()) : 0;
|
||||
} else {
|
||||
android.app.ActionBar actionBar = this.getActionBar();
|
||||
return actionBar != null && (actionBar.isShowing() || ((
|
||||
customView = actionBar.getCustomView()) != null &&
|
||||
customView.getVisibility() == View.VISIBLE)) ?
|
||||
Math.max(customView == null ? 0 : customView.getHeight(),
|
||||
actionBar.getHeight()) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getActionBarHeight(Activity activity) {
|
||||
TypedValue tv = new TypedValue();
|
||||
int actionBarHeight = 0;
|
||||
if (activity.getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
|
||||
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
|
||||
}
|
||||
return actionBarHeight;
|
||||
}
|
||||
|
||||
public void setActionBarBackground(Drawable drawable) {
|
||||
androidx.appcompat.app.ActionBar compatActionBar;
|
||||
try {
|
||||
compatActionBar = this.getSupportActionBar();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to call getSupportActionBar", e);
|
||||
compatActionBar = null; // Allow fallback to builtin actionBar.
|
||||
}
|
||||
if (compatActionBar != null) {
|
||||
compatActionBar.setBackgroundDrawable(drawable);
|
||||
} else {
|
||||
android.app.ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null)
|
||||
actionBar.setBackgroundDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
@Dimension @Px
|
||||
@SuppressLint("InternalInsetResource")
|
||||
public int getStatusBarHeight() {
|
||||
int height = WindowInsetsCompat.CONSUMED.getInsets(
|
||||
WindowInsetsCompat.Type.statusBars()).top;
|
||||
// Check system resources
|
||||
int id = Resources.getSystem().getIdentifier(
|
||||
"status_bar_height", "dimen", "android");
|
||||
return id <= 0 ? height : Math.max(height,
|
||||
Resources.getSystem().getDimensionPixelSize(id));
|
||||
}
|
||||
|
||||
@Dimension @Px
|
||||
@SuppressLint("InternalInsetResource")
|
||||
public int getNavigationBarHeight() {
|
||||
int height = WindowInsetsCompat.CONSUMED.getInsets(
|
||||
WindowInsetsCompat.Type.navigationBars()).bottom;
|
||||
// Check system resources
|
||||
int id = Resources.getSystem().getIdentifier(
|
||||
"config_showNavigationBar", "bool", "android");
|
||||
Log.d(TAG, "Nav 1: " + id);
|
||||
if ((id > 0 && Resources.getSystem().getBoolean(id))
|
||||
|| !this.hasHardwareNavBar()) {
|
||||
id = Resources.getSystem().getIdentifier(
|
||||
"navigation_bar_height", "dimen", "android");
|
||||
Log.d(TAG, "Nav 2: " + id);
|
||||
return id <= 0 ? height : Math.max(height,
|
||||
Resources.getSystem().getDimensionPixelSize(id));
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
public boolean hasHardwareNavBar() {
|
||||
// If onCreate has not been called yet, cached value is not valid
|
||||
return this.onCreateCalledOnce ? this.hasHardwareNavBar : this.hasHardwareNavBar0();
|
||||
}
|
||||
|
||||
private boolean hasHardwareNavBar0() {
|
||||
return (ViewConfiguration.get(this).hasPermanentMenuKey() ||
|
||||
KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK)) &&
|
||||
!"0".equals(SystemProperties.get("qemu.hw.mainkeys"));
|
||||
}
|
||||
|
||||
public void setActionBarExtraMenuButton(@DrawableRes int drawableResId,
|
||||
MenuItem.OnMenuItemClickListener menuClickListener) {
|
||||
this.setActionBarExtraMenuButton(drawableResId,
|
||||
menuClickListener, null);
|
||||
}
|
||||
|
||||
public void setActionBarExtraMenuButton(@DrawableRes int drawableResId,
|
||||
MenuItem.OnMenuItemClickListener menuClickListener,
|
||||
@StringRes int menuContentDescription) {
|
||||
this.setActionBarExtraMenuButton(drawableResId,
|
||||
menuClickListener, this.getString(menuContentDescription));
|
||||
}
|
||||
|
||||
public void setActionBarExtraMenuButton(@DrawableRes int drawableResId,
|
||||
MenuItem.OnMenuItemClickListener menuClickListener,
|
||||
CharSequence menuContentDescription) {
|
||||
Objects.requireNonNull(menuClickListener);
|
||||
this.drawableResId = drawableResId;
|
||||
this.menuClickListener = menuClickListener;
|
||||
this.menuContentDescription = menuContentDescription;
|
||||
if (this.menuItem != null) {
|
||||
this.menuItem.setOnMenuItemClickListener(this.menuClickListener);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
this.menuItem.setContentDescription(this.menuContentDescription);
|
||||
}
|
||||
this.menuItem.setIcon(this.drawableResId);
|
||||
this.menuItem.setEnabled(true);
|
||||
this.menuItem.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeActionBarExtraMenuButton() {
|
||||
this.drawableResId = 0;
|
||||
this.menuClickListener = null;
|
||||
this.menuContentDescription = null;
|
||||
if (this.menuItem != null) {
|
||||
this.menuItem.setOnMenuItemClickListener(null);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
this.menuItem.setContentDescription(null);
|
||||
}
|
||||
this.menuItem.setIcon(null);
|
||||
this.menuItem.setEnabled(false);
|
||||
this.menuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
// like setTheme but recreate the activity if needed
|
||||
public void setThemeRecreate(@StyleRes int resId) {
|
||||
if (!this.onCreateCalled) {
|
||||
this.setTheme(resId);
|
||||
return;
|
||||
}
|
||||
if (this.setThemeDynamic == resId)
|
||||
return;
|
||||
if (this.setThemeDynamic != 0)
|
||||
throw new IllegalStateException("setThemeDynamic called recursively");
|
||||
this.setThemeDynamic = resId;
|
||||
try {
|
||||
super.setTheme(resId);
|
||||
} finally {
|
||||
this.setThemeDynamic = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
if (resid != 0 && this.setThemeDynamic == resid) {
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
Activity parent = this.getParent();
|
||||
(parent == null ? this : parent).recreate();
|
||||
super.overridePendingTransition(
|
||||
android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
} else {
|
||||
this.compatConfigHelper.checkResourcesOverrides(theme,
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
this.compatConfigHelper.checkResourcesOverrides(newConfig,
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
public void setOnBackPressedCallback(OnBackPressedCallback onBackPressedCallback) {
|
||||
this.onBackPressedCallback = onBackPressedCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
androidx.appcompat.app.ActionBar compatActionBar;
|
||||
try {
|
||||
compatActionBar = this.getSupportActionBar();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to call getSupportActionBar", e);
|
||||
compatActionBar = null; // Allow fallback to builtin actionBar.
|
||||
}
|
||||
android.app.ActionBar actionBar = this.getActionBar();
|
||||
if (compatActionBar != null ? (compatActionBar.getDisplayOptions() &
|
||||
androidx.appcompat.app.ActionBar.DISPLAY_HOME_AS_UP) != 0 :
|
||||
actionBar != null && (actionBar.getDisplayOptions() &
|
||||
android.app.ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
this.onBackPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
this.getMenuInflater().inflate(R.menu.compat_menu, menu);
|
||||
this.menuItem = menu.findItem(R.id.compat_menu_item);
|
||||
if (this.menuClickListener != null) {
|
||||
this.menuItem.setOnMenuItemClickListener(this.menuClickListener);
|
||||
this.menuItem.setIcon(this.drawableResId);
|
||||
this.menuItem.setEnabled(true);
|
||||
this.menuItem.setVisible(true);
|
||||
}
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
public void startActivityForResult(Intent intent,
|
||||
OnActivityResultCallback onActivityResultCallback) {
|
||||
this.startActivityForResult(intent, null, onActivityResultCallback);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void startActivityForResult(Intent intent, @Nullable Bundle options,
|
||||
OnActivityResultCallback onActivityResultCallback) {
|
||||
super.startActivityForResult(intent, INTENT_ACTIVITY_REQUEST_CODE, options);
|
||||
this.onActivityResultCallback = onActivityResultCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
if (requestCode == INTENT_ACTIVITY_REQUEST_CODE) {
|
||||
OnActivityResultCallback callback = this.onActivityResultCallback;
|
||||
if (callback != null) {
|
||||
this.onActivityResultCallback = null;
|
||||
callback.onActivityResult(resultCode, data);
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
public void setForceEnglish(boolean forceEnglish) {
|
||||
if (this.forceEnglish == forceEnglish) return;
|
||||
this.forceEnglish = forceEnglish;
|
||||
this.checkResourcesOverrides(forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
|
||||
public void setNightModeOverride(Boolean nightModeOverride) {
|
||||
if (this.nightModeOverride == nightModeOverride) return;
|
||||
this.nightModeOverride = nightModeOverride;
|
||||
this.checkResourcesOverrides(this.forceEnglish, nightModeOverride);
|
||||
}
|
||||
|
||||
void propagateResourcesOverride(boolean forceEnglish, Boolean nightModeOverride) {
|
||||
if (this.forceEnglish == forceEnglish &&
|
||||
this.nightModeOverride == nightModeOverride) return;
|
||||
this.forceEnglish = forceEnglish;
|
||||
this.nightModeOverride = nightModeOverride;
|
||||
this.checkResourcesOverrides(forceEnglish, nightModeOverride);
|
||||
}
|
||||
|
||||
private void checkResourcesOverrides(boolean forceEnglish, Boolean nightModeOverride) {
|
||||
if (this.isRefreshUi || !this.onCreateCalled) return; // Wait before reload
|
||||
this.compatConfigHelper.checkResourcesOverrides(forceEnglish, nightModeOverride);
|
||||
}
|
||||
|
||||
public boolean isLightTheme() {
|
||||
Resources.Theme theme = this.getTheme();
|
||||
TypedValue typedValue = new TypedValue();
|
||||
theme.resolveAttribute(R.attr.isLightTheme, typedValue, true);
|
||||
if (typedValue.type == TypedValue.TYPE_INT_BOOLEAN) {
|
||||
return typedValue.data != 0;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
theme.resolveAttribute(android.R.attr.isLightTheme, typedValue, true);
|
||||
if (typedValue.type == TypedValue.TYPE_INT_BOOLEAN) {
|
||||
return typedValue.data != 0;
|
||||
}
|
||||
}
|
||||
theme.resolveAttribute(android.R.attr.background, typedValue, true);
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
|
||||
typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return ColorUtils.calculateLuminance(typedValue.data) > 0.7D;
|
||||
}
|
||||
throw new IllegalStateException("Theme is not a valid theme!");
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public final int getColorCompat(@ColorRes @AttrRes int color) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
this.getTheme().resolveAttribute(color, typedValue, true);
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
|
||||
typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return typedValue.data;
|
||||
}
|
||||
return ContextCompat.getColor(this, color);
|
||||
}
|
||||
|
||||
public Locale getUserLocale() {
|
||||
return this.compatConfigHelper.getUserLocale();
|
||||
}
|
||||
|
||||
public static CompatActivity getCompatActivity(View view) {
|
||||
return getCompatActivity(view.getContext());
|
||||
}
|
||||
|
||||
public static CompatActivity getCompatActivity(Fragment fragment) {
|
||||
return getCompatActivity(fragment.getContext());
|
||||
}
|
||||
|
||||
public static CompatActivity getCompatActivity(Context context) {
|
||||
while (!(context instanceof CompatActivity)) {
|
||||
if (context instanceof ContextWrapper) {
|
||||
context = ((ContextWrapper) context).getBaseContext();
|
||||
} else return null;
|
||||
}
|
||||
return (CompatActivity) context;
|
||||
}
|
||||
|
||||
public WeakReference<CompatActivity> asWeakReference() {
|
||||
return this.selfReference;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnActivityResultCallback {
|
||||
void onActivityResult(int resultCode, @Nullable Intent data);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnBackPressedCallback {
|
||||
boolean onBackPressed(CompatActivity compatActivity);
|
||||
}
|
||||
|
||||
public interface ApplicationCallbacks {
|
||||
void onCreateCompatActivity(CompatActivity compatActivity);
|
||||
|
||||
void onRefreshUI(CompatActivity compatActivity);
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
package com.fox2code.mmm.compat;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.fox2code.mmm.R;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* I will probably outsource this to a separate library later
|
||||
*/
|
||||
public class CompatApplication extends Application implements CompatActivity.ApplicationCallbacks {
|
||||
private static final String TAG = "CompatApplication";
|
||||
private final CompatConfigHelper compatConfigHelper = new CompatConfigHelper(this);
|
||||
private WeakReference<CompatActivity> lastCompatActivity;
|
||||
// CompatConfigHelper
|
||||
private boolean forceEnglish;
|
||||
private Boolean nightModeOverride;
|
||||
private boolean propagateOverrides;
|
||||
|
||||
public CompatApplication() {}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
this.compatConfigHelper.checkResourcesOverrides(newConfig,
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
public void setForceEnglish(boolean forceEnglish) {
|
||||
if (this.forceEnglish != forceEnglish) {
|
||||
this.forceEnglish = forceEnglish;
|
||||
this.checkResourcesOverrides(forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
// Propagate even if local value didn't changed
|
||||
if (this.propagateOverrides && this.lastCompatActivity != null) {
|
||||
CompatActivity compatActivity = this.lastCompatActivity.get();
|
||||
if (compatActivity != null)
|
||||
compatActivity.setForceEnglish(forceEnglish);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNightModeOverride(Boolean nightModeOverride) {
|
||||
if (this.nightModeOverride != nightModeOverride) {
|
||||
this.nightModeOverride = nightModeOverride;
|
||||
this.checkResourcesOverrides(this.forceEnglish, nightModeOverride);
|
||||
}
|
||||
// Propagate even if local value didn't changed
|
||||
if (this.propagateOverrides && this.lastCompatActivity != null) {
|
||||
CompatActivity compatActivity = this.lastCompatActivity.get();
|
||||
if (compatActivity != null)
|
||||
compatActivity.setNightModeOverride(nightModeOverride);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPropagateOverrides() {
|
||||
return propagateOverrides;
|
||||
}
|
||||
|
||||
public void setPropagateOverrides(boolean propagateOverrides) {
|
||||
this.propagateOverrides = propagateOverrides;
|
||||
WeakReference<CompatActivity> lastCompatActivity = this.lastCompatActivity;
|
||||
if (lastCompatActivity != null) {
|
||||
Log.d(TAG, "setPropagateOverrides(" + // This should be avoided
|
||||
propagateOverrides + ") called after first activity created!");
|
||||
CompatActivity compatActivity = lastCompatActivity.get();
|
||||
if (compatActivity != null && propagateOverrides) {
|
||||
this.propagateOverrides(compatActivity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkResourcesOverrides(boolean forceEnglish, Boolean nightModeOverride) {
|
||||
this.compatConfigHelper.checkResourcesOverrides(forceEnglish, nightModeOverride);
|
||||
}
|
||||
|
||||
public boolean isLightTheme() {
|
||||
Resources.Theme theme = this.getTheme();
|
||||
TypedValue typedValue = new TypedValue();
|
||||
theme.resolveAttribute(R.attr.isLightTheme, typedValue, true);
|
||||
if (typedValue.type == TypedValue.TYPE_INT_BOOLEAN) {
|
||||
return typedValue.data == 1;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
theme.resolveAttribute(android.R.attr.isLightTheme, typedValue, true);
|
||||
if (typedValue.type == TypedValue.TYPE_INT_BOOLEAN) {
|
||||
return typedValue.data == 1;
|
||||
}
|
||||
}
|
||||
theme.resolveAttribute(android.R.attr.background, typedValue, true);
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
|
||||
typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return ColorUtils.calculateLuminance(typedValue.data) > 0.7D;
|
||||
}
|
||||
throw new IllegalStateException("Theme is not a valid theme!");
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public final int getColorCompat(@ColorRes @AttrRes int color) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
this.getTheme().resolveAttribute(color, typedValue, true);
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
|
||||
typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return typedValue.data;
|
||||
}
|
||||
return ContextCompat.getColor(this, color);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onCreateCompatActivity(CompatActivity compatActivity) {
|
||||
this.lastCompatActivity = compatActivity.selfReference;
|
||||
if (this.propagateOverrides) {
|
||||
this.propagateOverrides(compatActivity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onRefreshUI(CompatActivity compatActivity) {
|
||||
this.lastCompatActivity = compatActivity.selfReference;
|
||||
if (this.propagateOverrides) {
|
||||
this.propagateOverrides(compatActivity);
|
||||
}
|
||||
}
|
||||
|
||||
private void propagateOverrides(CompatActivity compatActivity) {
|
||||
compatActivity.propagateResourcesOverride(
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
package com.fox2code.mmm.compat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* I will probably outsource this to a separate library later
|
||||
*/
|
||||
final class CompatConfigHelper {
|
||||
// ENGLISH like this is an unnatural local, as it doesn't precise the country
|
||||
// All english locales settable by the user precise the country (Ex: en-US)
|
||||
private static final Locale englishLocale = Locale.ENGLISH;
|
||||
private static final Object englishLocales =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
|
||||
new LocaleList(englishLocale) : null;
|
||||
|
||||
private final Context context;
|
||||
private Object userLocales;
|
||||
private Locale userLocale;
|
||||
|
||||
CompatConfigHelper(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
void checkResourcesOverrides(boolean forceEnglish,
|
||||
Boolean nightModeOverride) {
|
||||
this.checkResourcesOverrides(
|
||||
this.context.getTheme(),
|
||||
forceEnglish, nightModeOverride);
|
||||
}
|
||||
|
||||
void checkResourcesOverrides(Resources.Theme theme, boolean forceEnglish,
|
||||
Boolean nightModeOverride) {
|
||||
Resources res = theme.getResources();
|
||||
if (this.checkResourcesOverrides(res.getConfiguration(),
|
||||
forceEnglish, nightModeOverride)) {
|
||||
res.updateConfiguration(
|
||||
res.getConfiguration(),
|
||||
res.getDisplayMetrics());
|
||||
}
|
||||
}
|
||||
|
||||
boolean checkResourcesOverrides(Configuration conf, boolean forceEnglish,
|
||||
Boolean nightModeOverride) {
|
||||
Locale current = conf.locale;
|
||||
boolean didChange = false;
|
||||
boolean wasForceEnglish = englishLocale.equals(current);
|
||||
if (forceEnglish != wasForceEnglish) {
|
||||
didChange = true;
|
||||
if (forceEnglish) {
|
||||
this.userLocale = conf.locale;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
this.userLocales = conf.getLocales();
|
||||
}
|
||||
conf.locale = englishLocale;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
conf.setLocales((LocaleList) englishLocales);
|
||||
}
|
||||
} else {
|
||||
conf.locale = this.userLocale;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
conf.setLocales((LocaleList) this.userLocales);
|
||||
}
|
||||
}
|
||||
}
|
||||
int nightMode = conf.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
int sysNightMode = Resources.getSystem()
|
||||
.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
if (nightModeOverride == null ? sysNightMode != nightMode :
|
||||
nightMode != (nightModeOverride ?
|
||||
Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO)) {
|
||||
didChange = true;
|
||||
nightMode = nightModeOverride == null ? sysNightMode : nightModeOverride ?
|
||||
Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO;
|
||||
conf.uiMode = nightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
|
||||
}
|
||||
if (!forceEnglish && !wasForceEnglish) {
|
||||
this.userLocale = null;
|
||||
this.userLocales = null;
|
||||
}
|
||||
return didChange;
|
||||
}
|
||||
|
||||
public Locale getUserLocale() {
|
||||
// Only use cached value if force english
|
||||
Locale locale = this.context.getResources().getConfiguration().locale;
|
||||
return englishLocale.equals(locale) ? this.userLocale : locale;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.fox2code.mmm.compat;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.annotation.Dimension;
|
||||
import androidx.annotation.Px;
|
||||
|
||||
public final class CompatDisplay {
|
||||
@Dimension @Px
|
||||
public static int dpToPixel(@Dimension(unit = Dimension.DP) float dp){
|
||||
return (int) (dp * ((float) Resources.getSystem().getDisplayMetrics()
|
||||
.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
|
||||
}
|
||||
|
||||
@Dimension(unit = Dimension.DP)
|
||||
public static float pixelsToDp(@Dimension @Px int px){
|
||||
return (px / ((float) Resources.getSystem().getDisplayMetrics()
|
||||
.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
package com.fox2code.mmm.compat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.fox2code.mmm.R;
|
||||
|
||||
/**
|
||||
* I will probably outsource this to a separate library later
|
||||
*/
|
||||
public class CompatThemeWrapper extends ContextThemeWrapper {
|
||||
private final CompatConfigHelper compatConfigHelper = new CompatConfigHelper(this);
|
||||
private boolean canReload;
|
||||
// CompatConfigHelper
|
||||
private boolean forceEnglish;
|
||||
private Boolean nightModeOverride;
|
||||
|
||||
public CompatThemeWrapper(Context base, @StyleRes int themeResId) {
|
||||
super(base, themeResId);
|
||||
this.canReload = true;
|
||||
this.checkResourcesOverrides(
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
boolean couldReload = this.canReload;
|
||||
if (couldReload) this.canReload = false;
|
||||
this.compatConfigHelper.checkResourcesOverrides(theme,
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
if (couldReload) this.canReload = true;
|
||||
// In case value change while reload, should have no effect
|
||||
this.compatConfigHelper.checkResourcesOverrides(theme,
|
||||
this.forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
|
||||
public void setForceEnglish(boolean forceEnglish) {
|
||||
if (this.forceEnglish == forceEnglish) return;
|
||||
this.forceEnglish = forceEnglish;
|
||||
this.checkResourcesOverrides(forceEnglish, this.nightModeOverride);
|
||||
}
|
||||
|
||||
public void setNightModeOverride(Boolean nightModeOverride) {
|
||||
if (this.nightModeOverride == nightModeOverride) return;
|
||||
this.nightModeOverride = nightModeOverride;
|
||||
this.checkResourcesOverrides(this.forceEnglish, nightModeOverride);
|
||||
}
|
||||
|
||||
private void checkResourcesOverrides(boolean forceEnglish,Boolean nightModeOverride) {
|
||||
if (!this.canReload) return; // Do not reload during theme reload
|
||||
this.compatConfigHelper.checkResourcesOverrides(forceEnglish, nightModeOverride);
|
||||
}
|
||||
|
||||
public boolean isLightTheme() {
|
||||
Resources.Theme theme = this.getTheme();
|
||||
TypedValue typedValue = new TypedValue();
|
||||
theme.resolveAttribute(R.attr.isLightTheme, typedValue, true);
|
||||
if (typedValue.type == TypedValue.TYPE_INT_BOOLEAN) {
|
||||
return typedValue.data == 1;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
theme.resolveAttribute(android.R.attr.isLightTheme, typedValue, true);
|
||||
if (typedValue.type == TypedValue.TYPE_INT_BOOLEAN) {
|
||||
return typedValue.data == 1;
|
||||
}
|
||||
}
|
||||
theme.resolveAttribute(android.R.attr.background, typedValue, true);
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
|
||||
typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return ColorUtils.calculateLuminance(typedValue.data) > 0.7D;
|
||||
}
|
||||
throw new IllegalStateException("Theme is not a valid theme!");
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public final int getColorCompat(@ColorRes @AttrRes int color) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
this.getTheme().resolveAttribute(color, typedValue, true);
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
|
||||
typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return typedValue.data;
|
||||
}
|
||||
return ContextCompat.getColor(this, color);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package com.fox2code.mmm.manager;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.fox2code.mmm.MainApplication;
|
||||
import com.fox2code.mmm.installer.InstallerInitializer;
|
||||
|
||||
public class ModuleBootReceive extends BroadcastReceiver {
|
||||
private static final String BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent == null || !BOOT_COMPLETED.equals(intent.getAction())
|
||||
|| !MainApplication.hasGottenRootAccess()) {
|
||||
return;
|
||||
}
|
||||
InstallerInitializer.tryGetMagiskPathAsync(new InstallerInitializer.Callback() {
|
||||
@Override
|
||||
public void onPathReceived(String path) {
|
||||
MainApplication.notifyBootListenerCompleted();
|
||||
ModuleManager.getINSTANCE().scan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int error) {
|
||||
MainApplication.setHasGottenRootAccess(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.fox2code.mmm.repo;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.fox2code.mmm.utils.Http;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public final class CustomRepoData extends RepoData {
|
||||
boolean loadedExternal;
|
||||
String override;
|
||||
|
||||
CustomRepoData(String url, File cacheRoot, SharedPreferences cachedPreferences) {
|
||||
super(url, cacheRoot, cachedPreferences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return this.override != null || this.loadedExternal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceId() {
|
||||
return this.override == null ?
|
||||
this.id : this.override;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLimited() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void quickPrePopulate() throws IOException, JSONException {
|
||||
JSONObject jsonObject = new JSONObject(
|
||||
new String(Http.doHttpGet(this.getUrl(),
|
||||
false), StandardCharsets.UTF_8));
|
||||
this.name = jsonObject.getString("name").trim();
|
||||
this.website = jsonObject.optString("website");
|
||||
this.support = jsonObject.optString("support");
|
||||
this.donate = jsonObject.optString("donate");
|
||||
this.submitModule = jsonObject.optString("submitModule");
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package com.fox2code.mmm.repo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.fox2code.mmm.MainApplication;
|
||||
|
||||
public class CustomRepoManager {
|
||||
private static final boolean AUTO_RECOMPILE = true;
|
||||
public static final int MAX_CUSTOM_REPOS = 5;
|
||||
private final MainApplication mainApplication;
|
||||
private final RepoManager repoManager;
|
||||
private final String[] customRepos;
|
||||
private int customReposCount;
|
||||
boolean dirty;
|
||||
|
||||
CustomRepoManager(MainApplication mainApplication, RepoManager repoManager) {
|
||||
this.mainApplication = mainApplication;
|
||||
this.repoManager = repoManager;
|
||||
this.customRepos = new String[MAX_CUSTOM_REPOS];
|
||||
this.customReposCount = 0;
|
||||
SharedPreferences sharedPreferences = this.getSharedPreferences();
|
||||
int lastFilled = 0;
|
||||
for (int i = 0; i < MAX_CUSTOM_REPOS; i++) {
|
||||
String repo = sharedPreferences.getString("repo_" + i, "");
|
||||
if (!repo.isEmpty() && !RepoManager.isBuiltInRepo(repo)) {
|
||||
lastFilled = i;
|
||||
int index = AUTO_RECOMPILE ?
|
||||
this.customReposCount : i;
|
||||
this.customRepos[index] = repo;
|
||||
this.customReposCount++;
|
||||
((CustomRepoData) this.repoManager.addOrGet(repo))
|
||||
.override = "custom_repo_" + index;
|
||||
}
|
||||
}
|
||||
if (AUTO_RECOMPILE && (lastFilled + 1) != this.customReposCount) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit().clear();
|
||||
for (int i = 0; i < MAX_CUSTOM_REPOS; i++) {
|
||||
if (this.customRepos[i] != null)
|
||||
editor.putString("repo_" + i, this.customRepos[i]);
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
private SharedPreferences getSharedPreferences() {
|
||||
return this.mainApplication.getSharedPreferences(
|
||||
"mmm_custom_repos", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public CustomRepoData addRepo(String repo) {
|
||||
if (RepoManager.isBuiltInRepo(repo))
|
||||
throw new IllegalArgumentException("Can't add built-in repo to custom repos");
|
||||
for (String repoEntry : this.customRepos) {
|
||||
if (repo.equals(repoEntry))
|
||||
return (CustomRepoData) this.repoManager.get(repoEntry);
|
||||
}
|
||||
int i = 0;
|
||||
while (customRepos[i] != null) i++;
|
||||
customRepos[i] = repo;
|
||||
this.getSharedPreferences().edit()
|
||||
.putString("repo_" + i, repo).apply();
|
||||
this.dirty = true;
|
||||
CustomRepoData customRepoData = (CustomRepoData)
|
||||
this.repoManager.addOrGet(repo);
|
||||
customRepoData.override = "custom_repo_" + i;
|
||||
customRepoData.updateEnabledState();
|
||||
return customRepoData;
|
||||
}
|
||||
|
||||
public CustomRepoData getRepo(int index) {
|
||||
if (index >= MAX_CUSTOM_REPOS) return null;
|
||||
String repo = customRepos[index];
|
||||
return repo == null ? null :
|
||||
(CustomRepoData) this.repoManager.get(repo);
|
||||
}
|
||||
|
||||
public void removeRepo(int index) {
|
||||
String oldRepo = customRepos[index];
|
||||
if (oldRepo != null) {
|
||||
customRepos[index] = null;
|
||||
customReposCount--;
|
||||
CustomRepoData customRepoData =
|
||||
(CustomRepoData) this.repoManager.get(oldRepo);
|
||||
if (customRepoData != null) {
|
||||
customRepoData.setEnabled(false);
|
||||
customRepoData.override = null;
|
||||
}
|
||||
this.getSharedPreferences().edit()
|
||||
.remove("repo_" + index).apply();
|
||||
this.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasRepo(String repo) {
|
||||
for (String repoEntry : this.customRepos) {
|
||||
if (repo.equals(repoEntry))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canAddRepo() {
|
||||
return this.customReposCount < MAX_CUSTOM_REPOS;
|
||||
}
|
||||
|
||||
public boolean canAddRepo(String repo) {
|
||||
if (RepoManager.isBuiltInRepo(repo) ||
|
||||
this.hasRepo(repo) || !this.canAddRepo())
|
||||
return false;
|
||||
return repo.startsWith("https://") &&
|
||||
repo.indexOf('/', 9) != -1;
|
||||
}
|
||||
|
||||
public int getRepoCount() {
|
||||
return this.customReposCount;
|
||||
}
|
||||
|
||||
public boolean needUpdate() {
|
||||
boolean needUpdate = this.dirty;
|
||||
if (needUpdate) this.dirty = false;
|
||||
return needUpdate;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"/>
|
||||
</vector>
|
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
|
||||
</vector>
|
@ -0,0 +1,7 @@
|
||||
<resources>
|
||||
<string-array name="theme_values_names">
|
||||
<item>Προεπιλογή συστήματως</item>
|
||||
<item>Σκωτεινό</item>
|
||||
<item>Ανοιχτό</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -1,64 +1,127 @@
|
||||
<resources>
|
||||
<string name="app_name">Fox\'s Magisk Module Manager</string>
|
||||
<string name="app_name_short">Fox\'s Mmm</string>
|
||||
<string name="app_name">Fox Magisk Module Manager</string>
|
||||
<string name="app_name_short">Fox Mmm</string>
|
||||
<string name="fail_root_magisk">Impossibile ottenere l\'accesso al Root o a Magisk</string>
|
||||
<string name="loading">Caricamento</string>
|
||||
<string name="updatable">Aggiornamento disponibile</string>
|
||||
<string name="installed">Installato</string>
|
||||
<string name="online_repo">Repository Online</string>
|
||||
<string name="showcase_mode">L\'applicazione e\' in modalita\' protetta </string>
|
||||
<string name="showcase_mode">L\'applicazione è in modalità protetta </string>
|
||||
<string name="failed_download">Scaricamento del file non riuscito.</string>
|
||||
<string name="slow_modules">L\'avvio dei moduli e\' lento, prova a disabilitarne alcuni</string>
|
||||
<string name="slow_modules">L\'avvio dei moduli è lento, prova a disabilitarne alcuni</string>
|
||||
<string name="fail_internet">Impossibile connettersi a internet</string>
|
||||
<string name="title_activity_settings">Impostazione attivita\'</string>
|
||||
<string name="no_web_view">Impossibile aprire la WebView di sistema</string>
|
||||
<string name="title_activity_settings">Impostazione attività</string>
|
||||
<string name="app_update_available">Aggiornamento disponibile</string>
|
||||
<string name="app_update">Aggiornamento</string>
|
||||
<string name="no_desc_found">Nessuna descrizione trovata.</string>
|
||||
<string name="download_module">Scarica modulo</string>
|
||||
<string name="install_module">Installa modulo</string>
|
||||
<string name="update_module">Aggiorna modulo</string>
|
||||
<string name="changelog">Registri dei cambiamenti</string>
|
||||
<string name="website">Sito web</string>
|
||||
<string name="support">Supporto</string>
|
||||
<string name="donate">Dona</string>
|
||||
<string name="update">Aggiorna</string>
|
||||
<string name="install">Installa</string>
|
||||
<string name="description">Descrizione</string>
|
||||
<string name="uninstall">Disinstalla</string>
|
||||
<string name="config">Configurazione</string>
|
||||
<string name="submit_modules">Invia un modulo</string>
|
||||
<string name="require_android_6">Richiede Android 6.0+</string>
|
||||
<string name="require_android_12">Richiede Android 12+</string>
|
||||
<string name="install_terminal_reboot_now">Riavvia</string>
|
||||
<string name="language">Lingua</string>
|
||||
|
||||
<!-- Module section translation -->
|
||||
<string name="module_last_update">Ultimo aggiornamento:</string>
|
||||
<string name="module_repo">Repo:</string>
|
||||
<string name="module_by">da</string>
|
||||
<string name="module_downloads">Scaricamenti:</string>
|
||||
<string name="module_stars">Preferiti:</string>
|
||||
<string name="module_needs_ramdisk">Necessita ramdisk</string>
|
||||
<string name="module_needs_ramdisk_desc">Questo modulo ha bisogno dell\'avvio ramdisk per essere installato</string>
|
||||
<string name="module_can_change_boot">Può cambiare avvio</string>
|
||||
<string name="module_can_change_boot_desc">Questo modulo può cambiare l\'immagine di avvio</string>
|
||||
<string name="module_min_magisk_chip">Min. Magisk "%s"</string>
|
||||
<string name="module_min_sdk_chip">Min. Android</string>
|
||||
<string name="module_max_sdk_chip">Max. Android</string>
|
||||
|
||||
<!-- Preference Titles -->
|
||||
<!-- Note: Lockdown mode used to be called showcase mode -->
|
||||
<string name="showcase_mode_pref">Modalita\' protetta</string>
|
||||
<string name="showcase_mode_desc">La modalita\' protetta impedisce la modifica dei moduli da parte del gestore</string>
|
||||
<string name="manage_repos_pref">Gestisci repository</string>
|
||||
<string name="showcase_mode_pref">Modalità protetta</string>
|
||||
<string name="showcase_mode_desc">La modalità protetta impedisce la modifica dei moduli da parte del gestore</string>
|
||||
<string name="prevent_reboot_pref">Impedisci riavvio</string>
|
||||
<string name="prevent_reboot_desc">Impedisce riavvii inaspettati</string>
|
||||
<string name="pref_category_settings">Impostazioni</string>
|
||||
<string name="pref_category_info">Info</string>
|
||||
<string name="show_licenses">Mostra le autorizzazioni</string>
|
||||
<string name="licenses">Autorizzazioni</string>
|
||||
<string name="enable_monet">Abilita Monet</string>
|
||||
<string name="pref_category_info">Informazioni</string>
|
||||
<string name="show_licenses">Mostra le licenze</string>
|
||||
<string name="licenses">Licenze</string>
|
||||
<string name="show_incompatible_pref">Mostra moduli incompatibili</string>
|
||||
<string name="show_incompatible_desc">Mostra i moduli che in base ai metadati sono incompatibili con il tuo dispositivo </string>
|
||||
<string name="magisk_outdated">Magisk non e\' aggiornato!</string>
|
||||
<string name="pref_category_repos">Repos</string>
|
||||
<string name="show_incompatible_desc">Mostra i moduli che in base ai metadati sono incompatibili con il tuo dispositivo</string>
|
||||
<string name="magisk_outdated">Magisk non è aggiornato!</string>
|
||||
<string name="pref_category_repos">Repository</string>
|
||||
<string name="pref_category_security">Sicurezza</string>
|
||||
<string name="pref_category_appearance">Aspetto</string>
|
||||
<string name="pref_category_general">Generale</string>
|
||||
<string name="repo_main_desc">La repository che fornisce i moduli Magisk</string>
|
||||
<string name="repo_main_alt">Un\' alternativa alla Repository di default con meno restrizioni.</string>
|
||||
<string name="repo_main_alt">Un\'alternativa alla Repository di default con meno restrizioni.</string>
|
||||
<string name="master_delete">Rimuovere i dati del modulo?</string>
|
||||
<string name="master_delete_no">Mantieni i dati</string>
|
||||
<string name="master_delete_yes">Rimuovi i dati</string>
|
||||
<string name="master_delete_fail">Impossibile rimuovere i dati del modulo</string>
|
||||
<string name="theme_pref">Tema</string>
|
||||
<string name="theme_mode_pref">Modalità tema</string>
|
||||
<string name="module_id_prefix">Id del modulo: </string>
|
||||
<string name="install_from_storage">Installa un modulo dalla memoria di archiviazione</string>
|
||||
<string name="invalid_format">Il modulo selezionato ha un formato non valido</string>
|
||||
<string name="low_quality_module">Modulo qualità-bassa</string>
|
||||
<string name="local_install_title">Installazione locale</string>
|
||||
<string name="source_code">Codice sorgente</string>
|
||||
<string name="magisk_builtin_module">Modulo di Magisk integrato</string>
|
||||
<string name="substratum_builtin_module">modulo del sottostrato integrato</string>
|
||||
<string name="force_dark_terminal_title">Forza il terminale in modalita\' scura</string>
|
||||
<string name="substratum_builtin_module">Modulo di Substratum integrato</string>
|
||||
<string name="force_dark_terminal_title">Forza il terminale in modalità scura</string>
|
||||
<string name="file_picker_failure">Il tuo gestore file non ha potuto accedere ai dati sul dispositivo.</string>
|
||||
<string name="remote_install_title">Installazione remota</string>
|
||||
<string name="file_picker_wierd">Il tuo gestore file ha fornito una risposta non valida.</string>
|
||||
<string name="use_magisk_install_command_pref">Usa il comando di installazione del modulo magisk</string>
|
||||
<string name="use_magisk_install_command_desc">
|
||||
Questo ha causato dei problemi durante i test per l\' installazione dei moduli,
|
||||
per questo l\'opzione e\' disponibile solo in modalita\' sviluppatore, abilita a tuo rischio e pericolo!
|
||||
Questo ha causato dei problemi durante i test per l\'installazione dei moduli,
|
||||
per questo l\'opzione è disponibile solo in modalità sviluppatore, abilita a tuo rischio e pericolo!
|
||||
</string>
|
||||
<string name="dev_mode_enabled">Modalita\' sviluppatore attivata</string>
|
||||
<string name="dev_mode_enabled">Modalità sviluppatore attivata</string>
|
||||
<string name="force_english_pref">Forza la lingua inglese</string>
|
||||
<string name="disable_low_quality_module_filter_pref">Disabilita il filtro qualita\' per i moduli</string>
|
||||
<string name="disable_low_quality_module_filter_pref">Disabilita il filtro qualità per i moduli</string>
|
||||
<string name="disable_low_quality_module_filter_desc">
|
||||
Alcuni moduli non forniscono correttamente i loro metadati,causando errori grafici,
|
||||
e/o indicando bassa qualita\', disabilita a tuo rischio e pericolo!
|
||||
e/o indicando bassa qualità.\nDisabilita a tuo rischio e pericolo!
|
||||
</string>
|
||||
<string name="dns_over_https_pref">DNS over HTTPS</string>
|
||||
<string name="dns_over_https_desc">
|
||||
Potrebbe risolvere porblemi di connessione in alcuni casi.
|
||||
(Non si applica alla WebView.)
|
||||
</string>
|
||||
<string name="disable_extensions_pref">No Mmm</string>
|
||||
<string name="disable_extensions_desc">
|
||||
Disattiva le estensioni di Fox Mmm, impedendo ai moduli di usare
|
||||
le estensioni per il terminale.\nUtile se un modulo abusa le estensioni di Fox Mmm.
|
||||
</string>
|
||||
<string name="wrap_text_pref">Avvolgimento del testo</string>
|
||||
<string name="wrap_text_desc">
|
||||
Mostra il testo su righee multiple invece che mettere
|
||||
tutto il testo sulla stessa riga quando si installa un modulo.
|
||||
</string>
|
||||
<string name="enable_blur_pref">Sfocatura</string>
|
||||
<string name="disable_chips_in_description">Disabilita i chip nella descrizione</string>
|
||||
<string name="repo_enabled">Repository attiva</string>
|
||||
<string name="repo_disabled">Repository non attiva</string>
|
||||
<string name="add_repo">Aggiungi Repository</string>
|
||||
<string name="remove_repo">Rimuovi Repository</string>
|
||||
<string name="custom_url">Url personalizzato</string>
|
||||
<string name="androidacy_repo_info">La repository di Androidacy contiene pubblicità e trackers.</string>
|
||||
<string name="backup_module_list">Salva moduli</string>
|
||||
<string name="restore_module_list">Ripristina moduli</string>
|
||||
<string name="require_internet">Questa operazione richiede una connessione ad internet</string>
|
||||
</resources>
|
||||
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.MagiskModuleManager.Monet" parent="Theme.MagiskModuleManager.Monet.Dark" />
|
||||
</resources>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="monet_enabled_by_default">true</bool>
|
||||
</resources>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="lang_support_rtl">false</bool>
|
||||
<bool name="monet_enabled_by_default">false</bool>
|
||||
</resources>
|
@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Resource file for values that can be overwritten with an overlay -->
|
||||
<bool name="force_transparency">false</bool>
|
||||
<bool name="force_invalidate_blur">false</bool>
|
||||
</resources>
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue