OSDN Git Service

Remove Nvidia Shield from list of devices with broken setLaunchBounds() API
[android-x86/packages-apps-Taskbar.git] / app / src / main / java / com / farmerbb / taskbar / util / U.java
1 /* Copyright 2016 Braden Farmer
2  *
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
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 package com.farmerbb.taskbar.util;
17
18 import android.annotation.SuppressLint;
19 import android.annotation.TargetApi;
20 import android.app.ActivityManager;
21 import android.app.ActivityOptions;
22 import android.app.AlertDialog;
23 import android.app.AppOpsManager;
24 import android.app.Service;
25 import android.app.admin.DevicePolicyManager;
26 import android.content.ActivityNotFoundException;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.SharedPreferences;
31 import android.content.pm.ActivityInfo;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.LauncherActivityInfo;
34 import android.content.pm.LauncherApps;
35 import android.content.pm.PackageInfo;
36 import android.content.pm.PackageManager;
37 import android.content.pm.ResolveInfo;
38 import android.content.pm.ShortcutInfo;
39 import android.content.pm.ShortcutManager;
40 import android.content.pm.Signature;
41 import android.content.res.Configuration;
42 import android.graphics.Color;
43 import android.graphics.Rect;
44 import android.net.Uri;
45 import android.os.Build;
46 import android.os.Bundle;
47 import android.os.Handler;
48 import android.os.Process;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.provider.Settings;
52 import android.support.v4.content.LocalBroadcastManager;
53 import android.support.v7.view.ContextThemeWrapper;
54 import android.util.DisplayMetrics;
55 import android.view.Display;
56 import android.view.Surface;
57 import android.view.WindowManager;
58 import android.widget.Toast;
59
60 import com.farmerbb.taskbar.BuildConfig;
61 import com.farmerbb.taskbar.R;
62 import com.farmerbb.taskbar.activity.ContextMenuActivity;
63 import com.farmerbb.taskbar.activity.DummyActivity;
64 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
65 import com.farmerbb.taskbar.activity.ShortcutActivity;
66 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
67 import com.farmerbb.taskbar.activity.TouchAbsorberActivity;
68 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
69 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
70 import com.farmerbb.taskbar.service.DashboardService;
71 import com.farmerbb.taskbar.service.NotificationService;
72 import com.farmerbb.taskbar.service.PowerMenuService;
73 import com.farmerbb.taskbar.service.StartMenuService;
74 import com.farmerbb.taskbar.service.TaskbarService;
75
76 import java.lang.reflect.Method;
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.List;
80
81 public class U {
82
83     private U() {}
84
85     private static Integer cachedRotation;
86
87     private static final int MAXIMIZED = 0;
88     private static final int LEFT = -1;
89     private static final int RIGHT = 1;
90
91     public static final int HIDDEN = 0;
92     public static final int TOP_APPS = 1;
93
94     // From android.app.ActivityManager.StackId
95     private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
96     private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
97
98     // From android.app.WindowConfiguration
99     private static final int WINDOWING_MODE_FULLSCREEN = 1;
100     private static final int WINDOWING_MODE_FREEFORM = 5;
101
102     public static SharedPreferences getSharedPreferences(Context context) {
103         return context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
104     }
105
106     public static void showPermissionDialog(Context context) {
107         showPermissionDialog(context, null, null);
108     }
109
110     @TargetApi(Build.VERSION_CODES.M)
111     public static AlertDialog showPermissionDialog(Context context, Runnable onError, Runnable onFinish) {
112         Runnable finalOnFinish = onFinish == null
113                 ? () -> {}
114                 : onFinish;
115
116         Runnable finalOnError = onError == null
117                 ? () -> showErrorDialog(context, "SYSTEM_ALERT_WINDOW", finalOnFinish)
118                 : onError;
119
120         AlertDialog.Builder builder = new AlertDialog.Builder(context);
121         builder.setTitle(R.string.permission_dialog_title)
122                 .setMessage(R.string.permission_dialog_message)
123                 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
124                     try {
125                         context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
126                                 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
127
128                         finalOnFinish.run();
129                     } catch (ActivityNotFoundException e) {
130                         finalOnError.run();
131                     }
132                 });
133
134         AlertDialog dialog = builder.create();
135         dialog.show();
136         dialog.setCancelable(false);
137
138         return dialog;
139     }
140
141     public static AlertDialog showErrorDialog(Context context, String appopCmd) {
142         return showErrorDialog(context, appopCmd, null);
143     }
144
145     private static AlertDialog showErrorDialog(Context context, String appopCmd, Runnable onFinish) {
146         Runnable finalOnFinish = onFinish == null
147                 ? () -> {}
148                 : onFinish;
149
150         AlertDialog.Builder builder = new AlertDialog.Builder(context);
151         builder.setTitle(R.string.error_dialog_title)
152                 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
153                 .setPositiveButton(R.string.action_ok, (dialog, which) -> finalOnFinish.run());
154
155         AlertDialog dialog = builder.create();
156         dialog.show();
157         dialog.setCancelable(false);
158
159         return dialog;
160     }
161
162     public static void lockDevice(Context context) {
163         ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
164         context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
165                 PackageManager.DONT_KILL_APP);
166
167         DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
168         if(mDevicePolicyManager.isAdminActive(component))
169             mDevicePolicyManager.lockNow();
170         else {
171             launchApp(context, () -> {
172                 Intent intent = new Intent(context, DummyActivity.class);
173                 intent.putExtra("device_admin", true);
174                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
175
176                 try {
177                     context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
178                 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
179             });
180         }
181     }
182
183     public static void sendAccessibilityAction(Context context, int action) {
184         ComponentName component = new ComponentName(context, PowerMenuService.class);
185         context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
186                 PackageManager.DONT_KILL_APP);
187
188         if(isAccessibilityServiceEnabled(context)) {
189             Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
190             intent.putExtra("action", action);
191             LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
192         } else {
193             launchApp(context, () -> {
194                 Intent intent = new Intent(context, DummyActivity.class);
195                 intent.putExtra("accessibility", true);
196                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
197
198                 try {
199                     context.startActivity(intent, getActivityOptionsBundle(context, ApplicationType.APPLICATION));
200                 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
201             });
202         }
203     }
204
205     public static boolean isAccessibilityServiceEnabled(Context context) {
206         String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
207         ComponentName component = new ComponentName(context, PowerMenuService.class);
208
209         return accessibilityServices != null
210                 && (accessibilityServices.contains(component.flattenToString())
211                 || accessibilityServices.contains(component.flattenToShortString()));
212     }
213
214     public static void showToast(Context context, int message) {
215         showToast(context, context.getString(message), Toast.LENGTH_SHORT);
216     }
217
218     public static void showToastLong(Context context, int message) {
219         showToast(context, context.getString(message), Toast.LENGTH_LONG);
220     }
221
222     public static void showToast(Context context, String message, int length) {
223         cancelToast();
224
225         ToastInterface toast = DependencyUtils.createToast(context, message, length);
226         toast.show();
227
228         ToastHelper.getInstance().setLastToast(toast);
229     }
230
231     public static void cancelToast() {
232         ToastInterface toast = ToastHelper.getInstance().getLastToast();
233         if(toast != null) toast.cancel();
234     }
235
236     public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
237         launchApp(context,
238                 packageName,
239                 componentName,
240                 0,
241                 null,
242                 false,
243                 false,
244                 shortcut);
245     }
246
247     public static void launchApp(final Context context,
248                                  final String packageName,
249                                  final String componentName,
250                                  final long userId, final String windowSize,
251                                  final boolean launchedFromTaskbar,
252                                  final boolean openInNewWindow) {
253         launchApp(context,
254                 packageName,
255                 componentName,
256                 userId,
257                 windowSize,
258                 launchedFromTaskbar,
259                 openInNewWindow,
260                 null);
261     }
262
263     private static void launchApp(final Context context,
264                                   final String packageName,
265                                   final String componentName,
266                                   final long userId, final String windowSize,
267                                   final boolean launchedFromTaskbar,
268                                   final boolean openInNewWindow,
269                                   final ShortcutInfo shortcut) {
270         launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, packageName, componentName, userId,
271                 windowSize, openInNewWindow, shortcut));
272     }
273
274     public static void launchApp(Context context, Runnable runnable) {
275         launchApp(context, true, runnable);
276     }
277
278     private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
279         SharedPreferences pref = getSharedPreferences(context);
280         FreeformHackHelper helper = FreeformHackHelper.getInstance();
281
282         boolean specialLaunch = hasBrokenSetLaunchBoundsApi()
283                 && helper.isInFreeformWorkspace()
284                 && MenuHelper.getInstance().isContextMenuOpen();
285
286         boolean noAnimation = pref.getBoolean("disable_animations", false);
287
288         if(hasFreeformSupport(context)
289                 && pref.getBoolean("freeform_hack", false)
290                 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
291             new Handler().postDelayed(() -> {
292                 startFreeformHack(context, true);
293
294                 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
295             }, launchedFromTaskbar ? 0 : 100);
296         } else
297             new Handler().postDelayed(runnable, !launchedFromTaskbar && noAnimation ? 100 : 0);
298     }
299
300     public static void startFreeformHack(Context context) {
301         startFreeformHack(context, false);
302     }
303
304     @TargetApi(Build.VERSION_CODES.N)
305     public static void startFreeformHack(Context context, boolean checkMultiWindow) {
306         Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
307         freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
308                 | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
309                 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
310
311         if(checkMultiWindow)
312             freeformHackIntent.putExtra("check_multiwindow", true);
313
314         if(canDrawOverlays(context))
315             startActivityLowerRight(context, freeformHackIntent);
316     }
317
318     public static void stopFreeformHack(Context context) {
319         LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
320
321         if(isOverridingFreeformHack(context)) {
322             FreeformHackHelper helper = FreeformHackHelper.getInstance();
323             helper.setFreeformHackActive(false);
324             helper.setInFreeformWorkspace(false);
325         }
326     }
327
328     @TargetApi(Build.VERSION_CODES.N)
329     private static void continueLaunchingApp(Context context,
330                                              String packageName,
331                                              String componentName,
332                                              long userId,
333                                              String windowSize,
334                                              boolean openInNewWindow,
335                                              ShortcutInfo shortcut) {
336         SharedPreferences pref = getSharedPreferences(context);
337         Intent intent = new Intent();
338         intent.setComponent(ComponentName.unflattenFromString(componentName));
339         intent.setAction(Intent.ACTION_MAIN);
340         intent.addCategory(Intent.CATEGORY_LAUNCHER);
341         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
342         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
343
344         if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
345                 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
346             intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
347
348         if(pref.getBoolean("disable_animations", false))
349             intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
350
351         if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
352             intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
353
354             ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
355             if(activityInfo != null) {
356                 switch(activityInfo.launchMode) {
357                     case ActivityInfo.LAUNCH_SINGLE_TASK:
358                     case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
359                         intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
360                         break;
361                 }
362             }
363         }
364
365         ApplicationType type = getApplicationType(context, packageName);
366
367         if(windowSize == null)
368             windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
369
370         Bundle bundle = getActivityOptionsBundle(context, type, windowSize);
371
372         prepareToStartActivity(context, () -> {
373             if(shortcut == null) {
374                 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
375                 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
376                     try {
377                         context.startActivity(intent, bundle);
378                     } catch (ActivityNotFoundException e) {
379                         launchAndroidForWork(context, intent.getComponent(), bundle, userId);
380                     } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
381                 } else
382                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
383             } else
384                 launchShortcut(context, shortcut, bundle);
385         });
386
387         if(shouldCollapse(context, true))
388             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
389         else
390             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
391     }
392
393     @SuppressWarnings("deprecation")
394     @TargetApi(Build.VERSION_CODES.N)
395     private static Bundle launchMode1(Context context, ApplicationType type) {
396         DisplayInfo display = getDisplayInfo(context);
397
398         int width1 = display.width / 8;
399         int width2 = display.width - width1;
400         int height1 = display.height / 8;
401         int height2 = display.height - height1;
402
403         return getActivityOptions(context, type).setLaunchBounds(new Rect(
404                 width1,
405                 height1,
406                 width2,
407                 height2
408         )).toBundle();
409     }
410
411     @SuppressWarnings("deprecation")
412     @TargetApi(Build.VERSION_CODES.N)
413     private static Bundle launchMode2(Context context, int launchType, ApplicationType type) {
414         DisplayInfo display = getDisplayInfo(context);
415
416         int statusBarHeight = getStatusBarHeight(context);
417         String position = getTaskbarPosition(context);
418
419         boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
420         boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
421
422         int left = 0;
423         int top = statusBarHeight;
424         int right = display.width;
425         int bottom = display.height;
426
427         int iconSize = isOverridingFreeformHack(context) && !LauncherHelper.getInstance().isOnHomeScreen()
428                 ? 0
429                 : context.getResources().getDimensionPixelSize(R.dimen.icon_size);
430
431         if(position.contains("vertical_left"))
432             left = left + iconSize;
433         else if(position.contains("vertical_right"))
434             right = right - iconSize;
435         else if(position.contains("bottom"))
436             bottom = bottom - iconSize;
437         else
438             top = top + iconSize;
439
440         if(launchType == RIGHT && isLandscape)
441             left = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
442         else if(launchType == RIGHT && isPortrait)
443             top = (bottom / 2) + ((iconSize / 2)
444                     * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
445         else if(launchType == LEFT && isLandscape)
446             right = (right / 2) + ((iconSize / 2) * (position.contains("vertical_left") ? 1 : 0));
447         else if(launchType == LEFT && isPortrait)
448             bottom = (bottom / 2) + ((iconSize / 2)
449                     * ((position.equals("top_left") || position.equals("top_right")) ? 1 : 0));
450
451         return getActivityOptions(context, type)
452                 .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle();
453     }
454
455     @SuppressWarnings("deprecation")
456     @TargetApi(Build.VERSION_CODES.N)
457     private static Bundle launchMode3(Context context, ApplicationType type) {
458         DisplayInfo display = getDisplayInfo(context);
459
460         int width1 = display.width / 2;
461         int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
462         int height1 = display.height / 2;
463         int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
464
465         return getActivityOptions(context, type).setLaunchBounds(new Rect(
466                 width1 - width2,
467                 height1 - height2,
468                 width1 + width2,
469                 height1 + height2
470         )).toBundle();
471     }
472
473     private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
474         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
475         LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
476
477         try {
478             launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
479         } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
480     }
481
482     @TargetApi(Build.VERSION_CODES.N_MR1)
483     private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
484         LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
485
486         if(launcherApps.hasShortcutHostPermission()) {
487             try {
488                 launcherApps.startShortcut(shortcut, null, bundle);
489             } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
490         }
491     }
492
493     private static void prepareToStartActivity(Context context, Runnable runnable) {
494         boolean shouldLaunchTouchAbsorber =
495                 !FreeformHackHelper.getInstance().isTouchAbsorberActive()
496                         && isOverridingFreeformHack(context)
497                         && !isChromeOs(context);
498
499         if(!shouldLaunchTouchAbsorber) {
500             runnable.run();
501             return;
502         }
503
504         startTouchAbsorberActivity(context);
505         new Handler().postDelayed(runnable, 100);
506     }
507
508     public static void startActivityMaximized(Context context, Intent intent) {
509         Bundle bundle = launchMode2(context, MAXIMIZED, ApplicationType.CONTEXT_MENU);
510         prepareToStartActivity(context, () -> context.startActivity(intent, bundle));
511     }
512
513     @TargetApi(Build.VERSION_CODES.N)
514     public static void startActivityLowerRight(Context context, Intent intent) {
515         DisplayInfo display = getDisplayInfo(context);
516         try {
517             context.startActivity(intent,
518                     getActivityOptions(context, ApplicationType.FREEFORM_HACK)
519                             .setLaunchBounds(new Rect(
520                                     display.width,
521                                     display.height,
522                                     display.width + 1,
523                                     display.height + 1
524                             )).toBundle());
525         } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
526     }
527
528     @TargetApi(Build.VERSION_CODES.N)
529     public static void startTouchAbsorberActivity(Context context) {
530         String position = getTaskbarPosition(context);
531         DisplayInfo display = getDisplayInfo(context);
532
533         int left = 0;
534         int top = 0;
535         int right = display.width;
536         int bottom = display.height;
537
538         int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
539
540         if(position.contains("vertical_left"))
541             right = iconSize;
542         else if(position.contains("vertical_right"))
543             left = right - iconSize;
544         else if(position.contains("bottom"))
545             top = bottom - iconSize;
546         else
547             bottom = iconSize;
548
549         Intent intent = new Intent(context, TouchAbsorberActivity.class);
550         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
551         intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
552
553         try {
554             context.startActivity(intent,
555                     getActivityOptions(context, ApplicationType.FREEFORM_HACK)
556                             .setLaunchBounds(new Rect(left, top, right, bottom)).toBundle());
557         } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
558     }
559
560     public static void startContextMenuActivity(Context context, Bundle args) {
561         SharedPreferences pref = getSharedPreferences(context);
562         Intent intent = null;
563
564         switch(pref.getString("theme", "light")) {
565             case "light":
566                 intent = new Intent(context, ContextMenuActivity.class);
567                 break;
568             case "dark":
569                 intent = new Intent(context, ContextMenuActivityDark.class);
570                 break;
571         }
572
573         if(intent != null) {
574             intent.putExtra("args", args);
575             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
576         }
577
578         if(hasFreeformSupport(context) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
579             DisplayInfo display = getDisplayInfo(context);
580
581             if(intent != null && hasBrokenSetLaunchBoundsApi())
582                 intent.putExtra("context_menu_fix", true);
583
584             context.startActivity(intent,
585                     getActivityOptions(context, ApplicationType.CONTEXT_MENU)
586                             .setLaunchBounds(
587                                     new Rect(0, 0, display.width, display.height)
588                             ).toBundle());
589         } else
590             context.startActivity(intent);
591     }
592
593     public static void checkForUpdates(Context context) {
594         String url;
595         if(isPlayStoreRelease(context)) {
596             if(BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
597                     && !isPlayStoreInstalled(context))
598                 url = "https://github.com/farmerbb/Taskbar/releases";
599             else
600                 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
601         } else
602             url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.APPLICATION_ID;
603
604         Intent intent = new Intent(Intent.ACTION_VIEW);
605         intent.setData(Uri.parse(url));
606         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
607
608         try {
609             context.startActivity(intent);
610         } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
611     }
612
613     public static boolean launcherIsDefault(Context context) {
614         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
615         homeIntent.addCategory(Intent.CATEGORY_HOME);
616         ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
617
618         return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
619     }
620
621     public static void setCachedRotation(int cachedRotation) {
622         U.cachedRotation = cachedRotation;
623     }
624
625     public static String getTaskbarPosition(Context context) {
626         SharedPreferences pref = getSharedPreferences(context);
627         String position = pref.getString("position", "bottom_left");
628
629         if(pref.getBoolean("anchor", false)) {
630             WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
631             int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
632
633             switch(position) {
634                 case "bottom_left":
635                     switch(rotation) {
636                         case Surface.ROTATION_0:
637                             return "bottom_left";
638                         case Surface.ROTATION_90:
639                             return "bottom_vertical_right";
640                         case Surface.ROTATION_180:
641                             return "top_right";
642                         case Surface.ROTATION_270:
643                             return "top_vertical_left";
644                     }
645                     break;
646                 case "bottom_vertical_left":
647                     switch(rotation) {
648                         case Surface.ROTATION_0:
649                             return "bottom_vertical_left";
650                         case Surface.ROTATION_90:
651                             return "bottom_right";
652                         case Surface.ROTATION_180:
653                             return "top_vertical_right";
654                         case Surface.ROTATION_270:
655                             return "top_left";
656                     }
657                     break;
658                 case "bottom_right":
659                     switch(rotation) {
660                         case Surface.ROTATION_0:
661                             return "bottom_right";
662                         case Surface.ROTATION_90:
663                             return "top_vertical_right";
664                         case Surface.ROTATION_180:
665                             return "top_left";
666                         case Surface.ROTATION_270:
667                             return "bottom_vertical_left";
668                     }
669                     break;
670                 case "bottom_vertical_right":
671                     switch(rotation) {
672                         case Surface.ROTATION_0:
673                             return "bottom_vertical_right";
674                         case Surface.ROTATION_90:
675                             return "top_right";
676                         case Surface.ROTATION_180:
677                             return "top_vertical_left";
678                         case Surface.ROTATION_270:
679                             return "bottom_left";
680                     }
681                     break;
682                 case "top_left":
683                     switch(rotation) {
684                         case Surface.ROTATION_0:
685                             return "top_left";
686                         case Surface.ROTATION_90:
687                             return "bottom_vertical_left";
688                         case Surface.ROTATION_180:
689                             return "bottom_right";
690                         case Surface.ROTATION_270:
691                             return "top_vertical_right";
692                     }
693                     break;
694                 case "top_vertical_left":
695                     switch(rotation) {
696                         case Surface.ROTATION_0:
697                             return "top_vertical_left";
698                         case Surface.ROTATION_90:
699                             return "bottom_left";
700                         case Surface.ROTATION_180:
701                             return "bottom_vertical_right";
702                         case Surface.ROTATION_270:
703                             return "top_right";
704                     }
705                     break;
706                 case "top_right":
707                     switch(rotation) {
708                         case Surface.ROTATION_0:
709                             return "top_right";
710                         case Surface.ROTATION_90:
711                             return "top_vertical_left";
712                         case Surface.ROTATION_180:
713                             return "bottom_left";
714                         case Surface.ROTATION_270:
715                             return "bottom_vertical_right";
716                     }
717                     break;
718                 case "top_vertical_right":
719                     switch(rotation) {
720                         case Surface.ROTATION_0:
721                             return "top_vertical_right";
722                         case Surface.ROTATION_90:
723                             return "top_left";
724                         case Surface.ROTATION_180:
725                             return "bottom_vertical_left";
726                         case Surface.ROTATION_270:
727                             return "bottom_right";
728                     }
729                     break;
730             }
731         }
732
733         return position;
734     }
735
736     private static int getMaxNumOfColumns(Context context) {
737         SharedPreferences pref = getSharedPreferences(context);
738         DisplayInfo display = getDisplayInfo(context);
739         float density = display.density / 160;
740         float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / density;
741         int numOfColumns = 0;
742
743         float maxScreenSize = getTaskbarPosition(context).contains("vertical")
744                 ? (display.height - getStatusBarHeight(context)) / density
745                 : display.width / density;
746
747         float iconSize = context.getResources().getDimension(R.dimen.icon_size) / density;
748
749         int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
750
751         while(baseTaskbarSize + iconSize < maxScreenSize
752                 && numOfColumns < userMaxNumOfColumns) {
753             baseTaskbarSize = baseTaskbarSize + iconSize;
754             numOfColumns++;
755         }
756
757         return numOfColumns;
758     }
759
760     public static int getMaxNumOfEntries(Context context) {
761         SharedPreferences pref = getSharedPreferences(context);
762         return pref.getBoolean("disable_scrolling_list", false)
763                 ? getMaxNumOfColumns(context)
764                 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
765     }
766
767     public static int getStatusBarHeight(Context context) {
768         return getSystemDimen(context, "status_bar_height");
769     }
770
771     private static int getNavbarHeight(Context context) {
772         return getSystemDimen(context, "navigation_bar_height");
773     }
774
775     private static int getSystemDimen(Context context, String id) {
776         int value = 0;
777         int resourceId = context.getResources().getIdentifier(id, "dimen", "android");
778         if(resourceId > 0)
779             value = context.getResources().getDimensionPixelSize(resourceId);
780
781         return value;
782     }
783
784     public static void refreshPinnedIcons(Context context) {
785         IconCache.getInstance(context).clearCache();
786
787         PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
788         List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
789         List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
790         PackageManager pm = context.getPackageManager();
791
792         pba.clear(context);
793
794         for(AppEntry entry : pinnedAppsList) {
795             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
796             LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
797
798             final List<UserHandle> userHandles = userManager.getUserProfiles();
799             LauncherActivityInfo appInfo = null;
800
801             for(UserHandle handle : userHandles) {
802                 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
803                 if(!list.isEmpty()) {
804                     // Google App workaround
805                     if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
806                         appInfo = list.get(0);
807                     else {
808                         boolean added = false;
809                         for(LauncherActivityInfo info : list) {
810                             if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
811                                 appInfo = info;
812                                 added = true;
813                             }
814                         }
815
816                         if(!added) appInfo = list.get(0);
817                     }
818
819                     break;
820                 }
821             }
822
823             if(appInfo != null) {
824                 AppEntry newEntry = new AppEntry(
825                         entry.getPackageName(),
826                         entry.getComponentName(),
827                         entry.getLabel(),
828                         IconCache.getInstance(context).getIcon(context, pm, appInfo),
829                         true);
830
831                 newEntry.setUserId(entry.getUserId(context));
832                 pba.addPinnedApp(context, newEntry);
833             }
834         }
835
836         for(AppEntry entry : blockedAppsList) {
837             pba.addBlockedApp(context, entry);
838         }
839     }
840
841     public static Intent getShortcutIntent(Context context) {
842         Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
843         shortcutIntent.setAction(Intent.ACTION_MAIN);
844         shortcutIntent.putExtra("is_launching_shortcut", true);
845
846         Intent intent = new Intent();
847         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
848         intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
849         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
850
851         return intent;
852     }
853
854     public static Intent getStartStopIntent(Context context) {
855         Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
856         shortcutIntent.setAction(Intent.ACTION_MAIN);
857         shortcutIntent.putExtra("is_launching_shortcut", true);
858
859         Intent intent = new Intent();
860         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
861         intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
862         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
863
864         return intent;
865     }
866
867     public static boolean canEnableFreeform() {
868         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
869     }
870
871     @TargetApi(Build.VERSION_CODES.N)
872     public static boolean hasFreeformSupport(Context context) {
873         return canEnableFreeform()
874                 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
875                 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", 0) != 0
876                 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
877                 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", 0) != 0));
878     }
879
880     public static boolean isSamsungDevice() {
881         return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
882     }
883
884     public static boolean isNvidiaDevice() {
885         return Build.MANUFACTURER.equalsIgnoreCase("NVIDIA");
886     }
887
888     public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
889         return isServiceRunning(context, cls.getName());
890     }
891
892     private static boolean isServiceRunning(Context context, String className) {
893         ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
894         for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
895             if(className.equals(service.service.getClassName()))
896                 return true;
897         }
898
899         return false;
900     }
901
902     public static int getBackgroundTint(Context context) {
903         SharedPreferences pref = getSharedPreferences(context);
904
905         // Import old background tint preference
906         if(pref.contains("show_background")) {
907             SharedPreferences.Editor editor = pref.edit();
908
909             if(!pref.getBoolean("show_background", true))
910                 editor.putInt("background_tint", Color.TRANSPARENT).apply();
911
912             editor.remove("show_background");
913             editor.apply();
914         }
915
916         return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
917     }
918
919     public static int getAccentColor(Context context) {
920         SharedPreferences pref = getSharedPreferences(context);
921         return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
922     }
923
924     @TargetApi(Build.VERSION_CODES.M)
925     public static boolean canDrawOverlays(Context context) {
926         return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
927     }
928
929     public static boolean isGame(Context context, String packageName) {
930         SharedPreferences pref = getSharedPreferences(context);
931         if(pref.getBoolean("launch_games_fullscreen", true)) {
932             PackageManager pm = context.getPackageManager();
933
934             try {
935                 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
936                 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
937             } catch (PackageManager.NameNotFoundException e) {
938                 return false;
939             }
940         } else
941             return false;
942     }
943
944     @TargetApi(Build.VERSION_CODES.N)
945     public static ActivityOptions getActivityOptions(Context context, ApplicationType applicationType) {
946         ActivityOptions options = ActivityOptions.makeBasic();
947         int stackId = -1;
948
949         switch(applicationType) {
950             case APPLICATION:
951                 if(FreeformHackHelper.getInstance().isFreeformHackActive())
952                     stackId = getFreeformWindowModeId();
953                 else
954                     stackId = getFullscreenWindowModeId();
955                 break;
956             case GAME:
957                 stackId = getFullscreenWindowModeId();
958                 break;
959             case FREEFORM_HACK:
960                 stackId = getFreeformWindowModeId();
961                 break;
962             case CONTEXT_MENU:
963                 if(hasBrokenSetLaunchBoundsApi()
964                         || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f))
965                     stackId = getFullscreenWindowModeId();
966                 break;
967         }
968
969         try {
970             Method method = ActivityOptions.class.getMethod(getWindowingModeMethodName(), int.class);
971             method.invoke(options, stackId);
972         } catch (Exception e) { /* Gracefully fail */ }
973
974         return options;
975     }
976
977     private static int getFullscreenWindowModeId() {
978         if(getCurrentApiVersion() >= 28.0f)
979             return WINDOWING_MODE_FULLSCREEN;
980         else
981             return FULLSCREEN_WORKSPACE_STACK_ID;
982     }
983
984     private static int getFreeformWindowModeId() {
985         if(getCurrentApiVersion() >= 28.0f)
986             return WINDOWING_MODE_FREEFORM;
987         else
988             return FREEFORM_WORKSPACE_STACK_ID;
989     }
990
991     private static String getWindowingModeMethodName() {
992         if(getCurrentApiVersion() >= 28.0f)
993             return "setLaunchWindowingMode";
994         else
995             return "setLaunchStackId";
996     }
997
998     public static Bundle getActivityOptionsBundle(Context context, ApplicationType type) {
999         SharedPreferences pref = getSharedPreferences(context);
1000
1001         return getActivityOptionsBundle(context, type, pref.getString("window_size", "standard"));
1002     }
1003     
1004     private static Bundle getActivityOptionsBundle(Context context, ApplicationType type, String windowSize) {
1005         SharedPreferences pref = getSharedPreferences(context);
1006         if(!canEnableFreeform() || !pref.getBoolean("freeform_hack", false))
1007             return null;
1008         
1009         switch(windowSize) {
1010             case "large":
1011                 return launchMode1(context, type);
1012             case "fullscreen":
1013                 return launchMode2(context, MAXIMIZED, type);
1014             case "half_left":
1015                 return launchMode2(context, LEFT, type);
1016             case "half_right":
1017                 return launchMode2(context, RIGHT, type);
1018             case "phone_size":
1019                 return launchMode3(context, type);
1020         }
1021
1022         return getActivityOptions(context, type).toBundle();
1023     }
1024
1025     private static ApplicationType getApplicationType(Context context, String packageName) {
1026         return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
1027     }
1028
1029     public static boolean isSystemApp(Context context) {
1030         try {
1031             ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1032             int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
1033             return (info.flags & mask) != 0;
1034         } catch (PackageManager.NameNotFoundException e) {
1035             return false;
1036         }
1037     }
1038
1039     public static boolean isChromeOs(Context context) {
1040         return context.getPackageManager().hasSystemFeature("org.chromium.arc");
1041     }
1042
1043     public static boolean isBlissOs(Context context) {
1044         boolean validBlissOsBuildProp = false;
1045
1046         String blissVersion = DependencyUtils.getSystemProperty("ro.bliss.version");
1047         if(blissVersion != null && !blissVersion.isEmpty())
1048             validBlissOsBuildProp = true;
1049
1050         String buildUser = DependencyUtils.getSystemProperty("ro.build.user");
1051         if(buildUser != null && buildUser.equals("electrikjesus"))
1052             validBlissOsBuildProp = true;
1053
1054         return validBlissOsBuildProp
1055                 && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1056                 && isSystemApp(context);
1057     }
1058
1059     public static boolean isLauncherPermanentlyEnabled(Context context) {
1060         if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
1061             return true;
1062
1063         PackageManager pm = context.getPackageManager();
1064         try {
1065             pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
1066             return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
1067                     && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
1068                     && isSystemApp(context);
1069         } catch (PackageManager.NameNotFoundException e) {
1070             return false;
1071         }
1072     }
1073
1074     public static int getBaseTaskbarSize(Context context) {
1075         return Math.round(getBaseTaskbarSizeFloat(context));
1076     }
1077
1078     private static float getBaseTaskbarSizeFloat(Context context) {
1079         SharedPreferences pref = getSharedPreferences(context);
1080         float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
1081         boolean navbarButtonsEnabled = false;
1082
1083         if(pref.getBoolean("dashboard", false))
1084             baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
1085
1086         if(pref.getBoolean("button_back", false)) {
1087             navbarButtonsEnabled = true;
1088             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1089         }
1090
1091         if(pref.getBoolean("button_home", false)) {
1092             navbarButtonsEnabled = true;
1093             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1094         }
1095
1096         if(pref.getBoolean("button_recents", false)) {
1097             navbarButtonsEnabled = true;
1098             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
1099         }
1100
1101         if(navbarButtonsEnabled)
1102             baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
1103
1104         return baseTaskbarSize;
1105     }
1106
1107     private static void startTaskbarService(Context context, boolean fullRestart) {
1108         context.startService(new Intent(context, TaskbarService.class));
1109         context.startService(new Intent(context, StartMenuService.class));
1110         context.startService(new Intent(context, DashboardService.class));
1111         if(fullRestart) context.startService(new Intent(context, NotificationService.class));
1112     }
1113
1114     private static void stopTaskbarService(Context context, boolean fullRestart) {
1115         context.stopService(new Intent(context, TaskbarService.class));
1116         context.stopService(new Intent(context, StartMenuService.class));
1117         context.stopService(new Intent(context, DashboardService.class));
1118         if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1119     }
1120
1121     public static void restartTaskbar(Context context) {
1122         SharedPreferences pref = getSharedPreferences(context);
1123         if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1124             pref.edit()
1125                     .putBoolean("is_restarting", true)
1126                     .putBoolean("skip_auto_hide_navbar", true)
1127                     .apply();
1128
1129             stopTaskbarService(context, true);
1130             startTaskbarService(context, true);
1131         } else if(isServiceRunning(context, StartMenuService.class)) {
1132             pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1133
1134             stopTaskbarService(context, false);
1135             startTaskbarService(context, false);
1136         }
1137     }
1138
1139     public static void restartNotificationService(Context context) {
1140         if(isServiceRunning(context, NotificationService.class)) {
1141             SharedPreferences pref = getSharedPreferences(context);
1142             pref.edit().putBoolean("is_restarting", true).apply();
1143
1144             Intent intent = new Intent(context, NotificationService.class);
1145             context.stopService(intent);
1146             context.startService(intent);
1147         }
1148     }
1149
1150     public static void showHideNavigationBar(Context context, boolean show) {
1151         // Show or hide the system navigation bar on Bliss-x86
1152         try {
1153             Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1154         } catch (Exception e) { /* Gracefully fail */ }
1155     }
1156
1157     public static void initPrefs(Context context) {
1158         // On smaller-screened devices, set "Grid" as the default start menu layout
1159         SharedPreferences pref = getSharedPreferences(context);
1160         if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 720
1161                 && pref.getString("start_menu_layout", "null").equals("null")) {
1162             pref.edit().putString("start_menu_layout", "grid").apply();
1163         }
1164
1165         // Enable freeform hack automatically on supported devices
1166         if(canEnableFreeform()) {
1167             if(!pref.getBoolean("freeform_hack_override", false)) {
1168                 pref.edit()
1169                         .putBoolean("freeform_hack", hasFreeformSupport(context) && !isSamsungDevice())
1170                         .putBoolean("save_window_sizes", false)
1171                         .putBoolean("freeform_hack_override", true)
1172                         .apply();
1173             } else if(!hasFreeformSupport(context)) {
1174                 pref.edit().putBoolean("freeform_hack", false).apply();
1175
1176                 stopFreeformHack(context);
1177             }
1178         } else {
1179             boolean freeformWasEnabled = pref.getBoolean("freeform_hack", false)
1180                     || pref.getBoolean("show_freeform_disabled_message", false);
1181
1182             pref.edit()
1183                     .putBoolean("freeform_hack", false)
1184                     .putBoolean("show_freeform_disabled_message", freeformWasEnabled)
1185                     .apply();
1186
1187             SavedWindowSizes.getInstance(context).clear(context);
1188             stopFreeformHack(context);
1189         }
1190
1191         // Customizations for BlissOS
1192         if(isBlissOs(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
1193                 && !pref.getBoolean("bliss_os_prefs", false)) {
1194             SharedPreferences.Editor editor = pref.edit();
1195
1196             if(hasFreeformSupport(context)) {
1197                 editor.putBoolean("freeform_hack", true);
1198             }
1199
1200             editor.putString("recents_amount", "running_apps_only");
1201             editor.putString("refresh_frequency", "0");
1202             editor.putString("max_num_of_recents", "2147483647");
1203             editor.putString("sort_order", "true");
1204             editor.putBoolean("full_length", true);
1205             editor.putBoolean("dashboard", true);
1206             editor.putBoolean("app_drawer_icon", true);
1207             editor.putBoolean("button_back", true);
1208             editor.putBoolean("button_home", true);
1209             editor.putBoolean("button_recents", true);
1210             editor.putBoolean("auto_hide_navbar", true);
1211             editor.putBoolean("bliss_os_prefs", true);
1212
1213             try {
1214                 Settings.Secure.putString(context.getContentResolver(),
1215                         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
1216                         new ComponentName(context, PowerMenuService.class).flattenToString());
1217             } catch (Exception e) { /* Gracefully fail */ }
1218
1219             editor.apply();
1220         }
1221
1222         // Customizations for Android-x86 devices (non-Bliss)
1223         if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1224                 && isSystemApp(context)
1225                 && !pref.getBoolean("android_x86_prefs", false)) {
1226             pref.edit()
1227                     .putString("recents_amount", "running_apps_only")
1228                     .putString("refresh_frequency", "0")
1229                     .putString("max_num_of_recents", "2147483647")
1230                     .putString("sort_order", "true")
1231                     .putBoolean("full_length", true)
1232                     .putBoolean("dashboard", true)
1233                     .putBoolean("android_x86_prefs", true)
1234                     .apply();
1235         }
1236     }
1237
1238     public static DisplayInfo getDisplayInfo(Context context) {
1239         context = context.getApplicationContext();
1240
1241         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1242         Display disp = wm.getDefaultDisplay();
1243
1244         DisplayMetrics metrics = new DisplayMetrics();
1245         disp.getMetrics(metrics);
1246
1247         DisplayMetrics realMetrics = new DisplayMetrics();
1248         disp.getRealMetrics(realMetrics);
1249
1250         DisplayInfo display = new DisplayInfo(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
1251
1252         if(isChromeOs(context)) {
1253             SharedPreferences pref = getSharedPreferences(context);
1254             if(!pref.getBoolean("chrome_os_context_menu_fix", true)) {
1255                 display.width = realMetrics.widthPixels;
1256                 display.height = realMetrics.heightPixels;
1257             }
1258
1259             return display;
1260         }
1261
1262         boolean sameWidth = metrics.widthPixels == realMetrics.widthPixels;
1263         boolean sameHeight = metrics.heightPixels == realMetrics.heightPixels;
1264
1265         if(sameWidth && !sameHeight) {
1266             display.width = realMetrics.widthPixels;
1267             display.height = realMetrics.heightPixels - getNavbarHeight(context);
1268         }
1269
1270         if(!sameWidth && sameHeight) {
1271             display.width = realMetrics.widthPixels - getNavbarHeight(context);
1272             display.height = realMetrics.heightPixels;
1273         }
1274
1275         return display;
1276     }
1277
1278     public static void pinAppShortcut(Context context) {
1279         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1280             ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
1281
1282             if(mShortcutManager.isRequestPinShortcutSupported()) {
1283                 ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, "freeform_mode").build();
1284
1285                 mShortcutManager.requestPinShortcut(pinShortcutInfo, null);
1286             } else
1287                 showToastLong(context, R.string.pin_shortcut_not_supported);
1288         } else {
1289             Intent intent = getShortcutIntent(context);
1290             intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1291             intent.putExtra("duplicate", false);
1292
1293             Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1294             homeIntent.addCategory(Intent.CATEGORY_HOME);
1295             ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1296
1297             intent.setPackage(defaultLauncher.activityInfo.packageName);
1298             context.sendBroadcast(intent);
1299
1300             showToast(context, R.string.shortcut_created);
1301         }
1302     }
1303
1304     public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1305         SharedPreferences pref = getSharedPreferences(context);
1306         if(pref.getBoolean("hide_taskbar", true)) {
1307             if(isOverridingFreeformHack(context))
1308                 return !LauncherHelper.getInstance().isOnHomeScreen();
1309             else {
1310                 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1311                 if(pendingAppLaunch)
1312                     return !helper.isFreeformHackActive();
1313                 else
1314                     return !helper.isInFreeformWorkspace();
1315             }
1316         } else
1317             return false;
1318     }
1319
1320     public static boolean isOverridingFreeformHack(Context context) {
1321         SharedPreferences pref = getSharedPreferences(context);
1322         return pref.getBoolean("freeform_hack", false)
1323                 && ((isChromeOs(context) && pref.getBoolean("chrome_os_context_menu_fix", true))
1324                 || (!isChromeOs(context) && getCurrentApiVersion() >= 28.0f));
1325     }
1326
1327     public static boolean isPlayStoreInstalled(Context context) {
1328         try {
1329             context.getPackageManager().getPackageInfo("com.android.vending", 0);
1330             return true;
1331         } catch (PackageManager.NameNotFoundException e) {
1332             return false;
1333         }
1334     }
1335
1336     private static float getCurrentApiVersion() {
1337         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1338             return Float.valueOf(Build.VERSION.SDK_INT + "." + Build.VERSION.PREVIEW_SDK_INT);
1339         else
1340             return (float) Build.VERSION.SDK_INT;
1341     }
1342
1343     public static boolean hasBrokenSetLaunchBoundsApi() {
1344         return getCurrentApiVersion() >= 26.0f
1345                 && getCurrentApiVersion() < 28.0f
1346                 && !isSamsungDevice()
1347                 && !isNvidiaDevice();
1348     }
1349
1350     public static String getSecondScreenPackageName(Context context) {
1351         return getInstalledPackage(context, Arrays.asList(
1352                 "com.farmerbb.secondscreen.free",
1353                 "com.farmerbb.secondscreen"));
1354     }
1355
1356     // Returns the name of an installed package from a list of package names, in order of preference
1357     private static String getInstalledPackage(Context context, List<String> packageNames) {
1358         if(packageNames == null || packageNames.isEmpty())
1359             return null;
1360
1361         List<String> packages = packageNames instanceof ArrayList ? packageNames : new ArrayList<>(packageNames);
1362         String packageName = packages.get(0);
1363
1364         try {
1365             context.getPackageManager().getPackageInfo(packageName, 0);
1366             return packageName;
1367         } catch (PackageManager.NameNotFoundException e) {
1368             packages.remove(0);
1369             return getInstalledPackage(context, packages);
1370         }
1371     }
1372
1373     public static boolean visualFeedbackEnabled(Context context) {
1374         SharedPreferences pref = getSharedPreferences(context);
1375         return (getCurrentApiVersion() < 26.0f || getCurrentApiVersion() >= 28.0f)
1376                 && pref.getBoolean("visual_feedback", true);
1377     }
1378
1379     public static void showRecentAppsDialog(Context context) {
1380         showRecentAppsDialog(context, null, null);
1381     }
1382
1383     public static AlertDialog showRecentAppsDialog(Context context, Runnable onError, Runnable onFinish) {
1384         Runnable finalOnFinish = onFinish == null
1385                 ? () -> {}
1386                 : onFinish;
1387
1388         Runnable finalOnError = onError == null
1389                 ? () -> showErrorDialog(context, "GET_USAGE_STATS", finalOnFinish)
1390                 : onError;
1391
1392         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isSystemApp(context)) {
1393             ApplicationInfo applicationInfo = null;
1394             try {
1395                 applicationInfo = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
1396             } catch (PackageManager.NameNotFoundException e) { /* Gracefully fail */ }
1397
1398             if(applicationInfo != null) {
1399                 AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1400                 int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
1401
1402                 if(mode != AppOpsManager.MODE_ALLOWED) {
1403                     AlertDialog.Builder builder = new AlertDialog.Builder(context);
1404                     builder.setTitle(R.string.pref_header_recent_apps)
1405                             .setMessage(R.string.enable_recent_apps)
1406                             .setPositiveButton(R.string.action_ok, (dialog, which) -> {
1407                                 try {
1408                                     context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
1409                                     showToastLong(context, R.string.usage_stats_message);
1410
1411                                     finalOnFinish.run();
1412                                 } catch (ActivityNotFoundException e) {
1413                                     finalOnError.run();
1414                                 }
1415                             })
1416                             .setNegativeButton(R.string.action_cancel, (dialog, which) -> finalOnFinish.run());
1417
1418                     AlertDialog dialog = builder.create();
1419                     dialog.show();
1420                     dialog.setCancelable(false);
1421
1422                     return dialog;
1423                 }
1424             }
1425         }
1426
1427         finalOnFinish.run();
1428         return null;
1429     }
1430
1431     public static Context wrapContext(Context context) {
1432         SharedPreferences pref = getSharedPreferences(context);
1433
1434         int theme = -1;
1435         switch(pref.getString("theme", "light")) {
1436             case "light":
1437                 theme = R.style.AppTheme;
1438                 break;
1439             case "dark":
1440                 theme = R.style.AppTheme_Dark;
1441                 break;
1442         }
1443
1444         return theme > -1 ? new ContextThemeWrapper(context, theme) : context;
1445     }
1446
1447     @SuppressLint("PackageManagerGetSignatures")
1448     public static boolean isPlayStoreRelease(Context context) {
1449         Signature playStoreSignature = new Signature(context.getString(R.string.signature));
1450         try {
1451             PackageManager pm = context.getPackageManager();
1452             PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
1453             for(Signature signature : info.signatures) {
1454                 if(signature.equals(playStoreSignature))
1455                     return true;
1456             }
1457         } catch (Exception e) { /* Gracefully fail */ }
1458
1459         return false;
1460     }
1461
1462     public static boolean isExternalAccessDisabled(Context context) {
1463         SharedPreferences pref = getSharedPreferences(context);
1464         return !pref.getBoolean("tasker_enabled", true);
1465     }
1466
1467     public static boolean enableFreeformModeShortcut(Context context) {
1468         return canEnableFreeform()
1469                 && !U.isOverridingFreeformHack(context)
1470                 && !U.isChromeOs(context);
1471     }
1472
1473     public static void startForegroundService(Context context, Intent intent) {
1474         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1475             if(Settings.canDrawOverlays(context))
1476                 context.startForegroundService(intent);
1477         } else
1478             context.startService(intent);
1479     }
1480
1481     public static int getOverlayType() {
1482         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1483                 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1484                 : WindowManager.LayoutParams.TYPE_PHONE;
1485     }
1486
1487     public static boolean isDelegatingHomeActivity(Context context) {
1488         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1489         homeIntent.addCategory(Intent.CATEGORY_HOME);
1490
1491         final List<ResolveInfo> listOfLaunchers = context.getPackageManager().queryIntentActivities(homeIntent, 0);
1492         for(ResolveInfo launcher : listOfLaunchers) {
1493             if(launcher.activityInfo.packageName.equals(BuildConfig.SUPPORT_APPLICATION_ID))
1494                 return true;
1495         }
1496
1497         return false;
1498     }
1499 }