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.Manifest;
19 import android.annotation.SuppressLint;
20 import android.annotation.TargetApi;
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.WindowManager;
59 import android.widget.Toast;
61 import com.farmerbb.taskbar.BuildConfig;
62 import com.farmerbb.taskbar.R;
63 import com.farmerbb.taskbar.activity.ContextMenuActivity;
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.activity.dark.ContextMenuActivityDark;
70 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
71 import com.farmerbb.taskbar.service.DashboardService;
72 import com.farmerbb.taskbar.service.NotificationService;
73 import com.farmerbb.taskbar.service.PowerMenuService;
74 import com.farmerbb.taskbar.service.StartMenuService;
75 import com.farmerbb.taskbar.service.TaskbarService;
77 import java.lang.reflect.Method;
78 import java.util.ArrayList;
79 import java.util.Arrays;
80 import java.util.List;
86 private static Integer cachedRotation;
88 private static final int MAXIMIZED = 0;
89 private static final int LEFT = -1;
90 private static final int RIGHT = 1;
92 public static final int HIDDEN = 0;
93 public static final int TOP_APPS = 1;
95 // From android.app.ActivityManager.StackId
96 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
97 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
99 // From android.app.WindowConfiguration
100 private static final int WINDOWING_MODE_FULLSCREEN = 1;
101 private static final int WINDOWING_MODE_FREEFORM = 5;
103 public static SharedPreferences getSharedPreferences(Context context) {
104 return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
107 public static void showPermissionDialog(Context context) {
108 showPermissionDialog(context, null, null);
111 @TargetApi(Build.VERSION_CODES.M)
112 public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
113 Runnable finalOnFinish = onFinish == null
117 Runnable finalOnError = onError == null
118 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
121 AlertDialog.Builder builder = new AlertDialog.Builder(context);
122 builder.setTitle(R.string.permission_dialog_title)
123 .setMessage(R.string.permission_dialog_message)
124 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
126 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
127 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
130 } catch (ActivityNotFoundException e) {
135 AlertDialog dialog = builder.create();
137 dialog.setCancelable(false);
142 public static AlertDialog showErrorDialog(Context context, String appopCmd) {
143 return showErrorDialog(context, appopCmd, null);
146 private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
147 Runnable finalOnFinish = onFinish == null
151 AlertDialog.Builder builder = new AlertDialog.Builder(context);
152 builder.setTitle(R.string.error_dialog_title)
153 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
154 .setPositiveButton(R.string.action_ok, (dialog, which) -> finalOnFinish.run());
156 AlertDialog dialog = builder.create();
158 dialog.setCancelable(false);
163 public static void lockDevice(Context context) {
164 ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
165 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
166 PackageManager.DONT_KILL_APP);
168 DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
169 if(mDevicePolicyManager.isAdminActive(component))
170 mDevicePolicyManager.lockNow();
172 launchApp(context, () -> {
173 Intent intent = new Intent(context, DummyActivity.class);
174 intent.putExtra("device_admin", true);
175 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
178 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
179 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
184 public static void sendAccessibilityAction(Context context, int action) {
185 sendAccessibilityAction(context, action, null);
188 public static void sendAccessibilityAction(Context context, int action, Runnable onComplete) {
189 ComponentName component = new ComponentName(context, PowerMenuService.class);
190 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
191 PackageManager.DONT_KILL_APP);
193 boolean isAccessibilityServiceEnabled = isAccessibilityServiceEnabled(context);
195 if(!isAccessibilityServiceEnabled
196 && hasWriteSecureSettingsPermission(context)) {
197 String services = Settings.Secure.getString(context.getContentResolver(),
198 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
200 String finalServices = services == null ? "" : services;
202 String powerMenuService = new ComponentName(context, PowerMenuService.class).flattenToString();
204 if(!finalServices.contains(powerMenuService)) {
206 Settings.Secure.putString(context.getContentResolver(),
207 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
208 finalServices.isEmpty()
210 : finalServices + ":" + powerMenuService);
211 } catch (Exception e) { /* Gracefully fail */ }
214 new Handler().postDelayed(() -> {
215 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
216 intent.putExtra("action", action);
217 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
220 Settings.Secure.putString(context.getContentResolver(),
221 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
223 } catch (Exception e) { /* Gracefully fail */ }
225 if(onComplete != null) onComplete.run();
227 } else if(isAccessibilityServiceEnabled) {
228 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
229 intent.putExtra("action", action);
230 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
232 if(onComplete != null) onComplete.run();
234 launchApp(context, () -> {
235 Intent intent = new Intent(context, DummyActivity.class);
236 intent.putExtra("accessibility", true);
237 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
240 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
241 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
246 public static boolean isAccessibilityServiceEnabled(Context context) {
247 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
248 ComponentName component = new ComponentName(context, PowerMenuService.class);
250 return accessibilityServices != null
251 && (accessibilityServices.contains(component.flattenToString())
252 || accessibilityServices.contains(component.flattenToShortString()));
255 public static boolean hasWriteSecureSettingsPermission(Context context) {
256 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
257 && context.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
260 public static void showToast(Context context, int message) {
261 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
264 public static void showToastLong(Context context, int message) {
265 showToast(context, context.getString(message), Toast.LENGTH_LONG);
268 public static void showToast(Context context, String message, int length) {
271 ToastInterface toast = DependencyUtils.createToast(context, message, length);
274 ToastHelper.getInstance().setLastToast(toast);
277 public static void cancelToast() {
278 ToastInterface toast = ToastHelper.getInstance().getLastToast();
279 if(toast != null) toast.cancel();
282 public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
293 public static void launchApp(final Context context,
294 final String packageName,
295 final String componentName,
296 final long userId, final String windowSize,
297 final boolean launchedFromTaskbar,
298 final boolean openInNewWindow) {
309 private static void launchApp(final Context context,
310 final String packageName,
311 final String componentName,
312 final long userId, final String windowSize,
313 final boolean launchedFromTaskbar,
314 final boolean openInNewWindow,
315 final ShortcutInfo shortcut) {
316 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, packageName, componentName, userId,
317 windowSize, openInNewWindow, shortcut));
320 public static void launchApp(Context context, Runnable runnable) {
321 launchApp(context, true, runnable);
324 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
325 SharedPreferences pref = getSharedPreferences(context);
326 FreeformHackHelper helper = FreeformHackHelper.getInstance();
328 boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
329 && helper.isInFreeformWorkspace()
330 && MenuHelper.getInstance().isContextMenuOpen();
332 boolean noAnimation = pref.getBoolean("disable_animations", false);
334 if(hasFreeformSupport(context)
335 && pref.getBoolean("freeform_hack", false)
336 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
337 new Handler().postDelayed(() -> {
338 startFreeformHack(context, true);
340 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
341 }, launchedFromTaskbar ? 0 : 100);
343 new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
346 public static void startFreeformHack(Context context) {
347 startFreeformHack(context, false);
350 @TargetApi(Build.VERSION_CODES.N)
351 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
352 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
353 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
354 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
355 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
358 freeformHackIntent.putExtra("check_multiwindow", true);
360 if(canDrawOverlays(context))
361 startActivityLowerRight(context, freeformHackIntent);
364 public static void stopFreeformHack(Context context) {
365 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
367 if(isOverridingFreeformHack(context)) {
368 FreeformHackHelper helper = FreeformHackHelper.getInstance();
369 helper.setFreeformHackActive(false);
370 helper.setInFreeformWorkspace(false);
374 @TargetApi(Build.VERSION_CODES.N)
375 private static void continueLaunchingApp(Context context,
377 String componentName,
380 boolean openInNewWindow,
381 ShortcutInfo shortcut) {
382 SharedPreferences pref = getSharedPreferences(context);
383 Intent intent = new Intent();
384 intent.setComponent(ComponentName.unflattenFromString(componentName));
385 intent.setAction(Intent.ACTION_MAIN);
386 intent.addCategory(Intent.CATEGORY_LAUNCHER);
387 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
388 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
390 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
391 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
392 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
394 if(pref.getBoolean("disable_animations", false))
395 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
397 if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
398 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
400 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
401 if(activityInfo != null) {
402 switch(activityInfo.launchMode) {
403 case ActivityInfo.LAUNCH_SINGLE_TASK:
404 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
405 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
411 ApplicationType type = getApplicationType(context, packageName);
413 if(windowSize == null)
414 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
416 Bundle bundle = getActivityOptionsBundle(context, type, windowSize);
418 prepareToStartActivity(context, () -> {
419 if(shortcut == null) {
420 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
421 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
423 context.startActivity(intent, bundle);
424 } catch (ActivityNotFoundException e) {
425 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
426 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
428 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
430 launchShortcut(context, shortcut, bundle);
433 if(shouldCollapse(context, true))
434 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
436 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
439 @SuppressWarnings("deprecation")
440 @TargetApi(Build.VERSION_CODES.N)
441 private static Bundle launchMode1(Context context, ApplicationType type) {
442 DisplayInfo display = getDisplayInfo(context);
444 int width1 = display.width / 8;
445 int width2 = display.width - width1;
446 int height1 = display.height / 8;
447 int height2 = display.height - height1;
449 return getActivityOptions(context, type).setLaunchBounds(new Rect(
457 @SuppressWarnings("deprecation")
458 @TargetApi(Build.VERSION_CODES.N)
459 private static Bundle launchMode2(Context context, int launchType, ApplicationType type) {
460 DisplayInfo display = getDisplayInfo(context);
462 int statusBarHeight = getStatusBarHeight(context);
463 String position = getTaskbarPosition(context);
465 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
466 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
469 int top = statusBarHeight;
470 int right = display.width;
471 int bottom = display.height;
473 int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
475 : context.getResources().getDimensionPixelSize(R.dimen.icon_size);
477 if(position.contains("vertical_left"))
478 left = left + iconSize;
479 else if(position.contains("vertical_right"))
480 right = right - iconSize;
481 else if(position.contains("bottom"))
482 bottom = bottom - iconSize;
484 top = top + iconSize;
486 if(launchType == RIGHT && isLandscape)
487 left = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
488 else if(launchType == RIGHT && isPortrait)
489 top = (bottom / 2) + ((iconSize / 2)
490 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
491 else if(launchType == LEFT && isLandscape)
492 right = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
493 else if(launchType == LEFT && isPortrait)
494 bottom = (bottom / 2) + ((iconSize / 2)
495 * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
497 return getActivityOptions(context, type)
498 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
501 @SuppressWarnings("deprecation")
502 @TargetApi(Build.VERSION_CODES.N)
503 private static Bundle launchMode3(Context context, ApplicationType type) {
504 DisplayInfo display = getDisplayInfo(context);
506 int width1 = display.width / 2;
507 int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
508 int height1 = display.height / 2;
509 int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
511 return getActivityOptions(context, type).setLaunchBounds(new Rect(
519 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
520 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
521 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
524 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
525 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
528 @TargetApi(Build.VERSION_CODES.N_MR1)
529 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
530 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
532 if(launcherApps.hasShortcutHostPermission()) {
534 launcherApps.startShortcut(shortcut, null, bundle);
535 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
539 private static void prepareToStartActivity(Context context, Runnable runnable) {
540 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_CONTEXT_MENU"));
542 boolean shouldLaunchTouchAbsorber =
543 !FreeformHackHelper.getInstance().isTouchAbsorberActive()
544 && isOverridingFreeformHack(context)
545 && !isChromeOs(context);
547 if(!shouldLaunchTouchAbsorber) {
552 startTouchAbsorberActivity(context);
553 new Handler().postDelayed(runnable, 100);
556 public static void startActivityMaximized(Context context, Intent intent) {
557 Bundle bundle = launchMode2(context, MAXIMIZED, ApplicationType.CONTEXT_MENU);
558 prepareToStartActivity(context, () -> context.startActivity(intent, bundle));
561 @TargetApi(Build.VERSION_CODES.N)
562 public static void startActivityLowerRight(Context context, Intent intent) {
563 DisplayInfo display = getDisplayInfo(context);
565 context.startActivity(intent,
566 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
567 .setLaunchBounds(new Rect(
573 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
576 @TargetApi(Build.VERSION_CODES.N)
577 public static void startTouchAbsorberActivity(Context context) {
578 String position = getTaskbarPosition(context);
579 DisplayInfo display = getDisplayInfo(context);
583 int right = display.width;
584 int bottom = display.height;
586 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
588 if(position.contains("vertical_left"))
590 else if(position.contains("vertical_right"))
591 left = right - iconSize;
592 else if(position.contains("bottom"))
593 top = bottom - iconSize;
597 Intent intent = new Intent(context, TouchAbsorberActivity.class);
598 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
599 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
602 context.startActivity(intent,
603 getActivityOptions(context, ApplicationType.FREEFORM_HACK)
604 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle());
605 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
608 public static void startContextMenuActivity(Context context, Bundle args) {
609 SharedPreferences pref = getSharedPreferences(context);
610 Intent intent = null;
612 switch(pref.getString("theme", "light")) {
614 intent = new Intent(context, ContextMenuActivity.class);
617 intent = new Intent(context, ContextMenuActivityDark.class);
622 intent.putExtra("args", args);
623 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
626 if(hasFreeformSupport(context) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
627 DisplayInfo display = getDisplayInfo(context);
629 if(intent != null && hasBrokenSetLaunchBoundsApi())
630 intent.putExtra("context_menu_fix", true);
632 context.startActivity(intent,
633 getActivityOptions(context, ApplicationType.CONTEXT_MENU)
635 new Rect(0, 0, display.width, display.height)
638 context.startActivity(intent);
641 public static void checkForUpdates(Context context) {
643 if(isPlayStoreRelease(context)) {
644 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
645 && !isPlayStoreInstalled(context))
646 url = "https://github.com/farmerbb/Taskbar/releases";
648 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
650 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.APPLICATION_ID;
652 Intent intent = new Intent(Intent.ACTION_VIEW);
653 intent.setData(Uri.parse(url));
654 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
657 context.startActivity(intent);
658 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
661 public static boolean launcherIsDefault(Context context) {
662 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
663 homeIntent.addCategory(Intent.CATEGORY_HOME);
664 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
666 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
669 public static void setCachedRotation(int cachedRotation) {
670 U.cachedRotation = cachedRotation;
673 public static String getTaskbarPosition(Context context) {
674 SharedPreferences pref = getSharedPreferences(context);
675 String position = pref.getString("position", "bottom_left");
677 if(pref.getBoolean("anchor", false)) {
678 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
679 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
684 case Surface.ROTATION_0:
685 return "bottom_left";
686 case Surface.ROTATION_90:
687 return "bottom_vertical_right";
688 case Surface.ROTATION_180:
690 case Surface.ROTATION_270:
691 return "top_vertical_left";
694 case "bottom_vertical_left":
696 case Surface.ROTATION_0:
697 return "bottom_vertical_left";
698 case Surface.ROTATION_90:
699 return "bottom_right";
700 case Surface.ROTATION_180:
701 return "top_vertical_right";
702 case Surface.ROTATION_270:
708 case Surface.ROTATION_0:
709 return "bottom_right";
710 case Surface.ROTATION_90:
711 return "top_vertical_right";
712 case Surface.ROTATION_180:
714 case Surface.ROTATION_270:
715 return "bottom_vertical_left";
718 case "bottom_vertical_right":
720 case Surface.ROTATION_0:
721 return "bottom_vertical_right";
722 case Surface.ROTATION_90:
724 case Surface.ROTATION_180:
725 return "top_vertical_left";
726 case Surface.ROTATION_270:
727 return "bottom_left";
732 case Surface.ROTATION_0:
734 case Surface.ROTATION_90:
735 return "bottom_vertical_left";
736 case Surface.ROTATION_180:
737 return "bottom_right";
738 case Surface.ROTATION_270:
739 return "top_vertical_right";
742 case "top_vertical_left":
744 case Surface.ROTATION_0:
745 return "top_vertical_left";
746 case Surface.ROTATION_90:
747 return "bottom_left";
748 case Surface.ROTATION_180:
749 return "bottom_vertical_right";
750 case Surface.ROTATION_270:
756 case Surface.ROTATION_0:
758 case Surface.ROTATION_90:
759 return "top_vertical_left";
760 case Surface.ROTATION_180:
761 return "bottom_left";
762 case Surface.ROTATION_270:
763 return "bottom_vertical_right";
766 case "top_vertical_right":
768 case Surface.ROTATION_0:
769 return "top_vertical_right";
770 case Surface.ROTATION_90:
772 case Surface.ROTATION_180:
773 return "bottom_vertical_left";
774 case Surface.ROTATION_270:
775 return "bottom_right";
784 private static int getMaxNumOfColumns(Context context) {
785 SharedPreferences pref = getSharedPreferences(context);
786 DisplayInfo display = getDisplayInfo(context);
787 float density = display.density / 160;
788 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / density;
789 int numOfColumns = 0;
791 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
792 ? (display.height - getStatusBarHeight(context)) / density
793 : display.width / density;
795 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / density;
797 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
799 while(baseTaskbarSize + iconSize < maxScreenSize
800 && numOfColumns < userMaxNumOfColumns) {
801 baseTaskbarSize = baseTaskbarSize + iconSize;
808 public static int getMaxNumOfEntries(Context context) {
809 SharedPreferences pref = getSharedPreferences(context);
810 return pref.getBoolean("disable_scrolling_list", false)
811 ? getMaxNumOfColumns(context)
812 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
815 public static int getStatusBarHeight(Context context) {
816 return getSystemDimen(context, "status_bar_height");
819 private static int getNavbarHeight(Context context) {
820 return getSystemDimen(context, "navigation_bar_height");
823 private static int getSystemDimen(Context context, String id) {
825 int resourceId = context.getResources().getIdentifier(id, "dimen", "android");
827 value = context.getResources().getDimensionPixelSize(resourceId);
832 public static void refreshPinnedIcons(Context context) {
833 IconCache.getInstance(context).clearCache();
835 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
836 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
837 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
838 PackageManager pm = context.getPackageManager();
842 for(AppEntry entry : pinnedAppsList) {
843 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
844 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
846 final List<UserHandle> userHandles = userManager.getUserProfiles();
847 LauncherActivityInfo appInfo = null;
849 for(UserHandle handle : userHandles) {
850 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
851 if(!list.isEmpty()) {
852 // Google App workaround
853 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
854 appInfo = list.get(0);
856 boolean added = false;
857 for(LauncherActivityInfo info : list) {
858 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
864 if(!added) appInfo = list.get(0);
871 if(appInfo != null) {
872 AppEntry newEntry = new AppEntry(
873 entry.getPackageName(),
874 entry.getComponentName(),
876 IconCache.getInstance(context).getIcon(context, pm, appInfo),
879 newEntry.setUserId(entry.getUserId(context));
880 pba.addPinnedApp(context, newEntry);
884 for(AppEntry entry : blockedAppsList) {
885 pba.addBlockedApp(context, entry);
889 public static Intent getShortcutIntent(Context context) {
890 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
891 shortcutIntent.setAction(Intent.ACTION_MAIN);
892 shortcutIntent.putExtra("is_launching_shortcut", true);
894 Intent intent = new Intent();
895 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
896 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
897 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
902 public static Intent getStartStopIntent(Context context) {
903 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
904 shortcutIntent.setAction(Intent.ACTION_MAIN);
905 shortcutIntent.putExtra("is_launching_shortcut", true);
907 Intent intent = new Intent();
908 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
909 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
910 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
915 public static boolean canEnableFreeform() {
916 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
919 @TargetApi(Build.VERSION_CODES.N)
920 public static boolean hasFreeformSupport(Context context) {
921 return canEnableFreeform()
922 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
923 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
924 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
925 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
928 public static boolean isSamsungDevice() {
929 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
932 public static boolean isNvidiaDevice() {
933 return Build.MANUFACTURER.equalsIgnoreCase("NVIDIA");
936 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
937 return isServiceRunning(context, cls.getName());
940 private static boolean isServiceRunning(Context context, String className) {
941 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
942 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
943 if(className.equals(service.service.getClassName()))
950 public static int getBackgroundTint(Context context) {
951 SharedPreferences pref = getSharedPreferences(context);
953 // Import old background tint preference
954 if(pref.contains("show_background")) {
955 SharedPreferences.Editor editor = pref.edit();
957 if(!pref.getBoolean("show_background", true))
958 editor.putInt("background_tint", Color.TRANSPARENT).apply();
960 editor.remove("show_background");
964 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
967 public static int getAccentColor(Context context) {
968 SharedPreferences pref = getSharedPreferences(context);
969 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
972 @TargetApi(Build.VERSION_CODES.M)
973 public static boolean canDrawOverlays(Context context) {
974 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
977 public static boolean isGame(Context context, String packageName) {
978 SharedPreferences pref = getSharedPreferences(context);
979 if(pref.getBoolean("launch_games_fullscreen", true)) {
980 PackageManager pm = context.getPackageManager();
983 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
984 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
985 } catch (PackageManager.NameNotFoundException e) {
992 @TargetApi(Build.VERSION_CODES.N)
993 public static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType) {
994 ActivityOptions options = ActivityOptions.makeBasic();
997 switch(applicationType) {
999 if(FreeformHackHelper.getInstance().isFreeformHackActive())
1000 stackId = getFreeformWindowModeId();
1002 stackId = getFullscreenWindowModeId();
1005 stackId = getFullscreenWindowModeId();
1008 stackId = getFreeformWindowModeId();
1011 if(hasBrokenSetLaunchBoundsApi()
1012 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
1013 stackId = getFullscreenWindowModeId();
1018 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
1019 method.invoke(options, stackId);
1020 } catch (Exception e) { /* Gracefully fail */ }
1025 private static int getFullscreenWindowModeId() {
1026 if(getCurrentApiVersion() >= 28.0f)
1027 return WINDOWING_MODE_FULLSCREEN;
1029 return FULLSCREEN_WORKSPACE_STACK_ID;
1032 private static int getFreeformWindowModeId() {
1033 if(getCurrentApiVersion() >= 28.0f)
1034 return WINDOWING_MODE_FREEFORM;
1036 return FREEFORM_WORKSPACE_STACK_ID;
1039 private static String getWindowingModeMethodName() {
1040 if(getCurrentApiVersion() >= 28.0f)
1041 return "setLaunchWindowingMode";
1043 return "setLaunchStackId";
1046 public static Bundle getActivityOptionsBundle(Context context, ApplicationType type) {
1047 SharedPreferences pref = getSharedPreferences(context);
1049 return getActivityOptionsBundle(context, type, pref.getString("window_size", "standard"));
1052 private static Bundle getActivityOptionsBundle(Context context, ApplicationType type, String windowSize) {
1053 SharedPreferences pref = getSharedPreferences(context);
1054 if(!canEnableFreeform() || !pref.getBoolean("freeform_hack", false))
1057 switch(windowSize) {
1059 return launchMode1(context, type);
1061 return launchMode2(context, MAXIMIZED, type);
1063 return launchMode2(context, LEFT, type);
1065 return launchMode2(context, RIGHT, type);
1067 return launchMode3(context, type);
1070 return getActivityOptions(context, type).toBundle();
1073 private static ApplicationType getApplicationType(Context context, String packageName) {
1074 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1077 public static boolean isSystemApp(Context context) {
1079 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1080 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1081 return (info.flags & mask) != 0;
1082 } catch (PackageManager.NameNotFoundException e) {
1087 public static boolean isChromeOs(Context context) {
1088 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1091 public static boolean isBlissOs(Context context) {
1092 boolean validBlissOsBuildProp = false;
1094 String blissVersion = DependencyUtils.getSystemProperty("ro.bliss.version");
1095 if(blissVersion != null && !blissVersion.isEmpty())
1096 validBlissOsBuildProp = true;
1098 String buildUser = DependencyUtils.getSystemProperty("ro.build.user");
1099 if(buildUser != null && buildUser.equals("electrikjesus"))
1100 validBlissOsBuildProp = true;
1102 return validBlissOsBuildProp
1103 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1104 && isSystemApp(context);
1107 public static boolean isLauncherPermanentlyEnabled(Context context) {
1108 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1111 PackageManager pm = context.getPackageManager();
1113 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1114 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
1115 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1116 && isSystemApp(context);
1117 } catch (PackageManager.NameNotFoundException e) {
1122 public static int getBaseTaskbarSize(Context context) {
1123 return Math.round(getBaseTaskbarSizeFloat(context));
1126 private static float getBaseTaskbarSizeFloat(Context context) {
1127 SharedPreferences pref = getSharedPreferences(context);
1128 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
1129 boolean navbarButtonsEnabled = false;
1131 if(pref.getBoolean("dashboard", false))
1132 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
1134 if(pref.getBoolean("button_back", false)) {
1135 navbarButtonsEnabled = true;
1136 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1139 if(pref.getBoolean("button_home", false)) {
1140 navbarButtonsEnabled = true;
1141 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1144 if(pref.getBoolean("button_recents", false)) {
1145 navbarButtonsEnabled = true;
1146 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1149 if(navbarButtonsEnabled)
1150 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
1152 return baseTaskbarSize;
1155 private static void startTaskbarService(Context context, boolean fullRestart) {
1156 context.startService(new Intent(context, TaskbarService.class));
1157 context.startService(new Intent(context, StartMenuService.class));
1158 context.startService(new Intent(context, DashboardService.class));
1159 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1162 private static void stopTaskbarService(Context context, boolean fullRestart) {
1163 context.stopService(new Intent(context, TaskbarService.class));
1164 context.stopService(new Intent(context, StartMenuService.class));
1165 context.stopService(new Intent(context, DashboardService.class));
1166 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1169 public static void restartTaskbar(Context context) {
1170 SharedPreferences pref = getSharedPreferences(context);
1171 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1173 .putBoolean("is_restarting", true)
1174 .putBoolean("skip_auto_hide_navbar", true)
1177 stopTaskbarService(context, true);
1178 startTaskbarService(context, true);
1179 } else if(isServiceRunning(context, StartMenuService.class)) {
1180 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1182 stopTaskbarService(context, false);
1183 startTaskbarService(context, false);
1187 public static void restartNotificationService(Context context) {
1188 if(isServiceRunning(context, NotificationService.class)) {
1189 SharedPreferences pref = getSharedPreferences(context);
1190 pref.edit().putBoolean("is_restarting", true).apply();
1192 Intent intent = new Intent(context, NotificationService.class);
1193 context.stopService(intent);
1194 context.startService(intent);
1198 public static void showHideNavigationBar(Context context, boolean show) {
1199 // Show or hide the system navigation bar on Bliss-x86
1201 if(getCurrentApiVersion() >= 28.0f)
1202 Settings.Secure.putInt(context.getContentResolver(), "navigation_bar_visible", show ? 1 : 0);
1204 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1205 } catch (Exception e) { /* Gracefully fail */ }
1208 public static void initPrefs(Context context) {
1209 // On smaller-screened devices, set "Grid" as the default start menu layout
1210 SharedPreferences pref = getSharedPreferences(context);
1211 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 720
1212 && pref.getString("start_menu_layout", "null").equals("null")) {
1213 pref.edit().putString("start_menu_layout", "grid").apply();
1216 // Enable freeform hack automatically on supported devices
1217 if(canEnableFreeform()) {
1218 if(!pref.getBoolean("freeform_hack_override", false)) {
1220 .putBoolean("freeform_hack", hasFreeformSupport(context) && !isSamsungDevice())
1221 .putBoolean("save_window_sizes", false)
1222 .putBoolean("freeform_hack_override", true)
1224 } else if(!hasFreeformSupport(context)) {
1225 pref.edit().putBoolean("freeform_hack", false).apply();
1227 stopFreeformHack(context);
1230 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1231 || pref.getBoolean("show_freeform_disabled_message", false);
1234 .putBoolean("freeform_hack", false)
1235 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1238 SavedWindowSizes.getInstance(context).clear(context);
1239 stopFreeformHack(context);
1242 // Customizations for BlissOS
1243 if(isBlissOs(context)) {
1244 if(getCurrentApiVersion() >= 28.0f) {
1246 Settings.System.putString(context.getContentResolver(), "navigation_bar_show", "null");
1247 } catch (Exception e) { /* Gracefully fail */ }
1250 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1251 && !pref.getBoolean("bliss_os_prefs", false)) {
1252 SharedPreferences.Editor editor = pref.edit();
1254 if(hasFreeformSupport(context))
1255 editor.putBoolean("freeform_hack", true);
1257 editor.putString("recents_amount", "running_apps_only");
1258 editor.putString("refresh_frequency", "0");
1259 editor.putString("max_num_of_recents", "2147483647");
1260 editor.putString("sort_order", "true");
1261 editor.putString("window_size", "phone_size");
1262 editor.putBoolean("full_length", true);
1263 editor.putBoolean("dashboard", true);
1264 editor.putBoolean("app_drawer_icon", true);
1265 editor.putBoolean("button_back", true);
1266 editor.putBoolean("button_home", true);
1267 editor.putBoolean("button_recents", true);
1268 editor.putBoolean("auto_hide_navbar", true);
1269 // editor.putBoolean("shortcut_icon", false);
1270 editor.putBoolean("bliss_os_prefs", true);
1275 // Customizations for Android-x86 devices (non-Bliss)
1276 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1277 && isSystemApp(context)
1278 && !pref.getBoolean("android_x86_prefs", false)) {
1280 .putString("recents_amount", "running_apps_only")
1281 .putString("refresh_frequency", "0")
1282 .putString("max_num_of_recents", "2147483647")
1283 .putString("sort_order", "true")
1284 .putString("window_size", "phone_size")
1285 .putBoolean("full_length", true)
1286 .putBoolean("dashboard", true)
1287 // .putBoolean("shortcut_icon", false)
1288 .putBoolean("android_x86_prefs", true)
1293 public static DisplayInfo getDisplayInfo(Context context) {
1294 context = context.getApplicationContext();
1296 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1297 Display disp = wm.getDefaultDisplay();
1299 DisplayMetrics metrics = new DisplayMetrics();
1300 disp.getMetrics(metrics);
1302 DisplayMetrics realMetrics = new DisplayMetrics();
1303 disp.getRealMetrics(realMetrics);
1305 DisplayInfo display = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
1307 if(isChromeOs(context)) {
1308 SharedPreferences pref = getSharedPreferences(context);
1309 if(!pref.getBoolean("chrome_os_context_menu_fix", true)) {
1310 display.width = realMetrics.widthPixels;
1311 display.height = realMetrics.heightPixels;
1317 boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
1318 boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
1320 if(sameWidth && !sameHeight) {
1321 display.width = realMetrics.widthPixels;
1322 display.height = realMetrics.heightPixels - getNavbarHeight(context);
1325 if(!sameWidth && sameHeight) {
1326 display.width = realMetrics.widthPixels - getNavbarHeight(context);
1327 display.height = realMetrics.heightPixels;
1333 public static void pinAppShortcut(Context context) {
1334 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1335 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1337 if(mShortcutManager.isRequestPinShortcutSupported()) {
1338 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1340 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1342 showToastLong(context, R.string.pin_shortcut_not_supported);
1344 Intent intent = getShortcutIntent(context);
1345 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1346 intent.putExtra("duplicate", false);
1348 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1349 homeIntent.addCategory(Intent.CATEGORY_HOME);
1350 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1352 intent.setPackage(defaultLauncher.activityInfo.packageName);
1353 context.sendBroadcast(intent);
1355 showToast(context, R.string.shortcut_created);
1359 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1360 SharedPreferences pref = getSharedPreferences(context);
1361 if(pref.getBoolean("hide_taskbar", true)) {
1362 if(isOverridingFreeformHack(context))
1363 return !LauncherHelper.getInstance().isOnHomeScreen();
1365 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1366 if(pendingAppLaunch)
1367 return !helper.isFreeformHackActive();
1369 return !helper.isInFreeformWorkspace();
1375 public static boolean isOverridingFreeformHack(Context context) {
1376 SharedPreferences pref = getSharedPreferences(context);
1377 return pref.getBoolean("freeform_hack", false)
1378 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1379 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1382 public static boolean isPlayStoreInstalled(Context context) {
1384 context.getPackageManager().getPackageInfo("com.android.vending", 0);
1386 } catch (PackageManager.NameNotFoundException e) {
1391 private static float getCurrentApiVersion() {
1392 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1393 return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1395 return (float) Build.VERSION.SDK_INT;
1398 public static boolean hasBrokenSetLaunchBoundsApi() {
1399 return getCurrentApiVersion() >= 26.0f
1400 && getCurrentApiVersion() < 28.0f
1401 && !isSamsungDevice()
1402 && !isNvidiaDevice();
1405 public static String getSecondScreenPackageName(Context context) {
1406 return getInstalledPackage(context, Arrays.asList(
1407 "com.farmerbb.secondscreen.free",
1408 "com.farmerbb.secondscreen"));
1411 // Returns the name of an installed package from a list of package names, in order of preference
1412 private static String getInstalledPackage(Context context, List<String> packageNames) {
1413 if(packageNames == null || packageNames.isEmpty())
1416 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1417 String packageName = packages.get(0);
1420 context.getPackageManager().getPackageInfo(packageName, 0);
1422 } catch (PackageManager.NameNotFoundException e) {
1424 return getInstalledPackage(context, packages);
1428 public static boolean visualFeedbackEnabled(Context context) {
1429 SharedPreferences pref = getSharedPreferences(context);
1430 return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1431 && pref.getBoolean("visual_feedback", true);
1434 public static void showRecentAppsDialog(Context context) {
1435 showRecentAppsDialog(context, null, null);
1438 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1439 Runnable finalOnFinish = onFinish == null
1443 Runnable finalOnError = onError == null
1444 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1447 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1448 ApplicationInfo applicationInfo = null;
1450 applicationInfo = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1451 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1453 if(applicationInfo != null) {
1454 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1455 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1457 if(mode != AppOpsManager.MODE_ALLOWED) {
1458 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1459 builder.setTitle(R.string.pref_header_recent_apps)
1460 .setMessage(R.string.enable_recent_apps)
1461 .setPositiveButton(R.string.action_ok, (dialog, which) -> {
1463 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1464 showToastLong(context, R.string.usage_stats_message);
1466 finalOnFinish.run();
1467 } catch (ActivityNotFoundException e) {
1471 .setNegativeButton(R.string.action_cancel, (dialog, which) -> finalOnFinish.run());
1473 AlertDialog dialog = builder.create();
1475 dialog.setCancelable(false);
1482 finalOnFinish.run();
1486 public static Context wrapContext(Context context) {
1487 SharedPreferences pref = getSharedPreferences(context);
1490 switch(pref.getString("theme", "light")) {
1492 theme = R.style.AppTheme;
1495 theme = R.style.AppTheme_Dark;
1499 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1502 @SuppressLint("PackageManagerGetSignatures")
1503 public static boolean isPlayStoreRelease(Context context) {
1504 Signature playStoreSignature = new Signature(context.getString(R.string.signature));
1506 PackageManager pm = context.getPackageManager();
1507 PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
1508 for(Signature signature : info.signatures) {
1509 if(signature.equals(playStoreSignature))
1512 } catch (Exception e) { /* Gracefully fail */ }
1517 public static boolean isExternalAccessDisabled(Context context) {
1518 SharedPreferences pref = getSharedPreferences(context);
1519 return !pref.getBoolean("tasker_enabled", true);
1522 public static boolean enableFreeformModeShortcut(Context context) {
1523 return canEnableFreeform()
1524 && !U.isOverridingFreeformHack(context)
1525 && !U.isChromeOs(context);
1528 public static void startForegroundService(Context context, Intent intent) {
1529 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1530 if(Settings.canDrawOverlays(context))
1531 context.startForegroundService(intent);
1533 context.startService(intent);
1536 public static int getOverlayType() {
1537 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1538 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1539 : WindowManager.LayoutParams.TYPE_PHONE;
1542 public static boolean isDelegatingHomeActivity(Context context) {
1543 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1544 homeIntent.addCategory(Intent.CATEGORY_HOME);
1546 final List<ResolveInfo> listOfLaunchers = context.getPackageManager().queryIntentActivities(homeIntent, 0);
1547 for(ResolveInfo launcher : listOfLaunchers) {
1548 if(launcher.activityInfo.packageName.equals(BuildConfig.SUPPORT_APPLICATION_ID))