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, getActivityOptionsBundle(ApplicationType.APPLICATION));
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, getActivityOptionsBundle(ApplicationType.APPLICATION));
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.N)
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;
894 stackId = FREEFORM_WORKSPACE_STACK_ID;
897 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
900 stackId = FREEFORM_WORKSPACE_STACK_ID;
903 if(!FreeformHackHelper.getInstance().isFreeformHackActive()
904 || Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1)
905 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
907 stackId = FREEFORM_WORKSPACE_STACK_ID;
911 Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
912 method.invoke(options, stackId);
913 } catch (Exception e) { /* Gracefully fail */ }
918 public static Bundle getActivityOptionsBundle(ApplicationType applicationType) {
919 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
922 return getActivityOptions(applicationType).toBundle();
925 private static ApplicationType getApplicationType(Context context, String packageName) {
926 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
929 public static boolean isSystemApp(Context context) {
931 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
932 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
933 return (info.flags & mask) != 0;
934 } catch (PackageManager.NameNotFoundException e) {
939 public static boolean isChromeOs(Context context) {
940 return context.getPackageManager().hasSystemFeature("org.chromium.arc");
943 public static boolean hasSupportLibrary(Context context) {
944 PackageManager pm = context.getPackageManager();
946 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
947 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
948 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
949 && isSystemApp(context);
950 } catch (PackageManager.NameNotFoundException e) {
955 public static int getBaseTaskbarSize(Context context) {
956 return Math.round(getBaseTaskbarSizeFloat(context));
959 private static float getBaseTaskbarSizeFloat(Context context) {
960 SharedPreferences pref = getSharedPreferences(context);
961 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
962 boolean navbarButtonsEnabled = false;
964 if(pref.getBoolean("dashboard", false))
965 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
967 if(pref.getBoolean("button_back", false)) {
968 navbarButtonsEnabled = true;
969 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
972 if(pref.getBoolean("button_home", false)) {
973 navbarButtonsEnabled = true;
974 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
977 if(pref.getBoolean("button_recents", false)) {
978 navbarButtonsEnabled = true;
979 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
982 if(navbarButtonsEnabled)
983 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
985 return baseTaskbarSize;
988 private static void startTaskbarService(Context context, boolean fullRestart) {
989 context.startService(new Intent(context, TaskbarService.class));
990 context.startService(new Intent(context, StartMenuService.class));
991 context.startService(new Intent(context, DashboardService.class));
992 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
995 private static void stopTaskbarService(Context context, boolean fullRestart) {
996 context.stopService(new Intent(context, TaskbarService.class));
997 context.stopService(new Intent(context, StartMenuService.class));
998 context.stopService(new Intent(context, DashboardService.class));
999 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1002 public static void restartTaskbar(Context context) {
1003 SharedPreferences pref = getSharedPreferences(context);
1004 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1006 .putBoolean("is_restarting", true)
1007 .putBoolean("skip_auto_hide_navbar", true)
1010 stopTaskbarService(context, true);
1011 startTaskbarService(context, true);
1012 } else if(isServiceRunning(context, StartMenuService.class)) {
1013 pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1015 stopTaskbarService(context, false);
1016 startTaskbarService(context, false);
1020 public static void restartNotificationService(Context context) {
1021 if(isServiceRunning(context, NotificationService.class)) {
1022 SharedPreferences pref = getSharedPreferences(context);
1023 pref.edit().putBoolean("is_restarting", true).apply();
1025 Intent intent = new Intent(context, NotificationService.class);
1026 context.stopService(intent);
1027 context.startService(intent);
1031 public static void showHideNavigationBar(Context context, boolean show) {
1032 // Show or hide the system navigation bar on Bliss-x86
1034 Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1035 } catch (Exception e) { /* Gracefully fail */ }
1038 public static void initPrefs(Context context) {
1039 // On smaller-screened devices, set "Grid" as the default start menu layout
1040 SharedPreferences pref = getSharedPreferences(context);
1041 if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 600
1042 && pref.getString("start_menu_layout", "null").equals("null")) {
1043 pref.edit().putString("start_menu_layout", "grid").apply();
1046 // Enable freeform hack automatically on supported devices
1047 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
1048 if(!pref.getBoolean("freeform_hack_override", false)) {
1050 .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1051 .putBoolean("save_window_sizes", false)
1052 .putBoolean("freeform_hack_override", true)
1054 } else if(!hasFreeformSupport(context)) {
1055 pref.edit().putBoolean("freeform_hack", false).apply();
1057 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
1061 // Customizations for Android-x86 devices (non-Bliss)
1062 if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1063 && isSystemApp(context)
1064 && !pref.getBoolean("android_x86_prefs", false)) {
1066 .putString("recents_amount", "running_apps_only")
1067 .putString("refresh_frequency", "0")
1068 .putString("max_num_of_recents", "2147483647")
1069 .putString("sort_order", "true")
1070 .putBoolean("full_length", true)
1071 .putBoolean("dashboard", true)
1072 .putBoolean("android_x86_prefs", true)
1077 public static DisplayMetrics getRealDisplayMetrics(Context context) {
1078 DisplayMetrics metrics = new DisplayMetrics();
1079 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1080 Display disp = wm.getDefaultDisplay();
1082 SharedPreferences pref = getSharedPreferences(context);
1083 if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1084 disp.getRealMetrics(metrics);
1086 disp.getMetrics(metrics);
1091 static void pinAppShortcut(Context context) {
1092 Intent intent = getShortcutIntent(context);
1093 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1094 intent.putExtra("duplicate", false);
1096 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1097 homeIntent.addCategory(Intent.CATEGORY_HOME);
1098 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1100 intent.setPackage(defaultLauncher.activityInfo.packageName);
1101 context.sendBroadcast(intent);
1103 showToast(context, R.string.shortcut_created);
1106 public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1107 SharedPreferences pref = getSharedPreferences(context);
1108 if(pref.getBoolean("hide_taskbar", true)) {
1109 if(isChromeOs(context))
1112 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1113 if(pendingAppLaunch)
1114 return !helper.isFreeformHackActive();
1116 return !helper.isInFreeformWorkspace();
1122 @SuppressWarnings("unchecked")
1123 public static <T extends View> T findViewById(Activity target, int id) {
1124 return (T) target.findViewById(id);
1127 @SuppressWarnings("unchecked")
1128 public static <T extends View> T findViewById(View target, int id) {
1129 return (T) target.findViewById(id);