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.DummyActivity;
65 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
66 import com.farmerbb.taskbar.activity.ShortcutActivity;
67 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
68 import com.farmerbb.taskbar.activity.TouchAbsorberActivity;
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 getSharedPreferences(context, Context.MODE_PRIVATE);
106 private static SharedPreferences getSharedPreferences(Context context, int mode) {
107 return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", mode);
110 public static void showPermissionDialog(Context context) {
111 showPermissionDialog(context, null, null);
114 @TargetApi(Build.VERSION_CODES.M)
115 public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
116 Runnable finalOnFinish = onFinish == null
120 Runnable finalOnError = onError == null
121 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
124 AlertDialog.Builder builder = new AlertDialog.Builder(context);
125 builder.setTitle(R.string.permission_dialog_title)
126 .setMessage(R.string.permission_dialog_message)
127 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
129 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
130 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
133 } catch (ActivityNotFoundException e) {
138 AlertDialog dialog = builder.create();
140 dialog.setCancelable(false);
145 public static AlertDialog showErrorDialog(Context context, String appopCmd) {
146 return showErrorDialog(context, appopCmd, null);
149 private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
150 Runnable finalOnFinish = onFinish == null
154 AlertDialog.Builder builder = new AlertDialog.Builder(context);
155 builder.setTitle(R.string.error_dialog_title)
156 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
157 .setPositiveButton(R.string.action_ok, (dialog, which) -> finalOnFinish.run());
159 AlertDialog dialog = builder.create();
161 dialog.setCancelable(false);
166 public static void lockDevice(Context context) {
167 ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
168 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
169 PackageManager.DONT_KILL_APP);
171 DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
172 if(mDevicePolicyManager.isAdminActive(component))
173 mDevicePolicyManager.lockNow();
175 launchApp(context, () -> {
176 Intent intent = new Intent(context, DummyActivity.class);
177 intent.putExtra("device_admin", true);
178 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
179 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
184 public static void sendAccessibilityAction(Context context, int action) {
185 ComponentName component = new ComponentName(context, PowerMenuService.class);
186 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
187 PackageManager.DONT_KILL_APP);
189 if(isAccessibilityServiceEnabled(context)) {
190 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
191 intent.putExtra("action", action);
192 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
194 launchApp(context, () -> {
195 Intent intent = new Intent(context, DummyActivity.class);
196 intent.putExtra("accessibility", true);
197 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
198 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
203 private static boolean isAccessibilityServiceEnabled(Context context) {
204 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
205 ComponentName component = new ComponentName(context, PowerMenuService.class);
207 return accessibilityServices != null
208 && (accessibilityServices.contains(component.flattenToString())
209 || accessibilityServices.contains(component.flattenToShortString()));
212 public static void showToast(Context context, int message) {
213 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
216 public static void showToastLong(Context context, int message) {
217 showToast(context, context.getString(message), Toast.LENGTH_LONG);
220 public static void showToast(Context context, String message, int length) {
223 ToastInterface toast = DependencyUtils.createToast(context, message, length);
226 ToastHelper.getInstance().setLastToast(toast);
229 public static void cancelToast() {
230 ToastInterface toast = ToastHelper.getInstance().getLastToast();
231 if(toast != null) toast.cancel();
234 public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
245 public static void launchApp(final Context context,
246 final String packageName,
247 final String componentName,
248 final long userId, final String windowSize,
249 final boolean launchedFromTaskbar,
250 final boolean openInNewWindow) {
261 private static void launchApp(final Context context,
262 final String packageName,
263 final String componentName,
264 final long userId, final String windowSize,
265 final boolean launchedFromTaskbar,
266 final boolean openInNewWindow,
267 final ShortcutInfo shortcut) {
268 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, packageName, componentName, userId,
269 windowSize, openInNewWindow, shortcut));
272 public static void launchApp(Context context, Runnable runnable) {
273 launchApp(context, true, runnable);
276 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
277 SharedPreferences pref = getSharedPreferences(context);
278 FreeformHackHelper helper = FreeformHackHelper.getInstance();
280 boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
281 && FreeformHackHelper.getInstance().isInFreeformWorkspace()
282 && MenuHelper.getInstance().isContextMenuOpen();
284 if(hasFreeformSupport(context)
285 && pref.getBoolean("freeform_hack", false)
286 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
287 new Handler().postDelayed(() -> {
288 startFreeformHack(context, true);
290 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
291 }, launchedFromTaskbar ? 0 : 100);
296 public static void startFreeformHack(Context context) {
297 startFreeformHack(context, false);
300 @TargetApi(Build.VERSION_CODES.N)
301 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
302 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
303 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
304 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
305 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
308 freeformHackIntent.putExtra("check_multiwindow", true);
310 if(canDrawOverlays(context))
311 startActivityLowerRight(context, freeformHackIntent);
314 public static void stopFreeformHack(Context context) {
315 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
317 if(isOverridingFreeformHack(context)) {
318 FreeformHackHelper helper = FreeformHackHelper.getInstance();
319 helper.setFreeformHackActive(false);
320 helper.setInFreeformWorkspace(false);
324 @TargetApi(Build.VERSION_CODES.N)
325 private static void continueLaunchingApp(Context context,
327 String componentName,
330 boolean openInNewWindow,
331 ShortcutInfo shortcut) {
332 SharedPreferences pref = getSharedPreferences(context);
333 Intent intent = new Intent();
334 intent.setComponent(ComponentName.unflattenFromString(componentName));
335 intent.setAction(Intent.ACTION_MAIN);
336 intent.addCategory(Intent.CATEGORY_LAUNCHER);
337 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
338 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
340 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
341 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
342 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
344 if(pref.getBoolean("disable_animations", false))
345 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
347 if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
348 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
350 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
351 if(activityInfo != null) {
352 switch(activityInfo.launchMode) {
353 case ActivityInfo.LAUNCH_SINGLE_TASK:
354 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
355 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
361 ApplicationType type = getApplicationType(context, packageName);
363 if(windowSize == null)
364 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
366 if(!canEnableFreeform()
367 || !pref.getBoolean("freeform_hack", false)
368 || windowSize.equals("standard")) {
369 launchStandard(context, intent, userId, shortcut, type);
370 } else switch(windowSize) {
372 launchMode1(context, intent, userId, shortcut, type);
375 launchMode2(context, intent, MAXIMIZED, userId, shortcut, type);
378 launchMode2(context, intent, LEFT, userId, shortcut, type);
381 launchMode2(context, intent, RIGHT, userId, shortcut, type);
384 launchMode3(context, intent, userId, shortcut, type);
388 if(shouldCollapse(context, true))
389 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
391 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
394 private static void launchStandard(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
395 Bundle bundle = canEnableFreeform() ? getActivityOptions(context, type).toBundle() : null;
396 if(shortcut == null) {
397 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
398 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
400 startActivity(context, intent, bundle);
401 } catch (ActivityNotFoundException e) {
402 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
403 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
405 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
407 launchShortcut(context, shortcut, bundle);
410 @SuppressWarnings("deprecation")
411 @TargetApi(Build.VERSION_CODES.N)
412 private static void launchMode1(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
413 DisplayMetrics metrics = getRealDisplayMetrics(context);
415 int width1 = metrics.widthPixels / 8;
416 int width2 = metrics.widthPixels - width1;
417 int height1 = metrics.heightPixels / 8;
418 int height2 = metrics.heightPixels - height1;
420 Bundle bundle = getActivityOptions(context, type).setLaunchBounds(new Rect(
427 if(shortcut == null) {
428 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
429 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
431 startActivity(context, intent, bundle);
432 } catch (ActivityNotFoundException e) {
433 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
434 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
436 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
438 launchShortcut(context, shortcut, bundle);
441 @SuppressWarnings("deprecation")
442 @TargetApi(Build.VERSION_CODES.N)
443 private static void launchMode2(Context context, Intent intent, int launchType, long userId, ShortcutInfo shortcut, ApplicationType type) {
444 DisplayMetrics metrics = getRealDisplayMetrics(context);
446 int statusBarHeight = getStatusBarHeight(context);
447 String position = getTaskbarPosition(context);
449 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
450 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
453 int top = statusBarHeight;
454 int right = metrics.widthPixels;
455 int bottom = metrics.heightPixels;
457 int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
459 : context.getResources().getDimensionPixelSize(R.dimen.icon_size);
461 if(position.contains("vertical_left"))
462 left = left + iconSize;
463 else if(position.contains("vertical_right"))
464 right = right - iconSize;
465 else if(position.contains("bottom"))
466 bottom = bottom - iconSize;
468 top = top + iconSize;
470 if(launchType == RIGHT && isLandscape)
471 left = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
472 else if(launchType == RIGHT && isPortrait)
473 top = (bottom / 2) + ((iconSize / 2)
474 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
475 else if(launchType == LEFT && isLandscape)
476 right = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
477 else if(launchType == LEFT && isPortrait)
478 bottom = (bottom / 2) + ((iconSize / 2)
479 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
481 Bundle bundle = getActivityOptions(context, type)
482 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
484 if(shortcut == null) {
485 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
486 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
488 startActivity(context, intent, bundle);
489 } catch (ActivityNotFoundException e) {
490 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
491 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
493 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
495 launchShortcut(context, shortcut, bundle);
498 @SuppressWarnings("deprecation")
499 @TargetApi(Build.VERSION_CODES.N)
500 private static void launchMode3(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
501 DisplayMetrics metrics = getRealDisplayMetrics(context);
503 int width1 = metrics.widthPixels / 2;
504 int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
505 int height1 = metrics.heightPixels / 2;
506 int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
508 Bundle bundle = getActivityOptions(context, type).setLaunchBounds(new Rect(
515 if(shortcut == null) {
516 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
517 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
519 startActivity(context, intent, bundle);
520 } catch (ActivityNotFoundException e) {
521 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
522 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
524 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
526 launchShortcut(context, shortcut, bundle);
529 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
530 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
531 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
534 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
535 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
538 @TargetApi(Build.VERSION_CODES.N_MR1)
539 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
540 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
542 if(launcherApps.hasShortcutHostPermission()) {
544 launcherApps.startShortcut(shortcut, null, bundle);
545 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
549 private static void startActivity(Context context, Intent intent, Bundle bundle) {
550 boolean shouldLaunchTouchAbsorber =
551 !FreeformHackHelper.getInstance().isTouchAbsorberActive()
552 && isOverridingFreeformHack(context)
553 && !isChromeOs(context);
555 if(!shouldLaunchTouchAbsorber) {
556 context.startActivity(intent, bundle);
560 startTouchAbsorberActivity(context);
561 new Handler().postDelayed(() -> context.startActivity(intent, bundle), 100);
564 public static void startActivityMaximized(Context context, Intent intent) {
565 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
566 long userId = userManager.getSerialNumberForUser(Process.myUserHandle());
568 launchMode2(context, intent, MAXIMIZED, userId, null, ApplicationType.CONTEXT_MENU);
571 @TargetApi(Build.VERSION_CODES.N)
572 public static void startActivityLowerRight(Context context, Intent intent) {
573 DisplayMetrics metrics = getRealDisplayMetrics(context);
575 context.startActivity(intent,
576 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
577 .setLaunchBounds(new Rect(
579 metrics.heightPixels,
580 metrics.widthPixels + 1,
581 metrics.heightPixels + 1
583 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
586 @TargetApi(Build.VERSION_CODES.N)
587 public static void startTouchAbsorberActivity(Context context) {
588 String position = getTaskbarPosition(context);
589 DisplayMetrics metrics = getRealDisplayMetrics(context);
593 int right = metrics.widthPixels;
594 int bottom = metrics.heightPixels;
596 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
598 if(position.contains("vertical_left"))
600 else if(position.contains("vertical_right"))
601 left = right - iconSize;
602 else if(position.contains("bottom"))
603 top = bottom - iconSize;
607 Intent intent = new Intent(context, TouchAbsorberActivity.class);
608 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
609 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
612 context.startActivity(intent,
613 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
614 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle());
615 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
618 public static void checkForUpdates(Context context) {
620 if(isPlayStoreRelease(context))
621 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
623 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.APPLICATION_ID;
625 Intent intent = new Intent(Intent.ACTION_VIEW);
626 intent.setData(Uri.parse(url));
627 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
630 context.startActivity(intent);
631 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
634 public static boolean launcherIsDefault(Context context) {
635 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
636 homeIntent.addCategory(Intent.CATEGORY_HOME);
637 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
639 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
642 public static void setCachedRotation(int cachedRotation) {
643 U.cachedRotation = cachedRotation;
646 public static String getTaskbarPosition(Context context) {
647 SharedPreferences pref = getSharedPreferences(context);
648 String position = pref.getString("position", "bottom_left");
650 if(pref.getBoolean("anchor", false)) {
651 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
652 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
657 case Surface.ROTATION_0:
658 return "bottom_left";
659 case Surface.ROTATION_90:
660 return "bottom_vertical_right";
661 case Surface.ROTATION_180:
663 case Surface.ROTATION_270:
664 return "top_vertical_left";
667 case "bottom_vertical_left":
669 case Surface.ROTATION_0:
670 return "bottom_vertical_left";
671 case Surface.ROTATION_90:
672 return "bottom_right";
673 case Surface.ROTATION_180:
674 return "top_vertical_right";
675 case Surface.ROTATION_270:
681 case Surface.ROTATION_0:
682 return "bottom_right";
683 case Surface.ROTATION_90:
684 return "top_vertical_right";
685 case Surface.ROTATION_180:
687 case Surface.ROTATION_270:
688 return "bottom_vertical_left";
691 case "bottom_vertical_right":
693 case Surface.ROTATION_0:
694 return "bottom_vertical_right";
695 case Surface.ROTATION_90:
697 case Surface.ROTATION_180:
698 return "top_vertical_left";
699 case Surface.ROTATION_270:
700 return "bottom_left";
705 case Surface.ROTATION_0:
707 case Surface.ROTATION_90:
708 return "bottom_vertical_left";
709 case Surface.ROTATION_180:
710 return "bottom_right";
711 case Surface.ROTATION_270:
712 return "top_vertical_right";
715 case "top_vertical_left":
717 case Surface.ROTATION_0:
718 return "top_vertical_left";
719 case Surface.ROTATION_90:
720 return "bottom_left";
721 case Surface.ROTATION_180:
722 return "bottom_vertical_right";
723 case Surface.ROTATION_270:
729 case Surface.ROTATION_0:
731 case Surface.ROTATION_90:
732 return "top_vertical_left";
733 case Surface.ROTATION_180:
734 return "bottom_left";
735 case Surface.ROTATION_270:
736 return "bottom_vertical_right";
739 case "top_vertical_right":
741 case Surface.ROTATION_0:
742 return "top_vertical_right";
743 case Surface.ROTATION_90:
745 case Surface.ROTATION_180:
746 return "bottom_vertical_left";
747 case Surface.ROTATION_270:
748 return "bottom_right";
757 private static int getMaxNumOfColumns(Context context) {
758 SharedPreferences pref = getSharedPreferences(context);
759 DisplayMetrics metrics = getRealDisplayMetrics(context);
760 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / metrics.density;
761 int numOfColumns = 0;
763 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
764 ? (metrics.heightPixels - getStatusBarHeight(context)) / metrics.density
765 : metrics.widthPixels / metrics.density;
767 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
769 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
771 while(baseTaskbarSize + iconSize < maxScreenSize
772 && numOfColumns < userMaxNumOfColumns) {
773 baseTaskbarSize = baseTaskbarSize + iconSize;
780 public static int getMaxNumOfEntries(Context context) {
781 SharedPreferences pref = getSharedPreferences(context);
782 return pref.getBoolean("disable_scrolling_list", false)
783 ? getMaxNumOfColumns(context)
784 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
787 public static int getStatusBarHeight(Context context) {
788 int statusBarHeight = 0;
789 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
791 statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
793 return statusBarHeight;
796 public static void refreshPinnedIcons(Context context) {
797 IconCache.getInstance(context).clearCache();
799 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
800 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
801 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
802 PackageManager pm = context.getPackageManager();
806 for(AppEntry entry : pinnedAppsList) {
807 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
808 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
810 final List<UserHandle> userHandles = userManager.getUserProfiles();
811 LauncherActivityInfo appInfo = null;
813 for(UserHandle handle : userHandles) {
814 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
815 if(!list.isEmpty()) {
816 // Google App workaround
817 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
818 appInfo = list.get(0);
820 boolean added = false;
821 for(LauncherActivityInfo info : list) {
822 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
828 if(!added) appInfo = list.get(0);
835 if(appInfo != null) {
836 AppEntry newEntry = new AppEntry(
837 entry.getPackageName(),
838 entry.getComponentName(),
840 IconCache.getInstance(context).getIcon(context, pm, appInfo),
843 newEntry.setUserId(entry.getUserId(context));
844 pba.addPinnedApp(context, newEntry);
848 for(AppEntry entry : blockedAppsList) {
849 pba.addBlockedApp(context, entry);
853 public static Intent getShortcutIntent(Context context) {
854 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
855 shortcutIntent.setAction(Intent.ACTION_MAIN);
856 shortcutIntent.putExtra("is_launching_shortcut", true);
858 Intent intent = new Intent();
859 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
860 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
861 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
866 public static Intent getStartStopIntent(Context context) {
867 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
868 shortcutIntent.setAction(Intent.ACTION_MAIN);
869 shortcutIntent.putExtra("is_launching_shortcut", true);
871 Intent intent = new Intent();
872 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
873 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
874 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
879 public static boolean canEnableFreeform() {
880 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
883 @TargetApi(Build.VERSION_CODES.N)
884 public static boolean hasFreeformSupport(Context context) {
885 return canEnableFreeform()
886 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
887 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
888 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
889 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
892 public static boolean hasPartialFreeformSupport() {
893 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
896 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
897 return isServiceRunning(context, cls.getName());
900 private static boolean isServiceRunning(Context context, String className) {
901 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
902 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
903 if(className.equals(service.service.getClassName()))
910 public static int getBackgroundTint(Context context) {
911 SharedPreferences pref = getSharedPreferences(context);
913 // Import old background tint preference
914 if(pref.contains("show_background")) {
915 SharedPreferences.Editor editor = pref.edit();
917 if(!pref.getBoolean("show_background", true))
918 editor.putInt("background_tint", Color.TRANSPARENT).apply();
920 editor.remove("show_background");
924 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
927 public static int getAccentColor(Context context) {
928 SharedPreferences pref = getSharedPreferences(context);
929 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
932 @TargetApi(Build.VERSION_CODES.M)
933 public static boolean canDrawOverlays(Context context) {
934 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
937 public static boolean isGame(Context context, String packageName) {
938 SharedPreferences pref = getSharedPreferences(context);
939 if(pref.getBoolean("launch_games_fullscreen", true)) {
940 PackageManager pm = context.getPackageManager();
943 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
944 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
945 } catch (PackageManager.NameNotFoundException e) {
952 @TargetApi(Build.VERSION_CODES.N)
953 public static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType) {
954 ActivityOptions options = ActivityOptions.makeBasic();
957 switch(applicationType) {
959 if(FreeformHackHelper.getInstance().isFreeformHackActive())
960 stackId = getFreeformWindowModeId();
962 stackId = getFullscreenWindowModeId();
965 stackId = getFullscreenWindowModeId();
968 stackId = getFreeformWindowModeId();
971 if(hasBrokenSetLaunchBoundsApi()
972 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
973 stackId = getFullscreenWindowModeId();
978 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
979 method.invoke(options, stackId);
980 } catch (Exception e) { /* Gracefully fail */ }
985 private static int getFullscreenWindowModeId() {
986 if(getCurrentApiVersion() >= 28.0f)
987 return WINDOWING_MODE_FULLSCREEN;
989 return FULLSCREEN_WORKSPACE_STACK_ID;
992 private static int getFreeformWindowModeId() {
993 if(getCurrentApiVersion() >= 28.0f)
994 return WINDOWING_MODE_FREEFORM;
996 return FREEFORM_WORKSPACE_STACK_ID;
999 private static String getWindowingModeMethodName() {
1000 if(getCurrentApiVersion() >= 28.0f)
1001 return "setLaunchWindowingMode";
1003 return "setLaunchStackId";
1006 public static Bundle getActivityOptionsBundle(Context context, ApplicationType applicationType) {
1007 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
1010 return getActivityOptions(context, applicationType).toBundle();
1013 private static ApplicationType getApplicationType(Context context, String packageName) {
1014 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1017 public static boolean isSystemApp(Context context) {
1019 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1020 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1021 return (info.flags & mask) != 0;
1022 } catch (PackageManager.NameNotFoundException e) {
1027 public static boolean isChromeOs(Context context) {
1028 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1031 public static boolean isBlissOs(Context context) {
1032 boolean validBlissOsBuildProp = false;
1034 String blissVersion = DependencyUtils.getSystemProperty("ro.bliss.version");
1035 if(blissVersion != null && !blissVersion.isEmpty())
1036 validBlissOsBuildProp = true;
1038 String buildUser = DependencyUtils.getSystemProperty("ro.build.user");
1039 if(buildUser != null && buildUser.equals("electrikjesus"))
1040 validBlissOsBuildProp = true;
1042 return validBlissOsBuildProp
1043 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1044 && isSystemApp(context);
1047 public static boolean isLauncherPermanentlyEnabled(Context context) {
1048 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1051 PackageManager pm = context.getPackageManager();
1053 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1054 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
1055 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1056 && isSystemApp(context);
1057 } catch (PackageManager.NameNotFoundException e) {
1062 public static int getBaseTaskbarSize(Context context) {
1063 return Math.round(getBaseTaskbarSizeFloat(context));
1066 private static float getBaseTaskbarSizeFloat(Context context) {
1067 SharedPreferences pref = getSharedPreferences(context);
1068 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
1069 boolean navbarButtonsEnabled = false;
1071 if(pref.getBoolean("dashboard", false))
1072 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
1074 if(pref.getBoolean("button_back", false)) {
1075 navbarButtonsEnabled = true;
1076 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1079 if(pref.getBoolean("button_home", false)) {
1080 navbarButtonsEnabled = true;
1081 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1084 if(pref.getBoolean("button_recents", false)) {
1085 navbarButtonsEnabled = true;
1086 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1089 if(navbarButtonsEnabled)
1090 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
1092 return baseTaskbarSize;
1095 private static void startTaskbarService(Context context, boolean fullRestart) {
1096 context.startService(new Intent(context, TaskbarService.class));
1097 context.startService(new Intent(context, StartMenuService.class));
1098 context.startService(new Intent(context, DashboardService.class));
1099 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1102 private static void stopTaskbarService(Context context, boolean fullRestart) {
1103 context.stopService(new Intent(context, TaskbarService.class));
1104 context.stopService(new Intent(context, StartMenuService.class));
1105 context.stopService(new Intent(context, DashboardService.class));
1106 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1109 public static void restartTaskbar(Context context) {
1110 SharedPreferences pref = getSharedPreferences(context);
1111 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1113 .putBoolean("is_restarting", true)
1114 .putBoolean("skip_auto_hide_navbar", true)
1117 stopTaskbarService(context, true);
1118 startTaskbarService(context, true);
1119 } else if(isServiceRunning(context, StartMenuService.class)) {
1120 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1122 stopTaskbarService(context, false);
1123 startTaskbarService(context, false);
1127 public static void restartNotificationService(Context context) {
1128 if(isServiceRunning(context, NotificationService.class)) {
1129 SharedPreferences pref = getSharedPreferences(context);
1130 pref.edit().putBoolean("is_restarting", true).apply();
1132 Intent intent = new Intent(context, NotificationService.class);
1133 context.stopService(intent);
1134 context.startService(intent);
1138 public static void showHideNavigationBar(Context context, boolean show) {
1139 // Show or hide the system navigation bar on Bliss-x86
1141 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1142 } catch (Exception e) { /* Gracefully fail */ }
1145 public static void initPrefs(Context context) {
1146 // On smaller-screened devices, set "Grid" as the default start menu layout
1147 SharedPreferences pref = getSharedPreferences(context);
1148 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 720
1149 && pref.getString("start_menu_layout", "null").equals("null")) {
1150 pref.edit().putString("start_menu_layout", "grid").apply();
1153 // Enable freeform hack automatically on supported devices
1154 if(canEnableFreeform()) {
1155 if(!pref.getBoolean("freeform_hack_override", false)) {
1157 .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1158 .putBoolean("save_window_sizes", false)
1159 .putBoolean("freeform_hack_override", true)
1161 } else if(!hasFreeformSupport(context)) {
1162 pref.edit().putBoolean("freeform_hack", false).apply();
1164 stopFreeformHack(context);
1167 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1168 || pref.getBoolean("show_freeform_disabled_message", false);
1171 .putBoolean("freeform_hack", false)
1172 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1175 SavedWindowSizes.getInstance(context).clear(context);
1176 stopFreeformHack(context);
1179 // Customizations for BlissOS
1180 if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1181 && !pref.getBoolean("bliss_os_prefs", false)) {
1182 SharedPreferences.Editor editor = pref.edit();
1184 if(hasFreeformSupport(context)) {
1185 editor.putBoolean("freeform_hack", true);
1188 editor.putString("recents_amount", "running_apps_only");
1189 editor.putString("refresh_frequency", "0");
1190 editor.putString("max_num_of_recents", "2147483647");
1191 editor.putString("sort_order", "true");
1192 editor.putBoolean("full_length", true);
1193 editor.putBoolean("dashboard", true);
1194 editor.putBoolean("app_drawer_icon", true);
1195 editor.putBoolean("button_back", true);
1196 editor.putBoolean("button_home", true);
1197 editor.putBoolean("button_recents", true);
1198 editor.putBoolean("auto_hide_navbar", true);
1199 editor.putBoolean("bliss_os_prefs", true);
1202 Settings.Secure.putString(context.getContentResolver(),
1203 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
1204 new ComponentName(context, PowerMenuService.class).flattenToString());
1205 } catch (Exception e) { /* Gracefully fail */ }
1210 // Customizations for Android-x86 devices (non-Bliss)
1211 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1212 && isSystemApp(context)
1213 && !pref.getBoolean("android_x86_prefs", false)) {
1215 .putString("recents_amount", "running_apps_only")
1216 .putString("refresh_frequency", "0")
1217 .putString("max_num_of_recents", "2147483647")
1218 .putString("sort_order", "true")
1219 .putBoolean("full_length", true)
1220 .putBoolean("dashboard", true)
1221 .putBoolean("android_x86_prefs", true)
1226 public static DisplayMetrics getRealDisplayMetrics(Context context) {
1227 DisplayMetrics metrics = new DisplayMetrics();
1228 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1229 Display disp = wm.getDefaultDisplay();
1231 SharedPreferences pref = getSharedPreferences(context);
1232 if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1233 disp.getRealMetrics(metrics);
1235 disp.getMetrics(metrics);
1240 public static void pinAppShortcut(Context context) {
1241 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1242 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1244 if(mShortcutManager.isRequestPinShortcutSupported()) {
1245 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1247 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1249 showToastLong(context, R.string.pin_shortcut_not_supported);
1251 Intent intent = getShortcutIntent(context);
1252 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1253 intent.putExtra("duplicate", false);
1255 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1256 homeIntent.addCategory(Intent.CATEGORY_HOME);
1257 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1259 intent.setPackage(defaultLauncher.activityInfo.packageName);
1260 context.sendBroadcast(intent);
1262 showToast(context, R.string.shortcut_created);
1266 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1267 SharedPreferences pref = getSharedPreferences(context);
1268 if(pref.getBoolean("hide_taskbar", true)) {
1269 if(isOverridingFreeformHack(context))
1270 return !LauncherHelper.getInstance().isOnHomeScreen();
1272 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1273 if(pendingAppLaunch)
1274 return !helper.isFreeformHackActive();
1276 return !helper.isInFreeformWorkspace();
1282 public static boolean isOverridingFreeformHack(Context context) {
1283 SharedPreferences pref = getSharedPreferences(context);
1284 return pref.getBoolean("freeform_hack", false)
1285 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1286 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1289 @SuppressWarnings("unchecked")
1290 public static <T extends View> T findViewById(Activity target, int id) {
1291 return (T) target.findViewById(id);
1294 @SuppressWarnings("unchecked")
1295 public static <T extends View> T findViewById(View target, int id) {
1296 return (T) target.findViewById(id);
1299 public static boolean isPlayStoreInstalled(Context context) {
1301 context.getPackageManager().getPackageInfo("com.android.vending", 0);
1303 } catch (PackageManager.NameNotFoundException e) {
1308 private static float getCurrentApiVersion() {
1309 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1310 return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1312 return (float) Build.VERSION.SDK_INT;
1315 public static boolean hasBrokenSetLaunchBoundsApi() {
1316 return getCurrentApiVersion() >= 26.0f && getCurrentApiVersion() < 28.0f;
1319 public static String getSecondScreenPackageName(Context context) {
1320 return getInstalledPackage(context, Arrays.asList(
1321 "com.farmerbb.secondscreen.free",
1322 "com.farmerbb.secondscreen"));
1325 // Returns the name of an installed package from a list of package names, in order of preference
1326 private static String getInstalledPackage(Context context, List<String> packageNames) {
1327 if(packageNames == null || packageNames.isEmpty())
1330 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1331 String packageName = packages.get(0);
1334 context.getPackageManager().getPackageInfo(packageName, 0);
1336 } catch (PackageManager.NameNotFoundException e) {
1338 return getInstalledPackage(context, packages);
1342 public static boolean visualFeedbackEnabled(Context context) {
1343 SharedPreferences pref = getSharedPreferences(context);
1344 return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1345 && pref.getBoolean("visual_feedback", true);
1348 public static void showRecentAppsDialog(Context context) {
1349 showRecentAppsDialog(context, null, null);
1352 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1353 Runnable finalOnFinish = onFinish == null
1357 Runnable finalOnError = onError == null
1358 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1361 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1362 ApplicationInfo applicationInfo = null;
1364 applicationInfo = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1365 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1367 if(applicationInfo != null) {
1368 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1369 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1371 if(mode != AppOpsManager.MODE_ALLOWED) {
1372 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1373 builder.setTitle(R.string.pref_header_recent_apps)
1374 .setMessage(R.string.enable_recent_apps)
1375 .setPositiveButton(R.string.action_ok, (dialog, which) -> {
1377 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1378 showToastLong(context, R.string.usage_stats_message);
1380 finalOnFinish.run();
1381 } catch (ActivityNotFoundException e) {
1385 .setNegativeButton(R.string.action_cancel, (dialog, which) -> finalOnFinish.run());
1387 AlertDialog dialog = builder.create();
1389 dialog.setCancelable(false);
1396 finalOnFinish.run();
1400 public static Context wrapContext(Context context) {
1401 SharedPreferences pref = getSharedPreferences(context);
1404 switch(pref.getString("theme", "light")) {
1406 theme = R.style.AppTheme;
1409 theme = R.style.AppTheme_Dark;
1413 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1416 @SuppressLint("PackageManagerGetSignatures")
1417 public static boolean isPlayStoreRelease(Context context) {
1418 Signature playStoreSignature = new Signature(context.getString(R.string.signature));
1420 PackageManager pm = context.getPackageManager();
1421 PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
1422 for(Signature signature : info.signatures) {
1423 if(signature.equals(playStoreSignature))
1426 } catch (Exception e) { /* Gracefully fail */ }
1431 public static boolean isTaskerDisabled(Context context) {
1432 SharedPreferences pref = getSharedPreferences(context, Context.MODE_MULTI_PROCESS);
1433 return !pref.getBoolean("tasker_enabled", true);
1436 public static boolean enableFreeformModeShortcut(Context context) {
1437 return canEnableFreeform()
1438 && !U.isOverridingFreeformHack(context)
1439 && !U.isChromeOs(context);
1442 public static void startForegroundService(Context context, Intent intent) {
1443 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1444 if(Settings.canDrawOverlays(context))
1445 context.startForegroundService(intent);
1447 context.startService(intent);
1450 public static int getOverlayType() {
1451 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1452 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1453 : WindowManager.LayoutParams.TYPE_PHONE;