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.content.ActivityNotFoundException;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.SharedPreferences;
31 import android.content.pm.ActivityInfo;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.LauncherActivityInfo;
34 import android.content.pm.LauncherApps;
35 import android.content.pm.PackageInfo;
36 import android.content.pm.PackageManager;
37 import android.content.pm.ResolveInfo;
38 import android.content.pm.ShortcutInfo;
39 import android.content.pm.ShortcutManager;
40 import android.content.pm.Signature;
41 import android.content.res.Configuration;
42 import android.graphics.Color;
43 import android.graphics.Rect;
44 import android.net.Uri;
45 import android.os.Build;
46 import android.os.Bundle;
47 import android.os.Handler;
48 import android.os.Process;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.provider.Settings;
52 import android.support.v4.content.LocalBroadcastManager;
53 import android.support.v7.view.ContextThemeWrapper;
54 import android.util.DisplayMetrics;
55 import android.view.Display;
56 import android.view.Surface;
57 import android.view.View;
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.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.Method;
82 import java.util.ArrayList;
83 import java.util.Arrays;
84 import java.util.List;
90 private static Integer cachedRotation;
92 private static final int MAXIMIZED = 0;
93 private static final int LEFT = -1;
94 private static final int RIGHT = 1;
96 public static final int HIDDEN = 0;
97 public static final int TOP_APPS = 1;
99 // From android.app.ActivityManager.StackId
100 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
101 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
103 // From android.app.WindowConfiguration
104 private static final int WINDOWING_MODE_FULLSCREEN = 1;
105 private static final int WINDOWING_MODE_FREEFORM = 5;
107 public static SharedPreferences getSharedPreferences(Context context) {
108 return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
111 public static void showPermissionDialog(Context context) {
112 showPermissionDialog(context, null, null);
115 @TargetApi(Build.VERSION_CODES.M)
116 public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
117 Runnable finalOnFinish = onFinish == null
121 Runnable finalOnError = onError == null
122 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
125 AlertDialog.Builder builder = new AlertDialog.Builder(context);
126 builder.setTitle(R.string.permission_dialog_title)
127 .setMessage(R.string.permission_dialog_message)
128 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
130 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
131 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
134 } catch (ActivityNotFoundException e) {
139 AlertDialog dialog = builder.create();
141 dialog.setCancelable(false);
146 public static AlertDialog showErrorDialog(Context context, String appopCmd) {
147 return showErrorDialog(context, appopCmd, null);
150 private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
151 Runnable finalOnFinish = onFinish == null
155 AlertDialog.Builder builder = new AlertDialog.Builder(context);
156 builder.setTitle(R.string.error_dialog_title)
157 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
158 .setPositiveButton(R.string.action_ok, (dialog, which) -> finalOnFinish.run());
160 AlertDialog dialog = builder.create();
162 dialog.setCancelable(false);
167 public static void sendAccessibilityAction(Context context, int action) {
168 sendAccessibilityAction(context, action, null);
171 public static void sendAccessibilityAction(Context context, int action, Runnable onComplete) {
172 ComponentName component = new ComponentName(context, PowerMenuService.class);
173 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
174 PackageManager.DONT_KILL_APP);
176 boolean isAccessibilityServiceEnabled = isAccessibilityServiceEnabled(context);
178 if(!isAccessibilityServiceEnabled
179 && hasWriteSecureSettingsPermission(context)) {
180 String services = Settings.Secure.getString(context.getContentResolver(),
181 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
183 String finalServices = services == null ? "" : services;
185 String powerMenuService = new ComponentName(context, PowerMenuService.class).flattenToString();
187 if(!finalServices.contains(powerMenuService)) {
189 Settings.Secure.putString(context.getContentResolver(),
190 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
191 finalServices.isEmpty()
193 : finalServices + ":" + powerMenuService);
194 } catch (Exception e) { /* Gracefully fail */ }
197 new Handler().postDelayed(() -> {
198 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
199 intent.putExtra("action", action);
200 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
203 Settings.Secure.putString(context.getContentResolver(),
204 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
206 } catch (Exception e) { /* Gracefully fail */ }
208 if(onComplete != null) onComplete.run();
210 } else if(isAccessibilityServiceEnabled) {
211 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
212 intent.putExtra("action", action);
213 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
215 if(onComplete != null) onComplete.run();
217 launchApp(context, () -> {
218 Intent intent = new Intent(context, DummyActivity.class);
219 intent.putExtra("accessibility", true);
220 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
223 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION, null));
224 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
229 public static boolean isAccessibilityServiceEnabled(Context context) {
230 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
231 ComponentName component = new ComponentName(context, PowerMenuService.class);
233 return accessibilityServices != null
234 && (accessibilityServices.contains(component.flattenToString())
235 || accessibilityServices.contains(component.flattenToShortString()));
238 public static boolean hasWriteSecureSettingsPermission(Context context) {
239 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
240 && context.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
243 public static void showToast(Context context, int message) {
244 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
247 public static void showToastLong(Context context, int message) {
248 showToast(context, context.getString(message), Toast.LENGTH_LONG);
251 public static void showToast(Context context, String message, int length) {
254 ToastInterface toast = DependencyUtils.createToast(context.getApplicationContext(), message, length);
257 ToastHelper.getInstance().setLastToast(toast);
260 public static void cancelToast() {
261 ToastInterface toast = ToastHelper.getInstance().getLastToast();
262 if(toast != null) toast.cancel();
265 public static void startShortcut(Context context, AppEntry entry, ShortcutInfo shortcut, View view) {
275 public static void launchApp(final Context context,
276 final AppEntry entry,
277 final String windowSize,
278 final boolean launchedFromTaskbar,
279 final boolean openInNewWindow,
290 private static void launchApp(final Context context,
291 final AppEntry entry,
292 final String windowSize,
293 final boolean launchedFromTaskbar,
294 final boolean openInNewWindow,
295 final ShortcutInfo shortcut,
297 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, entry,
298 windowSize, openInNewWindow, shortcut, view));
301 public static void launchApp(Context context, Runnable runnable) {
302 launchApp(context, true, runnable);
305 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
306 SharedPreferences pref = getSharedPreferences(context);
307 FreeformHackHelper helper = FreeformHackHelper.getInstance();
309 boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
310 && helper.isInFreeformWorkspace()
311 && MenuHelper.getInstance().isContextMenuOpen();
313 boolean noAnimation = pref.getBoolean("disable_animations", false);
315 if(hasFreeformSupport(context)
316 && pref.getBoolean("freeform_hack", false)
317 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
318 new Handler().postDelayed(() -> {
319 startFreeformHack(context, true);
321 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
322 }, launchedFromTaskbar ? 0 : 100);
324 new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
327 public static void startFreeformHack(Context context) {
328 startFreeformHack(context, false);
331 @TargetApi(Build.VERSION_CODES.N)
332 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
333 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
334 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
335 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
336 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
339 freeformHackIntent.putExtra("check_multiwindow", true);
341 if(canDrawOverlays(context, false))
342 startActivityLowerRight(context, freeformHackIntent);
345 public static void stopFreeformHack(Context context) {
346 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
348 if(isOverridingFreeformHack(context)) {
349 FreeformHackHelper helper = FreeformHackHelper.getInstance();
350 helper.setFreeformHackActive(false);
351 helper.setInFreeformWorkspace(false);
355 @TargetApi(Build.VERSION_CODES.N)
356 private static void continueLaunchingApp(Context context,
359 boolean openInNewWindow,
360 ShortcutInfo shortcut,
362 SharedPreferences pref = getSharedPreferences(context);
363 Intent intent = new Intent();
364 intent.setComponent(ComponentName.unflattenFromString(entry.getComponentName()));
365 intent.setAction(Intent.ACTION_MAIN);
366 intent.addCategory(Intent.CATEGORY_LAUNCHER);
367 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
368 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
370 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
371 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
372 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
374 if(pref.getBoolean("disable_animations", false))
375 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
377 boolean realOpenInNewWindow = openInNewWindow || pref.getBoolean("force_new_window", false);
378 if(realOpenInNewWindow) {
379 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
381 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
382 if(activityInfo != null) {
383 switch(activityInfo.launchMode) {
384 case ActivityInfo.LAUNCH_SINGLE_TASK:
385 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
386 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
392 ApplicationType type = getApplicationType(context, entry.getPackageName());
394 if(windowSize == null)
395 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, entry.getPackageName());
397 Bundle bundle = getActivityOptionsBundle(context, type, windowSize, view);
399 prepareToStartActivity(context, realOpenInNewWindow, () -> {
400 if(shortcut == null) {
401 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
402 if(entry.getUserId(context) == userManager.getSerialNumberForUser(Process.myUserHandle())) {
404 context.startActivity(intent, bundle);
405 } catch (ActivityNotFoundException e) {
406 launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context));
407 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
409 launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context));
411 launchShortcut(context, shortcut, bundle);
414 if(shouldCollapse(context, true))
415 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
417 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
420 @TargetApi(Build.VERSION_CODES.N)
421 private static Bundle launchMode1(Context context, ApplicationType type, View view) {
422 DisplayInfo display = getDisplayInfo(context);
424 int width1 = display.width / 8;
425 int width2 = display.width - width1;
426 int height1 = display.height / 8;
427 int height2 = display.height - height1;
429 return getActivityOptions(context, type, view).setLaunchBounds(new Rect(
437 @TargetApi(Build.VERSION_CODES.N)
438 private static Bundle launchMode2(Context context, int launchType, ApplicationType type, View view) {
439 DisplayInfo display = getDisplayInfo(context);
441 int statusBarHeight = getStatusBarHeight(context);
442 String position = getTaskbarPosition(context);
444 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
445 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
448 int top = statusBarHeight;
449 int right = display.width;
450 int bottom = display.height;
452 int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
454 : context.getResources().getDimensionPixelSize(R.dimen.icon_size);
456 if(position.contains("vertical_left"))
457 left = left + iconSize;
458 else if(position.contains("vertical_right"))
459 right = right - iconSize;
460 else if(position.contains("bottom"))
461 bottom = bottom - iconSize;
463 top = top + iconSize;
465 int halfLandscape = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
466 int halfPortrait = (bottom / 2) + ((iconSize / 2) * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
468 if(launchType == RIGHT && isLandscape)
469 left = halfLandscape;
470 else if(launchType == RIGHT && isPortrait)
472 else if(launchType == LEFT && isLandscape)
473 right = halfLandscape;
474 else if(launchType == LEFT && isPortrait)
475 bottom = halfPortrait;
477 return getActivityOptions(context, type, view)
478 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
481 @TargetApi(Build.VERSION_CODES.N)
482 private static Bundle launchMode3(Context context, ApplicationType type, View view) {
483 DisplayInfo display = getDisplayInfo(context);
485 int width1 = display.width / 2;
486 int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
487 int height1 = display.height / 2;
488 int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
490 return getActivityOptions(context, type, view).setLaunchBounds(new Rect(
498 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
499 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
500 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
503 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
504 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
507 @TargetApi(Build.VERSION_CODES.N_MR1)
508 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
509 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
511 if(launcherApps.hasShortcutHostPermission()) {
513 launcherApps.startShortcut(shortcut, null, bundle);
514 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
518 private static void prepareToStartActivity(Context context, boolean openInNewWindow, Runnable runnable) {
519 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_CONTEXT_MENU"));
521 if(!FreeformHackHelper.getInstance().isTouchAbsorberActive()
522 && shouldLaunchTouchAbsorber(context)) {
523 startTouchAbsorberActivity(context);
524 new Handler().postDelayed(runnable, 100);
525 } else if(openInNewWindow) {
526 Intent intent = new Intent(context, DummyActivity.class);
527 intent.putExtra("finish_on_pause", true);
528 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
529 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
530 startActivityLowerRight(context, intent);
532 new Handler().postDelayed(runnable, 100);
537 public static void startActivityMaximized(Context context, Intent intent) {
538 Bundle bundle = launchMode2(context, MAXIMIZED, ApplicationType.CONTEXT_MENU, null);
539 prepareToStartActivity(context, false, () -> context.startActivity(intent, bundle));
542 @TargetApi(Build.VERSION_CODES.N)
543 public static void startActivityLowerRight(Context context, Intent intent) {
544 DisplayInfo display = getDisplayInfo(context);
546 context.startActivity(intent,
547 getActivityOptions(context, ApplicationType.FREEFORM_HACK, null)
548 .setLaunchBounds(new Rect(
554 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
557 @TargetApi(Build.VERSION_CODES.N)
558 public static void startTouchAbsorberActivity(Context context) {
559 String position = getTaskbarPosition(context);
560 DisplayInfo display = getDisplayInfo(context);
564 int right = display.width;
565 int bottom = display.height;
567 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
569 if(position.contains("vertical_left"))
571 else if(position.contains("vertical_right"))
572 left = right - iconSize;
573 else if(position.contains("bottom"))
574 top = bottom - iconSize;
578 Intent intent = new Intent(context, TouchAbsorberActivity.class);
579 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
580 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
583 context.startActivity(intent,
584 getActivityOptions(context, ApplicationType.FREEFORM_HACK, null)
585 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle());
586 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
589 public static void startContextMenuActivity(Context context, Bundle args) {
590 SharedPreferences pref = getSharedPreferences(context);
591 Intent intent = null;
593 switch(pref.getString("theme", "light")) {
595 intent = new Intent(context, ContextMenuActivity.class);
598 intent = new Intent(context, ContextMenuActivityDark.class);
603 intent.putExtra("args", args);
604 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
607 if(hasFreeformSupport(context) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
608 DisplayInfo display = getDisplayInfo(context);
610 if(intent != null && hasBrokenSetLaunchBoundsApi())
611 intent.putExtra("context_menu_fix", true);
613 context.startActivity(intent,
614 getActivityOptions(context, ApplicationType.CONTEXT_MENU, null)
616 new Rect(0, 0, display.width, display.height)
619 context.startActivity(intent);
622 public static void checkForUpdates(Context context) {
624 if(isPlayStoreRelease(context)) {
625 if(BuildConfig.APPLICATION_ID.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=" + BuildConfig.APPLICATION_ID;
631 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.APPLICATION_ID;
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(BuildConfig.APPLICATION_ID);
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.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 Intent getShortcutIntent(Context context) {
871 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
872 shortcutIntent.setAction(Intent.ACTION_MAIN);
873 shortcutIntent.putExtra("is_launching_shortcut", true);
875 Intent intent = new Intent();
876 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
877 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
878 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
883 public static Intent getStartStopIntent(Context context) {
884 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
885 shortcutIntent.setAction(Intent.ACTION_MAIN);
886 shortcutIntent.putExtra("is_launching_shortcut", true);
888 Intent intent = new Intent();
889 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
890 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
891 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
896 public static boolean canEnableFreeform() {
897 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
900 @TargetApi(Build.VERSION_CODES.N)
901 public static boolean hasFreeformSupport(Context context) {
902 return canEnableFreeform()
903 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
904 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
905 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
906 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
909 public static boolean canBootToFreeform(Context context) {
910 SharedPreferences pref = getSharedPreferences(context);
911 return hasFreeformSupport(context)
912 && pref.getBoolean("freeform_hack", false)
913 && !isOverridingFreeformHack(context);
916 public static boolean isSamsungDevice() {
917 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
920 private static boolean isNvidiaDevice() {
921 return Build.MANUFACTURER.equalsIgnoreCase("NVIDIA");
924 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
925 if(LauncherHelper.getInstance().isOnHomeScreen(false, true)
926 && (cls.equals(TaskbarService.class)
927 || cls.equals(StartMenuService.class)
928 || cls.equals(DashboardService.class)))
931 return isServiceRunning(context, cls.getName());
934 private static boolean isServiceRunning(Context context, String className) {
935 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
936 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
937 if(className.equals(service.service.getClassName()))
944 public static int getBackgroundTint(Context context) {
945 SharedPreferences pref = getSharedPreferences(context);
947 // Import old background tint preference
948 if(pref.contains("show_background")) {
949 SharedPreferences.Editor editor = pref.edit();
951 if(!pref.getBoolean("show_background", true))
952 editor.putInt("background_tint", Color.TRANSPARENT).apply();
954 editor.remove("show_background");
958 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
961 public static int getAccentColor(Context context) {
962 SharedPreferences pref = getSharedPreferences(context);
963 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
966 public static boolean canDrawOverlays(Context context) {
967 return canDrawOverlays(context, false);
970 @TargetApi(Build.VERSION_CODES.M)
971 public static boolean canDrawOverlays(Context context, boolean isSecondaryHomeScreen) {
972 return isSecondaryHomeScreen
973 || Build.VERSION.SDK_INT < Build.VERSION_CODES.M
974 || 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 private static ActivityOptions getActivityOptions(View view) {
993 return getActivityOptions(null, null, view);
996 @TargetApi(Build.VERSION_CODES.N)
997 private static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType, View view) {
998 ActivityOptions options;
1000 options = ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
1002 options = ActivityOptions.makeBasic();
1004 if(applicationType == null)
1009 switch(applicationType) {
1011 if(FreeformHackHelper.getInstance().isFreeformHackActive())
1012 stackId = getFreeformWindowModeId();
1014 stackId = getFullscreenWindowModeId();
1017 stackId = getFullscreenWindowModeId();
1020 stackId = getFreeformWindowModeId();
1023 if(hasBrokenSetLaunchBoundsApi()
1024 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
1025 stackId = getFullscreenWindowModeId();
1030 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
1031 method.invoke(options, stackId);
1032 } catch (Exception e) { /* Gracefully fail */ }
1034 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1035 int launchDisplayId = LauncherHelper.getInstance().getSecondaryDisplayId();
1036 if(launchDisplayId != -1)
1037 options.setLaunchDisplayId(launchDisplayId);
1043 private static int getFullscreenWindowModeId() {
1044 if(getCurrentApiVersion() >= 28.0f)
1045 return WINDOWING_MODE_FULLSCREEN;
1047 return FULLSCREEN_WORKSPACE_STACK_ID;
1050 private static int getFreeformWindowModeId() {
1051 if(getCurrentApiVersion() >= 28.0f)
1052 return WINDOWING_MODE_FREEFORM;
1054 return FREEFORM_WORKSPACE_STACK_ID;
1057 private static String getWindowingModeMethodName() {
1058 if(getCurrentApiVersion() >= 28.0f)
1059 return "setLaunchWindowingMode";
1061 return "setLaunchStackId";
1064 public static Bundle getActivityOptionsBundle(Context context, ApplicationType type, View view) {
1065 SharedPreferences pref = getSharedPreferences(context);
1067 return getActivityOptionsBundle(context, type, pref.getString("window_size", "standard"), view);
1070 private static Bundle getActivityOptionsBundle(Context context, ApplicationType type, String windowSize, View view) {
1071 SharedPreferences pref = getSharedPreferences(context);
1072 if(!canEnableFreeform() || !pref.getBoolean("freeform_hack", false))
1073 return getActivityOptions(view).toBundle();
1075 switch(windowSize) {
1077 return launchMode1(context, type, view);
1079 return launchMode2(context, MAXIMIZED, type, view);
1081 return launchMode2(context, LEFT, type, view);
1083 return launchMode2(context, RIGHT, type, view);
1085 return launchMode3(context, type, view);
1088 return getActivityOptions(context, type, view).toBundle();
1091 private static ApplicationType getApplicationType(Context context, String packageName) {
1092 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1095 public static boolean isSystemApp(Context context) {
1097 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1098 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1099 return (info.flags & mask) != 0;
1100 } catch (PackageManager.NameNotFoundException e) {
1105 public static boolean isChromeOs(Context context) {
1106 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1109 public static boolean isBlissOs(Context context) {
1110 boolean validBlissOsBuildProp = false;
1112 String blissVersion = getSystemProperty("ro.bliss.version");
1113 if(blissVersion != null && !blissVersion.isEmpty())
1114 validBlissOsBuildProp = true;
1116 String buildUser = getSystemProperty("ro.build.user");
1117 if(buildUser != null && buildUser.equals("electrikjesus"))
1118 validBlissOsBuildProp = true;
1120 return validBlissOsBuildProp
1121 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1122 && isSystemApp(context);
1125 public static boolean isLauncherPermanentlyEnabled(Context context) {
1126 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1129 PackageManager pm = context.getPackageManager();
1131 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1132 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
1133 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1134 && isSystemApp(context);
1135 } catch (PackageManager.NameNotFoundException e) {
1140 public static int getBaseTaskbarSize(Context context) {
1141 return Math.round(getBaseTaskbarSizeFloat(context));
1144 private static float getBaseTaskbarSizeFloat(Context context) {
1145 SharedPreferences pref = getSharedPreferences(context);
1146 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
1147 boolean navbarButtonsEnabled = false;
1149 if(pref.getBoolean("dashboard", false))
1150 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
1152 if(pref.getBoolean("button_back", false)) {
1153 navbarButtonsEnabled = true;
1154 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1157 if(pref.getBoolean("button_home", false)) {
1158 navbarButtonsEnabled = true;
1159 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1162 if(pref.getBoolean("button_recents", false)) {
1163 navbarButtonsEnabled = true;
1164 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1167 if(navbarButtonsEnabled)
1168 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
1170 if(isSystemTrayEnabled(context))
1171 baseTaskbarSize += context.getResources().getDimension(R.dimen.systray_size);
1173 return baseTaskbarSize;
1176 private static void startTaskbarService(Context context, boolean fullRestart) {
1177 context.startService(new Intent(context, TaskbarService.class));
1178 context.startService(new Intent(context, StartMenuService.class));
1179 context.startService(new Intent(context, DashboardService.class));
1180 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1183 private static void stopTaskbarService(Context context, boolean fullRestart) {
1184 context.stopService(new Intent(context, TaskbarService.class));
1185 context.stopService(new Intent(context, StartMenuService.class));
1186 context.stopService(new Intent(context, DashboardService.class));
1187 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1190 public static void restartTaskbar(Context context) {
1191 SharedPreferences pref = getSharedPreferences(context);
1192 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1194 .putBoolean("is_restarting", true)
1195 .putBoolean("skip_auto_hide_navbar", true)
1198 stopTaskbarService(context, true);
1199 startTaskbarService(context, true);
1200 } else if(isServiceRunning(context, StartMenuService.class)) {
1201 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1203 stopTaskbarService(context, false);
1204 startTaskbarService(context, false);
1207 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.RESTART"));
1210 public static void restartNotificationService(Context context) {
1211 if(isServiceRunning(context, NotificationService.class)) {
1212 SharedPreferences pref = getSharedPreferences(context);
1213 pref.edit().putBoolean("is_restarting", true).apply();
1215 Intent intent = new Intent(context, NotificationService.class);
1216 context.stopService(intent);
1217 context.startService(intent);
1221 public static void showHideNavigationBar(Context context, boolean show) {
1222 // Show or hide the system navigation bar on Bliss-x86
1224 if(getCurrentApiVersion() >= 28.0f)
1225 Settings.Secure.putInt(context.getContentResolver(), "navigation_bar_visible", show ? 1 : 0);
1227 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1228 } catch (Exception e) { /* Gracefully fail */ }
1231 public static void initPrefs(Context context) {
1232 // On smaller-screened devices, set "Grid" as the default start menu layout
1233 SharedPreferences pref = getSharedPreferences(context);
1234 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 720
1235 && pref.getString("start_menu_layout", "null").equals("null")) {
1236 pref.edit().putString("start_menu_layout", "grid").apply();
1239 // Enable freeform hack automatically on supported devices
1240 if(canEnableFreeform()) {
1241 if(!pref.getBoolean("freeform_hack_override", false)) {
1243 .putBoolean("freeform_hack", hasFreeformSupport(context) && !isSamsungDevice())
1244 .putBoolean("save_window_sizes", false)
1245 .putBoolean("freeform_hack_override", true)
1247 } else if(!hasFreeformSupport(context)) {
1248 pref.edit().putBoolean("freeform_hack", false).apply();
1250 stopFreeformHack(context);
1253 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1254 || pref.getBoolean("show_freeform_disabled_message", false);
1257 .putBoolean("freeform_hack", false)
1258 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1261 SavedWindowSizes.getInstance(context).clear(context);
1262 stopFreeformHack(context);
1265 // Customizations for BlissOS
1266 if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1267 && !pref.getBoolean("bliss_os_prefs", false)) {
1268 SharedPreferences.Editor editor = pref.edit();
1270 if(hasFreeformSupport(context)) {
1271 editor.putBoolean("freeform_hack", true);
1274 editor.putString("recents_amount", "running_apps_only");
1275 editor.putString("refresh_frequency", "0");
1276 editor.putString("max_num_of_recents", "2147483647");
1277 editor.putString("sort_order", "true");
1278 editor.putString("window_size", "phone_size");
1279 editor.putString("start_button_image", "app_logo");
1280 editor.putBoolean("full_length", true);
1281 editor.putBoolean("sys_tray", true);
1282 editor.putBoolean("dashboard", true);
1283 editor.putBoolean("button_back", true);
1284 editor.putBoolean("button_home", true);
1285 editor.putBoolean("button_recents", true);
1286 editor.putBoolean("auto_hide_navbar", true);
1287 // editor.putBoolean("shortcut_icon", false);
1288 editor.putBoolean("bliss_os_prefs", true);
1292 // Customizations for Android-x86 devices (non-Bliss)
1293 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1294 && isSystemApp(context)
1295 && !pref.getBoolean("android_x86_prefs", false)) {
1297 .putString("recents_amount", "running_apps_only")
1298 .putString("refresh_frequency", "0")
1299 .putString("max_num_of_recents", "2147483647")
1300 .putString("sort_order", "true")
1301 .putString("window_size", "phone_size")
1302 .putBoolean("full_length", true)
1303 .putBoolean("sys_tray", true)
1304 .putBoolean("dashboard", true)
1305 .putBoolean("shortcut_icon", false)
1306 .putBoolean("android_x86_prefs", true)
1311 public static DisplayInfo getDisplayInfo(Context context) {
1312 return getDisplayInfo(context, false);
1315 public static DisplayInfo getDisplayInfo(Context context, boolean fromTaskbar) {
1316 if(LauncherHelper.getInstance().getSecondaryDisplayId() == -1)
1317 context = context.getApplicationContext();
1319 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1320 Display disp = wm.getDefaultDisplay();
1322 DisplayMetrics metrics = new DisplayMetrics();
1323 disp.getMetrics(metrics);
1325 DisplayMetrics realMetrics = new DisplayMetrics();
1326 disp.getRealMetrics(realMetrics);
1328 DisplayInfo display = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
1330 if(isChromeOs(context)) {
1331 SharedPreferences pref = getSharedPreferences(context);
1332 if(!pref.getBoolean("chrome_os_context_menu_fix", true)) {
1333 display.width = realMetrics.widthPixels;
1334 display.height = realMetrics.heightPixels;
1340 // Workaround for incorrect display size on devices with notches in landscape mode
1341 if(fromTaskbar && context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
1344 boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
1345 boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
1347 if(sameWidth && !sameHeight) {
1348 display.width = realMetrics.widthPixels;
1349 display.height = realMetrics.heightPixels - getNavbarHeight(context);
1352 if(!sameWidth && sameHeight) {
1353 display.width = realMetrics.widthPixels - getNavbarHeight(context);
1354 display.height = realMetrics.heightPixels;
1360 public static void pinAppShortcut(Context context) {
1361 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1362 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1364 if(mShortcutManager.isRequestPinShortcutSupported()) {
1365 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1367 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1369 showToastLong(context, R.string.pin_shortcut_not_supported);
1371 Intent intent = getShortcutIntent(context);
1372 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1373 intent.putExtra("duplicate", false);
1375 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1376 homeIntent.addCategory(Intent.CATEGORY_HOME);
1377 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1379 intent.setPackage(defaultLauncher.activityInfo.packageName);
1380 context.sendBroadcast(intent);
1382 showToast(context, R.string.shortcut_created);
1386 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1387 SharedPreferences pref = getSharedPreferences(context);
1388 if(pref.getBoolean("hide_taskbar", true)) {
1389 if(isOverridingFreeformHack(context))
1390 return !LauncherHelper.getInstance().isOnHomeScreen();
1392 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1393 if(pendingAppLaunch)
1394 return !helper.isFreeformHackActive();
1396 return !helper.isInFreeformWorkspace();
1402 public static boolean isOverridingFreeformHack(Context context) {
1403 SharedPreferences pref = getSharedPreferences(context);
1404 return pref.getBoolean("freeform_hack", false)
1405 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1406 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1409 public static boolean isPlayStoreInstalled(Context context) {
1411 context.getPackageManager().getPackageInfo("com.android.vending", 0);
1413 } catch (PackageManager.NameNotFoundException e) {
1418 public static float getCurrentApiVersion() {
1419 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1420 return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1422 return (float) Build.VERSION.SDK_INT;
1425 public static boolean hasBrokenSetLaunchBoundsApi() {
1426 return getCurrentApiVersion() >= 26.0f
1427 && getCurrentApiVersion() < 28.0f
1428 && !isSamsungDevice()
1429 && !isNvidiaDevice();
1432 public static String getSecondScreenPackageName(Context context) {
1433 return getInstalledPackage(context, Arrays.asList(
1434 "com.farmerbb.secondscreen.free",
1435 "com.farmerbb.secondscreen"));
1438 // Returns the name of an installed package from a list of package names, in order of preference
1439 private static String getInstalledPackage(Context context, List<String> packageNames) {
1440 if(packageNames == null || packageNames.isEmpty())
1443 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1444 String packageName = packages.get(0);
1447 context.getPackageManager().getPackageInfo(packageName, 0);
1449 } catch (PackageManager.NameNotFoundException e) {
1451 return getInstalledPackage(context, packages);
1455 public static boolean visualFeedbackEnabled(Context context) {
1456 SharedPreferences pref = getSharedPreferences(context);
1457 return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1458 && pref.getBoolean("visual_feedback", true)
1459 && !isNvidiaDevice();
1462 public static void showRecentAppsDialog(Context context) {
1463 showRecentAppsDialog(context, null, null);
1466 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1467 Runnable finalOnFinish = onFinish == null
1471 Runnable finalOnError = onError == null
1472 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1475 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1476 ApplicationInfo applicationInfo = null;
1478 applicationInfo = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1479 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1481 if(applicationInfo != null) {
1482 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1483 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1485 if(mode != AppOpsManager.MODE_ALLOWED) {
1486 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1487 builder.setTitle(R.string.pref_header_recent_apps)
1488 .setMessage(R.string.enable_recent_apps)
1489 .setPositiveButton(R.string.action_ok, (dialog, which) -> {
1491 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1492 showToastLong(context, R.string.usage_stats_message);
1494 finalOnFinish.run();
1495 } catch (ActivityNotFoundException e) {
1499 .setNegativeButton(R.string.action_cancel, (dialog, which) -> finalOnFinish.run());
1501 AlertDialog dialog = builder.create();
1503 dialog.setCancelable(false);
1510 finalOnFinish.run();
1514 public static Context wrapContext(Context context) {
1515 SharedPreferences pref = getSharedPreferences(context);
1518 switch(pref.getString("theme", "light")) {
1520 theme = R.style.AppTheme;
1523 theme = R.style.AppTheme_Dark;
1527 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1530 public static boolean isPlayStoreRelease(Context context) {
1531 return isPlayStoreRelease(context, BuildConfig.APPLICATION_ID);
1534 @SuppressLint("PackageManagerGetSignatures")
1535 public static boolean isPlayStoreRelease(Context context, String packageName) {
1536 Signature playStoreSignature = new Signature(context.getString(R.string.signature));
1538 PackageManager pm = context.getPackageManager();
1539 PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
1540 for(Signature signature : info.signatures) {
1541 if(signature.equals(playStoreSignature))
1544 } catch (Exception e) { /* Gracefully fail */ }
1549 public static boolean isExternalAccessDisabled(Context context) {
1550 SharedPreferences pref = getSharedPreferences(context);
1551 return !pref.getBoolean("tasker_enabled", true);
1554 public static boolean enableFreeformModeShortcut(Context context) {
1555 return canEnableFreeform()
1556 && !isOverridingFreeformHack(context)
1557 && !isChromeOs(context);
1560 public static void startForegroundService(Context context, Intent intent) {
1561 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1562 if(Settings.canDrawOverlays(context))
1563 context.startForegroundService(intent);
1565 context.startService(intent);
1568 public static int getOverlayType() {
1569 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1570 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1571 : WindowManager.LayoutParams.TYPE_PHONE;
1574 public static boolean isDelegatingHomeActivity(Context context) {
1575 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1576 homeIntent.addCategory(Intent.CATEGORY_HOME);
1578 final List<ResolveInfo> listOfLaunchers = context.getPackageManager().queryIntentActivities(homeIntent, 0);
1579 for(ResolveInfo launcher : listOfLaunchers) {
1580 if(launcher.activityInfo.packageName.equals(BuildConfig.SUPPORT_APPLICATION_ID))
1587 @SuppressLint("PrivateApi")
1588 private static String getSystemProperty(String key) {
1590 Class<?> cls = Class.forName("android.os.SystemProperties");
1591 return cls.getMethod("get", String.class).invoke(null, key).toString();
1592 } catch (Exception e) {
1597 @SuppressWarnings("ResultOfMethodCallIgnored")
1598 public static boolean importCustomStartButtonImage(Context context, Uri uri) {
1600 File imagesDir = new File(context.getFilesDir(), "images");
1603 File importedFile = new File(imagesDir, "custom_image_new");
1604 if(importedFile.exists()) importedFile.delete();
1606 BufferedInputStream is = new BufferedInputStream(context.getContentResolver().openInputStream(uri));
1607 byte[] data = new byte[is.available()];
1609 if(data.length > 0) {
1610 BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(importedFile));
1617 File prevFile = new File(imagesDir, "custom_image");
1618 if(prevFile.exists()) prevFile.delete();
1620 importedFile.renameTo(prevFile);
1622 } catch (IOException e) {
1627 public static String getDefaultStartButtonImage(Context context) {
1628 SharedPreferences pref = getSharedPreferences(context);
1629 return pref.getBoolean("app_drawer_icon", false)
1634 private static boolean shouldLaunchTouchAbsorber(Context context) {
1635 return isOverridingFreeformHack(context) && !isChromeOs(context) && getCurrentApiVersion() < 29.0f;
1638 public static boolean isDesktopIconsEnabled(Context context) {
1639 return !canBootToFreeform(context) && !shouldLaunchTouchAbsorber(context);
1642 public static boolean isSystemTrayEnabled(Context context) {
1643 SharedPreferences pref = getSharedPreferences(context);
1645 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1646 && pref.getBoolean("sys_tray", false)
1647 && pref.getBoolean("full_length", false)
1648 && !getTaskbarPosition(context).contains("vertical");