import android.provider.Settings;
import androidx.annotation.DimenRes;
-import androidx.core.content.ContextCompat;
+import androidx.annotation.VisibleForTesting;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.appcompat.view.ContextThemeWrapper;
import android.util.DisplayMetrics;
import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
import com.farmerbb.taskbar.activity.MainActivity;
import com.farmerbb.taskbar.activity.TouchAbsorberActivity;
+import com.farmerbb.taskbar.helper.GlobalHelper;
+import com.farmerbb.taskbar.helper.FreeformHackHelper;
+import com.farmerbb.taskbar.helper.LauncherHelper;
+import com.farmerbb.taskbar.helper.MenuHelper;
+import com.farmerbb.taskbar.helper.ToastHelper;
import com.farmerbb.taskbar.service.DashboardService;
import com.farmerbb.taskbar.service.NotificationService;
import com.farmerbb.taskbar.service.PowerMenuService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import static com.farmerbb.taskbar.util.Constants.*;
public static final int IMAGE_REQUEST_CODE = 1001;
- @SuppressWarnings("deprecation")
public static SharedPreferences getSharedPreferences(Context context) {
return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
}
@TargetApi(Build.VERSION_CODES.M)
private static AlertDialog.Builder buildPermissionDialogStandard(Context context, Callbacks callbacks) {
+ String message = context.getString(R.string.tb_permission_dialog_message, getAppName(context))
+ + context.getString(R.string.tb_permission_dialog_instructions_phone);
+
return new AlertDialog.Builder(context)
.setTitle(R.string.tb_permission_dialog_title)
- .setMessage(R.string.tb_permission_dialog_message)
+ .setMessage(message)
.setPositiveButton(R.string.tb_action_grant_permission, (dialog, which) -> {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName()));
}
private static AlertDialog.Builder buildPermissionDialogAndroidTV(Context context, Callbacks callbacks) {
+ String message = context.getString(R.string.tb_permission_dialog_message, getAppName(context))
+ + context.getString(R.string.tb_permission_dialog_instructions_tv);
+
return new AlertDialog.Builder(context)
.setTitle(R.string.tb_permission_dialog_title)
- .setMessage(R.string.tb_permission_dialog_message_alt)
+ .setMessage(message)
.setPositiveButton(R.string.tb_action_open_settings, (dialog, which) -> {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
finalServices.isEmpty()
? powerMenuService
: finalServices + ":" + powerMenuService);
- } catch (Exception e) { /* Gracefully fail */ }
+ } catch (Exception ignored) {}
}
- new Handler().postDelayed(() -> {
+ newHandler().postDelayed(() -> {
Intent intent = new Intent(ACTION_ACCESSIBILITY_ACTION);
- intent.putExtra("action", action);
+ intent.putExtra(EXTRA_ACTION, action);
sendBroadcast(context, intent);
try {
Settings.Secure.putString(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
finalServices);
- } catch (Exception e) { /* Gracefully fail */ }
+ } catch (Exception ignored) {}
if(onComplete != null) onComplete.run();
}, 100);
} else if(isAccessibilityServiceEnabled) {
Intent intent = new Intent(ACTION_ACCESSIBILITY_ACTION);
- intent.putExtra("action", action);
+ intent.putExtra(EXTRA_ACTION, action);
sendBroadcast(context, intent);
if(onComplete != null) onComplete.run();
try {
context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APP_PORTRAIT, null));
- } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
+ } catch (IllegalArgumentException | SecurityException ignored) {}
});
}
}
&& helper.isInFreeformWorkspace()
&& MenuHelper.getInstance().isContextMenuOpen();
- boolean noAnimation =
- pref.getBoolean(PREF_DISABLE_ANIMATIONS, false);
+ boolean noAnimation = pref.getBoolean(PREF_DISABLE_ANIMATIONS, false);
+ boolean isAndroidR = getCurrentApiVersion() > 29.0;
if(hasFreeformSupport(context)
&& (isFreeformModeEnabled(context) || isPersistentShortcut)
&& (!helper.isInFreeformWorkspace() || specialLaunch)) {
- new Handler().postDelayed(() -> {
+ newHandler().postDelayed(() -> {
startFreeformHack(context, true);
- new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
+ newHandler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : isAndroidR ? 300 : 100);
}, launchedFromTaskbar ? 0 : 100);
} else
- new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
+ newHandler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : isAndroidR ? 100 : 0);
}
public static void startFreeformHack(Context context) {
context.startActivity(intent, bundle);
} catch (ActivityNotFoundException e) {
launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context), onError);
- } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
+ } catch (IllegalArgumentException | SecurityException ignored) {}
} else
launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context), onError);
} else
int right = display.width;
int bottom = display.height;
- int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
+ int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen(context)
? 0 : context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size);
if(TaskbarPosition.isVerticalLeft(position))
if(!FreeformHackHelper.getInstance().isTouchAbsorberActive()
&& shouldLaunchTouchAbsorber(context)) {
startTouchAbsorberActivity(context);
- new Handler().postDelayed(runnable, 100);
+ newHandler().postDelayed(runnable, 100);
} else if(openInNewWindow) {
Intent intent = new Intent(context, DummyActivity.class);
intent.putExtra("finish_on_pause", true);
| Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityLowerRight(context, intent);
- new Handler().postDelayed(runnable, 100);
+ newHandler().postDelayed(runnable, 100);
} else
runnable.run();
}
display.width + 1,
display.height + 1
));
- } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
+ } catch (IllegalArgumentException | SecurityException ignored) {}
}
public static void startTouchAbsorberActivity(Context context) {
context.startActivity(intent,
getActivityOptionsBundle(context, ApplicationType.FREEFORM_HACK, null,
left, top, right, bottom));
- } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
+ } catch (IllegalArgumentException | SecurityException ignored) {}
}
public static void startContextMenuActivity(Context context, Bundle args) {
if(hasFreeformSupport(context) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
DisplayInfo display = getDisplayInfo(context);
- if(hasBrokenSetLaunchBoundsApi())
- intent.putExtra("context_menu_fix", true);
+ if(hasBrokenSetLaunchBoundsApi()) {
+ intent.putExtra(EXTRA_CONTEXT_MENU_FIX, true);
+ }
context.startActivity(intent,
getActivityOptionsBundle(context, ApplicationType.CONTEXT_MENU, null,
else
url = "https://play.google.com/store/apps/details?id=" + context.getPackageName();
} else
- url = "https://f-droid.org/repository/browse/?fdid=" + context.getPackageName();
+ url = "https://f-droid.org/packages/" + context.getPackageName();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
try {
context.startActivity(intent);
- } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
+ } catch (ActivityNotFoundException ignored) {}
}
public static boolean launcherIsDefault(Context context) {
homeIntent.addCategory(Intent.CATEGORY_HOME);
ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
- return defaultLauncher.activityInfo.packageName.equals(context.getPackageName());
+ try {
+ return defaultLauncher.activityInfo.packageName.equals(context.getPackageName());
+ } catch (NullPointerException e) {
+ return false;
+ }
}
private static int getMaxNumOfColumns(Context context) {
SharedPreferences pref = getSharedPreferences(context);
DisplayInfo display = getDisplayInfo(context);
- float density = display.currentDensity / 160;
- float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / density;
+ float density = display.currentDensity / 160.0f;
+ float baseTaskbarSize = getBaseTaskbarSize(context) / density;
int numOfColumns = 0;
float maxScreenSize = TaskbarPosition.isVertical(context)
float iconSize = context.getResources().getDimension(R.dimen.tb_icon_size) / density;
- int userMaxNumOfColumns = Integer.valueOf(pref.getString(PREF_MAX_NUM_OF_RECENTS, "10"));
+ int userMaxNumOfColumns = Integer.parseInt(pref.getString(PREF_MAX_NUM_OF_RECENTS, "10"));
while(baseTaskbarSize + iconSize < maxScreenSize
&& numOfColumns < userMaxNumOfColumns) {
SharedPreferences pref = getSharedPreferences(context);
return pref.getBoolean(PREF_DISABLE_SCROLLING_LIST, false)
? getMaxNumOfColumns(context)
- : Integer.valueOf(pref.getString(PREF_MAX_NUM_OF_RECENTS, "10"));
+ : Integer.parseInt(pref.getString(PREF_MAX_NUM_OF_RECENTS, "10"));
}
public static int getStatusBarHeight(Context context) {
- return LauncherHelper.getInstance().isOnSecondaryHomeScreen()
+ return LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)
? 0 : getSystemDimen(context, "status_bar_height");
}
- private static int getNavbarHeight(Context context) {
+ public static int getNavbarHeight(Context context) {
SharedPreferences pref = getSharedPreferences(context);
- boolean isNavbarHidden = LauncherHelper.getInstance().isOnSecondaryHomeScreen()
+ boolean isNavbarHidden = isShowHideNavbarSupported()
+ && LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)
&& pref.getBoolean(PREF_AUTO_HIDE_NAVBAR_DESKTOP_MODE, false);
return isNavbarHidden ? 0 : getSystemDimen(context, "navigation_bar_height");
}
public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
- if(LauncherHelper.getInstance().isOnSecondaryHomeScreen()
+ if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)
&& (cls.equals(TaskbarService.class)
|| cls.equals(StartMenuService.class)
|| cls.equals(DashboardService.class)))
break;
case CONTEXT_MENU:
if(hasBrokenSetLaunchBoundsApi()
- || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
+ || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f)
+ || (isChromeOs(context) && getCurrentApiVersion() >= 30.0f))
stackId = getFullscreenWindowModeId();
break;
}
- try {
- Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
- method.invoke(options, stackId);
- } catch (Exception e) { /* Gracefully fail */ }
+ if(stackId != -1) {
+ allowReflection();
+ try {
+ Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
+ method.invoke(options, stackId);
+ } catch (Exception ignored) {}
+ }
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int launchDisplayId = LauncherHelper.getInstance().getSecondaryDisplayId();
case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
return ApplicationType.APP_PORTRAIT;
}
- } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
+ } catch (PackageManager.NameNotFoundException ignored) {}
return context.getPackageName().equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
? ApplicationType.APP_LANDSCAPE
return context.getPackageManager().hasSystemFeature("org.chromium.arc");
}
+ @TargetApi(Build.VERSION_CODES.O)
+ public static boolean isAndroidTV(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY);
+ }
+
public static boolean isBlissOs(Context context) {
boolean validBlissOsBuildProp = false;
if(blissVersion != null && !blissVersion.isEmpty())
validBlissOsBuildProp = true;
- String buildUser = getSystemProperty("ro.build.user");
- if(buildUser != null && buildUser.equals("electrikjesus"))
- validBlissOsBuildProp = true;
-
return validBlissOsBuildProp
&& context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
&& isSystemApp(context);
}
+ public static boolean isProjectSakura(Context context) {
+ boolean validSakuraBuildProp = false;
+
+ // TODO replace with more specific logic for Project Sakura
+ String sakuraVersion = getSystemProperty("ro.lineage.build.version");
+ if(sakuraVersion != null && !sakuraVersion.isEmpty())
+ validSakuraBuildProp = true;
+
+ return validSakuraBuildProp
+ && context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
+ && isSystemApp(context);
+ }
+
+ public static boolean isAndroidGeneric(Context context) {
+ if(isBlissOs(context) || isProjectSakura(context)) return true;
+
+ boolean validAndroidGenericBuildProp = false;
+
+ String manufacturer = getSystemProperty("ro.ag.product.manufacturer");
+ if(manufacturer != null && manufacturer.equals("Android-Generic"))
+ validAndroidGenericBuildProp = true;
+
+ return validAndroidGenericBuildProp
+ && context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
+ && isSystemApp(context);
+ }
+
public static boolean isLauncherPermanentlyEnabled(Context context) {
if(context.getPackageName().equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
return true;
}
}
- public static int getBaseTaskbarSize(Context context) {
- return Math.round(getBaseTaskbarSizeFloat(context));
+ @VisibleForTesting
+ public static float getBaseTaskbarSize(Context context) {
+ return getBaseTaskbarSizeStart(context) + getBaseTaskbarSizeEnd(context, null);
}
- private static float getBaseTaskbarSizeFloat(Context context) {
+ public static float getBaseTaskbarSizeStart(Context context) {
SharedPreferences pref = getSharedPreferences(context);
- float baseTaskbarSize = context.getResources().getDimension(R.dimen.tb_base_taskbar_size);
+ float baseTaskbarSize = context.getResources().getDimension(R.dimen.tb_base_size_start_plus_divider);
+
+ baseTaskbarSize += pref.getBoolean(PREF_ALT_BUTTON_CONFIG, false)
+ ? context.getResources().getDimension(R.dimen.tb_base_size_collapse_button) : 0;
+
boolean navbarButtonsEnabled = false;
if(getBooleanPrefWithDefault(context, PREF_DASHBOARD))
if(navbarButtonsEnabled)
baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_navbar_buttons_margin);
- if(isSystemTrayEnabled(context))
- baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_systray_size);
+ return baseTaskbarSize;
+ }
+
+ public static float getBaseTaskbarSizeEnd(Context context, Map<Integer, Boolean> sysTrayIconStates) {
+ SharedPreferences pref = getSharedPreferences(context);
+ float baseTaskbarSize = pref.getBoolean(PREF_ALT_BUTTON_CONFIG, false)
+ ? 0 : context.getResources().getDimension(R.dimen.tb_base_size_collapse_button);
+
+ if(isSystemTrayEnabled(context)) {
+ float sysTraySize = context.getResources().getDimension(R.dimen.tb_systray_size);
+
+ if(sysTrayIconStates != null) {
+ for(Integer key : sysTrayIconStates.keySet()) {
+ if(!sysTrayIconStates.get(key)) {
+ sysTraySize -= context.getResources().getDimension(key == R.id.notification_count
+ ? R.dimen.tb_systray_icon_size_notifications : R.dimen.tb_systray_icon_size);
+ }
+ }
+ }
+
+ baseTaskbarSize += sysTraySize;
+ }
return baseTaskbarSize;
}
}
public static void showHideNavigationBar(Context context, boolean show) {
- showHideNavigationBar(context, getTaskbarDisplayID(), show, 0);
+ showHideNavigationBar(context, getTaskbarDisplayID(context), show, 0);
}
public static void showHideNavigationBar(Context context, int displayID, boolean show, int delay) {
- if(!isDesktopModeActive(context)
+ if(!isShowHideNavbarSupported()
+ || (!isDesktopModeActive(context)
&& !isBlissOs(context)
- && !hasSupportLibrary(context, 7)) {
+ && !isProjectSakura(context)
+ && !hasSupportLibrary(context, 7))) {
return;
}
- int value = show ? 0 : getSystemDimen(context, "navigation_bar_height") * -1;
+ Lazy<Integer> value = () -> show ? 0 : getSystemDimen(context, "navigation_bar_height") * -1;
if(hasWriteSecureSettingsPermission(context)) {
Runnable runnable = () -> {
try {
- setOverscan(displayID, value);
- } catch (Exception e) { /* Gracefully fail */ }
+ setOverscan(displayID, value.get());
+ } catch (Exception ignored) {}
};
if(delay == 0)
runnable.run();
else
- new Handler().postDelayed(runnable, delay);
+ newHandler().postDelayed(runnable, delay);
return;
}
intent.setPackage(BuildConfig.SUPPORT_APPLICATION_ID);
intent.putExtra("display_id", displayID);
- intent.putExtra("value", value);
+ intent.putExtra("value", value.get());
context.sendBroadcast(intent);
return;
}
- // Show or hide the system navigation bar on Bliss-x86
- if(!isBlissOs(context)) return;
+ // Show or hide the system navigation bar on Bliss-x86 and Project Sakura
+ if(!isBlissOs(context) || !isProjectSakura(context)) return;
try {
if(getCurrentApiVersion() >= 28.0f)
Settings.Secure.putInt(context.getContentResolver(), "navigation_bar_visible", show ? 1 : 0);
else
Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
- } catch (Exception e) { /* Gracefully fail */ }
+ } catch (Exception ignored) {}
+ }
+
+ public static boolean isShowHideNavbarSupported() {
+ return getCurrentApiVersion() <= 29.0f;
}
public static void initPrefs(Context context) {
}
// Customizations for BlissOS
- if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ if(isAndroidGeneric(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !pref.getBoolean(PREF_BLISS_OS_PREFS, false)) {
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(PREF_FREEFORM_HACK, true);
}
+ editor.putString(PREF_START_BUTTON_IMAGE, PREF_START_BUTTON_IMAGE_APP_LOGO);
editor.putString(PREF_RECENTS_AMOUNT, PREF_RECENTS_AMOUNT_RUNNING_APPS_ONLY);
editor.putString(PREF_REFRESH_FREQUENCY, "0");
editor.putString(PREF_MAX_NUM_OF_RECENTS, "2147483647");
editor.putString(PREF_SORT_ORDER, "true");
- editor.putString(PREF_START_BUTTON_IMAGE, "app_logo");
editor.putBoolean(PREF_BUTTON_BACK, true);
editor.putBoolean(PREF_BUTTON_HOME, true);
editor.putBoolean(PREF_BUTTON_RECENTS, true);
}
public static DisplayInfo getDisplayInfo(Context context) {
- return getDisplayInfo(context, false);
- }
-
- public static DisplayInfo getDisplayInfo(Context context, boolean fromTaskbar) {
context = getDisplayContext(context);
- int displayID = getTaskbarDisplayID();
+ int displayID = getTaskbarDisplayID(context);
DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
Display currentDisplay = null;
}
if(currentDisplay == null)
- return new DisplayInfo(0, 0, 0, 0);
+ return new DisplayInfo(0, 0, 0, 0, false);
DisplayMetrics metrics = new DisplayMetrics();
- currentDisplay.getMetrics(metrics);
+ currentDisplay.getRealMetrics(metrics);
- DisplayMetrics realMetrics = new DisplayMetrics();
- currentDisplay.getRealMetrics(realMetrics);
-
- DisplayInfo info = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi, 0);
-
- if(isChromeOs(context)) {
- SharedPreferences pref = getSharedPreferences(context);
- if(!pref.getBoolean(PREF_CHROME_OS_CONTEXT_MENU_FIX, true)) {
- info.width = realMetrics.widthPixels;
- info.height = realMetrics.heightPixels;
- }
-
- return info;
- }
-
- // Workaround for incorrect display size on devices with notches in landscape mode
- if(fromTaskbar && getDisplayOrientation(context) == Configuration.ORIENTATION_LANDSCAPE)
- return info;
-
- boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
- boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
-
- if(sameWidth && !sameHeight) {
- info.width = realMetrics.widthPixels;
- info.height = realMetrics.heightPixels - getNavbarHeight(context);
- }
-
- if(!sameWidth && sameHeight) {
- info.width = realMetrics.widthPixels - getNavbarHeight(context);
- info.height = realMetrics.heightPixels;
- }
-
- return info;
+ boolean displayDefaultsToFreeform = displayDefaultsToFreeform(context, currentDisplay);
+ return new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi, 0, displayDefaultsToFreeform);
}
- private static int getTaskbarDisplayID() {
+ private static int getTaskbarDisplayID(Context context) {
LauncherHelper helper = LauncherHelper.getInstance();
- if(helper.isOnSecondaryHomeScreen())
+ if(helper.isOnSecondaryHomeScreen(context))
return helper.getSecondaryDisplayId();
else
return Display.DEFAULT_DISPLAY;
if(pref.getBoolean(PREF_HIDE_TASKBAR, true)) {
if(!isFreeformModeEnabled(context)
|| isOverridingFreeformHack(context, false))
- return !LauncherHelper.getInstance().isOnHomeScreen();
+ return !LauncherHelper.getInstance().isOnHomeScreen(context);
else {
FreeformHackHelper helper = FreeformHackHelper.getInstance();
if(pendingAppLaunch)
public static boolean isOverridingFreeformHack(Context context, boolean checkPref) {
SharedPreferences pref = getSharedPreferences(context);
return (!checkPref || isFreeformModeEnabled(context))
- && ((isChromeOs(context) && (pref.getBoolean(PREF_CHROME_OS_CONTEXT_MENU_FIX, true)
+ && ((isChromeOs(context) && (getChromeOsContextMenuFix(context)
|| (pref.getBoolean(PREF_LAUNCHER, false) && launcherIsDefault(context))))
- || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
+ || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f)
+ || (isChromeOs(context) && getCurrentApiVersion() >= 30.0f));
}
public static boolean isPlayStoreInstalled(Context context) {
public static float getCurrentApiVersion() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
- return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
+ return Float.parseFloat(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
else
return (float) Build.VERSION.SDK_INT;
}
ApplicationInfo applicationInfo = null;
try {
applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
- } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
+ } catch (PackageManager.NameNotFoundException ignored) {}
if(applicationInfo != null) {
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
private static AlertDialog.Builder buildRecentAppsDialogStandard(Context context, Callbacks callbacks) {
+ String message = context.getString(R.string.tb_enable_recent_apps, getAppName(context))
+ + context.getString(R.string.tb_enable_recent_apps_instructions_phone);
+
return new AlertDialog.Builder(context)
.setTitle(R.string.tb_pref_header_recent_apps)
- .setMessage(R.string.tb_enable_recent_apps)
+ .setMessage(message)
.setPositiveButton(R.string.tb_action_ok, (dialog, which) -> {
try {
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
LauncherHelper helper = LauncherHelper.getInstance();
- if(helper.isOnHomeScreen() || helper.isOnSecondaryHomeScreen())
+ if(helper.isOnHomeScreen(context))
applyOpenInNewWindow(context, intent);
context.startActivity(intent);
- showToastLong(context, R.string.tb_usage_stats_message);
+ showToast(context, context.getString(R.string.tb_usage_stats_message, getAppName(context)), Toast.LENGTH_LONG);
callbacks.onFinish.run();
} catch (ActivityNotFoundException e) {
}
private static AlertDialog.Builder buildRecentAppsDialogAndroidTV(Context context, Callbacks callbacks) {
+ String message = context.getString(R.string.tb_enable_recent_apps, getAppName(context))
+ + context.getString(R.string.tb_enable_recent_apps_instructions_tv);
+
return new AlertDialog.Builder(context)
.setTitle(R.string.tb_pref_header_recent_apps)
- .setMessage(R.string.tb_enable_recent_apps_alt)
+ .setMessage(message)
.setPositiveButton(R.string.tb_action_open_settings, (dialog, which) -> {
try {
context.startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
if(signature.equals(playStoreSignature))
return true;
}
- } catch (Exception e) { /* Gracefully fail */ }
+ } catch (Exception ignored) {}
return false;
}
}
@SuppressLint("PrivateApi")
- private static String getSystemProperty(String key) {
+ public static String getSystemProperty(String key) {
+ allowReflection();
try {
Class<?> cls = Class.forName("android.os.SystemProperties");
return cls.getMethod("get", String.class).invoke(null, key).toString();
public static void applyCustomImage(Context context, String filename, ImageView view, Drawable errorDrawable) {
File file = new File(context.getFilesDir() + "/tb_images", filename);
if(file.exists()) {
- Handler handler = new Handler();
+ Handler handler = newHandler();
new Thread(() -> {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
handler.post(() -> {
public static String getDefaultStartButtonImage(Context context) {
SharedPreferences pref = getSharedPreferences(context);
return pref.getBoolean(PREF_APP_DRAWER_ICON, false)
- ? "app_logo"
- : "default";
+ ? PREF_START_BUTTON_IMAGE_APP_LOGO
+ : PREF_START_BUTTON_IMAGE_DEFAULT;
}
private static boolean shouldLaunchTouchAbsorber(Context context) {
}
public static boolean isDesktopIconsEnabled(Context context) {
- return !canBootToFreeform(context, false) && !shouldLaunchTouchAbsorber(context);
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1
+ && !canBootToFreeform(context, false)
+ && !shouldLaunchTouchAbsorber(context);
}
public static boolean isSystemTrayEnabled(Context context) {
&& !TaskbarPosition.isVertical(context);
}
- @SuppressWarnings("deprecation")
public static boolean isLibrary(Context context) {
return !context.getPackageName().equals(BuildConfig.APPLICATION_ID);
}
return false;
}
- private static boolean hasAndroidTVSettings(Context context) {
+ public static boolean hasAndroidTVSettings(Context context) {
return getInstalledPackage(context, "com.android.tv.settings") != null
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}
}
public static boolean isDesktopModeSupported(Context context) {
- if(isLauncherPermanentlyEnabled(context) || isChromeOs(context))
+ if(isLauncherPermanentlyEnabled(context) || isChromeOs(context) || isAndroidTV(context))
return false;
return Build.VERSION.SDK_INT > Build.VERSION_CODES.P
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
}
- public static boolean isDesktopModeActive(Context context) {
+ public static boolean isDesktopModePrefEnabled(Context context) {
if(!isDesktopModeSupported(context) || !hasFreeformSupport(context))
return false;
desktopModePrefEnabled = false;
}
- return desktopModePrefEnabled && getExternalDisplayID(context) != Display.DEFAULT_DISPLAY;
+ return desktopModePrefEnabled;
+ }
+
+ public static boolean isDesktopModeActive(Context context) {
+ return isDesktopModePrefEnabled(context) && getExternalDisplayID(context) != Display.DEFAULT_DISPLAY;
}
private static Display getExternalDisplay(Context context) {
public static DisplayInfo getExternalDisplayInfo(Context context) {
Display display = getExternalDisplay(context);
if(display == null)
- return new DisplayInfo(0, 0, 0, 0);
+ return new DisplayInfo(0, 0, 0, 0, false);
DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
defaultDensity = 0;
}
- return new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi, defaultDensity);
+ boolean displayDefaultsToFreeform = displayDefaultsToFreeform(context, display);
+ return new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi, defaultDensity, displayDefaultsToFreeform);
}
@SuppressLint("PrivateApi")
private static Object getWindowManagerService() throws Exception {
+ allowReflection();
return Class.forName("android.view.WindowManagerGlobal")
.getMethod("getWindowManagerService")
.invoke(null);
// From android.os.UserHandle
final int USER_CURRENT_OR_SELF = -3;
+ allowReflection();
if(value.equals("reset")) {
Class.forName("android.view.IWindowManager")
.getMethod("clearForcedDisplayDensityForUser", int.class, int.class)
@SuppressLint("PrivateApi")
private static void setOverscan(int displayID, int value) throws Exception {
+ allowReflection();
Class.forName("android.view.IWindowManager")
.getMethod("setOverscan", int.class, int.class, int.class, int.class, int.class)
.invoke(getWindowManagerService(), displayID, 0, 0, 0, value);
@SuppressLint("PrivateApi")
private static Integer getDefaultDensity(int displayID) throws Exception {
+ allowReflection();
return (Integer) Class.forName("android.view.IWindowManager")
.getMethod("getInitialDisplayDensity", int.class)
.invoke(getWindowManagerService(), displayID);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
- @SuppressWarnings("rawtypes")
- public static void setComponentEnabled(Context context, Class clazz, boolean enabled) {
+ public static void setComponentEnabled(Context context, Class<?> clazz, boolean enabled) {
ComponentName component = new ComponentName(context, clazz);
context.getPackageManager().setComponentEnabledSetting(component,
enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !isChromeOs(context) && !isLibrary(context);
}
- @SuppressWarnings("rawtypes")
- public static Intent getThemedIntent(Context context, Class clazz) {
- Class newClass;
+ public static Intent getThemedIntent(Context context, Class<?> clazz) {
+ Class<?> newClass;
if(!isDarkTheme(context))
newClass = clazz;
public static boolean getBooleanPrefWithDefault(Context context, String key) {
context = getDisplayContext(context);
- int resId = getDefaultPrefResID(context, key, R.bool.class);
+ int resId = getDefaultPrefResID(key, R.bool.class);
SharedPreferences pref = getSharedPreferences(context);
- return pref.getBoolean(key, context.getResources().getBoolean(resId));
+ boolean def = pref.getBoolean(key + "_default", context.getResources().getBoolean(resId));
+ return pref.getBoolean(key, def);
}
public static int getIntPrefWithDefault(Context context, String key) {
context = getDisplayContext(context);
- int resId = getDefaultPrefResID(context, key, R.integer.class);
+ int resId = getDefaultPrefResID(key, R.integer.class);
SharedPreferences pref = getSharedPreferences(context);
- return pref.getInt(key, context.getResources().getInteger(resId));
+ int def = pref.getInt(key + "_default", context.getResources().getInteger(resId));
+ return pref.getInt(key, def);
}
- public static void sanitizePrefs(Context context, String... keys) {
- SharedPreferences pref = getSharedPreferences(context);
-
- for(String key : keys) {
- if(!pref.getBoolean(key + "_is_modified", false))
- pref.edit().remove(key).apply();
- }
- }
-
- @SuppressWarnings("rawtypes")
- private static int getDefaultPrefResID(Context context, String key, Class rClass) {
+ private static int getDefaultPrefResID(String key, Class<?> rClass) {
int resId;
try {
return 0;
}
- sanitizePrefs(context, key);
return resId;
}
}
}
}
+
+ public static boolean isConsumerBuild(Context context) {
+ return !BuildConfig.DEBUG
+ && !context.getPackageName().equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
+ && !isLibrary(context);
+ }
+
+ public static void allowReflection() {
+ GlobalHelper helper = GlobalHelper.getInstance();
+ if(helper.isReflectionAllowed()) return;
+
+ try {
+ Method forName = Class.class.getDeclaredMethod("forName", String.class);
+ Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
+
+ Class<?> vmRuntimeClass = (Class<?>) forName.invoke(null, "dalvik.system.VMRuntime");
+ Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
+ Method setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class});
+
+ Object vmRuntime = getRuntime.invoke(null);
+ setHiddenApiExemptions.invoke(vmRuntime, new Object[]{new String[]{"L"}});
+ } catch (Throwable ignored) {}
+
+ helper.setReflectionAllowed(true);
+ }
+
+ public static Handler newHandler() {
+ return new Handler(Looper.getMainLooper());
+ }
+
+ public static CharSequence getAppName(Context context) {
+ return context.getApplicationInfo().loadLabel(context.getPackageManager());
+ }
+
+ public static boolean getChromeOsContextMenuFix(Context context) {
+ if(getCurrentApiVersion() >= 30.0f) return false;
+
+ SharedPreferences pref = getSharedPreferences(context);
+ return pref.getBoolean(PREF_CHROME_OS_CONTEXT_MENU_FIX, true);
+ }
+
+ private static boolean displayDefaultsToFreeform(Context context, Display display) {
+ Context dispContext = context.createDisplayContext(display);
+ String configString = dispContext.getResources().getConfiguration().toString();
+ return configString.contains("mDisplayWindowingMode=freeform");
+ }
}