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.ActivityManager;
20 import android.app.ActivityOptions;
21 import android.app.AlertDialog;
22 import android.app.Service;
23 import android.app.admin.DevicePolicyManager;
24 import android.content.ActivityNotFoundException;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.SharedPreferences;
29 import android.content.pm.ActivityInfo;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.LauncherActivityInfo;
32 import android.content.pm.LauncherApps;
33 import android.content.pm.PackageManager;
34 import android.content.pm.ResolveInfo;
35 import android.content.pm.ShortcutInfo;
36 import android.content.res.Configuration;
37 import android.graphics.Rect;
38 import android.graphics.drawable.BitmapDrawable;
39 import android.hardware.display.DisplayManager;
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.WindowManager;
53 import android.widget.Toast;
55 import com.farmerbb.taskbar.BuildConfig;
56 import com.farmerbb.taskbar.R;
57 import com.farmerbb.taskbar.activity.DummyActivity;
58 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
59 import com.farmerbb.taskbar.activity.ShortcutActivity;
60 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
61 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
62 import com.farmerbb.taskbar.service.DashboardService;
63 import com.farmerbb.taskbar.service.NotificationService;
64 import com.farmerbb.taskbar.service.PowerMenuService;
65 import com.farmerbb.taskbar.service.StartMenuService;
66 import com.farmerbb.taskbar.service.TaskbarService;
68 import java.lang.reflect.Method;
69 import java.util.ArrayList;
70 import java.util.List;
72 import moe.banana.support.ToastCompat;
78 private static SharedPreferences pref;
79 private static Integer cachedRotation;
81 private static final int MAXIMIZED = 0;
82 private static final int LEFT = -1;
83 private static final int RIGHT = 1;
85 public static final int HIDDEN = 0;
86 public static final int TOP_APPS = 1;
88 // From android.app.ActivityManager.StackId
89 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
90 private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
92 public static SharedPreferences getSharedPreferences(Context context) {
93 if(pref == null) pref = context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
97 @TargetApi(Build.VERSION_CODES.M)
98 public static void showPermissionDialog(final Context context) {
99 AlertDialog.Builder builder = new AlertDialog.Builder(context);
100 builder.setTitle(R.string.permission_dialog_title)
101 .setMessage(R.string.permission_dialog_message)
102 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
104 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
105 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
106 } catch (ActivityNotFoundException e) {
107 showErrorDialog(context, "SYSTEM_ALERT_WINDOW");
111 AlertDialog dialog = builder.create();
113 dialog.setCancelable(false);
116 public static void showErrorDialog(final Context context, String appopCmd) {
117 AlertDialog.Builder builder = new AlertDialog.Builder(context);
118 builder.setTitle(R.string.error_dialog_title)
119 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
120 .setPositiveButton(R.string.action_ok, null);
122 AlertDialog dialog = builder.create();
126 public static void lockDevice(Context context) {
127 ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
128 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
129 PackageManager.DONT_KILL_APP);
131 DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
132 if(mDevicePolicyManager.isAdminActive(component))
133 mDevicePolicyManager.lockNow();
135 Intent intent = new Intent(context, DummyActivity.class);
136 intent.putExtra("device_admin", true);
137 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
138 context.startActivity(intent);
142 public static void sendAccessibilityAction(Context context, int action) {
143 ComponentName component = new ComponentName(context, PowerMenuService.class);
144 context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
145 PackageManager.DONT_KILL_APP);
147 if(isAccessibilityServiceEnabled(context)) {
148 Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
149 intent.putExtra("action", action);
150 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
152 Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
153 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
156 context.startActivity(intent);
157 showToastLong(context, R.string.enable_accessibility);
158 } catch (ActivityNotFoundException e) {
159 showToast(context, R.string.lock_device_not_supported);
164 private static boolean isAccessibilityServiceEnabled(Context context) {
165 String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
166 ComponentName component = new ComponentName(context, PowerMenuService.class);
168 return accessibilityServices != null
169 && (accessibilityServices.contains(component.flattenToString())
170 || accessibilityServices.contains(component.flattenToShortString()));
173 public static void showToast(Context context, int message) {
174 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
177 public static void showToastLong(Context context, int message) {
178 showToast(context, context.getString(message), Toast.LENGTH_LONG);
181 public static void showToast(Context context, String message, int length) {
184 ToastCompat toast = ToastCompat.makeText(context.getApplicationContext(), message, length);
187 ToastHelper.getInstance().setLastToast(toast);
190 public static void cancelToast() {
191 ToastCompat toast = ToastHelper.getInstance().getLastToast();
192 if(toast != null) toast.cancel();
195 public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
206 public static void launchApp(final Context context,
207 final String packageName,
208 final String componentName,
209 final long userId, final String windowSize,
210 final boolean launchedFromTaskbar,
211 final boolean openInNewWindow) {
222 private static void launchApp(final Context context,
223 final String packageName,
224 final String componentName,
225 final long userId, final String windowSize,
226 final boolean launchedFromTaskbar,
227 final boolean openInNewWindow,
228 final ShortcutInfo shortcut) {
229 SharedPreferences pref = getSharedPreferences(context);
230 FreeformHackHelper helper = FreeformHackHelper.getInstance();
232 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
233 && pref.getBoolean("freeform_hack", false)
234 && !helper.isInFreeformWorkspace()) {
235 new Handler().postDelayed(() -> {
236 startFreeformHack(context, true, launchedFromTaskbar);
238 new Handler().postDelayed(() -> continueLaunchingApp(context, packageName, componentName, userId,
239 windowSize, launchedFromTaskbar, openInNewWindow, shortcut), helper.isFreeformHackActive() ? 0 : 100);
240 }, launchedFromTaskbar ? 0 : 100);
242 continueLaunchingApp(context, packageName, componentName, userId,
243 windowSize, launchedFromTaskbar, openInNewWindow, shortcut);
246 @SuppressWarnings("deprecation")
247 @TargetApi(Build.VERSION_CODES.N)
248 public static void startFreeformHack(Context context, boolean checkMultiWindow, boolean launchedFromTaskbar) {
249 Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
250 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
253 freeformHackIntent.putExtra("check_multiwindow", true);
255 if(launchedFromTaskbar) {
256 SharedPreferences pref = getSharedPreferences(context);
257 if(pref.getBoolean("disable_animations", false))
258 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
261 if(canDrawOverlays(context))
262 launchAppLowerRight(context, freeformHackIntent);
265 @TargetApi(Build.VERSION_CODES.N)
266 private static void continueLaunchingApp(Context context,
268 String componentName,
271 boolean launchedFromTaskbar,
272 boolean openInNewWindow,
273 ShortcutInfo shortcut) {
274 SharedPreferences pref = getSharedPreferences(context);
275 Intent intent = new Intent();
276 intent.setComponent(ComponentName.unflattenFromString(componentName));
277 intent.setAction(Intent.ACTION_MAIN);
278 intent.addCategory(Intent.CATEGORY_LAUNCHER);
279 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
280 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
282 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
283 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
285 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
287 if(launchedFromTaskbar) {
288 if(pref.getBoolean("disable_animations", false))
289 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
292 if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
293 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
295 ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
296 if(activityInfo != null) {
297 switch(activityInfo.launchMode) {
298 case ActivityInfo.LAUNCH_SINGLE_TASK:
299 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
300 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
306 boolean specialLaunch = isOPreview() && FreeformHackHelper.getInstance().isFreeformHackActive() && openInNewWindow;
308 if(windowSize == null)
309 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
311 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !pref.getBoolean("freeform_hack", false)) {
312 Bundle bundle = Build.VERSION.SDK_INT < Build.VERSION_CODES.N ? null : getActivityOptions(getApplicationType(context, packageName)).toBundle();
313 if(shortcut == null) {
314 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
315 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
317 context.startActivity(intent, bundle);
318 } catch (ActivityNotFoundException e) {
319 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
320 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
322 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
324 launchShortcut(context, shortcut, bundle);
325 } else switch(windowSize) {
327 if(FreeformHackHelper.getInstance().isInFreeformWorkspace() && !specialLaunch) {
328 Bundle bundle = getActivityOptions(getApplicationType(context, packageName)).toBundle();
329 if(shortcut == null) {
330 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
331 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
333 context.startActivity(intent, bundle);
334 } catch (ActivityNotFoundException e) {
335 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
336 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
338 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
340 launchShortcut(context, shortcut, bundle);
342 launchMode1(context, intent, 1, userId, shortcut, getApplicationType(context, packageName), specialLaunch);
345 launchMode1(context, intent, 2, userId, shortcut, getApplicationType(context, packageName), specialLaunch);
348 launchMode2(context, intent, MAXIMIZED, userId, shortcut, getApplicationType(context, packageName), specialLaunch);
351 launchMode2(context, intent, LEFT, userId, shortcut, getApplicationType(context, packageName), specialLaunch);
354 launchMode2(context, intent, RIGHT, userId, shortcut, getApplicationType(context, packageName), specialLaunch);
357 launchMode3(context, intent, userId, shortcut, getApplicationType(context, packageName), specialLaunch);
361 if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
362 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
364 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
367 @SuppressWarnings("deprecation")
368 @TargetApi(Build.VERSION_CODES.N)
369 private static void launchMode1(Context context, Intent intent, int factor, long userId, ShortcutInfo shortcut, ApplicationType type, boolean specialLaunch) {
370 DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
371 Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
373 int width1 = display.getWidth() / (4 * factor);
374 int width2 = display.getWidth() - width1;
375 int height1 = display.getHeight() / (4 * factor);
376 int height2 = display.getHeight() - height1;
378 Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
385 if(shortcut == null) {
386 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
387 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
389 startActivity(context, intent, bundle, specialLaunch);
390 } catch (ActivityNotFoundException e) {
391 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
392 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
394 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
396 launchShortcut(context, shortcut, bundle);
399 @SuppressWarnings("deprecation")
400 @TargetApi(Build.VERSION_CODES.N)
401 private static void launchMode2(Context context, Intent intent, int launchType, long userId, ShortcutInfo shortcut, ApplicationType type, boolean specialLaunch) {
402 DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
403 Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
405 int statusBarHeight = getStatusBarHeight(context);
406 String position = getTaskbarPosition(context);
408 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
409 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
411 int left = launchType == RIGHT && isLandscape
412 ? display.getWidth() / 2
415 int top = launchType == RIGHT && isPortrait
416 ? display.getHeight() / 2
419 int right = launchType == LEFT && isLandscape
420 ? display.getWidth() / 2
421 : display.getWidth();
423 int bottom = launchType == LEFT && isPortrait
424 ? display.getHeight() / 2
425 : display.getHeight();
427 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
429 if(position.contains("vertical_left")) {
430 if(launchType != RIGHT || isPortrait) left = left + iconSize;
431 } else if(position.contains("vertical_right")) {
432 if(launchType != LEFT || isPortrait) right = right - iconSize;
433 } else if(position.contains("bottom")) {
434 if(isLandscape || (launchType != LEFT && isPortrait))
435 bottom = bottom - iconSize;
436 } else if(isLandscape || (launchType != RIGHT && isPortrait))
437 top = top + iconSize;
439 Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
446 if(shortcut == null) {
447 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
448 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
450 startActivity(context, intent, bundle, specialLaunch);
451 } catch (ActivityNotFoundException e) {
452 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
453 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
455 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
457 launchShortcut(context, shortcut, bundle);
460 @SuppressWarnings("deprecation")
461 @TargetApi(Build.VERSION_CODES.N)
462 private static void launchMode3(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type, boolean specialLaunch) {
463 DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
464 Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
466 int width1 = display.getWidth() / 2;
467 int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
468 int height1 = display.getHeight() / 2;
469 int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
471 Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
478 if(shortcut == null) {
479 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
480 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
482 startActivity(context, intent, bundle, specialLaunch);
483 } catch (ActivityNotFoundException e) {
484 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
485 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
487 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
489 launchShortcut(context, shortcut, bundle);
492 private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
493 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
494 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
497 launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
498 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
501 @TargetApi(Build.VERSION_CODES.N_MR1)
502 private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
503 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
505 if(launcherApps.hasShortcutHostPermission()) {
507 launcherApps.startShortcut(shortcut, null, bundle);
508 } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
512 public static void launchAppMaximized(Context context, Intent intent) {
513 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
514 long userId = userManager.getSerialNumberForUser(Process.myUserHandle());
516 launchMode2(context, intent, MAXIMIZED, userId, null, ApplicationType.CONTEXT_MENU, false);
519 @SuppressWarnings("deprecation")
520 @TargetApi(Build.VERSION_CODES.N)
521 public static void launchAppLowerRight(Context context, Intent intent) {
522 DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
523 Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
525 context.startActivity(intent, getActivityOptions(ApplicationType.FREEFORM_HACK).setLaunchBounds(new Rect(
528 display.getWidth() + 1,
529 display.getHeight() + 1
531 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
534 public static void checkForUpdates(Context context) {
535 if(!BuildConfig.DEBUG) {
538 context.getPackageManager().getPackageInfo("com.android.vending", 0);
539 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
540 } catch (PackageManager.NameNotFoundException e) {
541 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.BASE_APPLICATION_ID;
544 Intent intent = new Intent(Intent.ACTION_VIEW);
545 intent.setData(Uri.parse(url));
546 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
549 context.startActivity(intent);
550 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
552 showToast(context, R.string.debug_build);
555 public static boolean launcherIsDefault(Context context) {
556 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
557 homeIntent.addCategory(Intent.CATEGORY_HOME);
558 ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
560 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
563 public static void setCachedRotation(int cachedRotation) {
564 U.cachedRotation = cachedRotation;
567 public static String getTaskbarPosition(Context context) {
568 SharedPreferences pref = getSharedPreferences(context);
569 String position = pref.getString("position", "bottom_left");
571 if(pref.getBoolean("anchor", false)) {
572 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
573 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
578 case Surface.ROTATION_0:
579 return "bottom_left";
580 case Surface.ROTATION_90:
581 return "bottom_vertical_right";
582 case Surface.ROTATION_180:
584 case Surface.ROTATION_270:
585 return "top_vertical_left";
588 case "bottom_vertical_left":
590 case Surface.ROTATION_0:
591 return "bottom_vertical_left";
592 case Surface.ROTATION_90:
593 return "bottom_right";
594 case Surface.ROTATION_180:
595 return "top_vertical_right";
596 case Surface.ROTATION_270:
602 case Surface.ROTATION_0:
603 return "bottom_right";
604 case Surface.ROTATION_90:
605 return "top_vertical_right";
606 case Surface.ROTATION_180:
608 case Surface.ROTATION_270:
609 return "bottom_vertical_left";
612 case "bottom_vertical_right":
614 case Surface.ROTATION_0:
615 return "bottom_vertical_right";
616 case Surface.ROTATION_90:
618 case Surface.ROTATION_180:
619 return "top_vertical_left";
620 case Surface.ROTATION_270:
621 return "bottom_left";
626 case Surface.ROTATION_0:
628 case Surface.ROTATION_90:
629 return "bottom_vertical_left";
630 case Surface.ROTATION_180:
631 return "bottom_right";
632 case Surface.ROTATION_270:
633 return "top_vertical_right";
636 case "top_vertical_left":
638 case Surface.ROTATION_0:
639 return "top_vertical_left";
640 case Surface.ROTATION_90:
641 return "bottom_left";
642 case Surface.ROTATION_180:
643 return "bottom_vertical_right";
644 case Surface.ROTATION_270:
650 case Surface.ROTATION_0:
652 case Surface.ROTATION_90:
653 return "top_vertical_left";
654 case Surface.ROTATION_180:
655 return "bottom_left";
656 case Surface.ROTATION_270:
657 return "bottom_vertical_right";
660 case "top_vertical_right":
662 case Surface.ROTATION_0:
663 return "top_vertical_right";
664 case Surface.ROTATION_90:
666 case Surface.ROTATION_180:
667 return "bottom_vertical_left";
668 case Surface.ROTATION_270:
669 return "bottom_right";
678 private static int getMaxNumOfColumns(Context context) {
679 SharedPreferences pref = getSharedPreferences(context);
680 DisplayMetrics metrics = context.getResources().getDisplayMetrics();
681 float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / metrics.density;
682 int numOfColumns = 0;
684 float maxScreenSize = getTaskbarPosition(context).contains("vertical")
685 ? (metrics.heightPixels - getStatusBarHeight(context)) / metrics.density
686 : metrics.widthPixels / metrics.density;
688 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
690 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
692 while(baseTaskbarSize + iconSize < maxScreenSize
693 && numOfColumns < userMaxNumOfColumns) {
694 baseTaskbarSize = baseTaskbarSize + iconSize;
701 public static int getMaxNumOfEntries(Context context) {
702 SharedPreferences pref = getSharedPreferences(context);
703 return pref.getBoolean("disable_scrolling_list", false)
704 ? getMaxNumOfColumns(context)
705 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
708 public static int getStatusBarHeight(Context context) {
709 int statusBarHeight = 0;
710 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
712 statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
714 return statusBarHeight;
717 public static void refreshPinnedIcons(Context context) {
718 IconCache.getInstance(context).clearCache();
720 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
721 List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
722 List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
723 PackageManager pm = context.getPackageManager();
727 for(AppEntry entry : pinnedAppsList) {
728 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
729 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
731 final List<UserHandle> userHandles = userManager.getUserProfiles();
732 LauncherActivityInfo appInfo = null;
734 for(UserHandle handle : userHandles) {
735 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
736 if(!list.isEmpty()) {
737 // Google App workaround
738 if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
739 appInfo = list.get(0);
741 boolean added = false;
742 for(LauncherActivityInfo info : list) {
743 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
749 if(!added) appInfo = list.get(0);
756 if(appInfo != null) {
757 AppEntry newEntry = new AppEntry(
758 entry.getPackageName(),
759 entry.getComponentName(),
761 IconCache.getInstance(context).getIcon(context, pm, appInfo),
764 newEntry.setUserId(entry.getUserId(context));
765 pba.addPinnedApp(context, newEntry);
769 for(AppEntry entry : blockedAppsList) {
770 pba.addBlockedApp(context, entry);
774 public static Intent getShortcutIntent(Context context) {
775 Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
776 shortcutIntent.setAction(Intent.ACTION_MAIN);
777 shortcutIntent.putExtra("is_launching_shortcut", true);
779 BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_freeform_mode);
781 Intent intent = new Intent();
782 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
783 if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
784 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
789 public static Intent getStartStopIntent(Context context) {
790 Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
791 shortcutIntent.setAction(Intent.ACTION_MAIN);
792 shortcutIntent.putExtra("is_launching_shortcut", true);
794 BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_launcher);
796 Intent intent = new Intent();
797 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
798 if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
799 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
804 public static boolean hasFreeformSupport(Context context) {
805 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
806 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
807 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", -1) == 1
808 || Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", -1) == 1);
811 public static boolean hasPartialFreeformSupport() {
812 return Build.MANUFACTURER.equalsIgnoreCase("Samsung") || isOPreview();
815 public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
816 return isServiceRunning(context, cls.getName());
819 public static boolean isServiceRunning(Context context, String className) {
820 ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
821 for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
822 if(className.equals(service.service.getClassName()))
829 public static int getBackgroundTint(Context context) {
830 SharedPreferences pref = getSharedPreferences(context);
832 // Import old background tint preference
833 if(pref.contains("show_background")) {
834 SharedPreferences.Editor editor = pref.edit();
836 if(!pref.getBoolean("show_background", true))
837 editor.putInt("background_tint", 0).apply();
839 editor.remove("show_background");
843 return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
846 public static int getAccentColor(Context context) {
847 SharedPreferences pref = getSharedPreferences(context);
848 return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
851 @TargetApi(Build.VERSION_CODES.M)
852 public static boolean canDrawOverlays(Context context) {
853 return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
856 public static boolean isGame(Context context, String packageName) {
857 SharedPreferences pref = getSharedPreferences(context);
858 if(pref.getBoolean("launch_games_fullscreen", true)) {
859 PackageManager pm = context.getPackageManager();
862 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
863 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
864 } catch (PackageManager.NameNotFoundException e) {
871 @TargetApi(Build.VERSION_CODES.M)
872 public static ActivityOptions getActivityOptions(ApplicationType applicationType) {
873 ActivityOptions options = ActivityOptions.makeBasic();
874 Integer stackId = null;
876 switch(applicationType) {
878 if(!FreeformHackHelper.getInstance().isFreeformHackActive())
879 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
882 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
885 stackId = FREEFORM_WORKSPACE_STACK_ID;
888 if(isOPreview()) stackId = FULLSCREEN_WORKSPACE_STACK_ID;
891 if(stackId != null) {
893 Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
894 method.invoke(options, stackId);
895 } catch (Exception e) { /* Gracefully fail */ }
901 private static ApplicationType getApplicationType(Context context, String packageName) {
902 return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
905 public static boolean isSystemApp(Context context) {
907 ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
908 int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
909 return (info.flags & mask) != 0;
910 } catch (PackageManager.NameNotFoundException e) {
915 @TargetApi(Build.VERSION_CODES.M)
916 public static boolean isOPreview() {
917 return (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) || (Build.VERSION.RELEASE.equals("O") && Build.VERSION.PREVIEW_SDK_INT > 0);
920 public static boolean hasSupportLibrary(Context context) {
921 PackageManager pm = context.getPackageManager();
923 pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
924 return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
925 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
926 && isSystemApp(context);
927 } catch (PackageManager.NameNotFoundException e) {
932 private static void startActivity(Context context, Intent intent, Bundle options, boolean specialLaunch) {
934 startFreeformHack(context, true, false);
935 new Handler().post(() -> context.startActivity(intent, options));
937 context.startActivity(intent, options);
940 public static int getBaseTaskbarSize(Context context) {
941 return Math.round(getBaseTaskbarSizeFloat(context));
944 private static float getBaseTaskbarSizeFloat(Context context) {
945 SharedPreferences pref = getSharedPreferences(context);
946 float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
947 boolean navbarButtonsEnabled = false;
949 if(pref.getBoolean("dashboard", false))
950 baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
952 if(pref.getBoolean("button_back", false)) {
953 navbarButtonsEnabled = true;
954 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
957 if(pref.getBoolean("button_home", false)) {
958 navbarButtonsEnabled = true;
959 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
962 if(pref.getBoolean("button_recents", false)) {
963 navbarButtonsEnabled = true;
964 baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
967 if(navbarButtonsEnabled)
968 baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
970 return baseTaskbarSize;
973 private static void startTaskbarService(Context context, boolean fullRestart) {
974 context.startService(new Intent(context, TaskbarService.class));
975 context.startService(new Intent(context, StartMenuService.class));
976 context.startService(new Intent(context, DashboardService.class));
977 if(fullRestart) context.startService(new Intent(context, NotificationService.class));
980 private static void stopTaskbarService(Context context, boolean fullRestart) {
981 context.stopService(new Intent(context, TaskbarService.class));
982 context.stopService(new Intent(context, StartMenuService.class));
983 context.stopService(new Intent(context, DashboardService.class));
984 if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
987 public static void restartTaskbar(Context context) {
988 SharedPreferences pref = getSharedPreferences(context);
989 if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
990 pref.edit().putBoolean("is_restarting", true).apply();
992 stopTaskbarService(context, true);
993 startTaskbarService(context, true);
994 } else if(isServiceRunning(context, StartMenuService.class)) {
995 stopTaskbarService(context, false);
996 startTaskbarService(context, false);
1000 public static void restartNotificationService(Context context) {
1001 if(isServiceRunning(context, NotificationService.class)) {
1002 SharedPreferences pref = getSharedPreferences(context);
1003 pref.edit().putBoolean("is_restarting", true).apply();
1005 Intent intent = new Intent(context, NotificationService.class);
1006 context.stopService(intent);
1007 context.startService(intent);