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.accessibilityservice.AccessibilityService;
20 import android.annotation.SuppressLint;
21 import android.annotation.TargetApi;
22 import android.app.ActivityManager;
23 import android.app.ActivityOptions;
24 import android.app.AlertDialog;
25 import android.app.AppOpsManager;
26 import android.app.Service;
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.hardware.display.DisplayManager;
46 import android.net.Uri;
47 import android.os.Build;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.os.Process;
51 import android.os.UserHandle;
52 import android.os.UserManager;
53 import android.provider.Settings;
54 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
55 import androidx.appcompat.view.ContextThemeWrapper;
56 import android.util.DisplayMetrics;
57 import android.view.Display;
58 import android.view.Surface;
59 import android.view.View;
60 import android.view.WindowManager;
61 import android.widget.Toast;
63 import com.farmerbb.taskbar.BuildConfig;
64 import com.farmerbb.taskbar.R;
65 import com.farmerbb.taskbar.activity.ContextMenuActivity;
66 import com.farmerbb.taskbar.activity.DummyActivity;
67 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
68 import com.farmerbb.taskbar.activity.TouchAbsorberActivity;
69 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
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.io.BufferedInputStream;
77 import java.io.BufferedOutputStream;
79 import java.io.FileOutputStream;
80 import java.io.IOException;
81 import java.lang.reflect.Constructor;
82 import java.lang.reflect.Method;
83 import java.util.ArrayList;
84 import java.util.Arrays;
85 import java.util.List;
91 private static Integer cachedRotation;
93 private static final int MAXIMIZED = 0;
94 private static final int LEFT = -1;
95 private static final int RIGHT = 1;
97 public static final int HIDDEN = 0;
98 public static final int TOP_APPS = 1;
100 // From android.app.ActivityManager.StackId
101 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
102 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
104 // From android.app.WindowConfiguration
105 private static final int WINDOWING_MODE_FULLSCREEN = 1;
106 private static final int WINDOWING_MODE_FREEFORM = 5;
108 @SuppressWarnings("deprecation")
109 public static SharedPreferences getSharedPreferences(Context context) {
110 return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
113 public static void showPermissionDialog(Context context) {
114 showPermissionDialog(context, null, null);
117 @TargetApi(Build.VERSION_CODES.M)
118 public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
119 Runnable finalOnFinish = onFinish == null
123 Runnable finalOnError = onError == null
124 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
127 AlertDialog.Builder builder = new AlertDialog.Builder(context);
128 builder.setTitle(R.string.tb_permission_dialog_title)
129 .setMessage(R.string.tb_permission_dialog_message)
130 .setPositiveButton(R.string.tb_action_grant_permission, (dialog, which) -> {
132 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
133 Uri.parse("package:" + context.getPackageName())));
136 } catch (ActivityNotFoundException e) {
141 AlertDialog dialog = builder.create();
143 dialog.setCancelable(false);
148 public static AlertDialog showErrorDialog(Context context, String appopCmd) {
149 return showErrorDialog(context, appopCmd, null);
152 private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
153 Runnable finalOnFinish = onFinish == null
157 AlertDialog.Builder builder = new AlertDialog.Builder(context);
158 builder.setTitle(R.string.tb_error_dialog_title)
159 .setMessage(context.getString(R.string.tb_error_dialog_message, context.getPackageName(), appopCmd))
160 .setPositiveButton(R.string.tb_action_ok, (dialog, which) -> finalOnFinish.run());
162 AlertDialog dialog = builder.create();
164 dialog.setCancelable(false);
169 @TargetApi(Build.VERSION_CODES.P)
170 public static void lockDevice(Context context) {
171 sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
174 public static void sendAccessibilityAction(Context context, int action) {
175 sendAccessibilityAction(context, action, null);
178 public static void sendAccessibilityAction(Context context, int action, Runnable onComplete) {
179 ComponentName component = new ComponentName(context, PowerMenuService.class);
180 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
181 PackageManager.DONT_KILL_APP);
183 boolean isAccessibilityServiceEnabled = isAccessibilityServiceEnabled(context);
185 if(!isAccessibilityServiceEnabled
186 && hasWriteSecureSettingsPermission(context)) {
187 String services = Settings.Secure.getString(context.getContentResolver(),
188 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
190 String finalServices = services == null ? "" : services;
192 String powerMenuService = new ComponentName(context, PowerMenuService.class).flattenToString();
194 if(!finalServices.contains(powerMenuService)) {
196 Settings.Secure.putString(context.getContentResolver(),
197 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
198 finalServices.isEmpty()
200 : finalServices + ":" + powerMenuService);
201 } catch (Exception e) { /* Gracefully fail */ }
204 new Handler().postDelayed(() -> {
205 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
206 intent.putExtra("action", action);
207 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
210 Settings.Secure.putString(context.getContentResolver(),
211 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
213 } catch (Exception e) { /* Gracefully fail */ }
215 if(onComplete != null) onComplete.run();
217 } else if(isAccessibilityServiceEnabled) {
218 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
219 intent.putExtra("action", action);
220 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
222 if(onComplete != null) onComplete.run();
224 launchApp(context, () -> {
225 Intent intent = new Intent(context, DummyActivity.class);
226 intent.putExtra("accessibility", true);
227 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
230 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION, null));
231 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
236 public static boolean isAccessibilityServiceEnabled(Context context) {
237 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
238 ComponentName component = new ComponentName(context, PowerMenuService.class);
240 return accessibilityServices != null
241 && (accessibilityServices.contains(component.flattenToString())
242 || accessibilityServices.contains(component.flattenToShortString()));
245 public static boolean hasWriteSecureSettingsPermission(Context context) {
246 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
247 && context.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
250 public static void showToast(Context context, int message) {
251 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
254 public static void showToastLong(Context context, int message) {
255 showToast(context, context.getString(message), Toast.LENGTH_LONG);
258 public static void showToast(Context context, String message, int length) {
261 ToastInterface toast = DependencyUtils.createToast(context.getApplicationContext(), message, length);
264 ToastHelper.getInstance().setLastToast(toast);
267 public static void cancelToast() {
268 ToastInterface toast = ToastHelper.getInstance().getLastToast();
269 if(toast != null) toast.cancel();
272 public static void startShortcut(Context context, AppEntry entry, ShortcutInfo shortcut, View view) {
282 public static void launchApp(final Context context,
283 final AppEntry entry,
284 final String windowSize,
285 final boolean launchedFromTaskbar,
286 final boolean openInNewWindow,
297 private static void launchApp(final Context context,
298 final AppEntry entry,
299 final String windowSize,
300 final boolean launchedFromTaskbar,
301 final boolean openInNewWindow,
302 final ShortcutInfo shortcut,
304 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, entry,
305 windowSize, openInNewWindow, shortcut, view));
308 public static void launchApp(Context context, Runnable runnable) {
309 launchApp(context, true, runnable);
312 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
313 SharedPreferences pref = getSharedPreferences(context);
314 FreeformHackHelper helper = FreeformHackHelper.getInstance();
316 boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
317 && helper.isInFreeformWorkspace()
318 && MenuHelper.getInstance().isContextMenuOpen();
320 boolean noAnimation = pref.getBoolean("disable_animations", false);
322 if(hasFreeformSupport(context)
323 && pref.getBoolean("freeform_hack", false)
324 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
325 new Handler().postDelayed(() -> {
326 startFreeformHack(context, true);
328 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
329 }, launchedFromTaskbar ? 0 : 100);
331 new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
334 public static void startFreeformHack(Context context) {
335 startFreeformHack(context, false);
338 @TargetApi(Build.VERSION_CODES.N)
339 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
340 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
341 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
342 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
343 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
346 freeformHackIntent.putExtra("check_multiwindow", true);
348 if(canDrawOverlays(context))
349 startActivityLowerRight(context, freeformHackIntent);
352 public static void stopFreeformHack(Context context) {
353 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
355 if(isOverridingFreeformHack(context, false)) {
356 FreeformHackHelper helper = FreeformHackHelper.getInstance();
357 helper.setFreeformHackActive(false);
358 helper.setInFreeformWorkspace(false);
362 @TargetApi(Build.VERSION_CODES.N)
363 private static void continueLaunchingApp(Context context,
366 boolean openInNewWindow,
367 ShortcutInfo shortcut,
369 SharedPreferences pref = getSharedPreferences(context);
370 Intent intent = new Intent();
371 intent.setComponent(ComponentName.unflattenFromString(entry.getComponentName()));
372 intent.setAction(Intent.ACTION_MAIN);
373 intent.addCategory(Intent.CATEGORY_LAUNCHER);
374 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
375 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
377 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
378 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
379 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
381 if(pref.getBoolean("disable_animations", false))
382 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
384 boolean realOpenInNewWindow = openInNewWindow || pref.getBoolean("force_new_window", false);
385 if(realOpenInNewWindow) {
386 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
388 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
389 if(activityInfo != null) {
390 switch(activityInfo.launchMode) {
391 case ActivityInfo.LAUNCH_SINGLE_TASK:
392 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
393 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
399 ApplicationType type = getApplicationType(context, entry.getPackageName());
401 if(windowSize == null)
402 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, entry.getPackageName());
404 Bundle bundle = getActivityOptionsBundle(context, type, windowSize, view);
406 prepareToStartActivity(context, realOpenInNewWindow, () -> {
407 if(shortcut == null) {
408 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
409 if(entry.getUserId(context) == userManager.getSerialNumberForUser(Process.myUserHandle())) {
411 context.startActivity(intent, bundle);
412 } catch (ActivityNotFoundException e) {
413 launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context));
414 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
416 launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context));
418 launchShortcut(context, shortcut, bundle);
421 if(shouldCollapse(context, true))
422 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
424 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
427 private static Bundle launchMode1(Context context, ApplicationType type, View view) {
428 DisplayInfo display = getDisplayInfo(context);
430 int width1 = display.width / 8;
431 int width2 = display.width - width1;
432 int height1 = display.height / 8;
433 int height2 = display.height - height1;
435 return getActivityOptionsBundle(context, type, view,
443 private static Bundle launchMode2(Context context, int launchType, ApplicationType type, View view) {
444 DisplayInfo display = getDisplayInfo(context);
446 int statusBarHeight = getStatusBarHeight(context);
447 String position = getTaskbarPosition(context);
449 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
450 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
453 int top = statusBarHeight;
454 int right = display.width;
455 int bottom = display.height;
457 int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
459 : context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size);
461 if(position.contains("vertical_left"))
462 left = left + iconSize;
463 else if(position.contains("vertical_right"))
464 right = right - iconSize;
465 else if(position.contains("bottom"))
466 bottom = bottom - iconSize;
468 top = top + iconSize;
470 int halfLandscape = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
471 int halfPortrait = (bottom / 2) + ((iconSize / 2) * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
473 if(launchType == RIGHT && isLandscape)
474 left = halfLandscape;
475 else if(launchType == RIGHT && isPortrait)
477 else if(launchType == LEFT && isLandscape)
478 right = halfLandscape;
479 else if(launchType == LEFT && isPortrait)
480 bottom = halfPortrait;
482 return getActivityOptionsBundle(context, type, view, left, top, right, bottom);
485 private static Bundle launchMode3(Context context, ApplicationType type, View view) {
486 DisplayInfo display = getDisplayInfo(context);
488 int width1 = display.width / 2;
489 int width2 = context.getResources().getDimensionPixelSize(R.dimen.tb_phone_size_width) / 2;
490 int height1 = display.height / 2;
491 int height2 = context.getResources().getDimensionPixelSize(R.dimen.tb_phone_size_height) / 2;
493 return getActivityOptionsBundle(context, type, view,
501 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
502 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
503 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
506 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
507 } catch (ActivityNotFoundException | NullPointerException
508 | IllegalStateException | SecurityException e) { /* Gracefully fail */ }
511 @TargetApi(Build.VERSION_CODES.N_MR1)
512 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
513 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
515 if(launcherApps.hasShortcutHostPermission()) {
517 launcherApps.startShortcut(shortcut, null, bundle);
518 } catch (ActivityNotFoundException | NullPointerException
519 | IllegalStateException | SecurityException e) { /* Gracefully fail */ }
523 private static void prepareToStartActivity(Context context, boolean openInNewWindow, Runnable runnable) {
524 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_CONTEXT_MENU"));
526 if(!FreeformHackHelper.getInstance().isTouchAbsorberActive()
527 && shouldLaunchTouchAbsorber(context)) {
528 startTouchAbsorberActivity(context);
529 new Handler().postDelayed(runnable, 100);
530 } else if(openInNewWindow) {
531 Intent intent = new Intent(context, DummyActivity.class);
532 intent.putExtra("finish_on_pause", true);
533 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
534 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
535 startActivityLowerRight(context, intent);
537 new Handler().postDelayed(runnable, 100);
542 public static void startActivityMaximized(Context context, Intent intent) {
543 Bundle bundle = launchMode2(context, MAXIMIZED, ApplicationType.CONTEXT_MENU, null);
544 prepareToStartActivity(context, false, () -> context.startActivity(intent, bundle));
547 public static void startActivityLowerRight(Context context, Intent intent) {
548 DisplayInfo display = getDisplayInfo(context);
550 context.startActivity(intent,
551 getActivityOptionsBundle(context, ApplicationType.FREEFORM_HACK, null,
557 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
560 public static void startTouchAbsorberActivity(Context context) {
561 String position = getTaskbarPosition(context);
562 DisplayInfo display = getDisplayInfo(context);
566 int right = display.width;
567 int bottom = display.height;
569 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size);
571 if(position.contains("vertical_left"))
573 else if(position.contains("vertical_right"))
574 left = right - iconSize;
575 else if(position.contains("bottom"))
576 top = bottom - iconSize;
580 Intent intent = new Intent(context, TouchAbsorberActivity.class);
581 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
582 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
585 context.startActivity(intent,
586 getActivityOptionsBundle(context, ApplicationType.FREEFORM_HACK, null,
587 left, top, right, bottom));
588 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
591 public static void startContextMenuActivity(Context context, Bundle args) {
592 SharedPreferences pref = getSharedPreferences(context);
593 Intent intent = null;
595 switch(pref.getString("theme", "light")) {
597 intent = new Intent(context, ContextMenuActivity.class);
600 intent = new Intent(context, ContextMenuActivityDark.class);
605 intent.putExtra("args", args);
606 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
609 if(hasFreeformSupport(context) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
610 DisplayInfo display = getDisplayInfo(context);
612 if(intent != null && hasBrokenSetLaunchBoundsApi())
613 intent.putExtra("context_menu_fix", true);
615 context.startActivity(intent,
616 getActivityOptionsBundle(context, ApplicationType.CONTEXT_MENU, null,
617 0, 0, display.width, display.height));
619 context.startActivity(intent);
622 public static void checkForUpdates(Context context) {
624 if(isPlayStoreRelease(context)) {
625 if(context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
626 && !isPlayStoreInstalled(context))
627 url = "https://github.com/farmerbb/Taskbar/releases";
629 url = "https://play.google.com/store/apps/details?id=" + context.getPackageName();
631 url = "https://f-droid.org/repository/browse/?fdid=" + context.getPackageName();
633 Intent intent = new Intent(Intent.ACTION_VIEW);
634 intent.setData(Uri.parse(url));
635 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
638 context.startActivity(intent);
639 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
642 public static boolean launcherIsDefault(Context context) {
643 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
644 homeIntent.addCategory(Intent.CATEGORY_HOME);
645 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
647 return defaultLauncher.activityInfo.packageName.equals(context.getPackageName());
650 public static void setCachedRotation(int cachedRotation) {
651 U.cachedRotation = cachedRotation;
654 public static String getTaskbarPosition(Context context) {
655 SharedPreferences pref = getSharedPreferences(context);
656 String position = pref.getString("position", "bottom_left");
658 if(pref.getBoolean("anchor", false)) {
659 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
660 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
665 case Surface.ROTATION_0:
666 return "bottom_left";
667 case Surface.ROTATION_90:
668 return "bottom_vertical_right";
669 case Surface.ROTATION_180:
671 case Surface.ROTATION_270:
672 return "top_vertical_left";
675 case "bottom_vertical_left":
677 case Surface.ROTATION_0:
678 return "bottom_vertical_left";
679 case Surface.ROTATION_90:
680 return "bottom_right";
681 case Surface.ROTATION_180:
682 return "top_vertical_right";
683 case Surface.ROTATION_270:
689 case Surface.ROTATION_0:
690 return "bottom_right";
691 case Surface.ROTATION_90:
692 return "top_vertical_right";
693 case Surface.ROTATION_180:
695 case Surface.ROTATION_270:
696 return "bottom_vertical_left";
699 case "bottom_vertical_right":
701 case Surface.ROTATION_0:
702 return "bottom_vertical_right";
703 case Surface.ROTATION_90:
705 case Surface.ROTATION_180:
706 return "top_vertical_left";
707 case Surface.ROTATION_270:
708 return "bottom_left";
713 case Surface.ROTATION_0:
715 case Surface.ROTATION_90:
716 return "bottom_vertical_left";
717 case Surface.ROTATION_180:
718 return "bottom_right";
719 case Surface.ROTATION_270:
720 return "top_vertical_right";
723 case "top_vertical_left":
725 case Surface.ROTATION_0:
726 return "top_vertical_left";
727 case Surface.ROTATION_90:
728 return "bottom_left";
729 case Surface.ROTATION_180:
730 return "bottom_vertical_right";
731 case Surface.ROTATION_270:
737 case Surface.ROTATION_0:
739 case Surface.ROTATION_90:
740 return "top_vertical_left";
741 case Surface.ROTATION_180:
742 return "bottom_left";
743 case Surface.ROTATION_270:
744 return "bottom_vertical_right";
747 case "top_vertical_right":
749 case Surface.ROTATION_0:
750 return "top_vertical_right";
751 case Surface.ROTATION_90:
753 case Surface.ROTATION_180:
754 return "bottom_vertical_left";
755 case Surface.ROTATION_270:
756 return "bottom_right";
765 private static int getMaxNumOfColumns(Context context) {
766 SharedPreferences pref = getSharedPreferences(context);
767 DisplayInfo display = getDisplayInfo(context);
768 float density = display.density / 160;
769 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / density;
770 int numOfColumns = 0;
772 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
773 ? (display.height - getStatusBarHeight(context)) / density
774 : display.width / density;
776 float iconSize = context.getResources().getDimension(R.dimen.tb_icon_size) / density;
778 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
780 while(baseTaskbarSize + iconSize < maxScreenSize
781 && numOfColumns < userMaxNumOfColumns) {
782 baseTaskbarSize = baseTaskbarSize + iconSize;
789 public static int getMaxNumOfEntries(Context context) {
790 SharedPreferences pref = getSharedPreferences(context);
791 return pref.getBoolean("disable_scrolling_list", false)
792 ? getMaxNumOfColumns(context)
793 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
796 public static int getStatusBarHeight(Context context) {
797 return getSystemDimen(context, "status_bar_height");
800 private static int getNavbarHeight(Context context) {
801 return getSystemDimen(context, "navigation_bar_height");
804 private static int getSystemDimen(Context context, String id) {
806 int resourceId = context.getResources().getIdentifier(id, "dimen", "android");
808 value = context.getResources().getDimensionPixelSize(resourceId);
813 public static void refreshPinnedIcons(Context context) {
814 IconCache.getInstance(context).clearCache();
816 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
817 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
818 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
819 PackageManager pm = context.getPackageManager();
823 for(AppEntry entry : pinnedAppsList) {
824 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
825 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
827 final List<UserHandle> userHandles = userManager.getUserProfiles();
828 LauncherActivityInfo appInfo = null;
830 for(UserHandle handle : userHandles) {
831 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
832 if(!list.isEmpty()) {
833 // Google App workaround
834 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
835 appInfo = list.get(0);
837 boolean added = false;
838 for(LauncherActivityInfo info : list) {
839 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
845 if(!added) appInfo = list.get(0);
852 if(appInfo != null) {
853 AppEntry newEntry = new AppEntry(
854 entry.getPackageName(),
855 entry.getComponentName(),
857 IconCache.getInstance(context).getIcon(context, pm, appInfo),
860 newEntry.setUserId(entry.getUserId(context));
861 pba.addPinnedApp(context, newEntry);
865 for(AppEntry entry : blockedAppsList) {
866 pba.addBlockedApp(context, entry);
870 public static boolean canEnableFreeform() {
871 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
874 @TargetApi(Build.VERSION_CODES.N)
875 public static boolean hasFreeformSupport(Context context) {
876 return canEnableFreeform()
877 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
878 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
879 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
880 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
883 public static boolean canBootToFreeform(Context context) {
884 return hasFreeformSupport(context) && !isOverridingFreeformHack(context);
887 public static boolean isSamsungDevice() {
888 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
891 private static boolean isNvidiaDevice() {
892 return Build.MANUFACTURER.equalsIgnoreCase("NVIDIA");
895 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
896 if(LauncherHelper.getInstance().isOnSecondaryHomeScreen()
897 && (cls.equals(TaskbarService.class)
898 || cls.equals(StartMenuService.class)
899 || cls.equals(DashboardService.class)))
902 return isServiceRunning(context, cls.getName());
905 private static boolean isServiceRunning(Context context, String className) {
906 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
907 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
908 if(className.equals(service.service.getClassName()))
915 public static int getBackgroundTint(Context context) {
916 SharedPreferences pref = getSharedPreferences(context);
918 // Import old background tint preference
919 if(pref.contains("show_background")) {
920 SharedPreferences.Editor editor = pref.edit();
922 if(!pref.getBoolean("show_background", true))
923 editor.putInt("background_tint", Color.TRANSPARENT).apply();
925 editor.remove("show_background");
929 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.tb_translucent_gray));
932 public static int getAccentColor(Context context) {
933 SharedPreferences pref = getSharedPreferences(context);
934 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.tb_translucent_white));
937 public static boolean canDrawOverlays(Context context) {
938 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
941 public static boolean isGame(Context context, String packageName) {
942 SharedPreferences pref = getSharedPreferences(context);
943 if(pref.getBoolean("launch_games_fullscreen", true)) {
944 PackageManager pm = context.getPackageManager();
947 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
948 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
949 } catch (PackageManager.NameNotFoundException e) {
956 private static ActivityOptions getActivityOptions(View view) {
957 return getActivityOptions(null, null, view);
960 private static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType, View view) {
961 ActivityOptions options;
963 options = ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
964 else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
965 options = ActivityOptions.makeBasic();
968 Constructor<ActivityOptions> constructor = ActivityOptions.class.getDeclaredConstructor();
969 constructor.setAccessible(true);
970 options = constructor.newInstance();
971 } catch (Exception e) {
976 if(applicationType == null)
981 switch(applicationType) {
983 if(FreeformHackHelper.getInstance().isFreeformHackActive())
984 stackId = getFreeformWindowModeId();
986 stackId = getFullscreenWindowModeId();
989 stackId = getFullscreenWindowModeId();
992 stackId = getFreeformWindowModeId();
995 if(hasBrokenSetLaunchBoundsApi()
996 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
997 stackId = getFullscreenWindowModeId();
1002 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
1003 method.invoke(options, stackId);
1004 } catch (Exception e) { /* Gracefully fail */ }
1006 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1007 int launchDisplayId = LauncherHelper.getInstance().getSecondaryDisplayId();
1008 if(launchDisplayId != -1)
1009 options.setLaunchDisplayId(launchDisplayId);
1015 private static int getFullscreenWindowModeId() {
1016 if(getCurrentApiVersion() >= 28.0f)
1017 return WINDOWING_MODE_FULLSCREEN;
1019 return FULLSCREEN_WORKSPACE_STACK_ID;
1022 private static int getFreeformWindowModeId() {
1023 if(getCurrentApiVersion() >= 28.0f)
1024 return WINDOWING_MODE_FREEFORM;
1026 return FREEFORM_WORKSPACE_STACK_ID;
1029 private static String getWindowingModeMethodName() {
1030 if(getCurrentApiVersion() >= 28.0f)
1031 return "setLaunchWindowingMode";
1033 return "setLaunchStackId";
1036 public static Bundle getActivityOptionsBundle(Context context, ApplicationType type, View view) {
1037 SharedPreferences pref = getSharedPreferences(context);
1039 return getActivityOptionsBundle(context, type, pref.getString("window_size", context.getString(R.string.tb_def_window_size)), view);
1042 private static Bundle getActivityOptionsBundle(Context context, ApplicationType type, String windowSize, View view) {
1043 SharedPreferences pref = getSharedPreferences(context);
1044 if(!canEnableFreeform() || !pref.getBoolean("freeform_hack", false))
1045 return getActivityOptions(view).toBundle();
1047 switch(windowSize) {
1049 return launchMode1(context, type, view);
1051 return launchMode2(context, MAXIMIZED, type, view);
1053 return launchMode2(context, LEFT, type, view);
1055 return launchMode2(context, RIGHT, type, view);
1057 return launchMode3(context, type, view);
1060 return getActivityOptions(context, type, view).toBundle();
1063 private static Bundle getActivityOptionsBundle(Context context, ApplicationType applicationType, View view,
1064 int left, int top, int right, int bottom) {
1065 ActivityOptions options = getActivityOptions(context, applicationType, view);
1069 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
1070 return options.toBundle();
1072 return options.setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
1075 private static ApplicationType getApplicationType(Context context, String packageName) {
1076 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1079 public static boolean isSystemApp(Context context) {
1081 ApplicationInfo info = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
1082 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1083 return (info.flags & mask) != 0;
1084 } catch (PackageManager.NameNotFoundException e) {
1089 public static boolean isChromeOs(Context context) {
1090 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1093 public static boolean isBlissOs(Context context) {
1094 boolean validBlissOsBuildProp = false;
1096 String blissVersion = getSystemProperty("ro.bliss.version");
1097 if(blissVersion != null && !blissVersion.isEmpty())
1098 validBlissOsBuildProp = true;
1100 String buildUser = getSystemProperty("ro.build.user");
1101 if(buildUser != null && buildUser.equals("electrikjesus"))
1102 validBlissOsBuildProp = true;
1104 return validBlissOsBuildProp
1105 && context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
1106 && isSystemApp(context);
1109 public static boolean isLauncherPermanentlyEnabled(Context context) {
1110 if(context.getPackageName().equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1113 PackageManager pm = context.getPackageManager();
1115 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1116 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, context.getPackageName()) == PackageManager.SIGNATURE_MATCH
1117 && context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
1118 && isSystemApp(context);
1119 } catch (PackageManager.NameNotFoundException e) {
1124 public static boolean hasSupportLibrary(Context context, int minVersion) {
1125 PackageManager pm = context.getPackageManager();
1127 PackageInfo pInfo = pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1128 return pInfo.versionCode >= minVersion
1129 && pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, context.getPackageName()) == PackageManager.SIGNATURE_MATCH
1130 && context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
1131 && isSystemApp(context);
1132 } catch (PackageManager.NameNotFoundException e) {
1137 public static int getBaseTaskbarSize(Context context) {
1138 return Math.round(getBaseTaskbarSizeFloat(context));
1141 private static float getBaseTaskbarSizeFloat(Context context) {
1142 SharedPreferences pref = getSharedPreferences(context);
1143 float baseTaskbarSize = context.getResources().getDimension(R.dimen.tb_base_taskbar_size);
1144 boolean navbarButtonsEnabled = false;
1146 if(pref.getBoolean("dashboard", context.getResources().getBoolean(R.bool.tb_def_dashboard)))
1147 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_dashboard_button_size);
1149 if(pref.getBoolean("button_back", false)) {
1150 navbarButtonsEnabled = true;
1151 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_icon_size);
1154 if(pref.getBoolean("button_home", false)) {
1155 navbarButtonsEnabled = true;
1156 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_icon_size);
1159 if(pref.getBoolean("button_recents", false)) {
1160 navbarButtonsEnabled = true;
1161 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_icon_size);
1164 if(navbarButtonsEnabled)
1165 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_navbar_buttons_margin);
1167 if(isSystemTrayEnabled(context))
1168 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_systray_size);
1170 return baseTaskbarSize;
1173 private static void startTaskbarService(Context context, boolean fullRestart) {
1174 context.startService(new Intent(context, TaskbarService.class));
1175 context.startService(new Intent(context, StartMenuService.class));
1176 context.startService(new Intent(context, DashboardService.class));
1177 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1180 private static void stopTaskbarService(Context context, boolean fullRestart) {
1181 context.stopService(new Intent(context, TaskbarService.class));
1182 context.stopService(new Intent(context, StartMenuService.class));
1183 context.stopService(new Intent(context, DashboardService.class));
1184 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1187 public static void restartTaskbar(Context context) {
1188 SharedPreferences pref = getSharedPreferences(context);
1189 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1191 .putBoolean("is_restarting", true)
1192 .putBoolean("skip_auto_hide_navbar", true)
1195 stopTaskbarService(context, true);
1196 startTaskbarService(context, true);
1197 } else if(isServiceRunning(context, StartMenuService.class)) {
1198 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1200 stopTaskbarService(context, false);
1201 startTaskbarService(context, false);
1204 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.RESTART"));
1207 public static void restartNotificationService(Context context) {
1208 if(isServiceRunning(context, NotificationService.class)) {
1209 SharedPreferences pref = getSharedPreferences(context);
1210 pref.edit().putBoolean("is_restarting", true).apply();
1212 Intent intent = new Intent(context, NotificationService.class);
1213 context.stopService(intent);
1214 context.startService(intent);
1218 public static void showHideNavigationBar(Context context, boolean show) {
1219 if(hasSupportLibrary(context, 7)) {
1220 Intent intent = new Intent(BuildConfig.SUPPORT_APPLICATION_ID + ".CHANGE_OVERSCAN");
1221 intent.setPackage(BuildConfig.SUPPORT_APPLICATION_ID);
1223 intent.putExtra("display_id", getDisplayID());
1224 intent.putExtra("value", show ? 0 : getNavbarHeight(context) * -1);
1226 context.sendBroadcast(intent);
1230 // Show or hide the system navigation bar on Bliss-x86
1232 if(getCurrentApiVersion() >= 28.0f)
1233 Settings.Secure.putInt(context.getContentResolver(), "navigation_bar_visible", show ? 1 : 0);
1235 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1236 } catch (Exception e) { /* Gracefully fail */ }
1239 public static void initPrefs(Context context) {
1240 // Enable freeform hack automatically on supported devices
1241 SharedPreferences pref = getSharedPreferences(context);
1242 if(canEnableFreeform()) {
1243 if(!pref.getBoolean("freeform_hack_override", false)) {
1245 .putBoolean("freeform_hack", hasFreeformSupport(context) && !isSamsungDevice())
1246 .putBoolean("save_window_sizes", false)
1247 .putBoolean("freeform_hack_override", true)
1249 } else if(!hasFreeformSupport(context)) {
1250 pref.edit().putBoolean("freeform_hack", false).apply();
1252 stopFreeformHack(context);
1255 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1256 || pref.getBoolean("show_freeform_disabled_message", false);
1259 .putBoolean("freeform_hack", false)
1260 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1263 SavedWindowSizes.getInstance(context).clear(context);
1264 stopFreeformHack(context);
1267 // Customizations for BlissOS
1268 if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1269 && !pref.getBoolean("bliss_os_prefs", false)) {
1270 SharedPreferences.Editor editor = pref.edit();
1272 if(hasFreeformSupport(context)) {
1273 editor.putBoolean("freeform_hack", true);
1276 editor.putString("recents_amount", "running_apps_only");
1277 editor.putString("refresh_frequency", "0");
1278 editor.putString("max_num_of_recents", "2147483647");
1279 editor.putString("sort_order", "true");
1280 editor.putString("start_button_image", "app_logo");
1281 editor.putBoolean("button_back", true);
1282 editor.putBoolean("button_home", true);
1283 editor.putBoolean("button_recents", true);
1284 editor.putBoolean("auto_hide_navbar", true);
1285 editor.putBoolean("shortcut_icon", false);
1286 editor.putBoolean("bliss_os_prefs", true);
1290 // Customizations for Android-x86 devices (non-Bliss)
1291 if(context.getPackageName().equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1292 && isSystemApp(context)
1293 && !pref.getBoolean("android_x86_prefs", false)) {
1295 .putString("recents_amount", "running_apps_only")
1296 .putString("refresh_frequency", "0")
1297 .putString("max_num_of_recents", "2147483647")
1298 .putString("sort_order", "true")
1299 .putBoolean("shortcut_icon", false)
1300 .putBoolean("android_x86_prefs", true)
1305 public static DisplayInfo getDisplayInfo(Context context) {
1306 return getDisplayInfo(context, false);
1309 public static DisplayInfo getDisplayInfo(Context context, boolean fromTaskbar) {
1310 context = context.getApplicationContext();
1311 int displayID = getDisplayID();
1313 DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
1314 Display currentDisplay = null;
1316 for(Display display : dm.getDisplays()) {
1317 if(display.getDisplayId() == displayID) {
1318 currentDisplay = display;
1323 if(currentDisplay == null)
1324 return new DisplayInfo(0, 0, 0);
1326 DisplayMetrics metrics = new DisplayMetrics();
1327 currentDisplay.getMetrics(metrics);
1329 DisplayMetrics realMetrics = new DisplayMetrics();
1330 currentDisplay.getRealMetrics(realMetrics);
1332 DisplayInfo info = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
1334 if(isChromeOs(context)) {
1335 SharedPreferences pref = getSharedPreferences(context);
1336 if(!pref.getBoolean("chrome_os_context_menu_fix", true)) {
1337 info.width = realMetrics.widthPixels;
1338 info.height = realMetrics.heightPixels;
1344 // Workaround for incorrect display size on devices with notches in landscape mode
1345 if(fromTaskbar && context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
1348 boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
1349 boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
1351 if(sameWidth && !sameHeight) {
1352 info.width = realMetrics.widthPixels;
1353 info.height = realMetrics.heightPixels - getNavbarHeight(context);
1356 if(!sameWidth && sameHeight) {
1357 info.width = realMetrics.widthPixels - getNavbarHeight(context);
1358 info.height = realMetrics.heightPixels;
1364 private static int getDisplayID() {
1365 LauncherHelper helper = LauncherHelper.getInstance();
1367 if(helper.isOnSecondaryHomeScreen())
1368 return helper.getSecondaryDisplayId();
1370 return Display.DEFAULT_DISPLAY;
1373 public static void pinAppShortcut(Context context) {
1374 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1375 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1377 if(mShortcutManager.isRequestPinShortcutSupported()) {
1378 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1380 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1382 showToastLong(context, R.string.tb_pin_shortcut_not_supported);
1384 Intent intent = ShortcutUtils.getShortcutIntent(context);
1385 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1386 intent.putExtra("duplicate", false);
1388 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1389 homeIntent.addCategory(Intent.CATEGORY_HOME);
1390 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1392 intent.setPackage(defaultLauncher.activityInfo.packageName);
1393 context.sendBroadcast(intent);
1395 showToast(context, R.string.tb_shortcut_created);
1399 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1400 SharedPreferences pref = getSharedPreferences(context);
1401 if(pref.getBoolean("hide_taskbar", true)) {
1402 if(!pref.getBoolean("freeform_hack", false))
1404 else if(isOverridingFreeformHack(context, false))
1405 return !LauncherHelper.getInstance().isOnHomeScreen();
1407 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1408 if(pendingAppLaunch)
1409 return !helper.isFreeformHackActive();
1411 return !helper.isInFreeformWorkspace();
1417 public static boolean isOverridingFreeformHack(Context context) {
1418 return isOverridingFreeformHack(context, true);
1421 public static boolean isOverridingFreeformHack(Context context, boolean checkPref) {
1422 SharedPreferences pref = getSharedPreferences(context);
1423 return (!checkPref || pref.getBoolean("freeform_hack", false))
1424 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1425 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1428 public static boolean isPlayStoreInstalled(Context context) {
1430 context.getPackageManager().getPackageInfo("com.android.vending", 0);
1432 } catch (PackageManager.NameNotFoundException e) {
1437 private static float getCurrentApiVersion() {
1438 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1439 return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1441 return (float) Build.VERSION.SDK_INT;
1444 public static boolean hasBrokenSetLaunchBoundsApi() {
1445 return getCurrentApiVersion() >= 26.0f
1446 && getCurrentApiVersion() < 28.0f
1447 && !isSamsungDevice()
1448 && !isNvidiaDevice();
1451 public static String getSecondScreenPackageName(Context context) {
1452 return getInstalledPackage(context, Arrays.asList(
1453 "com.farmerbb.secondscreen.free",
1454 "com.farmerbb.secondscreen"));
1457 // Returns the name of an installed package from a list of package names, in order of preference
1458 private static String getInstalledPackage(Context context, List<String> packageNames) {
1459 if(packageNames == null || packageNames.isEmpty())
1462 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1463 String packageName = packages.get(0);
1466 context.getPackageManager().getPackageInfo(packageName, 0);
1468 } catch (PackageManager.NameNotFoundException e) {
1470 return getInstalledPackage(context, packages);
1474 public static void showRecentAppsDialog(Context context) {
1475 showRecentAppsDialog(context, null, null);
1478 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1479 Runnable finalOnFinish = onFinish == null
1483 Runnable finalOnError = onError == null
1484 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1487 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1488 ApplicationInfo applicationInfo = null;
1490 applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
1491 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1493 if(applicationInfo != null) {
1494 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1495 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1497 if(mode != AppOpsManager.MODE_ALLOWED) {
1498 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1499 builder.setTitle(R.string.tb_pref_header_recent_apps)
1500 .setMessage(R.string.tb_enable_recent_apps)
1501 .setPositiveButton(R.string.tb_action_ok, (dialog, which) -> {
1503 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1504 showToastLong(context, R.string.tb_usage_stats_message);
1506 finalOnFinish.run();
1507 } catch (ActivityNotFoundException e) {
1511 .setNegativeButton(R.string.tb_action_cancel, (dialog, which) -> finalOnFinish.run());
1513 AlertDialog dialog = builder.create();
1515 dialog.setCancelable(false);
1522 finalOnFinish.run();
1526 public static Context wrapContext(Context context) {
1527 SharedPreferences pref = getSharedPreferences(context);
1530 switch(pref.getString("theme", "light")) {
1532 theme = R.style.Taskbar;
1535 theme = R.style.Taskbar_Dark;
1539 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1542 public static boolean isPlayStoreRelease(Context context) {
1543 return isPlayStoreRelease(context, context.getPackageName());
1546 @SuppressLint("PackageManagerGetSignatures")
1547 public static boolean isPlayStoreRelease(Context context, String packageName) {
1548 Signature playStoreSignature = new Signature(context.getString(R.string.tb_signature));
1550 PackageManager pm = context.getPackageManager();
1551 PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
1552 for(Signature signature : info.signatures) {
1553 if(signature.equals(playStoreSignature))
1556 } catch (Exception e) { /* Gracefully fail */ }
1561 public static boolean isExternalAccessDisabled(Context context) {
1562 SharedPreferences pref = getSharedPreferences(context);
1563 return !pref.getBoolean("tasker_enabled", true);
1566 public static boolean enableFreeformModeShortcut(Context context) {
1567 return canEnableFreeform()
1568 && !isOverridingFreeformHack(context)
1569 && !isChromeOs(context);
1572 public static void startForegroundService(Context context, Intent intent) {
1573 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1574 if(Settings.canDrawOverlays(context))
1575 context.startForegroundService(intent);
1577 context.startService(intent);
1580 public static int getOverlayType() {
1581 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1582 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1583 : WindowManager.LayoutParams.TYPE_PHONE;
1586 public static boolean isDelegatingHomeActivity(Context context) {
1587 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1588 homeIntent.addCategory(Intent.CATEGORY_HOME);
1590 final List<ResolveInfo> listOfLaunchers = context.getPackageManager().queryIntentActivities(homeIntent, 0);
1591 for(ResolveInfo launcher : listOfLaunchers) {
1592 if(launcher.activityInfo.packageName.equals(BuildConfig.SUPPORT_APPLICATION_ID))
1599 @SuppressLint("PrivateApi")
1600 private static String getSystemProperty(String key) {
1602 Class<?> cls = Class.forName("android.os.SystemProperties");
1603 return cls.getMethod("get", String.class).invoke(null, key).toString();
1604 } catch (Exception e) {
1609 @SuppressWarnings("ResultOfMethodCallIgnored")
1610 public static boolean importCustomStartButtonImage(Context context, Uri uri) {
1612 File imagesDir = new File(context.getFilesDir(), "tb_images");
1615 File importedFile = new File(imagesDir, "custom_image_new");
1616 if(importedFile.exists()) importedFile.delete();
1618 BufferedInputStream is = new BufferedInputStream(context.getContentResolver().openInputStream(uri));
1619 byte[] data = new byte[is.available()];
1621 if(data.length > 0) {
1622 BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(importedFile));
1629 File prevFile = new File(imagesDir, "custom_image");
1630 if(prevFile.exists()) prevFile.delete();
1632 importedFile.renameTo(prevFile);
1634 } catch (IOException e) {
1639 public static String getDefaultStartButtonImage(Context context) {
1640 SharedPreferences pref = getSharedPreferences(context);
1641 return pref.getBoolean("app_drawer_icon", false)
1646 private static boolean shouldLaunchTouchAbsorber(Context context) {
1647 return isOverridingFreeformHack(context) && !isChromeOs(context) && getCurrentApiVersion() < 29.0f;
1650 public static boolean isDesktopIconsEnabled(Context context) {
1651 return !canBootToFreeform(context) && !shouldLaunchTouchAbsorber(context);
1654 public static boolean isSystemTrayEnabled(Context context) {
1655 SharedPreferences pref = getSharedPreferences(context);
1657 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1658 && pref.getBoolean("sys_tray", context.getResources().getBoolean(R.bool.tb_def_sys_tray))
1659 && pref.getBoolean("full_length", context.getResources().getBoolean(R.bool.tb_def_full_length))
1660 && !getTaskbarPosition(context).contains("vertical");
1663 @SuppressWarnings("deprecation")
1664 public static boolean isLibrary(Context context) {
1665 return !context.getPackageName().equals(BuildConfig.APPLICATION_ID);
1668 public static boolean applyDisplayCutoutModeTo(WindowManager.LayoutParams params) {
1669 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
1670 params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;