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.TouchAbsorberActivity;
67 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
68 import com.farmerbb.taskbar.service.DashboardService;
69 import com.farmerbb.taskbar.service.NotificationService;
70 import com.farmerbb.taskbar.service.PowerMenuService;
71 import com.farmerbb.taskbar.service.StartMenuService;
72 import com.farmerbb.taskbar.service.TaskbarService;
74 import java.io.BufferedInputStream;
75 import java.io.BufferedOutputStream;
77 import java.io.FileOutputStream;
78 import java.io.IOException;
79 import java.lang.reflect.Method;
80 import java.util.ArrayList;
81 import java.util.Arrays;
82 import java.util.List;
88 private static Integer cachedRotation;
90 private static final int MAXIMIZED = 0;
91 private static final int LEFT = -1;
92 private static final int RIGHT = 1;
94 public static final int HIDDEN = 0;
95 public static final int TOP_APPS = 1;
97 // From android.app.ActivityManager.StackId
98 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
99 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
101 // From android.app.WindowConfiguration
102 private static final int WINDOWING_MODE_FULLSCREEN = 1;
103 private static final int WINDOWING_MODE_FREEFORM = 5;
105 @SuppressWarnings("deprecation")
106 public static SharedPreferences getSharedPreferences(Context context) {
107 return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
110 public static void showPermissionDialog(Context context) {
111 showPermissionDialog(context, null, null);
114 @TargetApi(Build.VERSION_CODES.M)
115 public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
116 Runnable finalOnFinish = onFinish == null
120 Runnable finalOnError = onError == null
121 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
124 AlertDialog.Builder builder = new AlertDialog.Builder(context);
125 builder.setTitle(R.string.tb_permission_dialog_title)
126 .setMessage(R.string.tb_permission_dialog_message)
127 .setPositiveButton(R.string.tb_action_grant_permission, (dialog, which) -> {
129 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
130 Uri.parse("package:" + context.getPackageName())));
133 } catch (ActivityNotFoundException e) {
138 AlertDialog dialog = builder.create();
140 dialog.setCancelable(false);
145 public static AlertDialog showErrorDialog(Context context, String appopCmd) {
146 return showErrorDialog(context, appopCmd, null);
149 private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
150 Runnable finalOnFinish = onFinish == null
154 AlertDialog.Builder builder = new AlertDialog.Builder(context);
155 builder.setTitle(R.string.tb_error_dialog_title)
156 .setMessage(context.getString(R.string.tb_error_dialog_message, context.getPackageName(), appopCmd))
157 .setPositiveButton(R.string.tb_action_ok, (dialog, which) -> finalOnFinish.run());
159 AlertDialog dialog = builder.create();
161 dialog.setCancelable(false);
166 public static void sendAccessibilityAction(Context context, int action) {
167 sendAccessibilityAction(context, action, null);
170 public static void sendAccessibilityAction(Context context, int action, Runnable onComplete) {
171 ComponentName component = new ComponentName(context, PowerMenuService.class);
172 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
173 PackageManager.DONT_KILL_APP);
175 boolean isAccessibilityServiceEnabled = isAccessibilityServiceEnabled(context);
177 if(!isAccessibilityServiceEnabled
178 && hasWriteSecureSettingsPermission(context)) {
179 String services = Settings.Secure.getString(context.getContentResolver(),
180 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
182 String finalServices = services == null ? "" : services;
184 String powerMenuService = new ComponentName(context, PowerMenuService.class).flattenToString();
186 if(!finalServices.contains(powerMenuService)) {
188 Settings.Secure.putString(context.getContentResolver(),
189 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
190 finalServices.isEmpty()
192 : finalServices + ":" + powerMenuService);
193 } catch (Exception e) { /* Gracefully fail */ }
196 new Handler().postDelayed(() -> {
197 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
198 intent.putExtra("action", action);
199 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
202 Settings.Secure.putString(context.getContentResolver(),
203 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
205 } catch (Exception e) { /* Gracefully fail */ }
207 if(onComplete != null) onComplete.run();
209 } else if(isAccessibilityServiceEnabled) {
210 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
211 intent.putExtra("action", action);
212 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
214 if(onComplete != null) onComplete.run();
216 launchApp(context, () -> {
217 Intent intent = new Intent(context, DummyActivity.class);
218 intent.putExtra("accessibility", true);
219 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
222 context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION, null));
223 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
228 public static boolean isAccessibilityServiceEnabled(Context context) {
229 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
230 ComponentName component = new ComponentName(context, PowerMenuService.class);
232 return accessibilityServices != null
233 && (accessibilityServices.contains(component.flattenToString())
234 || accessibilityServices.contains(component.flattenToShortString()));
237 public static boolean hasWriteSecureSettingsPermission(Context context) {
238 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
239 && context.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
242 public static void showToast(Context context, int message) {
243 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
246 public static void showToastLong(Context context, int message) {
247 showToast(context, context.getString(message), Toast.LENGTH_LONG);
250 public static void showToast(Context context, String message, int length) {
253 ToastInterface toast = DependencyUtils.createToast(context.getApplicationContext(), message, length);
256 ToastHelper.getInstance().setLastToast(toast);
259 public static void cancelToast() {
260 ToastInterface toast = ToastHelper.getInstance().getLastToast();
261 if(toast != null) toast.cancel();
264 public static void startShortcut(Context context, AppEntry entry, ShortcutInfo shortcut, View view) {
274 public static void launchApp(final Context context,
275 final AppEntry entry,
276 final String windowSize,
277 final boolean launchedFromTaskbar,
278 final boolean openInNewWindow,
289 private static void launchApp(final Context context,
290 final AppEntry entry,
291 final String windowSize,
292 final boolean launchedFromTaskbar,
293 final boolean openInNewWindow,
294 final ShortcutInfo shortcut,
296 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, entry,
297 windowSize, openInNewWindow, shortcut, view));
300 public static void launchApp(Context context, Runnable runnable) {
301 launchApp(context, true, runnable);
304 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
305 SharedPreferences pref = getSharedPreferences(context);
306 FreeformHackHelper helper = FreeformHackHelper.getInstance();
308 boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
309 && helper.isInFreeformWorkspace()
310 && MenuHelper.getInstance().isContextMenuOpen();
312 boolean noAnimation = pref.getBoolean("disable_animations", false);
314 if(hasFreeformSupport(context)
315 && pref.getBoolean("freeform_hack", false)
316 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
317 new Handler().postDelayed(() -> {
318 startFreeformHack(context, true);
320 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
321 }, launchedFromTaskbar ? 0 : 100);
323 new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
326 public static void startFreeformHack(Context context) {
327 startFreeformHack(context, false);
330 @TargetApi(Build.VERSION_CODES.N)
331 public static void startFreeformHack(Context context, boolean checkMultiWindow) {
332 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
333 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
334 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
335 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
338 freeformHackIntent.putExtra("check_multiwindow", true);
340 if(canDrawOverlays(context))
341 startActivityLowerRight(context, freeformHackIntent);
344 public static void stopFreeformHack(Context context) {
345 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
347 if(isOverridingFreeformHack(context)) {
348 FreeformHackHelper helper = FreeformHackHelper.getInstance();
349 helper.setFreeformHackActive(false);
350 helper.setInFreeformWorkspace(false);
354 @TargetApi(Build.VERSION_CODES.N)
355 private static void continueLaunchingApp(Context context,
358 boolean openInNewWindow,
359 ShortcutInfo shortcut,
361 SharedPreferences pref = getSharedPreferences(context);
362 Intent intent = new Intent();
363 intent.setComponent(ComponentName.unflattenFromString(entry.getComponentName()));
364 intent.setAction(Intent.ACTION_MAIN);
365 intent.addCategory(Intent.CATEGORY_LAUNCHER);
366 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
367 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
369 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
370 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
371 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
373 if(pref.getBoolean("disable_animations", false))
374 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
376 boolean realOpenInNewWindow = openInNewWindow || pref.getBoolean("force_new_window", false);
377 if(realOpenInNewWindow) {
378 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
380 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
381 if(activityInfo != null) {
382 switch(activityInfo.launchMode) {
383 case ActivityInfo.LAUNCH_SINGLE_TASK:
384 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
385 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
391 ApplicationType type = getApplicationType(context, entry.getPackageName());
393 if(windowSize == null)
394 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, entry.getPackageName());
396 Bundle bundle = getActivityOptionsBundle(context, type, windowSize, view);
398 prepareToStartActivity(context, realOpenInNewWindow, () -> {
399 if(shortcut == null) {
400 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
401 if(entry.getUserId(context) == userManager.getSerialNumberForUser(Process.myUserHandle())) {
403 context.startActivity(intent, bundle);
404 } catch (ActivityNotFoundException e) {
405 launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context));
406 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
408 launchAndroidForWork(context, intent.getComponent(), bundle, entry.getUserId(context));
410 launchShortcut(context, shortcut, bundle);
413 if(shouldCollapse(context, true))
414 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
416 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
419 @TargetApi(Build.VERSION_CODES.N)
420 private static Bundle launchMode1(Context context, ApplicationType type, View view) {
421 DisplayInfo display = getDisplayInfo(context);
423 int width1 = display.width / 8;
424 int width2 = display.width - width1;
425 int height1 = display.height / 8;
426 int height2 = display.height - height1;
428 return getActivityOptions(context, type, view).setLaunchBounds(new Rect(
436 @TargetApi(Build.VERSION_CODES.N)
437 private static Bundle launchMode2(Context context, int launchType, ApplicationType type, View view) {
438 DisplayInfo display = getDisplayInfo(context);
440 int statusBarHeight = getStatusBarHeight(context);
441 String position = getTaskbarPosition(context);
443 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
444 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
447 int top = statusBarHeight;
448 int right = display.width;
449 int bottom = display.height;
451 int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
453 : context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size);
455 if(position.contains("vertical_left"))
456 left = left + iconSize;
457 else if(position.contains("vertical_right"))
458 right = right - iconSize;
459 else if(position.contains("bottom"))
460 bottom = bottom - iconSize;
462 top = top + iconSize;
464 int halfLandscape = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
465 int halfPortrait = (bottom / 2) + ((iconSize / 2) * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
467 if(launchType == RIGHT && isLandscape)
468 left = halfLandscape;
469 else if(launchType == RIGHT && isPortrait)
471 else if(launchType == LEFT && isLandscape)
472 right = halfLandscape;
473 else if(launchType == LEFT && isPortrait)
474 bottom = halfPortrait;
476 return getActivityOptions(context, type, view)
477 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
480 @TargetApi(Build.VERSION_CODES.N)
481 private static Bundle launchMode3(Context context, ApplicationType type, View view) {
482 DisplayInfo display = getDisplayInfo(context);
484 int width1 = display.width / 2;
485 int width2 = context.getResources().getDimensionPixelSize(R.dimen.tb_phone_size_width) / 2;
486 int height1 = display.height / 2;
487 int height2 = context.getResources().getDimensionPixelSize(R.dimen.tb_phone_size_height) / 2;
489 return getActivityOptions(context, type, view).setLaunchBounds(new Rect(
497 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
498 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
499 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
502 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
503 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
506 @TargetApi(Build.VERSION_CODES.N_MR1)
507 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
508 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
510 if(launcherApps.hasShortcutHostPermission()) {
512 launcherApps.startShortcut(shortcut, null, bundle);
513 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
517 private static void prepareToStartActivity(Context context, boolean openInNewWindow, Runnable runnable) {
518 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_CONTEXT_MENU"));
520 if(!FreeformHackHelper.getInstance().isTouchAbsorberActive()
521 && shouldLaunchTouchAbsorber(context)) {
522 startTouchAbsorberActivity(context);
523 new Handler().postDelayed(runnable, 100);
524 } else if(openInNewWindow) {
525 Intent intent = new Intent(context, DummyActivity.class);
526 intent.putExtra("finish_on_pause", true);
527 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
528 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
529 startActivityLowerRight(context, intent);
531 new Handler().postDelayed(runnable, 100);
536 public static void startActivityMaximized(Context context, Intent intent) {
537 Bundle bundle = launchMode2(context, MAXIMIZED, ApplicationType.CONTEXT_MENU, null);
538 prepareToStartActivity(context, false, () -> context.startActivity(intent, bundle));
541 @TargetApi(Build.VERSION_CODES.N)
542 public static void startActivityLowerRight(Context context, Intent intent) {
543 DisplayInfo display = getDisplayInfo(context);
545 context.startActivity(intent,
546 getActivityOptions(context, ApplicationType.FREEFORM_HACK, null)
547 .setLaunchBounds(new Rect(
553 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
556 @TargetApi(Build.VERSION_CODES.N)
557 public static void startTouchAbsorberActivity(Context context) {
558 String position = getTaskbarPosition(context);
559 DisplayInfo display = getDisplayInfo(context);
563 int right = display.width;
564 int bottom = display.height;
566 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size);
568 if(position.contains("vertical_left"))
570 else if(position.contains("vertical_right"))
571 left = right - iconSize;
572 else if(position.contains("bottom"))
573 top = bottom - iconSize;
577 Intent intent = new Intent(context, TouchAbsorberActivity.class);
578 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
579 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
582 context.startActivity(intent,
583 getActivityOptions(context, ApplicationType.FREEFORM_HACK, null)
584 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle());
585 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
588 public static void startContextMenuActivity(Context context, Bundle args) {
589 SharedPreferences pref = getSharedPreferences(context);
590 Intent intent = null;
592 switch(pref.getString("theme", "light")) {
594 intent = new Intent(context, ContextMenuActivity.class);
597 intent = new Intent(context, ContextMenuActivityDark.class);
602 intent.putExtra("args", args);
603 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
606 if(hasFreeformSupport(context) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
607 DisplayInfo display = getDisplayInfo(context);
609 if(intent != null && hasBrokenSetLaunchBoundsApi())
610 intent.putExtra("context_menu_fix", true);
612 context.startActivity(intent,
613 getActivityOptions(context, ApplicationType.CONTEXT_MENU, null)
615 new Rect(0, 0, display.width, display.height)
618 context.startActivity(intent);
621 public static void checkForUpdates(Context context) {
623 if(isPlayStoreRelease(context)) {
624 if(context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
625 && !isPlayStoreInstalled(context))
626 url = "https://github.com/farmerbb/Taskbar/releases";
628 url = "https://play.google.com/store/apps/details?id=" + context.getPackageName();
630 url = "https://f-droid.org/repository/browse/?fdid=" + context.getPackageName();
632 Intent intent = new Intent(Intent.ACTION_VIEW);
633 intent.setData(Uri.parse(url));
634 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
637 context.startActivity(intent);
638 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
641 public static boolean launcherIsDefault(Context context) {
642 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
643 homeIntent.addCategory(Intent.CATEGORY_HOME);
644 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
646 return defaultLauncher.activityInfo.packageName.equals(context.getPackageName());
649 public static void setCachedRotation(int cachedRotation) {
650 U.cachedRotation = cachedRotation;
653 public static String getTaskbarPosition(Context context) {
654 SharedPreferences pref = getSharedPreferences(context);
655 String position = pref.getString("position", "bottom_left");
657 if(pref.getBoolean("anchor", false)) {
658 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
659 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
664 case Surface.ROTATION_0:
665 return "bottom_left";
666 case Surface.ROTATION_90:
667 return "bottom_vertical_right";
668 case Surface.ROTATION_180:
670 case Surface.ROTATION_270:
671 return "top_vertical_left";
674 case "bottom_vertical_left":
676 case Surface.ROTATION_0:
677 return "bottom_vertical_left";
678 case Surface.ROTATION_90:
679 return "bottom_right";
680 case Surface.ROTATION_180:
681 return "top_vertical_right";
682 case Surface.ROTATION_270:
688 case Surface.ROTATION_0:
689 return "bottom_right";
690 case Surface.ROTATION_90:
691 return "top_vertical_right";
692 case Surface.ROTATION_180:
694 case Surface.ROTATION_270:
695 return "bottom_vertical_left";
698 case "bottom_vertical_right":
700 case Surface.ROTATION_0:
701 return "bottom_vertical_right";
702 case Surface.ROTATION_90:
704 case Surface.ROTATION_180:
705 return "top_vertical_left";
706 case Surface.ROTATION_270:
707 return "bottom_left";
712 case Surface.ROTATION_0:
714 case Surface.ROTATION_90:
715 return "bottom_vertical_left";
716 case Surface.ROTATION_180:
717 return "bottom_right";
718 case Surface.ROTATION_270:
719 return "top_vertical_right";
722 case "top_vertical_left":
724 case Surface.ROTATION_0:
725 return "top_vertical_left";
726 case Surface.ROTATION_90:
727 return "bottom_left";
728 case Surface.ROTATION_180:
729 return "bottom_vertical_right";
730 case Surface.ROTATION_270:
736 case Surface.ROTATION_0:
738 case Surface.ROTATION_90:
739 return "top_vertical_left";
740 case Surface.ROTATION_180:
741 return "bottom_left";
742 case Surface.ROTATION_270:
743 return "bottom_vertical_right";
746 case "top_vertical_right":
748 case Surface.ROTATION_0:
749 return "top_vertical_right";
750 case Surface.ROTATION_90:
752 case Surface.ROTATION_180:
753 return "bottom_vertical_left";
754 case Surface.ROTATION_270:
755 return "bottom_right";
764 private static int getMaxNumOfColumns(Context context) {
765 SharedPreferences pref = getSharedPreferences(context);
766 DisplayInfo display = getDisplayInfo(context);
767 float density = display.density / 160;
768 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / density;
769 int numOfColumns = 0;
771 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
772 ? (display.height - getStatusBarHeight(context)) / density
773 : display.width / density;
775 float iconSize = context.getResources().getDimension(R.dimen.tb_icon_size) / density;
777 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
779 while(baseTaskbarSize + iconSize < maxScreenSize
780 && numOfColumns < userMaxNumOfColumns) {
781 baseTaskbarSize = baseTaskbarSize + iconSize;
788 public static int getMaxNumOfEntries(Context context) {
789 SharedPreferences pref = getSharedPreferences(context);
790 return pref.getBoolean("disable_scrolling_list", false)
791 ? getMaxNumOfColumns(context)
792 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
795 public static int getStatusBarHeight(Context context) {
796 return getSystemDimen(context, "status_bar_height");
799 private static int getNavbarHeight(Context context) {
800 return getSystemDimen(context, "navigation_bar_height");
803 private static int getSystemDimen(Context context, String id) {
805 int resourceId = context.getResources().getIdentifier(id, "dimen", "android");
807 value = context.getResources().getDimensionPixelSize(resourceId);
812 public static void refreshPinnedIcons(Context context) {
813 IconCache.getInstance(context).clearCache();
815 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
816 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
817 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
818 PackageManager pm = context.getPackageManager();
822 for(AppEntry entry : pinnedAppsList) {
823 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
824 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
826 final List<UserHandle> userHandles = userManager.getUserProfiles();
827 LauncherActivityInfo appInfo = null;
829 for(UserHandle handle : userHandles) {
830 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
831 if(!list.isEmpty()) {
832 // Google App workaround
833 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
834 appInfo = list.get(0);
836 boolean added = false;
837 for(LauncherActivityInfo info : list) {
838 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
844 if(!added) appInfo = list.get(0);
851 if(appInfo != null) {
852 AppEntry newEntry = new AppEntry(
853 entry.getPackageName(),
854 entry.getComponentName(),
856 IconCache.getInstance(context).getIcon(context, pm, appInfo),
859 newEntry.setUserId(entry.getUserId(context));
860 pba.addPinnedApp(context, newEntry);
864 for(AppEntry entry : blockedAppsList) {
865 pba.addBlockedApp(context, entry);
869 public static boolean canEnableFreeform() {
870 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
873 @TargetApi(Build.VERSION_CODES.N)
874 public static boolean hasFreeformSupport(Context context) {
875 return canEnableFreeform()
876 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
877 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
878 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
879 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
882 public static boolean canBootToFreeform(Context context) {
883 SharedPreferences pref = getSharedPreferences(context);
884 return hasFreeformSupport(context)
885 && pref.getBoolean("freeform_hack", false)
886 && !isOverridingFreeformHack(context);
889 public static boolean isSamsungDevice() {
890 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
893 private static boolean isNvidiaDevice() {
894 return Build.MANUFACTURER.equalsIgnoreCase("NVIDIA");
897 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
898 if(LauncherHelper.getInstance().isOnHomeScreen(false, true)
899 && (cls.equals(TaskbarService.class)
900 || cls.equals(StartMenuService.class)
901 || cls.equals(DashboardService.class)))
904 return isServiceRunning(context, cls.getName());
907 private static boolean isServiceRunning(Context context, String className) {
908 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
909 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
910 if(className.equals(service.service.getClassName()))
917 public static int getBackgroundTint(Context context) {
918 SharedPreferences pref = getSharedPreferences(context);
920 // Import old background tint preference
921 if(pref.contains("show_background")) {
922 SharedPreferences.Editor editor = pref.edit();
924 if(!pref.getBoolean("show_background", true))
925 editor.putInt("background_tint", Color.TRANSPARENT).apply();
927 editor.remove("show_background");
931 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.tb_translucent_gray));
934 public static int getAccentColor(Context context) {
935 SharedPreferences pref = getSharedPreferences(context);
936 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.tb_translucent_white));
939 @TargetApi(Build.VERSION_CODES.M)
940 public static boolean canDrawOverlays(Context context) {
941 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
944 public static boolean isGame(Context context, String packageName) {
945 SharedPreferences pref = getSharedPreferences(context);
946 if(pref.getBoolean("launch_games_fullscreen", true)) {
947 PackageManager pm = context.getPackageManager();
950 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
951 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
952 } catch (PackageManager.NameNotFoundException e) {
959 private static ActivityOptions getActivityOptions(View view) {
960 return getActivityOptions(null, null, view);
963 @TargetApi(Build.VERSION_CODES.N)
964 private static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType, View view) {
965 ActivityOptions options;
967 options = ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
969 options = ActivityOptions.makeBasic();
971 if(applicationType == null)
976 switch(applicationType) {
978 if(FreeformHackHelper.getInstance().isFreeformHackActive())
979 stackId = getFreeformWindowModeId();
981 stackId = getFullscreenWindowModeId();
984 stackId = getFullscreenWindowModeId();
987 stackId = getFreeformWindowModeId();
990 if(hasBrokenSetLaunchBoundsApi()
991 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
992 stackId = getFullscreenWindowModeId();
997 Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
998 method.invoke(options, stackId);
999 } catch (Exception e) { /* Gracefully fail */ }
1001 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1002 int launchDisplayId = LauncherHelper.getInstance().getSecondaryDisplayId();
1003 if(launchDisplayId != -1)
1004 options.setLaunchDisplayId(launchDisplayId);
1010 private static int getFullscreenWindowModeId() {
1011 if(getCurrentApiVersion() >= 28.0f)
1012 return WINDOWING_MODE_FULLSCREEN;
1014 return FULLSCREEN_WORKSPACE_STACK_ID;
1017 private static int getFreeformWindowModeId() {
1018 if(getCurrentApiVersion() >= 28.0f)
1019 return WINDOWING_MODE_FREEFORM;
1021 return FREEFORM_WORKSPACE_STACK_ID;
1024 private static String getWindowingModeMethodName() {
1025 if(getCurrentApiVersion() >= 28.0f)
1026 return "setLaunchWindowingMode";
1028 return "setLaunchStackId";
1031 public static Bundle getActivityOptionsBundle(Context context, ApplicationType type, View view) {
1032 SharedPreferences pref = getSharedPreferences(context);
1034 return getActivityOptionsBundle(context, type, pref.getString("window_size", context.getString(R.string.tb_def_window_size)), view);
1037 private static Bundle getActivityOptionsBundle(Context context, ApplicationType type, String windowSize, View view) {
1038 SharedPreferences pref = getSharedPreferences(context);
1039 if(!canEnableFreeform() || !pref.getBoolean("freeform_hack", false))
1040 return getActivityOptions(view).toBundle();
1042 switch(windowSize) {
1044 return launchMode1(context, type, view);
1046 return launchMode2(context, MAXIMIZED, type, view);
1048 return launchMode2(context, LEFT, type, view);
1050 return launchMode2(context, RIGHT, type, view);
1052 return launchMode3(context, type, view);
1055 return getActivityOptions(context, type, view).toBundle();
1058 private static ApplicationType getApplicationType(Context context, String packageName) {
1059 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1062 public static boolean isSystemApp(Context context) {
1064 ApplicationInfo info = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
1065 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1066 return (info.flags & mask) != 0;
1067 } catch (PackageManager.NameNotFoundException e) {
1072 public static boolean isChromeOs(Context context) {
1073 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1076 public static boolean isBlissOs(Context context) {
1077 boolean validBlissOsBuildProp = false;
1079 String blissVersion = getSystemProperty("ro.bliss.version");
1080 if(blissVersion != null && !blissVersion.isEmpty())
1081 validBlissOsBuildProp = true;
1083 String buildUser = getSystemProperty("ro.build.user");
1084 if(buildUser != null && buildUser.equals("electrikjesus"))
1085 validBlissOsBuildProp = true;
1087 return validBlissOsBuildProp
1088 && context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
1089 && isSystemApp(context);
1092 public static boolean isLauncherPermanentlyEnabled(Context context) {
1093 if(context.getPackageName().equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1096 PackageManager pm = context.getPackageManager();
1098 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1099 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, context.getPackageName()) == PackageManager.SIGNATURE_MATCH
1100 && context.getPackageName().equals(BuildConfig.BASE_APPLICATION_ID)
1101 && isSystemApp(context);
1102 } catch (PackageManager.NameNotFoundException e) {
1107 public static int getBaseTaskbarSize(Context context) {
1108 return Math.round(getBaseTaskbarSizeFloat(context));
1111 private static float getBaseTaskbarSizeFloat(Context context) {
1112 SharedPreferences pref = getSharedPreferences(context);
1113 float baseTaskbarSize = context.getResources().getDimension(R.dimen.tb_base_taskbar_size);
1114 boolean navbarButtonsEnabled = false;
1116 if(pref.getBoolean("dashboard", context.getResources().getBoolean(R.bool.tb_def_dashboard)))
1117 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_dashboard_button_size);
1119 if(pref.getBoolean("button_back", false)) {
1120 navbarButtonsEnabled = true;
1121 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_icon_size);
1124 if(pref.getBoolean("button_home", false)) {
1125 navbarButtonsEnabled = true;
1126 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_icon_size);
1129 if(pref.getBoolean("button_recents", false)) {
1130 navbarButtonsEnabled = true;
1131 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_icon_size);
1134 if(navbarButtonsEnabled)
1135 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_navbar_buttons_margin);
1137 if(isSystemTrayEnabled(context))
1138 baseTaskbarSize += context.getResources().getDimension(R.dimen.tb_systray_size);
1140 return baseTaskbarSize;
1143 private static void startTaskbarService(Context context, boolean fullRestart) {
1144 context.startService(new Intent(context, TaskbarService.class));
1145 context.startService(new Intent(context, StartMenuService.class));
1146 context.startService(new Intent(context, DashboardService.class));
1147 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1150 private static void stopTaskbarService(Context context, boolean fullRestart) {
1151 context.stopService(new Intent(context, TaskbarService.class));
1152 context.stopService(new Intent(context, StartMenuService.class));
1153 context.stopService(new Intent(context, DashboardService.class));
1154 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1157 public static void restartTaskbar(Context context) {
1158 SharedPreferences pref = getSharedPreferences(context);
1159 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1161 .putBoolean("is_restarting", true)
1162 .putBoolean("skip_auto_hide_navbar", true)
1165 stopTaskbarService(context, true);
1166 startTaskbarService(context, true);
1167 } else if(isServiceRunning(context, StartMenuService.class)) {
1168 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1170 stopTaskbarService(context, false);
1171 startTaskbarService(context, false);
1174 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.RESTART"));
1177 public static void restartNotificationService(Context context) {
1178 if(isServiceRunning(context, NotificationService.class)) {
1179 SharedPreferences pref = getSharedPreferences(context);
1180 pref.edit().putBoolean("is_restarting", true).apply();
1182 Intent intent = new Intent(context, NotificationService.class);
1183 context.stopService(intent);
1184 context.startService(intent);
1188 public static void showHideNavigationBar(Context context, boolean show) {
1189 // Show or hide the system navigation bar on Bliss-x86
1191 if(getCurrentApiVersion() >= 28.0f)
1192 Settings.Secure.putInt(context.getContentResolver(), "navigation_bar_visible", show ? 1 : 0);
1194 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1195 } catch (Exception e) { /* Gracefully fail */ }
1198 public static void initPrefs(Context context) {
1199 // Enable freeform hack automatically on supported devices
1200 SharedPreferences pref = getSharedPreferences(context);
1201 if(canEnableFreeform()) {
1202 if(!pref.getBoolean("freeform_hack_override", false)) {
1204 .putBoolean("freeform_hack", hasFreeformSupport(context) && !isSamsungDevice())
1205 .putBoolean("save_window_sizes", false)
1206 .putBoolean("freeform_hack_override", true)
1208 } else if(!hasFreeformSupport(context)) {
1209 pref.edit().putBoolean("freeform_hack", false).apply();
1211 stopFreeformHack(context);
1214 boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1215 || pref.getBoolean("show_freeform_disabled_message", false);
1218 .putBoolean("freeform_hack", false)
1219 .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1222 SavedWindowSizes.getInstance(context).clear(context);
1223 stopFreeformHack(context);
1226 // Customizations for BlissOS
1227 if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1228 && !pref.getBoolean("bliss_os_prefs", false)) {
1229 SharedPreferences.Editor editor = pref.edit();
1231 if(hasFreeformSupport(context)) {
1232 editor.putBoolean("freeform_hack", true);
1235 editor.putString("recents_amount", "running_apps_only");
1236 editor.putString("refresh_frequency", "0");
1237 editor.putString("max_num_of_recents", "2147483647");
1238 editor.putString("sort_order", "true");
1239 editor.putString("start_button_image", "app_logo");
1240 editor.putBoolean("button_back", true);
1241 editor.putBoolean("button_home", true);
1242 editor.putBoolean("button_recents", true);
1243 // editor.putBoolean("auto_hide_navbar", true);
1244 // editor.putBoolean("shortcut_icon", false);
1245 editor.putBoolean("bliss_os_prefs", true);
1249 // Customizations for Android-x86 devices (non-Bliss)
1250 if(context.getPackageName().equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1251 && isSystemApp(context)
1252 && !pref.getBoolean("android_x86_prefs", false)) {
1254 .putString("recents_amount", "running_apps_only")
1255 .putString("refresh_frequency", "0")
1256 .putString("max_num_of_recents", "2147483647")
1257 .putString("sort_order", "true")
1258 .putBoolean("shortcut_icon", false)
1259 .putBoolean("android_x86_prefs", true)
1264 public static DisplayInfo getDisplayInfo(Context context) {
1265 return getDisplayInfo(context, false);
1268 public static DisplayInfo getDisplayInfo(Context context, boolean fromTaskbar) {
1269 if(LauncherHelper.getInstance().getSecondaryDisplayId() == -1)
1270 context = context.getApplicationContext();
1272 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1273 Display disp = wm.getDefaultDisplay();
1275 DisplayMetrics metrics = new DisplayMetrics();
1276 disp.getMetrics(metrics);
1278 DisplayMetrics realMetrics = new DisplayMetrics();
1279 disp.getRealMetrics(realMetrics);
1281 DisplayInfo display = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
1283 if(isChromeOs(context)) {
1284 SharedPreferences pref = getSharedPreferences(context);
1285 if(!pref.getBoolean("chrome_os_context_menu_fix", true)) {
1286 display.width = realMetrics.widthPixels;
1287 display.height = realMetrics.heightPixels;
1293 // Workaround for incorrect display size on devices with notches in landscape mode
1294 if(fromTaskbar && context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
1297 boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
1298 boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
1300 if(sameWidth && !sameHeight) {
1301 display.width = realMetrics.widthPixels;
1302 display.height = realMetrics.heightPixels - getNavbarHeight(context);
1305 if(!sameWidth && sameHeight) {
1306 display.width = realMetrics.widthPixels - getNavbarHeight(context);
1307 display.height = realMetrics.heightPixels;
1313 public static void pinAppShortcut(Context context) {
1314 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1315 ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1317 if(mShortcutManager.isRequestPinShortcutSupported()) {
1318 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1320 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1322 showToastLong(context, R.string.tb_pin_shortcut_not_supported);
1324 Intent intent = ShortcutUtils.getShortcutIntent(context);
1325 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1326 intent.putExtra("duplicate", false);
1328 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1329 homeIntent.addCategory(Intent.CATEGORY_HOME);
1330 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1332 intent.setPackage(defaultLauncher.activityInfo.packageName);
1333 context.sendBroadcast(intent);
1335 showToast(context, R.string.tb_shortcut_created);
1339 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1340 SharedPreferences pref = getSharedPreferences(context);
1341 if(pref.getBoolean("hide_taskbar", true)) {
1342 if(isOverridingFreeformHack(context))
1343 return !LauncherHelper.getInstance().isOnHomeScreen();
1345 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1346 if(pendingAppLaunch)
1347 return !helper.isFreeformHackActive();
1349 return !helper.isInFreeformWorkspace();
1355 public static boolean isOverridingFreeformHack(Context context) {
1356 SharedPreferences pref = getSharedPreferences(context);
1357 return pref.getBoolean("freeform_hack", false)
1358 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1359 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1362 public static boolean isPlayStoreInstalled(Context context) {
1364 context.getPackageManager().getPackageInfo("com.android.vending", 0);
1366 } catch (PackageManager.NameNotFoundException e) {
1371 public static float getCurrentApiVersion() {
1372 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1373 return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1375 return (float) Build.VERSION.SDK_INT;
1378 public static boolean hasBrokenSetLaunchBoundsApi() {
1379 return getCurrentApiVersion() >= 26.0f
1380 && getCurrentApiVersion() < 28.0f
1381 && !isSamsungDevice()
1382 && !isNvidiaDevice();
1385 public static String getSecondScreenPackageName(Context context) {
1386 return getInstalledPackage(context, Arrays.asList(
1387 "com.farmerbb.secondscreen.free",
1388 "com.farmerbb.secondscreen"));
1391 // Returns the name of an installed package from a list of package names, in order of preference
1392 private static String getInstalledPackage(Context context, List<String> packageNames) {
1393 if(packageNames == null || packageNames.isEmpty())
1396 List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1397 String packageName = packages.get(0);
1400 context.getPackageManager().getPackageInfo(packageName, 0);
1402 } catch (PackageManager.NameNotFoundException e) {
1404 return getInstalledPackage(context, packages);
1408 public static boolean visualFeedbackEnabled(Context context) {
1409 SharedPreferences pref = getSharedPreferences(context);
1410 return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1411 && pref.getBoolean("visual_feedback", true)
1412 && !isNvidiaDevice();
1415 public static void showRecentAppsDialog(Context context) {
1416 showRecentAppsDialog(context, null, null);
1419 public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1420 Runnable finalOnFinish = onFinish == null
1424 Runnable finalOnError = onError == null
1425 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1428 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1429 ApplicationInfo applicationInfo = null;
1431 applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
1432 } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1434 if(applicationInfo != null) {
1435 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1436 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1438 if(mode != AppOpsManager.MODE_ALLOWED) {
1439 AlertDialog.Builder builder = new AlertDialog.Builder(context);
1440 builder.setTitle(R.string.tb_pref_header_recent_apps)
1441 .setMessage(R.string.tb_enable_recent_apps)
1442 .setPositiveButton(R.string.tb_action_ok, (dialog, which) -> {
1444 context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1445 showToastLong(context, R.string.tb_usage_stats_message);
1447 finalOnFinish.run();
1448 } catch (ActivityNotFoundException e) {
1452 .setNegativeButton(R.string.tb_action_cancel, (dialog, which) -> finalOnFinish.run());
1454 AlertDialog dialog = builder.create();
1456 dialog.setCancelable(false);
1463 finalOnFinish.run();
1467 public static Context wrapContext(Context context) {
1468 SharedPreferences pref = getSharedPreferences(context);
1471 switch(pref.getString("theme", "light")) {
1473 theme = R.style.Taskbar;
1476 theme = R.style.Taskbar_Dark;
1480 return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1483 public static boolean isPlayStoreRelease(Context context) {
1484 return isPlayStoreRelease(context, context.getPackageName());
1487 @SuppressLint("PackageManagerGetSignatures")
1488 public static boolean isPlayStoreRelease(Context context, String packageName) {
1489 Signature playStoreSignature = new Signature(context.getString(R.string.tb_signature));
1491 PackageManager pm = context.getPackageManager();
1492 PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
1493 for(Signature signature : info.signatures) {
1494 if(signature.equals(playStoreSignature))
1497 } catch (Exception e) { /* Gracefully fail */ }
1502 public static boolean isExternalAccessDisabled(Context context) {
1503 SharedPreferences pref = getSharedPreferences(context);
1504 return !pref.getBoolean("tasker_enabled", true);
1507 public static boolean enableFreeformModeShortcut(Context context) {
1508 return canEnableFreeform()
1509 && !isOverridingFreeformHack(context)
1510 && !isChromeOs(context);
1513 public static void startForegroundService(Context context, Intent intent) {
1514 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1515 if(Settings.canDrawOverlays(context))
1516 context.startForegroundService(intent);
1518 context.startService(intent);
1521 public static int getOverlayType() {
1522 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1523 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1524 : WindowManager.LayoutParams.TYPE_PHONE;
1527 public static boolean isDelegatingHomeActivity(Context context) {
1528 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1529 homeIntent.addCategory(Intent.CATEGORY_HOME);
1531 final List<ResolveInfo> listOfLaunchers = context.getPackageManager().queryIntentActivities(homeIntent, 0);
1532 for(ResolveInfo launcher : listOfLaunchers) {
1533 if(launcher.activityInfo.packageName.equals(BuildConfig.SUPPORT_APPLICATION_ID))
1540 @SuppressLint("PrivateApi")
1541 private static String getSystemProperty(String key) {
1543 Class<?> cls = Class.forName("android.os.SystemProperties");
1544 return cls.getMethod("get", String.class).invoke(null, key).toString();
1545 } catch (Exception e) {
1550 @SuppressWarnings("ResultOfMethodCallIgnored")
1551 public static boolean importCustomStartButtonImage(Context context, Uri uri) {
1553 File imagesDir = new File(context.getFilesDir(), "tb_images");
1556 File importedFile = new File(imagesDir, "custom_image_new");
1557 if(importedFile.exists()) importedFile.delete();
1559 BufferedInputStream is = new BufferedInputStream(context.getContentResolver().openInputStream(uri));
1560 byte[] data = new byte[is.available()];
1562 if(data.length > 0) {
1563 BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(importedFile));
1570 File prevFile = new File(imagesDir, "custom_image");
1571 if(prevFile.exists()) prevFile.delete();
1573 importedFile.renameTo(prevFile);
1575 } catch (IOException e) {
1580 public static String getDefaultStartButtonImage(Context context) {
1581 SharedPreferences pref = getSharedPreferences(context);
1582 return pref.getBoolean("app_drawer_icon", false)
1587 private static boolean shouldLaunchTouchAbsorber(Context context) {
1588 return isOverridingFreeformHack(context) && !isChromeOs(context) && getCurrentApiVersion() < 29.0f;
1591 public static boolean isDesktopIconsEnabled(Context context) {
1592 return !canBootToFreeform(context) && !shouldLaunchTouchAbsorber(context);
1595 public static boolean isSystemTrayEnabled(Context context) {
1596 SharedPreferences pref = getSharedPreferences(context);
1598 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1599 && pref.getBoolean("sys_tray", context.getResources().getBoolean(R.bool.tb_def_sys_tray))
1600 && pref.getBoolean("full_length", context.getResources().getBoolean(R.bool.tb_def_full_length))
1601 && !getTaskbarPosition(context).contains("vertical");
1604 @SuppressWarnings("deprecation")
1605 public static boolean isLibrary(Context context) {
1606 return !context.getPackageName().equals(BuildConfig.APPLICATION_ID);