OSDN Git Service

Use safer method of creating ActivityOptions bundles on Android <7.0
[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.TargetApi;
19 import android.app.Activity;
20 import android.app.ActivityManager;
21 import android.app.ActivityOptions;
22 import android.app.AlertDialog;
23 import android.app.Service;
24 import android.app.admin.DevicePolicyManager;
25 import android.content.ActivityNotFoundException;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.SharedPreferences;
30 import android.content.pm.ActivityInfo;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.LauncherActivityInfo;
33 import android.content.pm.LauncherApps;
34 import android.content.pm.PackageManager;
35 import android.content.pm.ResolveInfo;
36 import android.content.pm.ShortcutInfo;
37 import android.content.res.Configuration;
38 import android.graphics.Color;
39 import android.graphics.Rect;
40 import android.net.Uri;
41 import android.os.Build;
42 import android.os.Bundle;
43 import android.os.Handler;
44 import android.os.Process;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.provider.Settings;
48 import android.support.v4.content.LocalBroadcastManager;
49 import android.util.DisplayMetrics;
50 import android.view.Display;
51 import android.view.Surface;
52 import android.view.View;
53 import android.view.WindowManager;
54 import android.widget.Toast;
55
56 import com.farmerbb.taskbar.BuildConfig;
57 import com.farmerbb.taskbar.R;
58 import com.farmerbb.taskbar.activity.DummyActivity;
59 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
60 import com.farmerbb.taskbar.activity.ShortcutActivity;
61 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
62 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
63 import com.farmerbb.taskbar.service.DashboardService;
64 import com.farmerbb.taskbar.service.NotificationService;
65 import com.farmerbb.taskbar.service.PowerMenuService;
66 import com.farmerbb.taskbar.service.StartMenuService;
67 import com.farmerbb.taskbar.service.TaskbarService;
68
69 import java.lang.reflect.Method;
70 import java.util.ArrayList;
71 import java.util.List;
72
73 public class U {
74
75     private U() {}
76
77     private static SharedPreferences pref;
78     private static Integer cachedRotation;
79
80     private static final int MAXIMIZED = 0;
81     private static final int LEFT = -1;
82     private static final int RIGHT = 1;
83
84     public static final int HIDDEN = 0;
85     public static final int TOP_APPS = 1;
86
87     // From android.app.ActivityManager.StackId
88     private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
89     private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
90
91     public static SharedPreferences getSharedPreferences(Context context) {
92         if(pref == null) pref = context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
93         return pref;
94     }
95
96     @TargetApi(Build.VERSION_CODES.M)
97     public static AlertDialog showPermissionDialog(final Context context) {
98         AlertDialog.Builder builder = new AlertDialog.Builder(context);
99         builder.setTitle(R.string.permission_dialog_title)
100                 .setMessage(R.string.permission_dialog_message)
101                 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
102                     try {
103                         context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
104                                 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
105                     } catch (ActivityNotFoundException e) {
106                         showErrorDialog(context, "SYSTEM_ALERT_WINDOW");
107                     }
108                 });
109
110         AlertDialog dialog = builder.create();
111         dialog.show();
112         dialog.setCancelable(false);
113
114         return dialog;
115     }
116
117     public static void showErrorDialog(final Context context, String appopCmd) {
118         AlertDialog.Builder builder = new AlertDialog.Builder(context);
119         builder.setTitle(R.string.error_dialog_title)
120                 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
121                 .setPositiveButton(R.string.action_ok, null);
122
123         AlertDialog dialog = builder.create();
124         dialog.show();
125     }
126
127     public static void lockDevice(Context context) {
128         ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
129         context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
130                 PackageManager.DONT_KILL_APP);
131
132         DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
133         if(mDevicePolicyManager.isAdminActive(component))
134             mDevicePolicyManager.lockNow();
135         else {
136             launchApp(context, () -> {
137                 Intent intent = new Intent(context, DummyActivity.class);
138                 intent.putExtra("device_admin", true);
139                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
140                 context.startActivity(intent, getActivityOptionsBundle(ApplicationType.APPLICATION));
141
142                 if(context instanceof Activity)
143                     ((Activity) context).overridePendingTransition(0, 0);
144             });
145         }
146     }
147
148     public static void sendAccessibilityAction(Context context, int action) {
149         ComponentName component = new ComponentName(context, PowerMenuService.class);
150         context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
151                 PackageManager.DONT_KILL_APP);
152
153         if(isAccessibilityServiceEnabled(context)) {
154             Intent intent = new Intent("com.farmerbb.taskbar.ACCESSIBILITY_ACTION");
155             intent.putExtra("action", action);
156             LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
157         } else {
158             launchApp(context, () -> {
159                 Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
160                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
161
162                 try {
163                     context.startActivity(intent, getActivityOptionsBundle(ApplicationType.APPLICATION));
164                     showToastLong(context, R.string.enable_accessibility);
165                 } catch (ActivityNotFoundException e) {
166                     showToast(context, R.string.lock_device_not_supported);
167                 }
168             });
169         }
170     }
171
172     private static boolean isAccessibilityServiceEnabled(Context context) {
173         String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
174         ComponentName component = new ComponentName(context, PowerMenuService.class);
175
176         return accessibilityServices != null
177                 && (accessibilityServices.contains(component.flattenToString())
178                 || accessibilityServices.contains(component.flattenToShortString()));
179     }
180
181     public static void showToast(Context context, int message) {
182         showToast(context, context.getString(message), Toast.LENGTH_SHORT);
183     }
184
185     public static void showToastLong(Context context, int message) {
186         showToast(context, context.getString(message), Toast.LENGTH_LONG);
187     }
188
189     public static void showToast(Context context, String message, int length) {
190         cancelToast();
191
192         ToastInterface toast;
193         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1
194                 || BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID))
195             toast = new ToastFrameworkImpl(context, message, length);
196         else
197             toast = new ToastCompatImpl(context, message, length);
198
199         toast.show();
200
201         ToastHelper.getInstance().setLastToast(toast);
202     }
203
204     public static void cancelToast() {
205         ToastInterface toast = ToastHelper.getInstance().getLastToast();
206         if(toast != null) toast.cancel();
207     }
208
209     public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
210         launchApp(context,
211                 packageName,
212                 componentName,
213                 0,
214                 null,
215                 false,
216                 false,
217                 shortcut);
218     }
219
220     public static void launchApp(final Context context,
221                                  final String packageName,
222                                  final String componentName,
223                                  final long userId, final String windowSize,
224                                  final boolean launchedFromTaskbar,
225                                  final boolean openInNewWindow) {
226         launchApp(context,
227                 packageName,
228                 componentName,
229                 userId,
230                 windowSize,
231                 launchedFromTaskbar,
232                 openInNewWindow,
233                 null);
234     }
235
236     private static void launchApp(final Context context,
237                                  final String packageName,
238                                  final String componentName,
239                                  final long userId, final String windowSize,
240                                  final boolean launchedFromTaskbar,
241                                  final boolean openInNewWindow,
242                                  final ShortcutInfo shortcut) {
243         launchApp(context, launchedFromTaskbar, () -> continueLaunchingApp(context, packageName, componentName, userId,
244                 windowSize, launchedFromTaskbar, openInNewWindow, shortcut));
245     }
246
247     public static void launchApp(Context context, Runnable runnable) {
248         launchApp(context, true, runnable);
249     }
250
251     private static void launchApp(Context context, boolean launchedFromTaskbar, Runnable runnable) {
252         SharedPreferences pref = getSharedPreferences(context);
253         FreeformHackHelper helper = FreeformHackHelper.getInstance();
254
255         boolean specialLaunch = Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1
256                 && FreeformHackHelper.getInstance().isInFreeformWorkspace()
257                 && MenuHelper.getInstance().isContextMenuOpen();
258
259         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
260                 && pref.getBoolean("freeform_hack", false)
261                 && (!helper.isInFreeformWorkspace() || specialLaunch)) {
262             new Handler().postDelayed(() -> {
263                 startFreeformHack(context, true, launchedFromTaskbar);
264
265                 new Handler().postDelayed(runnable, helper.isFreeformHackActive() ? 0 : 100);
266             }, launchedFromTaskbar ? 0 : 100);
267         } else
268             runnable.run();
269     }
270
271     @SuppressWarnings("deprecation")
272     @TargetApi(Build.VERSION_CODES.N)
273     public static void startFreeformHack(Context context, boolean checkMultiWindow, boolean launchedFromTaskbar) {
274         Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
275         freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
276
277         if(checkMultiWindow)
278             freeformHackIntent.putExtra("check_multiwindow", true);
279
280         if(launchedFromTaskbar) {
281             SharedPreferences pref = getSharedPreferences(context);
282             if(pref.getBoolean("disable_animations", false))
283                 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
284         }
285
286         if(canDrawOverlays(context))
287             launchAppLowerRight(context, freeformHackIntent);
288     }
289
290     @TargetApi(Build.VERSION_CODES.N)
291     private static void continueLaunchingApp(Context context,
292                                              String packageName,
293                                              String componentName,
294                                              long userId,
295                                              String windowSize,
296                                              boolean launchedFromTaskbar,
297                                              boolean openInNewWindow,
298                                              ShortcutInfo shortcut) {
299         SharedPreferences pref = getSharedPreferences(context);
300         Intent intent = new Intent();
301         intent.setComponent(ComponentName.unflattenFromString(componentName));
302         intent.setAction(Intent.ACTION_MAIN);
303         intent.addCategory(Intent.CATEGORY_LAUNCHER);
304         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
305         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
306
307         if(FreeformHackHelper.getInstance().isInFreeformWorkspace()
308                 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
309             intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
310
311         if(launchedFromTaskbar) {
312             if(pref.getBoolean("disable_animations", false))
313                 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
314         }
315
316         if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
317             intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
318
319             ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
320             if(activityInfo != null) {
321                 switch(activityInfo.launchMode) {
322                     case ActivityInfo.LAUNCH_SINGLE_TASK:
323                     case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
324                         intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
325                         break;
326                 }
327             }
328         }
329
330         ApplicationType type = getApplicationType(context, packageName);
331
332         if(windowSize == null)
333             windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
334
335         if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N
336                 || !pref.getBoolean("freeform_hack", false)
337                 || windowSize.equals("standard")) {
338             launchStandard(context, intent, userId, shortcut, type);
339         } else switch(windowSize) {
340             case "large":
341                 launchMode1(context, intent, userId, shortcut, type);
342                 break;
343             case "fullscreen":
344                 launchMode2(context, intent, MAXIMIZED, userId, shortcut, type);
345                 break;
346             case "half_left":
347                 launchMode2(context, intent, LEFT, userId, shortcut, type);
348                 break;
349             case "half_right":
350                 launchMode2(context, intent, RIGHT, userId, shortcut, type);
351                 break;
352             case "phone_size":
353                 launchMode3(context, intent, userId, shortcut, type);
354                 break;
355         }
356
357         if(shouldCollapse(context, true))
358             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
359         else
360             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
361     }
362     
363     private static void launchStandard(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
364         Bundle bundle = Build.VERSION.SDK_INT < Build.VERSION_CODES.N ? null : getActivityOptions(type).toBundle();
365         if(shortcut == null) {
366             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
367             if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
368                 try {
369                     context.startActivity(intent, bundle);
370                 } catch (ActivityNotFoundException e) {
371                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
372                 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
373             } else
374                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
375         } else
376             launchShortcut(context, shortcut, bundle);
377     }
378
379     @SuppressWarnings("deprecation")
380     @TargetApi(Build.VERSION_CODES.N)
381     private static void launchMode1(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
382         DisplayMetrics metrics = getRealDisplayMetrics(context);
383
384         int width1 = metrics.widthPixels / 8;
385         int width2 = metrics.widthPixels - width1;
386         int height1 = metrics.heightPixels / 8;
387         int height2 = metrics.heightPixels - height1;
388
389         Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
390                 width1,
391                 height1,
392                 width2,
393                 height2
394         )).toBundle();
395
396         if(shortcut == null) {
397             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
398             if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
399                 try {
400                     context.startActivity(intent, bundle);
401                 } catch (ActivityNotFoundException e) {
402                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
403                 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
404             } else
405                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
406         } else
407             launchShortcut(context, shortcut, bundle);
408     }
409
410     @SuppressWarnings("deprecation")
411     @TargetApi(Build.VERSION_CODES.N)
412     private static void launchMode2(Context context, Intent intent, int launchType, long userId, ShortcutInfo shortcut, ApplicationType type) {
413         DisplayMetrics metrics = getRealDisplayMetrics(context);
414         
415         int statusBarHeight = getStatusBarHeight(context);
416         String position = getTaskbarPosition(context);
417
418         boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
419         boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
420
421         int left = launchType == RIGHT && isLandscape
422                 ? metrics.widthPixels / 2
423                 : 0;
424
425         int top = launchType == RIGHT && isPortrait
426                 ? metrics.heightPixels / 2
427                 : statusBarHeight;
428
429         int right = launchType == LEFT && isLandscape
430                 ? metrics.widthPixels / 2
431                 : metrics.widthPixels;
432
433         int bottom = launchType == LEFT && isPortrait
434                 ? metrics.heightPixels / 2
435                 : metrics.heightPixels;
436
437         SharedPreferences pref = getSharedPreferences(context);
438         int iconSize;
439
440         if(isChromeOs(context) && pref.getBoolean("hide_taskbar", true))
441             iconSize = 0;
442         else
443             iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
444
445         if(position.contains("vertical_left")) {
446             if(launchType != RIGHT || isPortrait) left = left + iconSize;
447         } else if(position.contains("vertical_right")) {
448             if(launchType != LEFT || isPortrait) right = right - iconSize;
449         } else if(position.contains("bottom")) {
450             if(isLandscape || (launchType != LEFT && isPortrait))
451                 bottom = bottom - iconSize;
452         } else if(isLandscape || (launchType != RIGHT && isPortrait))
453             top = top + iconSize;
454
455         Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
456                 left,
457                 top,
458                 right,
459                 bottom
460         )).toBundle();
461
462         if(shortcut == null) {
463             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
464             if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
465                 try {
466                     context.startActivity(intent, bundle);
467                 } catch (ActivityNotFoundException e) {
468                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
469                 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
470             } else
471                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
472         } else
473             launchShortcut(context, shortcut, bundle);
474     }
475
476     @SuppressWarnings("deprecation")
477     @TargetApi(Build.VERSION_CODES.N)
478     private static void launchMode3(Context context, Intent intent, long userId, ShortcutInfo shortcut, ApplicationType type) {
479         DisplayMetrics metrics = getRealDisplayMetrics(context);
480
481         int width1 = metrics.widthPixels / 2;
482         int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
483         int height1 = metrics.heightPixels / 2;
484         int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
485
486         Bundle bundle = getActivityOptions(type).setLaunchBounds(new Rect(
487                 width1 - width2,
488                 height1 - height2,
489                 width1 + width2,
490                 height1 + height2
491         )).toBundle();
492
493         if(shortcut == null) {
494             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
495             if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
496                 try {
497                     context.startActivity(intent, bundle);
498                 } catch (ActivityNotFoundException e) {
499                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
500                 } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
501             } else
502                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
503         } else
504             launchShortcut(context, shortcut, bundle);
505     }
506
507     private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
508         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
509         LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
510
511         try {
512             launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
513         } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
514     }
515
516     @TargetApi(Build.VERSION_CODES.N_MR1)
517     private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
518         LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
519
520         if(launcherApps.hasShortcutHostPermission()) {
521             try {
522                 launcherApps.startShortcut(shortcut, null, bundle);
523             } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
524         }
525     }
526
527     public static void launchAppMaximized(Context context, Intent intent) {
528         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
529         long userId = userManager.getSerialNumberForUser(Process.myUserHandle());
530
531         launchMode2(context, intent, MAXIMIZED, userId, null, ApplicationType.CONTEXT_MENU);
532     }
533
534     @SuppressWarnings("deprecation")
535     @TargetApi(Build.VERSION_CODES.N)
536     public static void launchAppLowerRight(Context context, Intent intent) {
537         DisplayMetrics metrics = getRealDisplayMetrics(context);
538         try {
539             context.startActivity(intent, getActivityOptions(ApplicationType.FREEFORM_HACK).setLaunchBounds(new Rect(
540                     metrics.widthPixels,
541                     metrics.heightPixels,
542                     metrics.widthPixels + 1,
543                     metrics.heightPixels + 1
544             )).toBundle());
545         } catch (IllegalArgumentException | SecurityException e) { /* Gracefully fail */ }
546     }
547
548     public static void checkForUpdates(Context context) {
549         if(!BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)) {
550             if(!BuildConfig.DEBUG) {
551                 String url;
552                 try {
553                     context.getPackageManager().getPackageInfo("com.android.vending", 0);
554                     url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
555                 } catch (PackageManager.NameNotFoundException e) {
556                     url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.BASE_APPLICATION_ID;
557                 }
558
559                 Intent intent = new Intent(Intent.ACTION_VIEW);
560                 intent.setData(Uri.parse(url));
561                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
562
563                 try {
564                     context.startActivity(intent);
565                 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
566             } else
567                 showToast(context, R.string.debug_build);
568         }
569     }
570
571     public static boolean launcherIsDefault(Context context) {
572         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
573         homeIntent.addCategory(Intent.CATEGORY_HOME);
574         ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
575
576         return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
577     }
578
579     public static void setCachedRotation(int cachedRotation) {
580         U.cachedRotation = cachedRotation;
581     }
582
583     public static String getTaskbarPosition(Context context) {
584         SharedPreferences pref = getSharedPreferences(context);
585         String position = pref.getString("position", "bottom_left");
586
587         if(pref.getBoolean("anchor", false)) {
588             WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
589             int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
590
591             switch(position) {
592                 case "bottom_left":
593                     switch(rotation) {
594                         case Surface.ROTATION_0:
595                             return "bottom_left";
596                         case Surface.ROTATION_90:
597                             return "bottom_vertical_right";
598                         case Surface.ROTATION_180:
599                             return "top_right";
600                         case Surface.ROTATION_270:
601                             return "top_vertical_left";
602                     }
603                     break;
604                 case "bottom_vertical_left":
605                     switch(rotation) {
606                         case Surface.ROTATION_0:
607                             return "bottom_vertical_left";
608                         case Surface.ROTATION_90:
609                             return "bottom_right";
610                         case Surface.ROTATION_180:
611                             return "top_vertical_right";
612                         case Surface.ROTATION_270:
613                             return "top_left";
614                     }
615                     break;
616                 case "bottom_right":
617                     switch(rotation) {
618                         case Surface.ROTATION_0:
619                             return "bottom_right";
620                         case Surface.ROTATION_90:
621                             return "top_vertical_right";
622                         case Surface.ROTATION_180:
623                             return "top_left";
624                         case Surface.ROTATION_270:
625                             return "bottom_vertical_left";
626                     }
627                     break;
628                 case "bottom_vertical_right":
629                     switch(rotation) {
630                         case Surface.ROTATION_0:
631                             return "bottom_vertical_right";
632                         case Surface.ROTATION_90:
633                             return "top_right";
634                         case Surface.ROTATION_180:
635                             return "top_vertical_left";
636                         case Surface.ROTATION_270:
637                             return "bottom_left";
638                     }
639                     break;
640                 case "top_left":
641                     switch(rotation) {
642                         case Surface.ROTATION_0:
643                             return "top_left";
644                         case Surface.ROTATION_90:
645                             return "bottom_vertical_left";
646                         case Surface.ROTATION_180:
647                             return "bottom_right";
648                         case Surface.ROTATION_270:
649                             return "top_vertical_right";
650                     }
651                     break;
652                 case "top_vertical_left":
653                     switch(rotation) {
654                         case Surface.ROTATION_0:
655                             return "top_vertical_left";
656                         case Surface.ROTATION_90:
657                             return "bottom_left";
658                         case Surface.ROTATION_180:
659                             return "bottom_vertical_right";
660                         case Surface.ROTATION_270:
661                             return "top_right";
662                     }
663                     break;
664                 case "top_right":
665                     switch(rotation) {
666                         case Surface.ROTATION_0:
667                             return "top_right";
668                         case Surface.ROTATION_90:
669                             return "top_vertical_left";
670                         case Surface.ROTATION_180:
671                             return "bottom_left";
672                         case Surface.ROTATION_270:
673                             return "bottom_vertical_right";
674                     }
675                     break;
676                 case "top_vertical_right":
677                     switch(rotation) {
678                         case Surface.ROTATION_0:
679                             return "top_vertical_right";
680                         case Surface.ROTATION_90:
681                             return "top_left";
682                         case Surface.ROTATION_180:
683                             return "bottom_vertical_left";
684                         case Surface.ROTATION_270:
685                             return "bottom_right";
686                     }
687                     break;
688             }
689         }
690
691         return position;
692     }
693
694     private static int getMaxNumOfColumns(Context context) {
695         SharedPreferences pref = getSharedPreferences(context);
696         DisplayMetrics metrics = getRealDisplayMetrics(context);
697         float baseTaskbarSize = getBaseTaskbarSizeFloat(context) / metrics.density;
698         int numOfColumns = 0;
699
700         float maxScreenSize = getTaskbarPosition(context).contains("vertical")
701                 ? (metrics.heightPixels - getStatusBarHeight(context)) / metrics.density
702                 : metrics.widthPixels / metrics.density;
703
704         float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
705
706         int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
707
708         while(baseTaskbarSize + iconSize < maxScreenSize
709                 && numOfColumns < userMaxNumOfColumns) {
710             baseTaskbarSize = baseTaskbarSize + iconSize;
711             numOfColumns++;
712         }
713
714         return numOfColumns;
715     }
716
717     public static int getMaxNumOfEntries(Context context) {
718         SharedPreferences pref = getSharedPreferences(context);
719         return pref.getBoolean("disable_scrolling_list", false)
720                 ? getMaxNumOfColumns(context)
721                 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
722     }
723
724     public static int getStatusBarHeight(Context context) {
725         int statusBarHeight = 0;
726         int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
727         if(resourceId > 0)
728             statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
729
730         return statusBarHeight;
731     }
732
733     public static void refreshPinnedIcons(Context context) {
734         IconCache.getInstance(context).clearCache();
735
736         PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
737         List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
738         List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
739         PackageManager pm = context.getPackageManager();
740
741         pba.clear(context);
742
743         for(AppEntry entry : pinnedAppsList) {
744             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
745             LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
746
747             final List<UserHandle> userHandles = userManager.getUserProfiles();
748             LauncherActivityInfo appInfo = null;
749
750             for(UserHandle handle : userHandles) {
751                 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
752                 if(!list.isEmpty()) {
753                     // Google App workaround
754                     if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
755                         appInfo = list.get(0);
756                     else {
757                         boolean added = false;
758                         for(LauncherActivityInfo info : list) {
759                             if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
760                                 appInfo = info;
761                                 added = true;
762                             }
763                         }
764
765                         if(!added) appInfo = list.get(0);
766                     }
767
768                     break;
769                 }
770             }
771
772             if(appInfo != null) {
773                 AppEntry newEntry = new AppEntry(
774                         entry.getPackageName(),
775                         entry.getComponentName(),
776                         entry.getLabel(),
777                         IconCache.getInstance(context).getIcon(context, pm, appInfo),
778                         true);
779
780                 newEntry.setUserId(entry.getUserId(context));
781                 pba.addPinnedApp(context, newEntry);
782             }
783         }
784
785         for(AppEntry entry : blockedAppsList) {
786             pba.addBlockedApp(context, entry);
787         }
788     }
789
790     public static Intent getShortcutIntent(Context context) {
791         Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
792         shortcutIntent.setAction(Intent.ACTION_MAIN);
793         shortcutIntent.putExtra("is_launching_shortcut", true);
794
795         Intent intent = new Intent();
796         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
797         intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_freeform_mode));
798         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
799
800         return intent;
801     }
802
803     public static Intent getStartStopIntent(Context context) {
804         Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
805         shortcutIntent.setAction(Intent.ACTION_MAIN);
806         shortcutIntent.putExtra("is_launching_shortcut", true);
807
808         Intent intent = new Intent();
809         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
810         intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher));
811         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
812
813         return intent;
814     }
815
816     public static boolean hasFreeformSupport(Context context) {
817         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
818                 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
819                 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", -1) == 1
820                 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
821                 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", -1) == 1));
822     }
823
824     public static boolean hasPartialFreeformSupport() {
825          return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
826     }
827
828     public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
829         return isServiceRunning(context, cls.getName());
830     }
831
832     public static boolean isServiceRunning(Context context, String className) {
833         ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
834         for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
835             if(className.equals(service.service.getClassName()))
836                 return true;
837         }
838
839         return false;
840     }
841
842     public static int getBackgroundTint(Context context) {
843         SharedPreferences pref = getSharedPreferences(context);
844
845         // Import old background tint preference
846         if(pref.contains("show_background")) {
847             SharedPreferences.Editor editor = pref.edit();
848
849             if(!pref.getBoolean("show_background", true))
850                 editor.putInt("background_tint", Color.TRANSPARENT).apply();
851
852             editor.remove("show_background");
853             editor.apply();
854         }
855
856         return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
857     }
858
859     public static int getAccentColor(Context context) {
860         SharedPreferences pref = getSharedPreferences(context);
861         return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
862     }
863
864     @TargetApi(Build.VERSION_CODES.M)
865     public static boolean canDrawOverlays(Context context) {
866         return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
867     }
868
869     public static boolean isGame(Context context, String packageName) {
870         SharedPreferences pref = getSharedPreferences(context);
871         if(pref.getBoolean("launch_games_fullscreen", true)) {
872             PackageManager pm = context.getPackageManager();
873
874             try {
875                 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
876                 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
877             } catch (PackageManager.NameNotFoundException e) {
878                 return false;
879             }
880         } else
881             return false;
882     }
883
884     @TargetApi(Build.VERSION_CODES.N)
885     public static ActivityOptions getActivityOptions(ApplicationType applicationType) {
886         ActivityOptions options = ActivityOptions.makeBasic();
887         Integer stackId = null;
888
889         switch(applicationType) {
890             case APPLICATION:
891                 if(!FreeformHackHelper.getInstance().isFreeformHackActive())
892                     stackId = FULLSCREEN_WORKSPACE_STACK_ID;
893                 else
894                     stackId = FREEFORM_WORKSPACE_STACK_ID;
895                 break;
896             case GAME:
897                 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
898                 break;
899             case FREEFORM_HACK:
900                 stackId = FREEFORM_WORKSPACE_STACK_ID;
901                 break;
902             case CONTEXT_MENU:
903                 if(!FreeformHackHelper.getInstance().isFreeformHackActive()
904                         || Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1)
905                     stackId = FULLSCREEN_WORKSPACE_STACK_ID;
906                 else
907                     stackId = FREEFORM_WORKSPACE_STACK_ID;
908         }
909
910         try {
911             Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
912             method.invoke(options, stackId);
913         } catch (Exception e) { /* Gracefully fail */ }
914
915         return options;
916     }
917
918     public static Bundle getActivityOptionsBundle(ApplicationType applicationType) {
919         if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
920             return null;
921         else
922             return getActivityOptions(applicationType).toBundle();
923     }
924
925     private static ApplicationType getApplicationType(Context context, String packageName) {
926         return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
927     }
928
929     public static boolean isSystemApp(Context context) {
930         try {
931             ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
932             int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
933             return (info.flags & mask) != 0;
934         } catch (PackageManager.NameNotFoundException e) {
935             return false;
936         }
937     }
938
939     public static boolean isChromeOs(Context context) {
940         return context.getPackageManager().hasSystemFeature("org.chromium.arc");
941     }
942
943     public static boolean hasSupportLibrary(Context context) {
944         PackageManager pm = context.getPackageManager();
945         try {
946             pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
947             return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
948                     && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
949                     && isSystemApp(context);
950         } catch (PackageManager.NameNotFoundException e) {
951             return false;
952         }
953     }
954
955     public static int getBaseTaskbarSize(Context context) {
956         return Math.round(getBaseTaskbarSizeFloat(context));
957     }
958
959     private static float getBaseTaskbarSizeFloat(Context context) {
960         SharedPreferences pref = getSharedPreferences(context);
961         float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
962         boolean navbarButtonsEnabled = false;
963
964         if(pref.getBoolean("dashboard", false))
965             baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
966
967         if(pref.getBoolean("button_back", false)) {
968             navbarButtonsEnabled = true;
969             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
970         }
971
972         if(pref.getBoolean("button_home", false)) {
973             navbarButtonsEnabled = true;
974             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
975         }
976
977         if(pref.getBoolean("button_recents", false)) {
978             navbarButtonsEnabled = true;
979             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
980         }
981
982         if(navbarButtonsEnabled)
983             baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
984
985         return baseTaskbarSize;
986     }
987
988     private static void startTaskbarService(Context context, boolean fullRestart) {
989         context.startService(new Intent(context, TaskbarService.class));
990         context.startService(new Intent(context, StartMenuService.class));
991         context.startService(new Intent(context, DashboardService.class));
992         if(fullRestart) context.startService(new Intent(context, NotificationService.class));
993     }
994
995     private static void stopTaskbarService(Context context, boolean fullRestart) {
996         context.stopService(new Intent(context, TaskbarService.class));
997         context.stopService(new Intent(context, StartMenuService.class));
998         context.stopService(new Intent(context, DashboardService.class));
999         if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
1000     }
1001
1002     public static void restartTaskbar(Context context) {
1003         SharedPreferences pref = getSharedPreferences(context);
1004         if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
1005             pref.edit()
1006                     .putBoolean("is_restarting", true)
1007                     .putBoolean("skip_auto_hide_navbar", true)
1008                     .apply();
1009
1010             stopTaskbarService(context, true);
1011             startTaskbarService(context, true);
1012         } else if(isServiceRunning(context, StartMenuService.class)) {
1013             pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1014
1015             stopTaskbarService(context, false);
1016             startTaskbarService(context, false);
1017         }
1018     }
1019
1020     public static void restartNotificationService(Context context) {
1021         if(isServiceRunning(context, NotificationService.class)) {
1022             SharedPreferences pref = getSharedPreferences(context);
1023             pref.edit().putBoolean("is_restarting", true).apply();
1024
1025             Intent intent = new Intent(context, NotificationService.class);
1026             context.stopService(intent);
1027             context.startService(intent);
1028         }
1029     }
1030
1031     public static void showHideNavigationBar(Context context, boolean show) {
1032         // Show or hide the system navigation bar on Bliss-x86
1033         try {
1034             Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1035         } catch (Exception e) { /* Gracefully fail */ }
1036     }
1037
1038     public static void initPrefs(Context context) {
1039         // On smaller-screened devices, set "Grid" as the default start menu layout
1040         SharedPreferences pref = getSharedPreferences(context);
1041         if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 600
1042                 && pref.getString("start_menu_layout", "null").equals("null")) {
1043             pref.edit().putString("start_menu_layout", "grid").apply();
1044         }
1045
1046         // Enable freeform hack automatically on supported devices
1047         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
1048             if(!pref.getBoolean("freeform_hack_override", false)) {
1049                 pref.edit()
1050                         .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1051                         .putBoolean("save_window_sizes", false)
1052                         .putBoolean("freeform_hack_override", true)
1053                         .apply();
1054             } else if(!hasFreeformSupport(context)) {
1055                 pref.edit().putBoolean("freeform_hack", false).apply();
1056
1057                 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
1058             }
1059         }
1060
1061         // Customizations for Android-x86 devices (non-Bliss)
1062         if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1063                 && isSystemApp(context)
1064                 && !pref.getBoolean("android_x86_prefs", false)) {
1065             pref.edit()
1066                     .putString("recents_amount", "running_apps_only")
1067                     .putString("refresh_frequency", "0")
1068                     .putString("max_num_of_recents", "2147483647")
1069                     .putString("sort_order", "true")
1070                     .putBoolean("full_length", true)
1071                     .putBoolean("dashboard", true)
1072                     .putBoolean("android_x86_prefs", true)
1073                     .apply();
1074         }
1075     }
1076
1077     public static DisplayMetrics getRealDisplayMetrics(Context context) {
1078         DisplayMetrics metrics = new DisplayMetrics();
1079         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1080         Display disp = wm.getDefaultDisplay();
1081
1082         SharedPreferences pref = getSharedPreferences(context);
1083         if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1084             disp.getRealMetrics(metrics);
1085         else
1086             disp.getMetrics(metrics);
1087
1088         return metrics;
1089     }
1090
1091     static void pinAppShortcut(Context context) {
1092         Intent intent = getShortcutIntent(context);
1093         intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1094         intent.putExtra("duplicate", false);
1095
1096         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1097         homeIntent.addCategory(Intent.CATEGORY_HOME);
1098         ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1099
1100         intent.setPackage(defaultLauncher.activityInfo.packageName);
1101         context.sendBroadcast(intent);
1102
1103         showToast(context, R.string.shortcut_created);
1104     }
1105
1106     public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1107         SharedPreferences pref = getSharedPreferences(context);
1108         if(pref.getBoolean("hide_taskbar", true)) {
1109             if(isChromeOs(context))
1110                 return true;
1111             else {
1112                 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1113                 if(pendingAppLaunch)
1114                     return !helper.isFreeformHackActive();
1115                 else
1116                     return !helper.isInFreeformWorkspace();
1117             }
1118         } else
1119             return false;
1120     }
1121
1122     @SuppressWarnings("unchecked")
1123     public static <T extends View> T findViewById(Activity target, int id) {
1124         return (T) target.findViewById(id);
1125     }
1126
1127     @SuppressWarnings("unchecked")
1128     public static <T extends View> T findViewById(View target, int id) {
1129         return (T) target.findViewById(id);
1130     }
1131 }