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.net.Uri;
41 import android.os.Build;
42 import android.os.Bundle;
43 import android.os.Handler;
44 import android.os.Process;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.provider.Settings;
48 import android.support.v4.content.LocalBroadcastManager;
49 import android.util.DisplayMetrics;
50 import android.view.Display;
51 import android.view.Surface;
52 import android.view.View;
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 public 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.N_MR1
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, true))
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 Intent intent = new Intent();
796 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
797 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
798 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
803 public static Intent getStartStopIntent(Context context) {
804 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
805 shortcutIntent.setAction(Intent.ACTION_MAIN);
806 shortcutIntent.putExtra("is_launching_shortcut", true);
808 Intent intent = new Intent();
809 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
810 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
811 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
816 public static boolean hasFreeformSupport(Context context) {
817 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
818 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
819 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", -1) == 1
820 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
821 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", -1) == 1));
824 public static boolean hasPartialFreeformSupport() {
825 return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
828 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
829 return isServiceRunning(context, cls.getName());
832 public static boolean isServiceRunning(Context context, String className) {
833 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
834 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
835 if(className.equals(service.service.getClassName()))
842 public static int getBackgroundTint(Context context) {
843 SharedPreferences pref = getSharedPreferences(context);
845 // Import old background tint preference
846 if(pref.contains("show_background")) {
847 SharedPreferences.Editor editor = pref.edit();
849 if(!pref.getBoolean("show_background", true))
850 editor.putInt("background_tint", Color.TRANSPARENT).apply();
852 editor.remove("show_background");
856 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
859 public static int getAccentColor(Context context) {
860 SharedPreferences pref = getSharedPreferences(context);
861 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
864 @TargetApi(Build.VERSION_CODES.M)
865 public static boolean canDrawOverlays(Context context) {
866 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
869 public static boolean isGame(Context context, String packageName) {
870 SharedPreferences pref = getSharedPreferences(context);
871 if(pref.getBoolean("launch_games_fullscreen", true)) {
872 PackageManager pm = context.getPackageManager();
875 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
876 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
877 } catch (PackageManager.NameNotFoundException e) {
884 @TargetApi(Build.VERSION_CODES.M)
885 public static ActivityOptions getActivityOptions(ApplicationType applicationType) {
886 ActivityOptions options = ActivityOptions.makeBasic();
887 Integer stackId = null;
889 switch(applicationType) {
891 if(!FreeformHackHelper.getInstance().isFreeformHackActive())
892 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
895 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
898 stackId = FREEFORM_WORKSPACE_STACK_ID;
901 if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1)
902 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
905 if(stackId != null) {
907 Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
908 method.invoke(options, stackId);
909 } catch (Exception e) { /* Gracefully fail */ }
915 private static ApplicationType getApplicationType(Context context, String packageName) {
916 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
919 public static boolean isSystemApp(Context context) {
921 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
922 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
923 return (info.flags & mask) != 0;
924 } catch (PackageManager.NameNotFoundException e) {
929 public static boolean isChromeOs(Context context) {
930 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
933 public static boolean hasSupportLibrary(Context context) {
934 PackageManager pm = context.getPackageManager();
936 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
937 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
938 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
939 && isSystemApp(context);
940 } catch (PackageManager.NameNotFoundException e) {
945 public static int getBaseTaskbarSize(Context context) {
946 return Math.round(getBaseTaskbarSizeFloat(context));
949 private static float getBaseTaskbarSizeFloat(Context context) {
950 SharedPreferences pref = getSharedPreferences(context);
951 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
952 boolean navbarButtonsEnabled = false;
954 if(pref.getBoolean("dashboard", false))
955 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
957 if(pref.getBoolean("button_back", false)) {
958 navbarButtonsEnabled = true;
959 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
962 if(pref.getBoolean("button_home", false)) {
963 navbarButtonsEnabled = true;
964 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
967 if(pref.getBoolean("button_recents", false)) {
968 navbarButtonsEnabled = true;
969 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
972 if(navbarButtonsEnabled)
973 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
975 return baseTaskbarSize;
978 private static void startTaskbarService(Context context, boolean fullRestart) {
979 context.startService(new Intent(context, TaskbarService.class));
980 context.startService(new Intent(context, StartMenuService.class));
981 context.startService(new Intent(context, DashboardService.class));
982 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
985 private static void stopTaskbarService(Context context, boolean fullRestart) {
986 context.stopService(new Intent(context, TaskbarService.class));
987 context.stopService(new Intent(context, StartMenuService.class));
988 context.stopService(new Intent(context, DashboardService.class));
989 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
992 public static void restartTaskbar(Context context) {
993 SharedPreferences pref = getSharedPreferences(context);
994 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
996 .putBoolean("is_restarting", true)
997 .putBoolean("skip_auto_hide_navbar", true)
1000 stopTaskbarService(context, true);
1001 startTaskbarService(context, true);
1002 } else if(isServiceRunning(context, StartMenuService.class)) {
1003 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1005 stopTaskbarService(context, false);
1006 startTaskbarService(context, false);
1010 public static void restartNotificationService(Context context) {
1011 if(isServiceRunning(context, NotificationService.class)) {
1012 SharedPreferences pref = getSharedPreferences(context);
1013 pref.edit().putBoolean("is_restarting", true).apply();
1015 Intent intent = new Intent(context, NotificationService.class);
1016 context.stopService(intent);
1017 context.startService(intent);
1021 public static void showHideNavigationBar(Context context, boolean show) {
1022 // Show or hide the system navigation bar on Bliss-x86
1024 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1025 } catch (Exception e) { /* Gracefully fail */ }
1028 public static void initPrefs(Context context) {
1029 // On smaller-screened devices, set "Grid" as the default start menu layout
1030 SharedPreferences pref = getSharedPreferences(context);
1031 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 600
1032 && pref.getString("start_menu_layout", "null").equals("null")) {
1033 pref.edit().putString("start_menu_layout", "grid").apply();
1036 // Enable freeform hack automatically on supported devices
1037 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
1038 if(!pref.getBoolean("freeform_hack_override", false)) {
1040 .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1041 .putBoolean("save_window_sizes", false)
1042 .putBoolean("freeform_hack_override", true)
1044 } else if(!hasFreeformSupport(context)) {
1045 pref.edit().putBoolean("freeform_hack", false).apply();
1047 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
1051 // Customizations for Android-x86 devices (non-Bliss)
1052 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1053 && isSystemApp(context)
1054 && !pref.getBoolean("android_x86_prefs", false)) {
1056 .putString("recents_amount", "running_apps_only")
1057 .putString("refresh_frequency", "0")
1058 .putString("max_num_of_recents", "2147483647")
1059 .putString("sort_order", "true")
1060 .putBoolean("full_length", true)
1061 .putBoolean("dashboard", true)
1062 .putBoolean("android_x86_prefs", true)
1067 public static DisplayMetrics getRealDisplayMetrics(Context context) {
1068 DisplayMetrics metrics = new DisplayMetrics();
1069 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1070 Display disp = wm.getDefaultDisplay();
1072 SharedPreferences pref = getSharedPreferences(context);
1073 if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1074 disp.getRealMetrics(metrics);
1076 disp.getMetrics(metrics);
1081 static void pinAppShortcut(Context context) {
1082 Intent intent = getShortcutIntent(context);
1083 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1084 intent.putExtra("duplicate", false);
1086 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1087 homeIntent.addCategory(Intent.CATEGORY_HOME);
1088 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1090 intent.setPackage(defaultLauncher.activityInfo.packageName);
1091 context.sendBroadcast(intent);
1093 showToast(context, R.string.shortcut_created);
1096 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1097 SharedPreferences pref = getSharedPreferences(context);
1098 if(pref.getBoolean("hide_taskbar", true)) {
1099 if(isChromeOs(context))
1102 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1103 if(pendingAppLaunch)
1104 return !helper.isFreeformHackActive();
1106 return !helper.isInFreeformWorkspace();
1112 @SuppressWarnings("unchecked")
1113 public static <T extends View> T findViewById(Activity target, int id) {
1114 return (T) target.findViewById(id);
1117 @SuppressWarnings("unchecked")
1118 public static <T extends View> T findViewById(View target, int id) {
1119 return (T) target.findViewById(id);