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 boolean noAnimation = pref.getBoolean("disable_animations", false);
286 if(hasFreeformSupport(context)
287 && pref.getBoolean("freeform_hack", false)
288 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
289 new Handler().postDelayed(() -> {
290 startFreeformHack(context, true);
292 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
293 }, launchedFromTaskbar ? 0 : 100);
295 new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
298 public static void startFreeformHack(Context context) {
299 startFreeformHack(context, false);
302 @TargetApi(Build.VERSION_CODES.N)
303 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
304 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
305 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
306 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
307 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
310 freeformHackIntent.putExtra("check_multiwindow", true);
312 if(canDrawOverlays(context))
313 startActivityLowerRight(context, freeformHackIntent);
316 public static void stopFreeformHack(Context context) {
317 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
319 if(isOverridingFreeformHack(context)) {
320 FreeformHackHelper helper = FreeformHackHelper.getInstance();
321 helper.setFreeformHackActive(false);
322 helper.setInFreeformWorkspace(false);
326 @TargetApi(Build.VERSION_CODES.N)
327 private static void continueLaunchingApp(Context context,
329 String componentName,
332 boolean openInNewWindow,
333 ShortcutInfo shortcut) {
334 SharedPreferences pref = getSharedPreferences(context);
335 Intent intent = new Intent();
336 intent.setComponent(ComponentName.unflattenFromString(componentName));
337 intent.setAction(Intent.ACTION_MAIN);
338 intent.addCategory(Intent.CATEGORY_LAUNCHER);
339 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
340 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
342 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
343 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
344 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
346 if(pref.getBoolean("disable_animations", false))
347 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
349 if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
350 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
352 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
353 if(activityInfo != null) {
354 switch(activityInfo.launchMode) {
355 case ActivityInfo.LAUNCH_SINGLE_TASK:
356 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
357 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
363 ApplicationType type = getApplicationType(context, packageName);
365 if(windowSize == null)
366 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
368 if(!canEnableFreeform()
369 || !pref.getBoolean("freeform_hack", false)
370 || windowSize.equals("standard")) {
371 launchStandard(context, intent, userId, shortcut, type);
372 } else switch(windowSize) {
374 launchMode1(context, intent, userId, shortcut, type);
377 launchMode2(context, intent, MAXIMIZED, userId, shortcut, type);
380 launchMode2(context, intent, LEFT, userId, shortcut, type);
383 launchMode2(context, intent, RIGHT, userId, shortcut, type);
386 launchMode3(context, intent, userId, shortcut, type);
390 if(shouldCollapse(context, true))
391 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
393 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
396 private static void launchStandard(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
397 Bundle bundle = canEnableFreeform() ? getActivityOptions(context, type).toBundle() : null;
398 if(shortcut == null) {
399 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
400 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
402 startActivity(context, intent, bundle);
403 } catch (ActivityNotFoundException e) {
404 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
405 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
407 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
409 launchShortcut(context, shortcut, bundle);
412 @SuppressWarnings("deprecation")
413 @TargetApi(Build.VERSION_CODES.N)
414 private static void launchMode1(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
415 DisplayMetrics metrics = getRealDisplayMetrics(context);
417 int width1 = metrics.widthPixels / 8;
418 int width2 = metrics.widthPixels - width1;
419 int height1 = metrics.heightPixels / 8;
420 int height2 = metrics.heightPixels - height1;
422 Bundle bundle = getActivityOptions(context, type).setLaunchBounds(new Rect(
429 if(shortcut == null) {
430 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
431 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
433 startActivity(context, intent, bundle);
434 } catch (ActivityNotFoundException e) {
435 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
436 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
438 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
440 launchShortcut(context, shortcut, bundle);
443 @SuppressWarnings("deprecation")
444 @TargetApi(Build.VERSION_CODES.N)
445 private static void launchMode2(Context context, Intent intent, int launchType, long userId, ShortcutInfo shortcut, ApplicationType type) {
446 DisplayMetrics metrics = getRealDisplayMetrics(context);
448 int statusBarHeight = getStatusBarHeight(context);
449 String position = getTaskbarPosition(context);
451 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
452 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
455 int top = statusBarHeight;
456 int right = metrics.widthPixels;
457 int bottom = metrics.heightPixels;
459 int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
461 : context.getResources().getDimensionPixelSize(R.dimen.icon_size);
463 if(position.contains("vertical_left"))
464 left = left + iconSize;
465 else if(position.contains("vertical_right"))
466 right = right - iconSize;
467 else if(position.contains("bottom"))
468 bottom = bottom - iconSize;
470 top = top + iconSize;
472 if(launchType == RIGHT && isLandscape)
473 left = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
474 else if(launchType == RIGHT && isPortrait)
475 top = (bottom / 2) + ((iconSize / 2)
476 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
477 else if(launchType == LEFT && isLandscape)
478 right = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
479 else if(launchType == LEFT && isPortrait)
480 bottom = (bottom / 2) + ((iconSize / 2)
481 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
483 Bundle bundle = getActivityOptions(context, type)
484 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
486 if(shortcut == null) {
487 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
488 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
490 startActivity(context, intent, bundle);
491 } catch (ActivityNotFoundException e) {
492 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
493 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
495 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
497 launchShortcut(context, shortcut, bundle);
500 @SuppressWarnings("deprecation")
501 @TargetApi(Build.VERSION_CODES.N)
502 private static void launchMode3(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
503 DisplayMetrics metrics = getRealDisplayMetrics(context);
505 int width1 = metrics.widthPixels / 2;
506 int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
507 int height1 = metrics.heightPixels / 2;
508 int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
510 Bundle bundle = getActivityOptions(context, type).setLaunchBounds(new Rect(
517 if(shortcut == null) {
518 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
519 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
521 startActivity(context, intent, bundle);
522 } catch (ActivityNotFoundException e) {
523 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
524 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
526 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
528 launchShortcut(context, shortcut, bundle);
531 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
532 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
533 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
536 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
537 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
540 @TargetApi(Build.VERSION_CODES.N_MR1)
541 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
542 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
544 if(launcherApps.hasShortcutHostPermission()) {
546 launcherApps.startShortcut(shortcut, null, bundle);
547 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
551 private static void startActivity(Context context, Intent intent, Bundle bundle) {
552 boolean shouldLaunchTouchAbsorber =
553 !FreeformHackHelper.getInstance().isTouchAbsorberActive()
554 && isOverridingFreeformHack(context)
555 && !isChromeOs(context);
557 if(!shouldLaunchTouchAbsorber) {
558 context.startActivity(intent, bundle);
562 startTouchAbsorberActivity(context);
563 new Handler().postDelayed(() -> context.startActivity(intent, bundle), 100);
566 public static void startActivityMaximized(Context context, Intent intent) {
567 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
568 long userId = userManager.getSerialNumberForUser(Process.myUserHandle());
570 launchMode2(context, intent, MAXIMIZED, userId, null, ApplicationType.CONTEXT_MENU);
573 @TargetApi(Build.VERSION_CODES.N)
574 public static void startActivityLowerRight(Context context, Intent intent) {
575 DisplayMetrics metrics = getRealDisplayMetrics(context);
577 context.startActivity(intent,
578 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
579 .setLaunchBounds(new Rect(
581 metrics.heightPixels,
582 metrics.widthPixels + 1,
583 metrics.heightPixels + 1
585 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
588 @TargetApi(Build.VERSION_CODES.N)
589 public static void startTouchAbsorberActivity(Context context) {
590 String position = getTaskbarPosition(context);
591 DisplayMetrics metrics = getRealDisplayMetrics(context);
595 int right = metrics.widthPixels;
596 int bottom = metrics.heightPixels;
598 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
600 if(position.contains("vertical_left"))
602 else if(position.contains("vertical_right"))
603 left = right - iconSize;
604 else if(position.contains("bottom"))
605 top = bottom - iconSize;
609 Intent intent = new Intent(context, TouchAbsorberActivity.class);
610 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
611 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
614 context.startActivity(intent,
615 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
616 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle());
617 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
620 public static void checkForUpdates(Context context) {
622 if(isPlayStoreRelease(context))
623 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
625 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.APPLICATION_ID;
627 Intent intent = new Intent(Intent.ACTION_VIEW);
628 intent.setData(Uri.parse(url));
629 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
632 context.startActivity(intent);
633 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
636 public static boolean launcherIsDefault(Context context) {
637 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
638 homeIntent.addCategory(Intent.CATEGORY_HOME);
639 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
641 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
644 public static void setCachedRotation(int cachedRotation) {
645 U.cachedRotation = cachedRotation;
648 public static String getTaskbarPosition(Context context) {
649 SharedPreferences pref = getSharedPreferences(context);
650 String position = pref.getString("position", "bottom_left");
652 if(pref.getBoolean("anchor", false)) {
653 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
654 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
659 case Surface.ROTATION_0:
660 return "bottom_left";
661 case Surface.ROTATION_90:
662 return "bottom_vertical_right";
663 case Surface.ROTATION_180:
665 case Surface.ROTATION_270:
666 return "top_vertical_left";
669 case "bottom_vertical_left":
671 case Surface.ROTATION_0:
672 return "bottom_vertical_left";
673 case Surface.ROTATION_90:
674 return "bottom_right";
675 case Surface.ROTATION_180:
676 return "top_vertical_right";
677 case Surface.ROTATION_270:
683 case Surface.ROTATION_0:
684 return "bottom_right";
685 case Surface.ROTATION_90:
686 return "top_vertical_right";
687 case Surface.ROTATION_180:
689 case Surface.ROTATION_270:
690 return "bottom_vertical_left";
693 case "bottom_vertical_right":
695 case Surface.ROTATION_0:
696 return "bottom_vertical_right";
697 case Surface.ROTATION_90:
699 case Surface.ROTATION_180:
700 return "top_vertical_left";
701 case Surface.ROTATION_270:
702 return "bottom_left";
707 case Surface.ROTATION_0:
709 case Surface.ROTATION_90:
710 return "bottom_vertical_left";
711 case Surface.ROTATION_180:
712 return "bottom_right";
713 case Surface.ROTATION_270:
714 return "top_vertical_right";
717 case "top_vertical_left":
719 case Surface.ROTATION_0:
720 return "top_vertical_left";
721 case Surface.ROTATION_90:
722 return "bottom_left";
723 case Surface.ROTATION_180:
724 return "bottom_vertical_right";
725 case Surface.ROTATION_270:
731 case Surface.ROTATION_0:
733 case Surface.ROTATION_90:
734 return "top_vertical_left";
735 case Surface.ROTATION_180:
736 return "bottom_left";
737 case Surface.ROTATION_270:
738 return "bottom_vertical_right";
741 case "top_vertical_right":
743 case Surface.ROTATION_0:
744 return "top_vertical_right";
745 case Surface.ROTATION_90:
747 case Surface.ROTATION_180:
748 return "bottom_vertical_left";
749 case Surface.ROTATION_270:
750 return "bottom_right";
759 private static int getMaxNumOfColumns(Context context) {
760 SharedPreferences pref = getSharedPreferences(context);
761 DisplayMetrics metrics = getRealDisplayMetrics(context);
762 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / metrics.density;
763 int numOfColumns = 0;
765 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
766 ? (metrics.heightPixels - getStatusBarHeight(context)) / metrics.density
767 : metrics.widthPixels / metrics.density;
769 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
771 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
773 while(baseTaskbarSize + iconSize < maxScreenSize
774 && numOfColumns < userMaxNumOfColumns) {
775 baseTaskbarSize = baseTaskbarSize + iconSize;
782 public static int getMaxNumOfEntries(Context context) {
783 SharedPreferences pref = getSharedPreferences(context);
784 return pref.getBoolean("disable_scrolling_list", false)
785 ? getMaxNumOfColumns(context)
786 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
789 public static int getStatusBarHeight(Context context) {
790 int statusBarHeight = 0;
791 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
793 statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
795 return statusBarHeight;
798 public static void refreshPinnedIcons(Context context) {
799 IconCache.getInstance(context).clearCache();
801 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
802 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
803 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
804 PackageManager pm = context.getPackageManager();
808 for(AppEntry entry : pinnedAppsList) {
809 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
810 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
812 final List<UserHandle> userHandles = userManager.getUserProfiles();
813 LauncherActivityInfo appInfo = null;
815 for(UserHandle handle : userHandles) {
816 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
817 if(!list.isEmpty()) {
818 // Google App workaround
819 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
820 appInfo = list.get(0);
822 boolean added = false;
823 for(LauncherActivityInfo info : list) {
824 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
830 if(!added) appInfo = list.get(0);
837 if(appInfo != null) {
838 AppEntry newEntry = new AppEntry(
839 entry.getPackageName(),
840 entry.getComponentName(),
842 IconCache.getInstance(context).getIcon(context, pm, appInfo),
845 newEntry.setUserId(entry.getUserId(context));
846 pba.addPinnedApp(context, newEntry);
850 for(AppEntry entry : blockedAppsList) {
851 pba.addBlockedApp(context, entry);
855 public static Intent getShortcutIntent(Context context) {
856 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
857 shortcutIntent.setAction(Intent.ACTION_MAIN);
858 shortcutIntent.putExtra("is_launching_shortcut", true);
860 Intent intent = new Intent();
861 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
862 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
863 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
868 public static Intent getStartStopIntent(Context context) {
869 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
870 shortcutIntent.setAction(Intent.ACTION_MAIN);
871 shortcutIntent.putExtra("is_launching_shortcut", true);
873 Intent intent = new Intent();
874 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
875 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
876 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
881 public static boolean canEnableFreeform() {
882 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
885 @TargetApi(Build.VERSION_CODES.N)
886 public static boolean hasFreeformSupport(Context context) {
887 return canEnableFreeform()
888 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
889 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
890 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
891 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
894 public static boolean hasPartialFreeformSupport() {
895 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
898 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
899 return isServiceRunning(context, cls.getName());
902 private static boolean isServiceRunning(Context context, String className) {
903 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
904 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
905 if(className.equals(service.service.getClassName()))
912 public static int getBackgroundTint(Context context) {
913 SharedPreferences pref = getSharedPreferences(context);
915 // Import old background tint preference
916 if(pref.contains("show_background")) {
917 SharedPreferences.Editor editor = pref.edit();
919 if(!pref.getBoolean("show_background", true))
920 editor.putInt("background_tint", Color.TRANSPARENT).apply();
922 editor.remove("show_background");
926 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
929 public static int getAccentColor(Context context) {
930 SharedPreferences pref = getSharedPreferences(context);
931 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
934 @TargetApi(Build.VERSION_CODES.M)
935 public static boolean canDrawOverlays(Context context) {
936 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
939 public static boolean isGame(Context context, String packageName) {
940 SharedPreferences pref = getSharedPreferences(context);
941 if(pref.getBoolean("launch_games_fullscreen", true)) {
942 PackageManager pm = context.getPackageManager();
945 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
946 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
947 } catch (PackageManager.NameNotFoundException e) {
954 @TargetApi(Build.VERSION_CODES.N)
955 public static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType) {
956 ActivityOptions options = ActivityOptions.makeBasic();
959 switch(applicationType) {
961 if(FreeformHackHelper.getInstance().isFreeformHackActive())
962 stackId = getFreeformWindowModeId();
964 stackId = getFullscreenWindowModeId();
967 stackId = getFullscreenWindowModeId();
970 stackId = getFreeformWindowModeId();
973 if(hasBrokenSetLaunchBoundsApi()
974 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
975 stackId = getFullscreenWindowModeId();
980 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
981 method.invoke(options, stackId);
982 } catch (Exception e) { /* Gracefully fail */ }
987 private static int getFullscreenWindowModeId() {
988 if(getCurrentApiVersion() >= 28.0f)
989 return WINDOWING_MODE_FULLSCREEN;
991 return FULLSCREEN_WORKSPACE_STACK_ID;
994 private static int getFreeformWindowModeId() {
995 if(getCurrentApiVersion() >= 28.0f)
996 return WINDOWING_MODE_FREEFORM;
998 return FREEFORM_WORKSPACE_STACK_ID;
1001 private static String getWindowingModeMethodName() {
1002 if(getCurrentApiVersion() >= 28.0f)
1003 return "setLaunchWindowingMode";
1005 return "setLaunchStackId";
1008 public static Bundle getActivityOptionsBundle(Context context, ApplicationType applicationType) {
1009 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
1012 return getActivityOptions(context, applicationType).toBundle();
1015 private static ApplicationType getApplicationType(Context context, String packageName) {
1016 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1019 public static boolean isSystemApp(Context context) {
1021 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1022 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1023 return (info.flags & mask) != 0;
1024 } catch (PackageManager.NameNotFoundException e) {
1029 public static boolean isChromeOs(Context context) {
1030 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1033 public static boolean isBlissOs(Context context) {
1034 boolean validBlissOsBuildProp = false;
1036 String blissVersion = DependencyUtils.getSystemProperty("ro.bliss.version");
1037 if(blissVersion != null && !blissVersion.isEmpty())
1038 validBlissOsBuildProp = true;
1040 String buildUser = DependencyUtils.getSystemProperty("ro.build.user");
1041 if(buildUser != null && buildUser.equals("electrikjesus"))
1042 validBlissOsBuildProp = true;
1044 return validBlissOsBuildProp
1045 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1046 && isSystemApp(context);
1049 public static boolean isLauncherPermanentlyEnabled(Context context) {
1050 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1053 PackageManager pm = context.getPackageManager();
1055 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1056 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
1057 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1058 && isSystemApp(context);
1059 } catch (PackageManager.NameNotFoundException e) {
1064 public static int getBaseTaskbarSize(Context context) {
1065 return Math.round(getBaseTaskbarSizeFloat(context));
1068 private static float getBaseTaskbarSizeFloat(Context context) {
1069 SharedPreferences pref = getSharedPreferences(context);
1070 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
1071 boolean navbarButtonsEnabled = false;
1073 if(pref.getBoolean("dashboard", false))
1074 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
1076 if(pref.getBoolean("button_back", false)) {
1077 navbarButtonsEnabled = true;
1078 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1081 if(pref.getBoolean("button_home", false)) {
1082 navbarButtonsEnabled = true;
1083 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1086 if(pref.getBoolean("button_recents", false)) {
1087 navbarButtonsEnabled = true;
1088 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1091 if(navbarButtonsEnabled)
1092 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
1094 return baseTaskbarSize;
1097 private static void startTaskbarService(Context context, boolean fullRestart) {
1098 context.startService(new Intent(context, TaskbarService.class));
1099 context.startService(new Intent(context, StartMenuService.class));
1100 context.startService(new Intent(context, DashboardService.class));
1101 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1104 private static void stopTaskbarService(Context context, boolean fullRestart) {
1105 context.stopService(new Intent(context, TaskbarService.class));
1106 context.stopService(new Intent(context, StartMenuService.class));
1107 context.stopService(new Intent(context, DashboardService.class));
1108 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1111 public static void restartTaskbar(Context context) {
1112 SharedPreferences pref = getSharedPreferences(context);
1113 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1115 .putBoolean("is_restarting", true)
1116 .putBoolean("skip_auto_hide_navbar", true)
1119 stopTaskbarService(context, true);
1120 startTaskbarService(context, true);
1121 } else if(isServiceRunning(context, StartMenuService.class)) {
1122 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1124 stopTaskbarService(context, false);
1125 startTaskbarService(context, false);
1129 public static void restartNotificationService(Context context) {
1130 if(isServiceRunning(context, NotificationService.class)) {
1131 SharedPreferences pref = getSharedPreferences(context);
1132 pref.edit().putBoolean("is_restarting", true).apply();
1134 Intent intent = new Intent(context, NotificationService.class);
1135 context.stopService(intent);
1136 context.startService(intent);
1140 public static void showHideNavigationBar(Context context, boolean show) {
1141 // Show or hide the system navigation bar on Bliss-x86
1143 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1144 } catch (Exception e) { /* Gracefully fail */ }
1147 public static void initPrefs(Context context) {
1148 // On smaller-screened devices, set "Grid" as the default start menu layout
1149 SharedPreferences pref = getSharedPreferences(context);
1150 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 720
1151 && pref.getString("start_menu_layout", "null").equals("null")) {
1152 pref.edit().putString("start_menu_layout", "grid").apply();
1155 // Enable freeform hack automatically on supported devices
1156 if(canEnableFreeform()) {
1157 if(!pref.getBoolean("freeform_hack_override", false)) {
1159 .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1160 .putBoolean("save_window_sizes", false)
1161 .putBoolean("freeform_hack_override", true)
1163 } else if(!hasFreeformSupport(context)) {
1164 pref.edit().putBoolean("freeform_hack", false).apply();
1166 stopFreeformHack(context);
1169 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1170 || pref.getBoolean("show_freeform_disabled_message", false);
1173 .putBoolean("freeform_hack", false)
1174 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1177 SavedWindowSizes.getInstance(context).clear(context);
1178 stopFreeformHack(context);
1181 // Customizations for BlissOS
1182 if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1183 && !pref.getBoolean("bliss_os_prefs", false)) {
1184 SharedPreferences.Editor editor = pref.edit();
1186 if(hasFreeformSupport(context)) {
1187 editor.putBoolean("freeform_hack", true);
1190 editor.putString("recents_amount", "running_apps_only");
1191 editor.putString("refresh_frequency", "0");
1192 editor.putString("max_num_of_recents", "2147483647");
1193 editor.putString("sort_order", "true");
1194 editor.putBoolean("full_length", true);
1195 editor.putBoolean("dashboard", true);
1196 editor.putBoolean("app_drawer_icon", true);
1197 editor.putBoolean("button_back", true);
1198 editor.putBoolean("button_home", true);
1199 editor.putBoolean("button_recents", true);
1200 editor.putBoolean("auto_hide_navbar", true);
1201 editor.putBoolean("bliss_os_prefs", true);
1204 Settings.Secure.putString(context.getContentResolver(),
1205 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
1206 new ComponentName(context, PowerMenuService.class).flattenToString());
1207 } catch (Exception e) { /* Gracefully fail */ }
1212 // Customizations for Android-x86 devices (non-Bliss)
1213 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1214 && isSystemApp(context)
1215 && !pref.getBoolean("android_x86_prefs", false)) {
1217 .putString("recents_amount", "running_apps_only")
1218 .putString("refresh_frequency", "0")
1219 .putString("max_num_of_recents", "2147483647")
1220 .putString("sort_order", "true")
1221 .putBoolean("full_length", true)
1222 .putBoolean("dashboard", true)
1223 .putBoolean("android_x86_prefs", true)
1228 public static DisplayMetrics getRealDisplayMetrics(Context context) {
1229 DisplayMetrics metrics = new DisplayMetrics();
1230 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1231 Display disp = wm.getDefaultDisplay();
1233 SharedPreferences pref = getSharedPreferences(context);
1234 if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1235 disp.getRealMetrics(metrics);
1237 disp.getMetrics(metrics);
1242 public static void pinAppShortcut(Context context) {
1243 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1244 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1246 if(mShortcutManager.isRequestPinShortcutSupported()) {
1247 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1249 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1251 showToastLong(context, R.string.pin_shortcut_not_supported);
1253 Intent intent = getShortcutIntent(context);
1254 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1255 intent.putExtra("duplicate", false);
1257 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1258 homeIntent.addCategory(Intent.CATEGORY_HOME);
1259 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1261 intent.setPackage(defaultLauncher.activityInfo.packageName);
1262 context.sendBroadcast(intent);
1264 showToast(context, R.string.shortcut_created);
1268 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1269 SharedPreferences pref = getSharedPreferences(context);
1270 if(pref.getBoolean("hide_taskbar", true)) {
1271 if(isOverridingFreeformHack(context))
1272 return !LauncherHelper.getInstance().isOnHomeScreen();
1274 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1275 if(pendingAppLaunch)
1276 return !helper.isFreeformHackActive();
1278 return !helper.isInFreeformWorkspace();
1284 public static boolean isOverridingFreeformHack(Context context) {
1285 SharedPreferences pref = getSharedPreferences(context);
1286 return pref.getBoolean("freeform_hack", false)
1287 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1288 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1291 @SuppressWarnings("unchecked")
1292 public static <T extends View> T findViewById(Activity target, int id) {
1293 return (T) target.findViewById(id);
1296 @SuppressWarnings("unchecked")
1297 public static <T extends View> T findViewById(View target, int id) {
1298 return (T) target.findViewById(id);
1301 public static boolean isPlayStoreInstalled(Context context) {
1303 context.getPackageManager().getPackageInfo("com.android.vending", 0);
1305 } catch (PackageManager.NameNotFoundException e) {
1310 private static float getCurrentApiVersion() {
1311 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1312 return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1314 return (float) Build.VERSION.SDK_INT;
1317 public static boolean hasBrokenSetLaunchBoundsApi() {
1318 return getCurrentApiVersion() >= 26.0f && getCurrentApiVersion() < 28.0f;
1321 public static String getSecondScreenPackageName(Context context) {
1322 return getInstalledPackage(context, Arrays.asList(
1323 "com.farmerbb.secondscreen.free",
1324 "com.farmerbb.secondscreen"));
1327 // Returns the name of an installed package from a list of package names, in order of preference
1328 private static String getInstalledPackage(Context context, List<String> packageNames) {
1329 if(packageNames == null || packageNames.isEmpty())
1332 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1333 String packageName = packages.get(0);
1336 context.getPackageManager().getPackageInfo(packageName, 0);
1338 } catch (PackageManager.NameNotFoundException e) {
1340 return getInstalledPackage(context, packages);
1344 public static boolean visualFeedbackEnabled(Context context) {
1345 SharedPreferences pref = getSharedPreferences(context);
1346 return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1347 && pref.getBoolean("visual_feedback", true);
1350 public static void showRecentAppsDialog(Context context) {
1351 showRecentAppsDialog(context, null, null);
1354 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1355 Runnable finalOnFinish = onFinish == null
1359 Runnable finalOnError = onError == null
1360 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1363 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1364 ApplicationInfo applicationInfo = null;
1366 applicationInfo = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1367 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1369 if(applicationInfo != null) {
1370 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1371 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1373 if(mode != AppOpsManager.MODE_ALLOWED) {
1374 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1375 builder.setTitle(R.string.pref_header_recent_apps)
1376 .setMessage(R.string.enable_recent_apps)
1377 .setPositiveButton(R.string.action_ok, (dialog, which) -> {
1379 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1380 showToastLong(context, R.string.usage_stats_message);
1382 finalOnFinish.run();
1383 } catch (ActivityNotFoundException e) {
1387 .setNegativeButton(R.string.action_cancel, (dialog, which) -> finalOnFinish.run());
1389 AlertDialog dialog = builder.create();
1391 dialog.setCancelable(false);
1398 finalOnFinish.run();
1402 public static Context wrapContext(Context context) {
1403 SharedPreferences pref = getSharedPreferences(context);
1406 switch(pref.getString("theme", "light")) {
1408 theme = R.style.AppTheme;
1411 theme = R.style.AppTheme_Dark;
1415 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1418 @SuppressLint("PackageManagerGetSignatures")
1419 public static boolean isPlayStoreRelease(Context context) {
1420 Signature playStoreSignature = new Signature(context.getString(R.string.signature));
1422 PackageManager pm = context.getPackageManager();
1423 PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
1424 for(Signature signature : info.signatures) {
1425 if(signature.equals(playStoreSignature))
1428 } catch (Exception e) { /* Gracefully fail */ }
1433 public static boolean isTaskerDisabled(Context context) {
1434 SharedPreferences pref = getSharedPreferences(context, Context.MODE_MULTI_PROCESS);
1435 return !pref.getBoolean("tasker_enabled", true);
1438 public static boolean enableFreeformModeShortcut(Context context) {
1439 return canEnableFreeform()
1440 && !U.isOverridingFreeformHack(context)
1441 && !U.isChromeOs(context);
1444 public static void startForegroundService(Context context, Intent intent) {
1445 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1446 if(Settings.canDrawOverlays(context))
1447 context.startForegroundService(intent);
1449 context.startService(intent);
1452 public static int getOverlayType() {
1453 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1454 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1455 : WindowManager.LayoutParams.TYPE_PHONE;