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.Activity;
21 import android.app.ActivityManager;
22 import android.app.ActivityOptions;
23 import android.app.AlertDialog;
24 import android.app.AppOpsManager;
25 import android.app.Service;
26 import android.app.admin.DevicePolicyManager;
27 import android.content.ActivityNotFoundException;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.SharedPreferences;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.LauncherActivityInfo;
35 import android.content.pm.LauncherApps;
36 import android.content.pm.PackageInfo;
37 import android.content.pm.PackageManager;
38 import android.content.pm.ResolveInfo;
39 import android.content.pm.ShortcutInfo;
40 import android.content.pm.ShortcutManager;
41 import android.content.pm.Signature;
42 import android.content.res.Configuration;
43 import android.graphics.Color;
44 import android.graphics.Rect;
45 import android.net.Uri;
46 import android.os.Build;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.Process;
50 import android.os.UserHandle;
51 import android.os.UserManager;
52 import android.provider.Settings;
53 import android.support.v4.content.LocalBroadcastManager;
54 import android.support.v7.view.ContextThemeWrapper;
55 import android.util.DisplayMetrics;
56 import android.view.Display;
57 import android.view.Surface;
58 import android.view.View;
59 import android.view.WindowManager;
60 import android.widget.Toast;
62 import com.farmerbb.taskbar.BuildConfig;
63 import com.farmerbb.taskbar.R;
64 import com.farmerbb.taskbar.activity.ContextMenuActivity;
65 import com.farmerbb.taskbar.activity.DummyActivity;
66 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
67 import com.farmerbb.taskbar.activity.ShortcutActivity;
68 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
69 import com.farmerbb.taskbar.activity.TouchAbsorberActivity;
70 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
71 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
72 import com.farmerbb.taskbar.service.DashboardService;
73 import com.farmerbb.taskbar.service.NotificationService;
74 import com.farmerbb.taskbar.service.PowerMenuService;
75 import com.farmerbb.taskbar.service.StartMenuService;
76 import com.farmerbb.taskbar.service.TaskbarService;
78 import java.lang.reflect.Method;
79 import java.util.ArrayList;
80 import java.util.Arrays;
81 import java.util.List;
87 private static Integer cachedRotation;
89 private static final int MAXIMIZED = 0;
90 private static final int LEFT = -1;
91 private static final int RIGHT = 1;
93 public static final int HIDDEN = 0;
94 public static final int TOP_APPS = 1;
96 // From android.app.ActivityManager.StackId
97 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
98 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
100 // From android.app.WindowConfiguration
101 private static final int WINDOWING_MODE_FULLSCREEN = 1;
102 private static final int WINDOWING_MODE_FREEFORM = 5;
104 public static SharedPreferences getSharedPreferences(Context context) {
105 return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
108 public static void showPermissionDialog(Context context) {
109 showPermissionDialog(context, null, null);
112 @TargetApi(Build.VERSION_CODES.M)
113 public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
114 Runnable finalOnFinish = onFinish == null
118 Runnable finalOnError = onError == null
119 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
122 AlertDialog.Builder builder = new AlertDialog.Builder(context);
123 builder.setTitle(R.string.permission_dialog_title)
124 .setMessage(R.string.permission_dialog_message)
125 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
127 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
128 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
131 } catch (ActivityNotFoundException e) {
136 AlertDialog dialog = builder.create();
138 dialog.setCancelable(false);
143 public static AlertDialog showErrorDialog(Context context, String appopCmd) {
144 return showErrorDialog(context, appopCmd, null);
147 private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
148 Runnable finalOnFinish = onFinish == null
152 AlertDialog.Builder builder = new AlertDialog.Builder(context);
153 builder.setTitle(R.string.error_dialog_title)
154 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
155 .setPositiveButton(R.string.action_ok, (dialog, which) -> finalOnFinish.run());
157 AlertDialog dialog = builder.create();
159 dialog.setCancelable(false);
164 public static void lockDevice(Context context) {
165 ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
166 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
167 PackageManager.DONT_KILL_APP);
169 DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
170 if(mDevicePolicyManager.isAdminActive(component))
171 mDevicePolicyManager.lockNow();
173 launchApp(context, () -> {
174 Intent intent = new Intent(context, DummyActivity.class);
175 intent.putExtra("device_admin", true);
176 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
179 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
180 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
185 public static void sendAccessibilityAction(Context context, int action) {
186 ComponentName component = new ComponentName(context, PowerMenuService.class);
187 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
188 PackageManager.DONT_KILL_APP);
190 if(isAccessibilityServiceEnabled(context)) {
191 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
192 intent.putExtra("action", action);
193 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
195 launchApp(context, () -> {
196 Intent intent = new Intent(context, DummyActivity.class);
197 intent.putExtra("accessibility", true);
198 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
201 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
202 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
207 private static boolean isAccessibilityServiceEnabled(Context context) {
208 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
209 ComponentName component = new ComponentName(context, PowerMenuService.class);
211 return accessibilityServices != null
212 && (accessibilityServices.contains(component.flattenToString())
213 || accessibilityServices.contains(component.flattenToShortString()));
216 public static void showToast(Context context, int message) {
217 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
220 public static void showToastLong(Context context, int message) {
221 showToast(context, context.getString(message), Toast.LENGTH_LONG);
224 public static void showToast(Context context, String message, int length) {
227 ToastInterface toast = DependencyUtils.createToast(context, message, length);
230 ToastHelper.getInstance().setLastToast(toast);
233 public static void cancelToast() {
234 ToastInterface toast = ToastHelper.getInstance().getLastToast();
235 if(toast != null) toast.cancel();
238 public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
249 public static void launchApp(final Context context,
250 final String packageName,
251 final String componentName,
252 final long userId, final String windowSize,
253 final boolean launchedFromTaskbar,
254 final boolean openInNewWindow) {
265 private static void launchApp(final Context context,
266 final String packageName,
267 final String componentName,
268 final long userId, final String windowSize,
269 final boolean launchedFromTaskbar,
270 final boolean openInNewWindow,
271 final ShortcutInfo shortcut) {
272 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, packageName, componentName, userId,
273 windowSize, openInNewWindow, shortcut));
276 public static void launchApp(Context context, Runnable runnable) {
277 launchApp(context, true, runnable);
280 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
281 SharedPreferences pref = getSharedPreferences(context);
282 FreeformHackHelper helper = FreeformHackHelper.getInstance();
284 boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
285 && helper.isInFreeformWorkspace()
286 && MenuHelper.getInstance().isContextMenuOpen();
288 boolean noAnimation = pref.getBoolean("disable_animations", false);
290 if(hasFreeformSupport(context)
291 && pref.getBoolean("freeform_hack", false)
292 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
293 new Handler().postDelayed(() -> {
294 startFreeformHack(context, true);
296 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
297 }, launchedFromTaskbar ? 0 : 100);
299 new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
302 public static void startFreeformHack(Context context) {
303 startFreeformHack(context, false);
306 @TargetApi(Build.VERSION_CODES.N)
307 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
308 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
309 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
310 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
311 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
314 freeformHackIntent.putExtra("check_multiwindow", true);
316 if(canDrawOverlays(context))
317 startActivityLowerRight(context, freeformHackIntent);
320 public static void stopFreeformHack(Context context) {
321 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
323 if(isOverridingFreeformHack(context)) {
324 FreeformHackHelper helper = FreeformHackHelper.getInstance();
325 helper.setFreeformHackActive(false);
326 helper.setInFreeformWorkspace(false);
330 @TargetApi(Build.VERSION_CODES.N)
331 private static void continueLaunchingApp(Context context,
333 String componentName,
336 boolean openInNewWindow,
337 ShortcutInfo shortcut) {
338 SharedPreferences pref = getSharedPreferences(context);
339 Intent intent = new Intent();
340 intent.setComponent(ComponentName.unflattenFromString(componentName));
341 intent.setAction(Intent.ACTION_MAIN);
342 intent.addCategory(Intent.CATEGORY_LAUNCHER);
343 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
344 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
346 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
347 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
348 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
350 if(pref.getBoolean("disable_animations", false))
351 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
353 if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
354 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
356 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
357 if(activityInfo != null) {
358 switch(activityInfo.launchMode) {
359 case ActivityInfo.LAUNCH_SINGLE_TASK:
360 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
361 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
367 ApplicationType type = getApplicationType(context, packageName);
369 if(windowSize == null)
370 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
372 Bundle bundle = getActivityOptionsBundle(context, type, windowSize);
374 if(shortcut == null) {
375 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
376 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
378 startActivity(context, intent, bundle);
379 } catch (ActivityNotFoundException e) {
380 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
381 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
383 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
385 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 startActivity(Context context, Intent intent, Bundle bundle) {
494 boolean shouldLaunchTouchAbsorber =
495 !FreeformHackHelper.getInstance().isTouchAbsorberActive()
496 && isOverridingFreeformHack(context)
497 && !isChromeOs(context);
499 if(!shouldLaunchTouchAbsorber) {
500 context.startActivity(intent, bundle);
504 startTouchAbsorberActivity(context);
505 new Handler().postDelayed(() -> context.startActivity(intent, bundle), 100);
508 public static void startActivityMaximized(Context context, Intent intent) {
509 Bundle bundle = launchMode2(context, MAXIMIZED, ApplicationType.CONTEXT_MENU);
510 startActivity(context, 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 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
598 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.APPLICATION_ID;
600 Intent intent = new Intent(Intent.ACTION_VIEW);
601 intent.setData(Uri.parse(url));
602 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
605 context.startActivity(intent);
606 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
609 public static boolean launcherIsDefault(Context context) {
610 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
611 homeIntent.addCategory(Intent.CATEGORY_HOME);
612 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
614 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
617 public static void setCachedRotation(int cachedRotation) {
618 U.cachedRotation = cachedRotation;
621 public static String getTaskbarPosition(Context context) {
622 SharedPreferences pref = getSharedPreferences(context);
623 String position = pref.getString("position", "bottom_left");
625 if(pref.getBoolean("anchor", false)) {
626 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
627 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
632 case Surface.ROTATION_0:
633 return "bottom_left";
634 case Surface.ROTATION_90:
635 return "bottom_vertical_right";
636 case Surface.ROTATION_180:
638 case Surface.ROTATION_270:
639 return "top_vertical_left";
642 case "bottom_vertical_left":
644 case Surface.ROTATION_0:
645 return "bottom_vertical_left";
646 case Surface.ROTATION_90:
647 return "bottom_right";
648 case Surface.ROTATION_180:
649 return "top_vertical_right";
650 case Surface.ROTATION_270:
656 case Surface.ROTATION_0:
657 return "bottom_right";
658 case Surface.ROTATION_90:
659 return "top_vertical_right";
660 case Surface.ROTATION_180:
662 case Surface.ROTATION_270:
663 return "bottom_vertical_left";
666 case "bottom_vertical_right":
668 case Surface.ROTATION_0:
669 return "bottom_vertical_right";
670 case Surface.ROTATION_90:
672 case Surface.ROTATION_180:
673 return "top_vertical_left";
674 case Surface.ROTATION_270:
675 return "bottom_left";
680 case Surface.ROTATION_0:
682 case Surface.ROTATION_90:
683 return "bottom_vertical_left";
684 case Surface.ROTATION_180:
685 return "bottom_right";
686 case Surface.ROTATION_270:
687 return "top_vertical_right";
690 case "top_vertical_left":
692 case Surface.ROTATION_0:
693 return "top_vertical_left";
694 case Surface.ROTATION_90:
695 return "bottom_left";
696 case Surface.ROTATION_180:
697 return "bottom_vertical_right";
698 case Surface.ROTATION_270:
704 case Surface.ROTATION_0:
706 case Surface.ROTATION_90:
707 return "top_vertical_left";
708 case Surface.ROTATION_180:
709 return "bottom_left";
710 case Surface.ROTATION_270:
711 return "bottom_vertical_right";
714 case "top_vertical_right":
716 case Surface.ROTATION_0:
717 return "top_vertical_right";
718 case Surface.ROTATION_90:
720 case Surface.ROTATION_180:
721 return "bottom_vertical_left";
722 case Surface.ROTATION_270:
723 return "bottom_right";
732 private static int getMaxNumOfColumns(Context context) {
733 SharedPreferences pref = getSharedPreferences(context);
734 DisplayInfo display = getDisplayInfo(context);
735 float density = display.density / 160;
736 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / density;
737 int numOfColumns = 0;
739 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
740 ? (display.height - getStatusBarHeight(context)) / density
741 : display.width / density;
743 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / density;
745 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
747 while(baseTaskbarSize + iconSize < maxScreenSize
748 && numOfColumns < userMaxNumOfColumns) {
749 baseTaskbarSize = baseTaskbarSize + iconSize;
756 public static int getMaxNumOfEntries(Context context) {
757 SharedPreferences pref = getSharedPreferences(context);
758 return pref.getBoolean("disable_scrolling_list", false)
759 ? getMaxNumOfColumns(context)
760 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
763 public static int getStatusBarHeight(Context context) {
764 return getSystemDimen(context, "status_bar_height");
767 private static int getNavbarHeight(Context context) {
768 return getSystemDimen(context, "navigation_bar_height");
771 private static int getSystemDimen(Context context, String id) {
773 int resourceId = context.getResources().getIdentifier(id, "dimen", "android");
775 value = context.getResources().getDimensionPixelSize(resourceId);
780 public static void refreshPinnedIcons(Context context) {
781 IconCache.getInstance(context).clearCache();
783 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
784 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
785 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
786 PackageManager pm = context.getPackageManager();
790 for(AppEntry entry : pinnedAppsList) {
791 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
792 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
794 final List<UserHandle> userHandles = userManager.getUserProfiles();
795 LauncherActivityInfo appInfo = null;
797 for(UserHandle handle : userHandles) {
798 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
799 if(!list.isEmpty()) {
800 // Google App workaround
801 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
802 appInfo = list.get(0);
804 boolean added = false;
805 for(LauncherActivityInfo info : list) {
806 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
812 if(!added) appInfo = list.get(0);
819 if(appInfo != null) {
820 AppEntry newEntry = new AppEntry(
821 entry.getPackageName(),
822 entry.getComponentName(),
824 IconCache.getInstance(context).getIcon(context, pm, appInfo),
827 newEntry.setUserId(entry.getUserId(context));
828 pba.addPinnedApp(context, newEntry);
832 for(AppEntry entry : blockedAppsList) {
833 pba.addBlockedApp(context, entry);
837 public static Intent getShortcutIntent(Context context) {
838 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
839 shortcutIntent.setAction(Intent.ACTION_MAIN);
840 shortcutIntent.putExtra("is_launching_shortcut", true);
842 Intent intent = new Intent();
843 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
844 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
845 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
850 public static Intent getStartStopIntent(Context context) {
851 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
852 shortcutIntent.setAction(Intent.ACTION_MAIN);
853 shortcutIntent.putExtra("is_launching_shortcut", true);
855 Intent intent = new Intent();
856 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
857 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
858 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
863 public static boolean canEnableFreeform() {
864 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
867 @TargetApi(Build.VERSION_CODES.N)
868 public static boolean hasFreeformSupport(Context context) {
869 return canEnableFreeform()
870 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
871 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
872 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
873 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
876 public static boolean hasPartialFreeformSupport() {
877 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
880 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
881 return isServiceRunning(context, cls.getName());
884 private static boolean isServiceRunning(Context context, String className) {
885 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
886 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
887 if(className.equals(service.service.getClassName()))
894 public static int getBackgroundTint(Context context) {
895 SharedPreferences pref = getSharedPreferences(context);
897 // Import old background tint preference
898 if(pref.contains("show_background")) {
899 SharedPreferences.Editor editor = pref.edit();
901 if(!pref.getBoolean("show_background", true))
902 editor.putInt("background_tint", Color.TRANSPARENT).apply();
904 editor.remove("show_background");
908 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
911 public static int getAccentColor(Context context) {
912 SharedPreferences pref = getSharedPreferences(context);
913 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
916 @TargetApi(Build.VERSION_CODES.M)
917 public static boolean canDrawOverlays(Context context) {
918 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
921 public static boolean isGame(Context context, String packageName) {
922 SharedPreferences pref = getSharedPreferences(context);
923 if(pref.getBoolean("launch_games_fullscreen", true)) {
924 PackageManager pm = context.getPackageManager();
927 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
928 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
929 } catch (PackageManager.NameNotFoundException e) {
936 @TargetApi(Build.VERSION_CODES.N)
937 public static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType) {
938 ActivityOptions options = ActivityOptions.makeBasic();
941 switch(applicationType) {
943 if(FreeformHackHelper.getInstance().isFreeformHackActive())
944 stackId = getFreeformWindowModeId();
946 stackId = getFullscreenWindowModeId();
949 stackId = getFullscreenWindowModeId();
952 stackId = getFreeformWindowModeId();
955 if(hasBrokenSetLaunchBoundsApi()
956 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
957 stackId = getFullscreenWindowModeId();
962 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
963 method.invoke(options, stackId);
964 } catch (Exception e) { /* Gracefully fail */ }
969 private static int getFullscreenWindowModeId() {
970 if(getCurrentApiVersion() >= 28.0f)
971 return WINDOWING_MODE_FULLSCREEN;
973 return FULLSCREEN_WORKSPACE_STACK_ID;
976 private static int getFreeformWindowModeId() {
977 if(getCurrentApiVersion() >= 28.0f)
978 return WINDOWING_MODE_FREEFORM;
980 return FREEFORM_WORKSPACE_STACK_ID;
983 private static String getWindowingModeMethodName() {
984 if(getCurrentApiVersion() >= 28.0f)
985 return "setLaunchWindowingMode";
987 return "setLaunchStackId";
990 public static Bundle getActivityOptionsBundle(Context context, ApplicationType type) {
991 SharedPreferences pref = getSharedPreferences(context);
993 return getActivityOptionsBundle(context, type, pref.getString("window_size", "standard"));
996 private static Bundle getActivityOptionsBundle(Context context, ApplicationType type, String windowSize) {
997 SharedPreferences pref = getSharedPreferences(context);
998 if(!canEnableFreeform() || !pref.getBoolean("freeform_hack", false))
1001 switch(windowSize) {
1003 return launchMode1(context, type);
1005 return launchMode2(context, MAXIMIZED, type);
1007 return launchMode2(context, LEFT, type);
1009 return launchMode2(context, RIGHT, type);
1011 return launchMode3(context, type);
1014 return getActivityOptions(context, type).toBundle();
1017 private static ApplicationType getApplicationType(Context context, String packageName) {
1018 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1021 public static boolean isSystemApp(Context context) {
1023 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1024 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1025 return (info.flags & mask) != 0;
1026 } catch (PackageManager.NameNotFoundException e) {
1031 public static boolean isChromeOs(Context context) {
1032 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1035 public static boolean isBlissOs(Context context) {
1036 boolean validBlissOsBuildProp = false;
1038 String blissVersion = DependencyUtils.getSystemProperty("ro.bliss.version");
1039 if(blissVersion != null && !blissVersion.isEmpty())
1040 validBlissOsBuildProp = true;
1042 String buildUser = DependencyUtils.getSystemProperty("ro.build.user");
1043 if(buildUser != null && buildUser.equals("electrikjesus"))
1044 validBlissOsBuildProp = true;
1046 return validBlissOsBuildProp
1047 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1048 && isSystemApp(context);
1051 public static boolean isLauncherPermanentlyEnabled(Context context) {
1052 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1055 PackageManager pm = context.getPackageManager();
1057 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1058 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
1059 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1060 && isSystemApp(context);
1061 } catch (PackageManager.NameNotFoundException e) {
1066 public static int getBaseTaskbarSize(Context context) {
1067 return Math.round(getBaseTaskbarSizeFloat(context));
1070 private static float getBaseTaskbarSizeFloat(Context context) {
1071 SharedPreferences pref = getSharedPreferences(context);
1072 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
1073 boolean navbarButtonsEnabled = false;
1075 if(pref.getBoolean("dashboard", false))
1076 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
1078 if(pref.getBoolean("button_back", false)) {
1079 navbarButtonsEnabled = true;
1080 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1083 if(pref.getBoolean("button_home", false)) {
1084 navbarButtonsEnabled = true;
1085 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1088 if(pref.getBoolean("button_recents", false)) {
1089 navbarButtonsEnabled = true;
1090 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1093 if(navbarButtonsEnabled)
1094 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
1096 return baseTaskbarSize;
1099 private static void startTaskbarService(Context context, boolean fullRestart) {
1100 context.startService(new Intent(context, TaskbarService.class));
1101 context.startService(new Intent(context, StartMenuService.class));
1102 context.startService(new Intent(context, DashboardService.class));
1103 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1106 private static void stopTaskbarService(Context context, boolean fullRestart) {
1107 context.stopService(new Intent(context, TaskbarService.class));
1108 context.stopService(new Intent(context, StartMenuService.class));
1109 context.stopService(new Intent(context, DashboardService.class));
1110 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1113 public static void restartTaskbar(Context context) {
1114 SharedPreferences pref = getSharedPreferences(context);
1115 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1117 .putBoolean("is_restarting", true)
1118 .putBoolean("skip_auto_hide_navbar", true)
1121 stopTaskbarService(context, true);
1122 startTaskbarService(context, true);
1123 } else if(isServiceRunning(context, StartMenuService.class)) {
1124 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1126 stopTaskbarService(context, false);
1127 startTaskbarService(context, false);
1131 public static void restartNotificationService(Context context) {
1132 if(isServiceRunning(context, NotificationService.class)) {
1133 SharedPreferences pref = getSharedPreferences(context);
1134 pref.edit().putBoolean("is_restarting", true).apply();
1136 Intent intent = new Intent(context, NotificationService.class);
1137 context.stopService(intent);
1138 context.startService(intent);
1142 public static void showHideNavigationBar(Context context, boolean show) {
1143 // Show or hide the system navigation bar on Bliss-x86
1145 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1146 } catch (Exception e) { /* Gracefully fail */ }
1149 public static void initPrefs(Context context) {
1150 // On smaller-screened devices, set "Grid" as the default start menu layout
1151 SharedPreferences pref = getSharedPreferences(context);
1152 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 720
1153 && pref.getString("start_menu_layout", "null").equals("null")) {
1154 pref.edit().putString("start_menu_layout", "grid").apply();
1157 // Enable freeform hack automatically on supported devices
1158 if(canEnableFreeform()) {
1159 if(!pref.getBoolean("freeform_hack_override", false)) {
1161 .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1162 .putBoolean("save_window_sizes", false)
1163 .putBoolean("freeform_hack_override", true)
1165 } else if(!hasFreeformSupport(context)) {
1166 pref.edit().putBoolean("freeform_hack", false).apply();
1168 stopFreeformHack(context);
1171 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1172 || pref.getBoolean("show_freeform_disabled_message", false);
1175 .putBoolean("freeform_hack", false)
1176 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1179 SavedWindowSizes.getInstance(context).clear(context);
1180 stopFreeformHack(context);
1183 // Customizations for BlissOS
1184 if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1185 && !pref.getBoolean("bliss_os_prefs", false)) {
1186 SharedPreferences.Editor editor = pref.edit();
1188 if(hasFreeformSupport(context)) {
1189 editor.putBoolean("freeform_hack", true);
1192 editor.putString("recents_amount", "running_apps_only");
1193 editor.putString("refresh_frequency", "0");
1194 editor.putString("max_num_of_recents", "2147483647");
1195 editor.putString("sort_order", "true");
1196 editor.putBoolean("full_length", true);
1197 editor.putBoolean("dashboard", true);
1198 editor.putBoolean("app_drawer_icon", true);
1199 editor.putBoolean("button_back", true);
1200 editor.putBoolean("button_home", true);
1201 editor.putBoolean("button_recents", true);
1202 editor.putBoolean("auto_hide_navbar", true);
1203 editor.putBoolean("bliss_os_prefs", true);
1206 Settings.Secure.putString(context.getContentResolver(),
1207 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
1208 new ComponentName(context, PowerMenuService.class).flattenToString());
1209 } catch (Exception e) { /* Gracefully fail */ }
1214 // Customizations for Android-x86 devices (non-Bliss)
1215 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1216 && isSystemApp(context)
1217 && !pref.getBoolean("android_x86_prefs", false)) {
1219 .putString("recents_amount", "running_apps_only")
1220 .putString("refresh_frequency", "0")
1221 .putString("max_num_of_recents", "2147483647")
1222 .putString("sort_order", "true")
1223 .putBoolean("full_length", true)
1224 .putBoolean("dashboard", true)
1225 .putBoolean("android_x86_prefs", true)
1230 public static DisplayInfo getDisplayInfo(Context context) {
1231 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1232 Display disp = wm.getDefaultDisplay();
1234 DisplayMetrics metrics = new DisplayMetrics();
1235 disp.getMetrics(metrics);
1237 DisplayMetrics realMetrics = new DisplayMetrics();
1238 disp.getRealMetrics(realMetrics);
1240 DisplayInfo display = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
1242 if(isChromeOs(context)) {
1243 SharedPreferences pref = getSharedPreferences(context);
1244 if(!pref.getBoolean("chrome_os_context_menu_fix", true)) {
1245 display.width = realMetrics.widthPixels;
1246 display.height = realMetrics.heightPixels;
1252 boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
1253 boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
1255 if(sameWidth && !sameHeight) {
1256 display.width = realMetrics.widthPixels;
1257 display.height = realMetrics.heightPixels - getNavbarHeight(context);
1260 if(!sameWidth && sameHeight) {
1261 display.width = realMetrics.widthPixels - getNavbarHeight(context);
1262 display.height = realMetrics.heightPixels;
1268 public static void pinAppShortcut(Context context) {
1269 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1270 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1272 if(mShortcutManager.isRequestPinShortcutSupported()) {
1273 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1275 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1277 showToastLong(context, R.string.pin_shortcut_not_supported);
1279 Intent intent = getShortcutIntent(context);
1280 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1281 intent.putExtra("duplicate", false);
1283 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1284 homeIntent.addCategory(Intent.CATEGORY_HOME);
1285 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1287 intent.setPackage(defaultLauncher.activityInfo.packageName);
1288 context.sendBroadcast(intent);
1290 showToast(context, R.string.shortcut_created);
1294 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1295 SharedPreferences pref = getSharedPreferences(context);
1296 if(pref.getBoolean("hide_taskbar", true)) {
1297 if(isOverridingFreeformHack(context))
1298 return !LauncherHelper.getInstance().isOnHomeScreen();
1300 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1301 if(pendingAppLaunch)
1302 return !helper.isFreeformHackActive();
1304 return !helper.isInFreeformWorkspace();
1310 public static boolean isOverridingFreeformHack(Context context) {
1311 SharedPreferences pref = getSharedPreferences(context);
1312 return pref.getBoolean("freeform_hack", false)
1313 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1314 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1317 @SuppressWarnings("unchecked")
1318 public static <T extends View> T findViewById(Activity target, int id) {
1319 return (T) target.findViewById(id);
1322 @SuppressWarnings("unchecked")
1323 public static <T extends View> T findViewById(View target, int id) {
1324 return (T) target.findViewById(id);
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 && getCurrentApiVersion() < 28.0f;
1347 public static String getSecondScreenPackageName(Context context) {
1348 return getInstalledPackage(context, Arrays.asList(
1349 "com.farmerbb.secondscreen.free",
1350 "com.farmerbb.secondscreen"));
1353 // Returns the name of an installed package from a list of package names, in order of preference
1354 private static String getInstalledPackage(Context context, List<String> packageNames) {
1355 if(packageNames == null || packageNames.isEmpty())
1358 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1359 String packageName = packages.get(0);
1362 context.getPackageManager().getPackageInfo(packageName, 0);
1364 } catch (PackageManager.NameNotFoundException e) {
1366 return getInstalledPackage(context, packages);
1370 public static boolean visualFeedbackEnabled(Context context) {
1371 SharedPreferences pref = getSharedPreferences(context);
1372 return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1373 && pref.getBoolean("visual_feedback", true);
1376 public static void showRecentAppsDialog(Context context) {
1377 showRecentAppsDialog(context, null, null);
1380 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1381 Runnable finalOnFinish = onFinish == null
1385 Runnable finalOnError = onError == null
1386 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1389 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1390 ApplicationInfo applicationInfo = null;
1392 applicationInfo = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1393 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1395 if(applicationInfo != null) {
1396 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1397 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1399 if(mode != AppOpsManager.MODE_ALLOWED) {
1400 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1401 builder.setTitle(R.string.pref_header_recent_apps)
1402 .setMessage(R.string.enable_recent_apps)
1403 .setPositiveButton(R.string.action_ok, (dialog, which) -> {
1405 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1406 showToastLong(context, R.string.usage_stats_message);
1408 finalOnFinish.run();
1409 } catch (ActivityNotFoundException e) {
1413 .setNegativeButton(R.string.action_cancel, (dialog, which) -> finalOnFinish.run());
1415 AlertDialog dialog = builder.create();
1417 dialog.setCancelable(false);
1424 finalOnFinish.run();
1428 public static Context wrapContext(Context context) {
1429 SharedPreferences pref = getSharedPreferences(context);
1432 switch(pref.getString("theme", "light")) {
1434 theme = R.style.AppTheme;
1437 theme = R.style.AppTheme_Dark;
1441 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1444 @SuppressLint("PackageManagerGetSignatures")
1445 public static boolean isPlayStoreRelease(Context context) {
1446 Signature playStoreSignature = new Signature(context.getString(R.string.signature));
1448 PackageManager pm = context.getPackageManager();
1449 PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
1450 for(Signature signature : info.signatures) {
1451 if(signature.equals(playStoreSignature))
1454 } catch (Exception e) { /* Gracefully fail */ }
1459 public static boolean isExternalAccessDisabled(Context context) {
1460 SharedPreferences pref = getSharedPreferences(context);
1461 return !pref.getBoolean("tasker_enabled", true);
1464 public static boolean enableFreeformModeShortcut(Context context) {
1465 return canEnableFreeform()
1466 && !U.isOverridingFreeformHack(context)
1467 && !U.isChromeOs(context);
1470 public static void startForegroundService(Context context, Intent intent) {
1471 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1472 if(Settings.canDrawOverlays(context))
1473 context.startForegroundService(intent);
1475 context.startService(intent);
1478 public static int getOverlayType() {
1479 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1480 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1481 : WindowManager.LayoutParams.TYPE_PHONE;