1 /* Copyright 2016 Braden Farmer
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 package com.farmerbb.taskbar.util;
18 import android.annotation.SuppressLint;
19 import android.annotation.TargetApi;
20 import android.app.ActivityManager;
21 import android.app.ActivityOptions;
22 import android.app.AlertDialog;
23 import android.app.AppOpsManager;
24 import android.app.Service;
25 import android.app.admin.DevicePolicyManager;
26 import android.content.ActivityNotFoundException;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.SharedPreferences;
31 import android.content.pm.ActivityInfo;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.LauncherActivityInfo;
34 import android.content.pm.LauncherApps;
35 import android.content.pm.PackageInfo;
36 import android.content.pm.PackageManager;
37 import android.content.pm.ResolveInfo;
38 import android.content.pm.ShortcutInfo;
39 import android.content.pm.ShortcutManager;
40 import android.content.pm.Signature;
41 import android.content.res.Configuration;
42 import android.graphics.Color;
43 import android.graphics.Rect;
44 import android.net.Uri;
45 import android.os.Build;
46 import android.os.Bundle;
47 import android.os.Handler;
48 import android.os.Process;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.provider.Settings;
52 import android.support.v4.content.LocalBroadcastManager;
53 import android.support.v7.view.ContextThemeWrapper;
54 import android.util.DisplayMetrics;
55 import android.view.Display;
56 import android.view.Surface;
57 import android.view.WindowManager;
58 import android.widget.Toast;
60 import com.farmerbb.taskbar.BuildConfig;
61 import com.farmerbb.taskbar.R;
62 import com.farmerbb.taskbar.activity.ContextMenuActivity;
63 import com.farmerbb.taskbar.activity.DummyActivity;
64 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
65 import com.farmerbb.taskbar.activity.ShortcutActivity;
66 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
67 import com.farmerbb.taskbar.activity.TouchAbsorberActivity;
68 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
69 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
70 import com.farmerbb.taskbar.service.DashboardService;
71 import com.farmerbb.taskbar.service.NotificationService;
72 import com.farmerbb.taskbar.service.PowerMenuService;
73 import com.farmerbb.taskbar.service.StartMenuService;
74 import com.farmerbb.taskbar.service.TaskbarService;
76 import java.lang.reflect.Method;
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.List;
85 private static Integer cachedRotation;
87 private static final int MAXIMIZED = 0;
88 private static final int LEFT = -1;
89 private static final int RIGHT = 1;
91 public static final int HIDDEN = 0;
92 public static final int TOP_APPS = 1;
94 // From android.app.ActivityManager.StackId
95 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
96 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
98 // From android.app.WindowConfiguration
99 private static final int WINDOWING_MODE_FULLSCREEN = 1;
100 private static final int WINDOWING_MODE_FREEFORM = 5;
102 public static SharedPreferences getSharedPreferences(Context context) {
103 return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
106 public static void showPermissionDialog(Context context) {
107 showPermissionDialog(context, null, null);
110 @TargetApi(Build.VERSION_CODES.M)
111 public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
112 Runnable finalOnFinish = onFinish == null
116 Runnable finalOnError = onError == null
117 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
120 AlertDialog.Builder builder = new AlertDialog.Builder(context);
121 builder.setTitle(R.string.permission_dialog_title)
122 .setMessage(R.string.permission_dialog_message)
123 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
125 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
126 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
129 } catch (ActivityNotFoundException e) {
134 AlertDialog dialog = builder.create();
136 dialog.setCancelable(false);
141 public static AlertDialog showErrorDialog(Context context, String appopCmd) {
142 return showErrorDialog(context, appopCmd, null);
145 private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
146 Runnable finalOnFinish = onFinish == null
150 AlertDialog.Builder builder = new AlertDialog.Builder(context);
151 builder.setTitle(R.string.error_dialog_title)
152 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
153 .setPositiveButton(R.string.action_ok, (dialog, which) -> finalOnFinish.run());
155 AlertDialog dialog = builder.create();
157 dialog.setCancelable(false);
162 public static void lockDevice(Context context) {
163 ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
164 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
165 PackageManager.DONT_KILL_APP);
167 DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
168 if(mDevicePolicyManager.isAdminActive(component))
169 mDevicePolicyManager.lockNow();
171 launchApp(context, () -> {
172 Intent intent = new Intent(context, DummyActivity.class);
173 intent.putExtra("device_admin", true);
174 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
177 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
178 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
183 public static void sendAccessibilityAction(Context context, int action) {
184 ComponentName component = new ComponentName(context, PowerMenuService.class);
185 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
186 PackageManager.DONT_KILL_APP);
188 if(isAccessibilityServiceEnabled(context)) {
189 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
190 intent.putExtra("action", action);
191 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
193 launchApp(context, () -> {
194 Intent intent = new Intent(context, DummyActivity.class);
195 intent.putExtra("accessibility", true);
196 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
199 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
200 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
205 public static boolean isAccessibilityServiceEnabled(Context context) {
206 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
207 ComponentName component = new ComponentName(context, PowerMenuService.class);
209 return accessibilityServices != null
210 && (accessibilityServices.contains(component.flattenToString())
211 || accessibilityServices.contains(component.flattenToShortString()));
214 public static void showToast(Context context, int message) {
215 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
218 public static void showToastLong(Context context, int message) {
219 showToast(context, context.getString(message), Toast.LENGTH_LONG);
222 public static void showToast(Context context, String message, int length) {
225 ToastInterface toast = DependencyUtils.createToast(context, message, length);
228 ToastHelper.getInstance().setLastToast(toast);
231 public static void cancelToast() {
232 ToastInterface toast = ToastHelper.getInstance().getLastToast();
233 if(toast != null) toast.cancel();
236 public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
247 public static void launchApp(final Context context,
248 final String packageName,
249 final String componentName,
250 final long userId, final String windowSize,
251 final boolean launchedFromTaskbar,
252 final boolean openInNewWindow) {
263 private static void launchApp(final Context context,
264 final String packageName,
265 final String componentName,
266 final long userId, final String windowSize,
267 final boolean launchedFromTaskbar,
268 final boolean openInNewWindow,
269 final ShortcutInfo shortcut) {
270 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, packageName, componentName, userId,
271 windowSize, openInNewWindow, shortcut));
274 public static void launchApp(Context context, Runnable runnable) {
275 launchApp(context, true, runnable);
278 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
279 SharedPreferences pref = getSharedPreferences(context);
280 FreeformHackHelper helper = FreeformHackHelper.getInstance();
282 boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
283 && helper.isInFreeformWorkspace()
284 && MenuHelper.getInstance().isContextMenuOpen();
286 boolean noAnimation = pref.getBoolean("disable_animations", false);
288 if(hasFreeformSupport(context)
289 && pref.getBoolean("freeform_hack", false)
290 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
291 new Handler().postDelayed(() -> {
292 startFreeformHack(context, true);
294 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
295 }, launchedFromTaskbar ? 0 : 100);
297 new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
300 public static void startFreeformHack(Context context) {
301 startFreeformHack(context, false);
304 @TargetApi(Build.VERSION_CODES.N)
305 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
306 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
307 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
308 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
309 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
312 freeformHackIntent.putExtra("check_multiwindow", true);
314 if(canDrawOverlays(context))
315 startActivityLowerRight(context, freeformHackIntent);
318 public static void stopFreeformHack(Context context) {
319 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
321 if(isOverridingFreeformHack(context)) {
322 FreeformHackHelper helper = FreeformHackHelper.getInstance();
323 helper.setFreeformHackActive(false);
324 helper.setInFreeformWorkspace(false);
328 @TargetApi(Build.VERSION_CODES.N)
329 private static void continueLaunchingApp(Context context,
331 String componentName,
334 boolean openInNewWindow,
335 ShortcutInfo shortcut) {
336 SharedPreferences pref = getSharedPreferences(context);
337 Intent intent = new Intent();
338 intent.setComponent(ComponentName.unflattenFromString(componentName));
339 intent.setAction(Intent.ACTION_MAIN);
340 intent.addCategory(Intent.CATEGORY_LAUNCHER);
341 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
342 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
344 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
345 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
346 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
348 if(pref.getBoolean("disable_animations", false))
349 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
351 if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
352 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
354 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
355 if(activityInfo != null) {
356 switch(activityInfo.launchMode) {
357 case ActivityInfo.LAUNCH_SINGLE_TASK:
358 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
359 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
365 ApplicationType type = getApplicationType(context, packageName);
367 if(windowSize == null)
368 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
370 Bundle bundle = getActivityOptionsBundle(context, type, windowSize);
372 prepareToStartActivity(context, () -> {
373 if(shortcut == null) {
374 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
375 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
377 context.startActivity(intent, bundle);
378 } catch (ActivityNotFoundException e) {
379 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
380 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
382 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
384 launchShortcut(context, shortcut, bundle);
387 if(shouldCollapse(context, true))
388 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
390 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
393 @SuppressWarnings("deprecation")
394 @TargetApi(Build.VERSION_CODES.N)
395 private static Bundle launchMode1(Context context, ApplicationType type) {
396 DisplayInfo display = getDisplayInfo(context);
398 int width1 = display.width / 8;
399 int width2 = display.width - width1;
400 int height1 = display.height / 8;
401 int height2 = display.height - height1;
403 return getActivityOptions(context, type).setLaunchBounds(new Rect(
411 @SuppressWarnings("deprecation")
412 @TargetApi(Build.VERSION_CODES.N)
413 private static Bundle launchMode2(Context context, int launchType, ApplicationType type) {
414 DisplayInfo display = getDisplayInfo(context);
416 int statusBarHeight = getStatusBarHeight(context);
417 String position = getTaskbarPosition(context);
419 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
420 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
423 int top = statusBarHeight;
424 int right = display.width;
425 int bottom = display.height;
427 int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
429 : context.getResources().getDimensionPixelSize(R.dimen.icon_size);
431 if(position.contains("vertical_left"))
432 left = left + iconSize;
433 else if(position.contains("vertical_right"))
434 right = right - iconSize;
435 else if(position.contains("bottom"))
436 bottom = bottom - iconSize;
438 top = top + iconSize;
440 if(launchType == RIGHT && isLandscape)
441 left = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
442 else if(launchType == RIGHT && isPortrait)
443 top = (bottom / 2) + ((iconSize / 2)
444 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
445 else if(launchType == LEFT && isLandscape)
446 right = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
447 else if(launchType == LEFT && isPortrait)
448 bottom = (bottom / 2) + ((iconSize / 2)
449 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
451 return getActivityOptions(context, type)
452 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
455 @SuppressWarnings("deprecation")
456 @TargetApi(Build.VERSION_CODES.N)
457 private static Bundle launchMode3(Context context, ApplicationType type) {
458 DisplayInfo display = getDisplayInfo(context);
460 int width1 = display.width / 2;
461 int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
462 int height1 = display.height / 2;
463 int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
465 return getActivityOptions(context, type).setLaunchBounds(new Rect(
473 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
474 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
475 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
478 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
479 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
482 @TargetApi(Build.VERSION_CODES.N_MR1)
483 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
484 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
486 if(launcherApps.hasShortcutHostPermission()) {
488 launcherApps.startShortcut(shortcut, null, bundle);
489 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
493 private static void prepareToStartActivity(Context context, Runnable runnable) {
494 boolean shouldLaunchTouchAbsorber =
495 !FreeformHackHelper.getInstance().isTouchAbsorberActive()
496 && isOverridingFreeformHack(context)
497 && !isChromeOs(context);
499 if(!shouldLaunchTouchAbsorber) {
504 startTouchAbsorberActivity(context);
505 new Handler().postDelayed(runnable, 100);
508 public static void startActivityMaximized(Context context, Intent intent) {
509 Bundle bundle = launchMode2(context, MAXIMIZED, ApplicationType.CONTEXT_MENU);
510 prepareToStartActivity(context, () -> context.startActivity(intent, bundle));
513 @TargetApi(Build.VERSION_CODES.N)
514 public static void startActivityLowerRight(Context context, Intent intent) {
515 DisplayInfo display = getDisplayInfo(context);
517 context.startActivity(intent,
518 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
519 .setLaunchBounds(new Rect(
525 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
528 @TargetApi(Build.VERSION_CODES.N)
529 public static void startTouchAbsorberActivity(Context context) {
530 String position = getTaskbarPosition(context);
531 DisplayInfo display = getDisplayInfo(context);
535 int right = display.width;
536 int bottom = display.height;
538 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
540 if(position.contains("vertical_left"))
542 else if(position.contains("vertical_right"))
543 left = right - iconSize;
544 else if(position.contains("bottom"))
545 top = bottom - iconSize;
549 Intent intent = new Intent(context, TouchAbsorberActivity.class);
550 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
551 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
554 context.startActivity(intent,
555 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
556 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle());
557 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
560 public static void startContextMenuActivity(Context context, Bundle args) {
561 SharedPreferences pref = getSharedPreferences(context);
562 Intent intent = null;
564 switch(pref.getString("theme", "light")) {
566 intent = new Intent(context, ContextMenuActivity.class);
569 intent = new Intent(context, ContextMenuActivityDark.class);
574 intent.putExtra("args", args);
575 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
578 if(hasFreeformSupport(context) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
579 DisplayInfo display = getDisplayInfo(context);
581 if(intent != null && hasBrokenSetLaunchBoundsApi())
582 intent.putExtra("context_menu_fix", true);
584 context.startActivity(intent,
585 getActivityOptions(context, ApplicationType.CONTEXT_MENU)
587 new Rect(0, 0, display.width, display.height)
590 context.startActivity(intent);
593 public static void checkForUpdates(Context context) {
595 if(isPlayStoreRelease(context)) {
596 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
597 && !isPlayStoreInstalled(context))
598 url = "https://github.com/farmerbb/Taskbar/releases";
600 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
602 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.APPLICATION_ID;
604 Intent intent = new Intent(Intent.ACTION_VIEW);
605 intent.setData(Uri.parse(url));
606 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
609 context.startActivity(intent);
610 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
613 public static boolean launcherIsDefault(Context context) {
614 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
615 homeIntent.addCategory(Intent.CATEGORY_HOME);
616 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
618 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
621 public static void setCachedRotation(int cachedRotation) {
622 U.cachedRotation = cachedRotation;
625 public static String getTaskbarPosition(Context context) {
626 SharedPreferences pref = getSharedPreferences(context);
627 String position = pref.getString("position", "bottom_left");
629 if(pref.getBoolean("anchor", false)) {
630 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
631 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
636 case Surface.ROTATION_0:
637 return "bottom_left";
638 case Surface.ROTATION_90:
639 return "bottom_vertical_right";
640 case Surface.ROTATION_180:
642 case Surface.ROTATION_270:
643 return "top_vertical_left";
646 case "bottom_vertical_left":
648 case Surface.ROTATION_0:
649 return "bottom_vertical_left";
650 case Surface.ROTATION_90:
651 return "bottom_right";
652 case Surface.ROTATION_180:
653 return "top_vertical_right";
654 case Surface.ROTATION_270:
660 case Surface.ROTATION_0:
661 return "bottom_right";
662 case Surface.ROTATION_90:
663 return "top_vertical_right";
664 case Surface.ROTATION_180:
666 case Surface.ROTATION_270:
667 return "bottom_vertical_left";
670 case "bottom_vertical_right":
672 case Surface.ROTATION_0:
673 return "bottom_vertical_right";
674 case Surface.ROTATION_90:
676 case Surface.ROTATION_180:
677 return "top_vertical_left";
678 case Surface.ROTATION_270:
679 return "bottom_left";
684 case Surface.ROTATION_0:
686 case Surface.ROTATION_90:
687 return "bottom_vertical_left";
688 case Surface.ROTATION_180:
689 return "bottom_right";
690 case Surface.ROTATION_270:
691 return "top_vertical_right";
694 case "top_vertical_left":
696 case Surface.ROTATION_0:
697 return "top_vertical_left";
698 case Surface.ROTATION_90:
699 return "bottom_left";
700 case Surface.ROTATION_180:
701 return "bottom_vertical_right";
702 case Surface.ROTATION_270:
708 case Surface.ROTATION_0:
710 case Surface.ROTATION_90:
711 return "top_vertical_left";
712 case Surface.ROTATION_180:
713 return "bottom_left";
714 case Surface.ROTATION_270:
715 return "bottom_vertical_right";
718 case "top_vertical_right":
720 case Surface.ROTATION_0:
721 return "top_vertical_right";
722 case Surface.ROTATION_90:
724 case Surface.ROTATION_180:
725 return "bottom_vertical_left";
726 case Surface.ROTATION_270:
727 return "bottom_right";
736 private static int getMaxNumOfColumns(Context context) {
737 SharedPreferences pref = getSharedPreferences(context);
738 DisplayInfo display = getDisplayInfo(context);
739 float density = display.density / 160;
740 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / density;
741 int numOfColumns = 0;
743 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
744 ? (display.height - getStatusBarHeight(context)) / density
745 : display.width / density;
747 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / density;
749 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
751 while(baseTaskbarSize + iconSize < maxScreenSize
752 && numOfColumns < userMaxNumOfColumns) {
753 baseTaskbarSize = baseTaskbarSize + iconSize;
760 public static int getMaxNumOfEntries(Context context) {
761 SharedPreferences pref = getSharedPreferences(context);
762 return pref.getBoolean("disable_scrolling_list", false)
763 ? getMaxNumOfColumns(context)
764 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
767 public static int getStatusBarHeight(Context context) {
768 return getSystemDimen(context, "status_bar_height");
771 private static int getNavbarHeight(Context context) {
772 return getSystemDimen(context, "navigation_bar_height");
775 private static int getSystemDimen(Context context, String id) {
777 int resourceId = context.getResources().getIdentifier(id, "dimen", "android");
779 value = context.getResources().getDimensionPixelSize(resourceId);
784 public static void refreshPinnedIcons(Context context) {
785 IconCache.getInstance(context).clearCache();
787 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
788 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
789 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
790 PackageManager pm = context.getPackageManager();
794 for(AppEntry entry : pinnedAppsList) {
795 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
796 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
798 final List<UserHandle> userHandles = userManager.getUserProfiles();
799 LauncherActivityInfo appInfo = null;
801 for(UserHandle handle : userHandles) {
802 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
803 if(!list.isEmpty()) {
804 // Google App workaround
805 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
806 appInfo = list.get(0);
808 boolean added = false;
809 for(LauncherActivityInfo info : list) {
810 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
816 if(!added) appInfo = list.get(0);
823 if(appInfo != null) {
824 AppEntry newEntry = new AppEntry(
825 entry.getPackageName(),
826 entry.getComponentName(),
828 IconCache.getInstance(context).getIcon(context, pm, appInfo),
831 newEntry.setUserId(entry.getUserId(context));
832 pba.addPinnedApp(context, newEntry);
836 for(AppEntry entry : blockedAppsList) {
837 pba.addBlockedApp(context, entry);
841 public static Intent getShortcutIntent(Context context) {
842 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
843 shortcutIntent.setAction(Intent.ACTION_MAIN);
844 shortcutIntent.putExtra("is_launching_shortcut", true);
846 Intent intent = new Intent();
847 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
848 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
849 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
854 public static Intent getStartStopIntent(Context context) {
855 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
856 shortcutIntent.setAction(Intent.ACTION_MAIN);
857 shortcutIntent.putExtra("is_launching_shortcut", true);
859 Intent intent = new Intent();
860 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
861 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
862 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
867 public static boolean canEnableFreeform() {
868 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
871 @TargetApi(Build.VERSION_CODES.N)
872 public static boolean hasFreeformSupport(Context context) {
873 return canEnableFreeform()
874 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
875 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
876 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
877 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
880 public static boolean isSamsungDevice() {
881 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
884 public static boolean isNvidiaDevice() {
885 return Build.MANUFACTURER.equalsIgnoreCase("NVIDIA");
888 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
889 return isServiceRunning(context, cls.getName());
892 private static boolean isServiceRunning(Context context, String className) {
893 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
894 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
895 if(className.equals(service.service.getClassName()))
902 public static int getBackgroundTint(Context context) {
903 SharedPreferences pref = getSharedPreferences(context);
905 // Import old background tint preference
906 if(pref.contains("show_background")) {
907 SharedPreferences.Editor editor = pref.edit();
909 if(!pref.getBoolean("show_background", true))
910 editor.putInt("background_tint", Color.TRANSPARENT).apply();
912 editor.remove("show_background");
916 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
919 public static int getAccentColor(Context context) {
920 SharedPreferences pref = getSharedPreferences(context);
921 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
924 @TargetApi(Build.VERSION_CODES.M)
925 public static boolean canDrawOverlays(Context context) {
926 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
929 public static boolean isGame(Context context, String packageName) {
930 SharedPreferences pref = getSharedPreferences(context);
931 if(pref.getBoolean("launch_games_fullscreen", true)) {
932 PackageManager pm = context.getPackageManager();
935 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
936 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
937 } catch (PackageManager.NameNotFoundException e) {
944 @TargetApi(Build.VERSION_CODES.N)
945 public static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType) {
946 ActivityOptions options = ActivityOptions.makeBasic();
949 switch(applicationType) {
951 if(FreeformHackHelper.getInstance().isFreeformHackActive())
952 stackId = getFreeformWindowModeId();
954 stackId = getFullscreenWindowModeId();
957 stackId = getFullscreenWindowModeId();
960 stackId = getFreeformWindowModeId();
963 if(hasBrokenSetLaunchBoundsApi()
964 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
965 stackId = getFullscreenWindowModeId();
970 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
971 method.invoke(options, stackId);
972 } catch (Exception e) { /* Gracefully fail */ }
977 private static int getFullscreenWindowModeId() {
978 if(getCurrentApiVersion() >= 28.0f)
979 return WINDOWING_MODE_FULLSCREEN;
981 return FULLSCREEN_WORKSPACE_STACK_ID;
984 private static int getFreeformWindowModeId() {
985 if(getCurrentApiVersion() >= 28.0f)
986 return WINDOWING_MODE_FREEFORM;
988 return FREEFORM_WORKSPACE_STACK_ID;
991 private static String getWindowingModeMethodName() {
992 if(getCurrentApiVersion() >= 28.0f)
993 return "setLaunchWindowingMode";
995 return "setLaunchStackId";
998 public static Bundle getActivityOptionsBundle(Context context, ApplicationType type) {
999 SharedPreferences pref = getSharedPreferences(context);
1001 return getActivityOptionsBundle(context, type, pref.getString("window_size", "standard"));
1004 private static Bundle getActivityOptionsBundle(Context context, ApplicationType type, String windowSize) {
1005 SharedPreferences pref = getSharedPreferences(context);
1006 if(!canEnableFreeform() || !pref.getBoolean("freeform_hack", false))
1009 switch(windowSize) {
1011 return launchMode1(context, type);
1013 return launchMode2(context, MAXIMIZED, type);
1015 return launchMode2(context, LEFT, type);
1017 return launchMode2(context, RIGHT, type);
1019 return launchMode3(context, type);
1022 return getActivityOptions(context, type).toBundle();
1025 private static ApplicationType getApplicationType(Context context, String packageName) {
1026 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1029 public static boolean isSystemApp(Context context) {
1031 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1032 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1033 return (info.flags & mask) != 0;
1034 } catch (PackageManager.NameNotFoundException e) {
1039 public static boolean isChromeOs(Context context) {
1040 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1043 public static boolean isBlissOs(Context context) {
1044 boolean validBlissOsBuildProp = false;
1046 String blissVersion = DependencyUtils.getSystemProperty("ro.bliss.version");
1047 if(blissVersion != null && !blissVersion.isEmpty())
1048 validBlissOsBuildProp = true;
1050 String buildUser = DependencyUtils.getSystemProperty("ro.build.user");
1051 if(buildUser != null && buildUser.equals("electrikjesus"))
1052 validBlissOsBuildProp = true;
1054 return validBlissOsBuildProp
1055 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1056 && isSystemApp(context);
1059 public static boolean isLauncherPermanentlyEnabled(Context context) {
1060 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1063 PackageManager pm = context.getPackageManager();
1065 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1066 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
1067 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1068 && isSystemApp(context);
1069 } catch (PackageManager.NameNotFoundException e) {
1074 public static int getBaseTaskbarSize(Context context) {
1075 return Math.round(getBaseTaskbarSizeFloat(context));
1078 private static float getBaseTaskbarSizeFloat(Context context) {
1079 SharedPreferences pref = getSharedPreferences(context);
1080 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
1081 boolean navbarButtonsEnabled = false;
1083 if(pref.getBoolean("dashboard", false))
1084 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
1086 if(pref.getBoolean("button_back", false)) {
1087 navbarButtonsEnabled = true;
1088 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1091 if(pref.getBoolean("button_home", false)) {
1092 navbarButtonsEnabled = true;
1093 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1096 if(pref.getBoolean("button_recents", false)) {
1097 navbarButtonsEnabled = true;
1098 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1101 if(navbarButtonsEnabled)
1102 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
1104 return baseTaskbarSize;
1107 private static void startTaskbarService(Context context, boolean fullRestart) {
1108 context.startService(new Intent(context, TaskbarService.class));
1109 context.startService(new Intent(context, StartMenuService.class));
1110 context.startService(new Intent(context, DashboardService.class));
1111 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1114 private static void stopTaskbarService(Context context, boolean fullRestart) {
1115 context.stopService(new Intent(context, TaskbarService.class));
1116 context.stopService(new Intent(context, StartMenuService.class));
1117 context.stopService(new Intent(context, DashboardService.class));
1118 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1121 public static void restartTaskbar(Context context) {
1122 SharedPreferences pref = getSharedPreferences(context);
1123 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1125 .putBoolean("is_restarting", true)
1126 .putBoolean("skip_auto_hide_navbar", true)
1129 stopTaskbarService(context, true);
1130 startTaskbarService(context, true);
1131 } else if(isServiceRunning(context, StartMenuService.class)) {
1132 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1134 stopTaskbarService(context, false);
1135 startTaskbarService(context, false);
1139 public static void restartNotificationService(Context context) {
1140 if(isServiceRunning(context, NotificationService.class)) {
1141 SharedPreferences pref = getSharedPreferences(context);
1142 pref.edit().putBoolean("is_restarting", true).apply();
1144 Intent intent = new Intent(context, NotificationService.class);
1145 context.stopService(intent);
1146 context.startService(intent);
1150 public static void showHideNavigationBar(Context context, boolean show) {
1151 // Show or hide the system navigation bar on Bliss-x86
1153 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1154 } catch (Exception e) { /* Gracefully fail */ }
1157 public static void initPrefs(Context context) {
1158 // On smaller-screened devices, set "Grid" as the default start menu layout
1159 SharedPreferences pref = getSharedPreferences(context);
1160 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 720
1161 && pref.getString("start_menu_layout", "null").equals("null")) {
1162 pref.edit().putString("start_menu_layout", "grid").apply();
1165 // Enable freeform hack automatically on supported devices
1166 if(canEnableFreeform()) {
1167 if(!pref.getBoolean("freeform_hack_override", false)) {
1169 .putBoolean("freeform_hack", hasFreeformSupport(context) && !isSamsungDevice())
1170 .putBoolean("save_window_sizes", false)
1171 .putBoolean("freeform_hack_override", true)
1173 } else if(!hasFreeformSupport(context)) {
1174 pref.edit().putBoolean("freeform_hack", false).apply();
1176 stopFreeformHack(context);
1179 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1180 || pref.getBoolean("show_freeform_disabled_message", false);
1183 .putBoolean("freeform_hack", false)
1184 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1187 SavedWindowSizes.getInstance(context).clear(context);
1188 stopFreeformHack(context);
1191 // Customizations for BlissOS
1192 if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1193 && !pref.getBoolean("bliss_os_prefs", false)) {
1194 SharedPreferences.Editor editor = pref.edit();
1196 if(hasFreeformSupport(context)) {
1197 editor.putBoolean("freeform_hack", true);
1200 editor.putString("recents_amount", "running_apps_only");
1201 editor.putString("refresh_frequency", "0");
1202 editor.putString("max_num_of_recents", "2147483647");
1203 editor.putString("sort_order", "true");
1204 editor.putBoolean("full_length", true);
1205 editor.putBoolean("dashboard", true);
1206 editor.putBoolean("app_drawer_icon", true);
1207 editor.putBoolean("button_back", true);
1208 editor.putBoolean("button_home", true);
1209 editor.putBoolean("button_recents", true);
1210 editor.putBoolean("auto_hide_navbar", true);
1211 editor.putBoolean("bliss_os_prefs", true);
1214 Settings.Secure.putString(context.getContentResolver(),
1215 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
1216 new ComponentName(context, PowerMenuService.class).flattenToString());
1217 } catch (Exception e) { /* Gracefully fail */ }
1222 // Customizations for Android-x86 devices (non-Bliss)
1223 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1224 && isSystemApp(context)
1225 && !pref.getBoolean("android_x86_prefs", false)) {
1227 .putString("recents_amount", "running_apps_only")
1228 .putString("refresh_frequency", "0")
1229 .putString("max_num_of_recents", "2147483647")
1230 .putString("sort_order", "true")
1231 .putBoolean("full_length", true)
1232 .putBoolean("dashboard", true)
1233 .putBoolean("android_x86_prefs", true)
1238 public static DisplayInfo getDisplayInfo(Context context) {
1239 context = context.getApplicationContext();
1241 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1242 Display disp = wm.getDefaultDisplay();
1244 DisplayMetrics metrics = new DisplayMetrics();
1245 disp.getMetrics(metrics);
1247 DisplayMetrics realMetrics = new DisplayMetrics();
1248 disp.getRealMetrics(realMetrics);
1250 DisplayInfo display = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
1252 if(isChromeOs(context)) {
1253 SharedPreferences pref = getSharedPreferences(context);
1254 if(!pref.getBoolean("chrome_os_context_menu_fix", true)) {
1255 display.width = realMetrics.widthPixels;
1256 display.height = realMetrics.heightPixels;
1262 boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
1263 boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
1265 if(sameWidth && !sameHeight) {
1266 display.width = realMetrics.widthPixels;
1267 display.height = realMetrics.heightPixels - getNavbarHeight(context);
1270 if(!sameWidth && sameHeight) {
1271 display.width = realMetrics.widthPixels - getNavbarHeight(context);
1272 display.height = realMetrics.heightPixels;
1278 public static void pinAppShortcut(Context context) {
1279 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1280 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1282 if(mShortcutManager.isRequestPinShortcutSupported()) {
1283 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1285 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1287 showToastLong(context, R.string.pin_shortcut_not_supported);
1289 Intent intent = getShortcutIntent(context);
1290 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1291 intent.putExtra("duplicate", false);
1293 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1294 homeIntent.addCategory(Intent.CATEGORY_HOME);
1295 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1297 intent.setPackage(defaultLauncher.activityInfo.packageName);
1298 context.sendBroadcast(intent);
1300 showToast(context, R.string.shortcut_created);
1304 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1305 SharedPreferences pref = getSharedPreferences(context);
1306 if(pref.getBoolean("hide_taskbar", true)) {
1307 if(isOverridingFreeformHack(context))
1308 return !LauncherHelper.getInstance().isOnHomeScreen();
1310 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1311 if(pendingAppLaunch)
1312 return !helper.isFreeformHackActive();
1314 return !helper.isInFreeformWorkspace();
1320 public static boolean isOverridingFreeformHack(Context context) {
1321 SharedPreferences pref = getSharedPreferences(context);
1322 return pref.getBoolean("freeform_hack", false)
1323 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1324 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1327 public static boolean isPlayStoreInstalled(Context context) {
1329 context.getPackageManager().getPackageInfo("com.android.vending", 0);
1331 } catch (PackageManager.NameNotFoundException e) {
1336 private static float getCurrentApiVersion() {
1337 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1338 return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1340 return (float) Build.VERSION.SDK_INT;
1343 public static boolean hasBrokenSetLaunchBoundsApi() {
1344 return getCurrentApiVersion() >= 26.0f
1345 && getCurrentApiVersion() < 28.0f
1346 && !isSamsungDevice()
1347 && !isNvidiaDevice();
1350 public static String getSecondScreenPackageName(Context context) {
1351 return getInstalledPackage(context, Arrays.asList(
1352 "com.farmerbb.secondscreen.free",
1353 "com.farmerbb.secondscreen"));
1356 // Returns the name of an installed package from a list of package names, in order of preference
1357 private static String getInstalledPackage(Context context, List<String> packageNames) {
1358 if(packageNames == null || packageNames.isEmpty())
1361 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1362 String packageName = packages.get(0);
1365 context.getPackageManager().getPackageInfo(packageName, 0);
1367 } catch (PackageManager.NameNotFoundException e) {
1369 return getInstalledPackage(context, packages);
1373 public static boolean visualFeedbackEnabled(Context context) {
1374 SharedPreferences pref = getSharedPreferences(context);
1375 return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1376 && pref.getBoolean("visual_feedback", true);
1379 public static void showRecentAppsDialog(Context context) {
1380 showRecentAppsDialog(context, null, null);
1383 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1384 Runnable finalOnFinish = onFinish == null
1388 Runnable finalOnError = onError == null
1389 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1392 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1393 ApplicationInfo applicationInfo = null;
1395 applicationInfo = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1396 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1398 if(applicationInfo != null) {
1399 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1400 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1402 if(mode != AppOpsManager.MODE_ALLOWED) {
1403 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1404 builder.setTitle(R.string.pref_header_recent_apps)
1405 .setMessage(R.string.enable_recent_apps)
1406 .setPositiveButton(R.string.action_ok, (dialog, which) -> {
1408 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1409 showToastLong(context, R.string.usage_stats_message);
1411 finalOnFinish.run();
1412 } catch (ActivityNotFoundException e) {
1416 .setNegativeButton(R.string.action_cancel, (dialog, which) -> finalOnFinish.run());
1418 AlertDialog dialog = builder.create();
1420 dialog.setCancelable(false);
1427 finalOnFinish.run();
1431 public static Context wrapContext(Context context) {
1432 SharedPreferences pref = getSharedPreferences(context);
1435 switch(pref.getString("theme", "light")) {
1437 theme = R.style.AppTheme;
1440 theme = R.style.AppTheme_Dark;
1444 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1447 @SuppressLint("PackageManagerGetSignatures")
1448 public static boolean isPlayStoreRelease(Context context) {
1449 Signature playStoreSignature = new Signature(context.getString(R.string.signature));
1451 PackageManager pm = context.getPackageManager();
1452 PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
1453 for(Signature signature : info.signatures) {
1454 if(signature.equals(playStoreSignature))
1457 } catch (Exception e) { /* Gracefully fail */ }
1462 public static boolean isExternalAccessDisabled(Context context) {
1463 SharedPreferences pref = getSharedPreferences(context);
1464 return !pref.getBoolean("tasker_enabled", true);
1467 public static boolean enableFreeformModeShortcut(Context context) {
1468 return canEnableFreeform()
1469 && !U.isOverridingFreeformHack(context)
1470 && !U.isChromeOs(context);
1473 public static void startForegroundService(Context context, Intent intent) {
1474 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1475 if(Settings.canDrawOverlays(context))
1476 context.startForegroundService(intent);
1478 context.startService(intent);
1481 public static int getOverlayType() {
1482 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1483 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1484 : WindowManager.LayoutParams.TYPE_PHONE;
1487 public static boolean isDelegatingHomeActivity(Context context) {
1488 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1489 homeIntent.addCategory(Intent.CATEGORY_HOME);
1491 final List<ResolveInfo> listOfLaunchers = context.getPackageManager().queryIntentActivities(homeIntent, 0);
1492 for(ResolveInfo launcher : listOfLaunchers) {
1493 if(launcher.activityInfo.packageName.equals(BuildConfig.SUPPORT_APPLICATION_ID))