OSDN Git Service

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