1 /* Copyright 2016 Braden Farmer
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 package com.farmerbb.taskbar.util;
18 import android.annotation.TargetApi;
19 import android.app.Activity;
20 import android.app.ActivityManager;
21 import android.app.ActivityOptions;
22 import android.app.AlertDialog;
23 import android.app.Service;
24 import android.app.admin.DevicePolicyManager;
25 import android.content.ActivityNotFoundException;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.SharedPreferences;
30 import android.content.pm.ActivityInfo;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.LauncherActivityInfo;
33 import android.content.pm.LauncherApps;
34 import android.content.pm.PackageManager;
35 import android.content.pm.ResolveInfo;
36 import android.content.pm.ShortcutInfo;
37 import android.content.res.Configuration;
38 import android.graphics.Color;
39 import android.graphics.Rect;
40 import android.graphics.drawable.BitmapDrawable;
41 import android.net.Uri;
42 import android.os.Build;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.Process;
46 import android.os.UserHandle;
47 import android.os.UserManager;
48 import android.provider.Settings;
49 import android.support.v4.content.LocalBroadcastManager;
50 import android.util.DisplayMetrics;
51 import android.view.Display;
52 import android.view.Surface;
53 import android.view.WindowManager;
54 import android.widget.Toast;
56 import com.farmerbb.taskbar.BuildConfig;
57 import com.farmerbb.taskbar.R;
58 import com.farmerbb.taskbar.activity.DummyActivity;
59 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
60 import com.farmerbb.taskbar.activity.ShortcutActivity;
61 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
62 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
63 import com.farmerbb.taskbar.service.DashboardService;
64 import com.farmerbb.taskbar.service.NotificationService;
65 import com.farmerbb.taskbar.service.PowerMenuService;
66 import com.farmerbb.taskbar.service.StartMenuService;
67 import com.farmerbb.taskbar.service.TaskbarService;
69 import java.lang.reflect.Method;
70 import java.util.ArrayList;
71 import java.util.List;
77 private static SharedPreferences pref;
78 private static Integer cachedRotation;
80 private static final int MAXIMIZED = 0;
81 private static final int LEFT = -1;
82 private static final int RIGHT = 1;
84 public static final int HIDDEN = 0;
85 public static final int TOP_APPS = 1;
87 // From android.app.ActivityManager.StackId
88 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
89 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
91 public static SharedPreferences getSharedPreferences(Context context) {
92 if(pref == null) pref = context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
96 @TargetApi(Build.VERSION_CODES.M)
97 public static AlertDialog showPermissionDialog(final Context context) {
98 AlertDialog.Builder builder = new AlertDialog.Builder(context);
99 builder.setTitle(R.string.permission_dialog_title)
100 .setMessage(R.string.permission_dialog_message)
101 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
103 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
104 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
105 } catch (ActivityNotFoundException e) {
106 showErrorDialog(context, "SYSTEM_ALERT_WINDOW");
110 AlertDialog dialog = builder.create();
112 dialog.setCancelable(false);
117 public static void showErrorDialog(final Context context, String appopCmd) {
118 AlertDialog.Builder builder = new AlertDialog.Builder(context);
119 builder.setTitle(R.string.error_dialog_title)
120 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
121 .setPositiveButton(R.string.action_ok, null);
123 AlertDialog dialog = builder.create();
127 public static void lockDevice(Context context) {
128 ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
129 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
130 PackageManager.DONT_KILL_APP);
132 DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
133 if(mDevicePolicyManager.isAdminActive(component))
134 mDevicePolicyManager.lockNow();
136 launchApp(context, () -> {
137 Intent intent = new Intent(context, DummyActivity.class);
138 intent.putExtra("device_admin", true);
139 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
140 context.startActivity(intent);
142 if(context instanceof Activity)
143 ((Activity) context).overridePendingTransition(0, 0);
148 public static void sendAccessibilityAction(Context context, int action) {
149 ComponentName component = new ComponentName(context, PowerMenuService.class);
150 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
151 PackageManager.DONT_KILL_APP);
153 if(isAccessibilityServiceEnabled(context)) {
154 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
155 intent.putExtra("action", action);
156 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
158 launchApp(context, () -> {
159 Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
160 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
163 context.startActivity(intent);
164 showToastLong(context, R.string.enable_accessibility);
165 } catch (ActivityNotFoundException e) {
166 showToast(context, R.string.lock_device_not_supported);
172 private static boolean isAccessibilityServiceEnabled(Context context) {
173 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
174 ComponentName component = new ComponentName(context, PowerMenuService.class);
176 return accessibilityServices != null
177 && (accessibilityServices.contains(component.flattenToString())
178 || accessibilityServices.contains(component.flattenToShortString()));
181 public static void showToast(Context context, int message) {
182 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
185 public static void showToastLong(Context context, int message) {
186 showToast(context, context.getString(message), Toast.LENGTH_LONG);
189 public static void showToast(Context context, String message, int length) {
192 ToastInterface toast;
193 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1
194 || BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
195 toast = new ToastFrameworkImpl(context, message, length);
197 toast = new ToastCompatImpl(context, message, length);
201 ToastHelper.getInstance().setLastToast(toast);
204 private static void cancelToast() {
205 ToastInterface toast = ToastHelper.getInstance().getLastToast();
206 if(toast != null) toast.cancel();
209 public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
220 public static void launchApp(final Context context,
221 final String packageName,
222 final String componentName,
223 final long userId, final String windowSize,
224 final boolean launchedFromTaskbar,
225 final boolean openInNewWindow) {
236 private static void launchApp(final Context context,
237 final String packageName,
238 final String componentName,
239 final long userId, final String windowSize,
240 final boolean launchedFromTaskbar,
241 final boolean openInNewWindow,
242 final ShortcutInfo shortcut) {
243 launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, packageName, componentName, userId,
244 windowSize, launchedFromTaskbar, openInNewWindow, shortcut));
247 public static void launchApp(Context context, Runnable runnable) {
248 launchApp(context, true, runnable);
251 private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
252 SharedPreferences pref = getSharedPreferences(context);
253 FreeformHackHelper helper = FreeformHackHelper.getInstance();
255 boolean specialLaunch = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
256 && FreeformHackHelper.getInstance().isInFreeformWorkspace()
257 && MenuHelper.getInstance().isContextMenuOpen();
259 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
260 && pref.getBoolean("freeform_hack", false)
261 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
262 new Handler().postDelayed(() -> {
263 startFreeformHack(context, true, launchedFromTaskbar);
265 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
266 }, launchedFromTaskbar ? 0 : 100);
271 @SuppressWarnings("deprecation")
272 @TargetApi(Build.VERSION_CODES.N)
273 public static void startFreeformHack(Context context, boolean checkMultiWindow, boolean launchedFromTaskbar) {
274 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
275 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
278 freeformHackIntent.putExtra("check_multiwindow", true);
280 if(launchedFromTaskbar) {
281 SharedPreferences pref = getSharedPreferences(context);
282 if(pref.getBoolean("disable_animations", false))
283 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
286 if(canDrawOverlays(context))
287 launchAppLowerRight(context, freeformHackIntent);
290 @TargetApi(Build.VERSION_CODES.N)
291 private static void continueLaunchingApp(Context context,
293 String componentName,
296 boolean launchedFromTaskbar,
297 boolean openInNewWindow,
298 ShortcutInfo shortcut) {
299 SharedPreferences pref = getSharedPreferences(context);
300 Intent intent = new Intent();
301 intent.setComponent(ComponentName.unflattenFromString(componentName));
302 intent.setAction(Intent.ACTION_MAIN);
303 intent.addCategory(Intent.CATEGORY_LAUNCHER);
304 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
305 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
307 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
308 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
309 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
311 if(launchedFromTaskbar) {
312 if(pref.getBoolean("disable_animations", false))
313 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
316 if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
317 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
319 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
320 if(activityInfo != null) {
321 switch(activityInfo.launchMode) {
322 case ActivityInfo.LAUNCH_SINGLE_TASK:
323 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
324 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
330 ApplicationType type = getApplicationType(context, packageName);
332 if(windowSize == null)
333 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
335 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N
336 || !pref.getBoolean("freeform_hack", false)
337 || windowSize.equals("standard")) {
338 launchStandard(context, intent, userId, shortcut, type);
339 } else switch(windowSize) {
341 launchMode1(context, intent, userId, shortcut, type);
344 launchMode2(context, intent, MAXIMIZED, userId, shortcut, type);
347 launchMode2(context, intent, LEFT, userId, shortcut, type);
350 launchMode2(context, intent, RIGHT, userId, shortcut, type);
353 launchMode3(context, intent, userId, shortcut, type);
357 if(shouldCollapse(context))
358 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
360 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
363 private static void launchStandard(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
364 Bundle bundle = Build.VERSION.SDK_INT < Build.VERSION_CODES.N ? null : getActivityOptions(type).toBundle();
365 if(shortcut == null) {
366 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
367 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
369 context.startActivity(intent, bundle);
370 } catch (ActivityNotFoundException e) {
371 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
372 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
374 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
376 launchShortcut(context, shortcut, bundle);
379 @SuppressWarnings("deprecation")
380 @TargetApi(Build.VERSION_CODES.N)
381 private static void launchMode1(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
382 DisplayMetrics metrics = getRealDisplayMetrics(context);
384 int width1 = metrics.widthPixels / 8;
385 int width2 = metrics.widthPixels - width1;
386 int height1 = metrics.heightPixels / 8;
387 int height2 = metrics.heightPixels - height1;
389 Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
396 if(shortcut == null) {
397 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
398 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
400 context.startActivity(intent, bundle);
401 } catch (ActivityNotFoundException e) {
402 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
403 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
405 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
407 launchShortcut(context, shortcut, bundle);
410 @SuppressWarnings("deprecation")
411 @TargetApi(Build.VERSION_CODES.N)
412 private static void launchMode2(Context context, Intent intent, int launchType, long userId, ShortcutInfo shortcut, ApplicationType type) {
413 DisplayMetrics metrics = getRealDisplayMetrics(context);
415 int statusBarHeight = getStatusBarHeight(context);
416 String position = getTaskbarPosition(context);
418 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
419 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
421 int left = launchType == RIGHT && isLandscape
422 ? metrics.widthPixels / 2
425 int top = launchType == RIGHT && isPortrait
426 ? metrics.heightPixels / 2
429 int right = launchType == LEFT && isLandscape
430 ? metrics.widthPixels / 2
431 : metrics.widthPixels;
433 int bottom = launchType == LEFT && isPortrait
434 ? metrics.heightPixels / 2
435 : metrics.heightPixels;
437 SharedPreferences pref = getSharedPreferences(context);
440 if(isChromeOs(context) && pref.getBoolean("hide_taskbar", true))
443 iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
445 if(position.contains("vertical_left")) {
446 if(launchType != RIGHT || isPortrait) left = left + iconSize;
447 } else if(position.contains("vertical_right")) {
448 if(launchType != LEFT || isPortrait) right = right - iconSize;
449 } else if(position.contains("bottom")) {
450 if(isLandscape || (launchType != LEFT && isPortrait))
451 bottom = bottom - iconSize;
452 } else if(isLandscape || (launchType != RIGHT && isPortrait))
453 top = top + iconSize;
455 Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
462 if(shortcut == null) {
463 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
464 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
466 context.startActivity(intent, bundle);
467 } catch (ActivityNotFoundException e) {
468 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
469 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
471 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
473 launchShortcut(context, shortcut, bundle);
476 @SuppressWarnings("deprecation")
477 @TargetApi(Build.VERSION_CODES.N)
478 private static void launchMode3(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
479 DisplayMetrics metrics = getRealDisplayMetrics(context);
481 int width1 = metrics.widthPixels / 2;
482 int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
483 int height1 = metrics.heightPixels / 2;
484 int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
486 Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
493 if(shortcut == null) {
494 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
495 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
497 context.startActivity(intent, bundle);
498 } catch (ActivityNotFoundException e) {
499 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
500 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
502 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
504 launchShortcut(context, shortcut, bundle);
507 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
508 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
509 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
512 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
513 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
516 @TargetApi(Build.VERSION_CODES.N_MR1)
517 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
518 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
520 if(launcherApps.hasShortcutHostPermission()) {
522 launcherApps.startShortcut(shortcut, null, bundle);
523 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
527 public static void launchAppMaximized(Context context, Intent intent) {
528 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
529 long userId = userManager.getSerialNumberForUser(Process.myUserHandle());
531 launchMode2(context, intent, MAXIMIZED, userId, null, ApplicationType.CONTEXT_MENU);
534 @SuppressWarnings("deprecation")
535 @TargetApi(Build.VERSION_CODES.N)
536 public static void launchAppLowerRight(Context context, Intent intent) {
537 DisplayMetrics metrics = getRealDisplayMetrics(context);
539 context.startActivity(intent, getActivityOptions(ApplicationType.FREEFORM_HACK).setLaunchBounds(new Rect(
541 metrics.heightPixels,
542 metrics.widthPixels + 1,
543 metrics.heightPixels + 1
545 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
548 public static void checkForUpdates(Context context) {
549 if(!BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)) {
550 if(!BuildConfig.DEBUG) {
553 context.getPackageManager().getPackageInfo("com.android.vending", 0);
554 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
555 } catch (PackageManager.NameNotFoundException e) {
556 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.BASE_APPLICATION_ID;
559 Intent intent = new Intent(Intent.ACTION_VIEW);
560 intent.setData(Uri.parse(url));
561 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
564 context.startActivity(intent);
565 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
567 showToast(context, R.string.debug_build);
571 public static boolean launcherIsDefault(Context context) {
572 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
573 homeIntent.addCategory(Intent.CATEGORY_HOME);
574 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
576 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
579 public static void setCachedRotation(int cachedRotation) {
580 U.cachedRotation = cachedRotation;
583 public static String getTaskbarPosition(Context context) {
584 SharedPreferences pref = getSharedPreferences(context);
585 String position = pref.getString("position", "bottom_left");
587 if(pref.getBoolean("anchor", false)) {
588 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
589 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
594 case Surface.ROTATION_0:
595 return "bottom_left";
596 case Surface.ROTATION_90:
597 return "bottom_vertical_right";
598 case Surface.ROTATION_180:
600 case Surface.ROTATION_270:
601 return "top_vertical_left";
604 case "bottom_vertical_left":
606 case Surface.ROTATION_0:
607 return "bottom_vertical_left";
608 case Surface.ROTATION_90:
609 return "bottom_right";
610 case Surface.ROTATION_180:
611 return "top_vertical_right";
612 case Surface.ROTATION_270:
618 case Surface.ROTATION_0:
619 return "bottom_right";
620 case Surface.ROTATION_90:
621 return "top_vertical_right";
622 case Surface.ROTATION_180:
624 case Surface.ROTATION_270:
625 return "bottom_vertical_left";
628 case "bottom_vertical_right":
630 case Surface.ROTATION_0:
631 return "bottom_vertical_right";
632 case Surface.ROTATION_90:
634 case Surface.ROTATION_180:
635 return "top_vertical_left";
636 case Surface.ROTATION_270:
637 return "bottom_left";
642 case Surface.ROTATION_0:
644 case Surface.ROTATION_90:
645 return "bottom_vertical_left";
646 case Surface.ROTATION_180:
647 return "bottom_right";
648 case Surface.ROTATION_270:
649 return "top_vertical_right";
652 case "top_vertical_left":
654 case Surface.ROTATION_0:
655 return "top_vertical_left";
656 case Surface.ROTATION_90:
657 return "bottom_left";
658 case Surface.ROTATION_180:
659 return "bottom_vertical_right";
660 case Surface.ROTATION_270:
666 case Surface.ROTATION_0:
668 case Surface.ROTATION_90:
669 return "top_vertical_left";
670 case Surface.ROTATION_180:
671 return "bottom_left";
672 case Surface.ROTATION_270:
673 return "bottom_vertical_right";
676 case "top_vertical_right":
678 case Surface.ROTATION_0:
679 return "top_vertical_right";
680 case Surface.ROTATION_90:
682 case Surface.ROTATION_180:
683 return "bottom_vertical_left";
684 case Surface.ROTATION_270:
685 return "bottom_right";
694 private static int getMaxNumOfColumns(Context context) {
695 SharedPreferences pref = getSharedPreferences(context);
696 DisplayMetrics metrics = getRealDisplayMetrics(context);
697 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / metrics.density;
698 int numOfColumns = 0;
700 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
701 ? (metrics.heightPixels - getStatusBarHeight(context)) / metrics.density
702 : metrics.widthPixels / metrics.density;
704 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
706 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
708 while(baseTaskbarSize + iconSize < maxScreenSize
709 && numOfColumns < userMaxNumOfColumns) {
710 baseTaskbarSize = baseTaskbarSize + iconSize;
717 public static int getMaxNumOfEntries(Context context) {
718 SharedPreferences pref = getSharedPreferences(context);
719 return pref.getBoolean("disable_scrolling_list", false)
720 ? getMaxNumOfColumns(context)
721 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
724 public static int getStatusBarHeight(Context context) {
725 int statusBarHeight = 0;
726 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
728 statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
730 return statusBarHeight;
733 public static void refreshPinnedIcons(Context context) {
734 IconCache.getInstance(context).clearCache();
736 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
737 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
738 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
739 PackageManager pm = context.getPackageManager();
743 for(AppEntry entry : pinnedAppsList) {
744 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
745 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
747 final List<UserHandle> userHandles = userManager.getUserProfiles();
748 LauncherActivityInfo appInfo = null;
750 for(UserHandle handle : userHandles) {
751 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
752 if(!list.isEmpty()) {
753 // Google App workaround
754 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
755 appInfo = list.get(0);
757 boolean added = false;
758 for(LauncherActivityInfo info : list) {
759 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
765 if(!added) appInfo = list.get(0);
772 if(appInfo != null) {
773 AppEntry newEntry = new AppEntry(
774 entry.getPackageName(),
775 entry.getComponentName(),
777 IconCache.getInstance(context).getIcon(context, pm, appInfo),
780 newEntry.setUserId(entry.getUserId(context));
781 pba.addPinnedApp(context, newEntry);
785 for(AppEntry entry : blockedAppsList) {
786 pba.addBlockedApp(context, entry);
790 public static Intent getShortcutIntent(Context context) {
791 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
792 shortcutIntent.setAction(Intent.ACTION_MAIN);
793 shortcutIntent.putExtra("is_launching_shortcut", true);
795 BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_freeform_mode);
797 Intent intent = new Intent();
798 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
799 if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
800 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
805 public static Intent getStartStopIntent(Context context) {
806 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
807 shortcutIntent.setAction(Intent.ACTION_MAIN);
808 shortcutIntent.putExtra("is_launching_shortcut", true);
810 BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_launcher);
812 Intent intent = new Intent();
813 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
814 if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
815 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
820 public static boolean hasFreeformSupport(Context context) {
821 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
822 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
823 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", -1) == 1
824 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
825 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", -1) == 1));
828 public static boolean hasPartialFreeformSupport() {
829 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
832 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
833 return isServiceRunning(context, cls.getName());
836 public static boolean isServiceRunning(Context context, String className) {
837 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
838 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
839 if(className.equals(service.service.getClassName()))
846 public static int getBackgroundTint(Context context) {
847 SharedPreferences pref = getSharedPreferences(context);
849 // Import old background tint preference
850 if(pref.contains("show_background")) {
851 SharedPreferences.Editor editor = pref.edit();
853 if(!pref.getBoolean("show_background", true))
854 editor.putInt("background_tint", Color.TRANSPARENT).apply();
856 editor.remove("show_background");
860 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
863 public static int getAccentColor(Context context) {
864 SharedPreferences pref = getSharedPreferences(context);
865 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
868 @TargetApi(Build.VERSION_CODES.M)
869 public static boolean canDrawOverlays(Context context) {
870 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
873 public static boolean isGame(Context context, String packageName) {
874 SharedPreferences pref = getSharedPreferences(context);
875 if(pref.getBoolean("launch_games_fullscreen", true)) {
876 PackageManager pm = context.getPackageManager();
879 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
880 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
881 } catch (PackageManager.NameNotFoundException e) {
888 @TargetApi(Build.VERSION_CODES.M)
889 public static ActivityOptions getActivityOptions(ApplicationType applicationType) {
890 ActivityOptions options = ActivityOptions.makeBasic();
891 Integer stackId = null;
893 switch(applicationType) {
895 if(!FreeformHackHelper.getInstance().isFreeformHackActive())
896 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
899 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
902 stackId = FREEFORM_WORKSPACE_STACK_ID;
905 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
906 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
909 if(stackId != null) {
911 Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
912 method.invoke(options, stackId);
913 } catch (Exception e) { /* Gracefully fail */ }
919 private static ApplicationType getApplicationType(Context context, String packageName) {
920 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
923 public static boolean isSystemApp(Context context) {
925 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
926 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
927 return (info.flags & mask) != 0;
928 } catch (PackageManager.NameNotFoundException e) {
933 public static boolean isChromeOs(Context context) {
934 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
937 public static boolean hasSupportLibrary(Context context) {
938 PackageManager pm = context.getPackageManager();
940 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
941 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
942 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
943 && isSystemApp(context);
944 } catch (PackageManager.NameNotFoundException e) {
949 public static int getBaseTaskbarSize(Context context) {
950 return Math.round(getBaseTaskbarSizeFloat(context));
953 private static float getBaseTaskbarSizeFloat(Context context) {
954 SharedPreferences pref = getSharedPreferences(context);
955 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
956 boolean navbarButtonsEnabled = false;
958 if(pref.getBoolean("dashboard", false))
959 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
961 if(pref.getBoolean("button_back", false)) {
962 navbarButtonsEnabled = true;
963 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
966 if(pref.getBoolean("button_home", false)) {
967 navbarButtonsEnabled = true;
968 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
971 if(pref.getBoolean("button_recents", false)) {
972 navbarButtonsEnabled = true;
973 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
976 if(navbarButtonsEnabled)
977 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
979 return baseTaskbarSize;
982 private static void startTaskbarService(Context context, boolean fullRestart) {
983 context.startService(new Intent(context, TaskbarService.class));
984 context.startService(new Intent(context, StartMenuService.class));
985 context.startService(new Intent(context, DashboardService.class));
986 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
989 private static void stopTaskbarService(Context context, boolean fullRestart) {
990 context.stopService(new Intent(context, TaskbarService.class));
991 context.stopService(new Intent(context, StartMenuService.class));
992 context.stopService(new Intent(context, DashboardService.class));
993 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
996 public static void restartTaskbar(Context context) {
997 SharedPreferences pref = getSharedPreferences(context);
998 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1000 .putBoolean("is_restarting", true)
1001 .putBoolean("skip_auto_hide_navbar", true)
1004 stopTaskbarService(context, true);
1005 startTaskbarService(context, true);
1006 } else if(isServiceRunning(context, StartMenuService.class)) {
1007 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1009 stopTaskbarService(context, false);
1010 startTaskbarService(context, false);
1014 public static void restartNotificationService(Context context) {
1015 if(isServiceRunning(context, NotificationService.class)) {
1016 SharedPreferences pref = getSharedPreferences(context);
1017 pref.edit().putBoolean("is_restarting", true).apply();
1019 Intent intent = new Intent(context, NotificationService.class);
1020 context.stopService(intent);
1021 context.startService(intent);
1025 public static void showHideNavigationBar(Context context, boolean show) {
1026 // Show or hide the system navigation bar on Bliss-x86
1028 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1029 } catch (Exception e) { /* Gracefully fail */ }
1032 public static void initPrefs(Context context) {
1033 // On smaller-screened devices, set "Grid" as the default start menu layout
1034 SharedPreferences pref = getSharedPreferences(context);
1035 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 600
1036 && pref.getString("start_menu_layout", "null").equals("null")) {
1037 pref.edit().putString("start_menu_layout", "grid").apply();
1040 // Enable freeform hack automatically on supported devices
1041 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
1042 if(!pref.getBoolean("freeform_hack_override", false)) {
1044 .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1045 .putBoolean("save_window_sizes", false)
1046 .putBoolean("freeform_hack_override", true)
1048 } else if(!hasFreeformSupport(context)) {
1049 pref.edit().putBoolean("freeform_hack", false).apply();
1051 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
1055 // Customizations for Android-x86 devices (non-Bliss)
1056 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1057 && isSystemApp(context)
1058 && !pref.getBoolean("android_x86_prefs", false)) {
1060 .putString("recents_amount", "running_apps_only")
1061 .putString("refresh_frequency", "0")
1062 .putString("max_num_of_recents", "2147483647")
1063 .putString("sort_order", "true")
1064 .putBoolean("full_length", true)
1065 .putBoolean("dashboard", true)
1066 .putBoolean("android_x86_prefs", true)
1071 public static DisplayMetrics getRealDisplayMetrics(Context context) {
1072 DisplayMetrics metrics = new DisplayMetrics();
1073 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1074 Display disp = wm.getDefaultDisplay();
1076 SharedPreferences pref = getSharedPreferences(context);
1077 if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1078 disp.getRealMetrics(metrics);
1080 disp.getMetrics(metrics);
1085 public static int getOverlayType() {
1086 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1087 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1088 : WindowManager.LayoutParams.TYPE_PHONE;
1091 public static boolean shouldCollapse(Context context) {
1092 SharedPreferences pref = getSharedPreferences(context);
1093 if(pref.getBoolean("hide_taskbar", true)) {
1094 if(isChromeOs(context))
1097 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1098 return !helper.isFreeformHackActive();