OSDN Git Service

Sort apps by first active time when showing running apps only
[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.graphics.drawable.BitmapDrawable;
41 import android.net.Uri;
42 import android.os.Build;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.Process;
46 import android.os.UserHandle;
47 import android.os.UserManager;
48 import android.provider.Settings;
49 import android.support.v4.content.LocalBroadcastManager;
50 import android.util.DisplayMetrics;
51 import android.view.Display;
52 import android.view.Surface;
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);
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);
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     private 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.O
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))
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         BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_freeform_mode);
796
797         Intent intent = new Intent();
798         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
799         if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
800         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
801
802         return intent;
803     }
804
805     public static Intent getStartStopIntent(Context context) {
806         Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
807         shortcutIntent.setAction(Intent.ACTION_MAIN);
808         shortcutIntent.putExtra("is_launching_shortcut", true);
809
810         BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_launcher);
811
812         Intent intent = new Intent();
813         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
814         if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
815         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
816
817         return intent;
818     }
819
820     public static boolean hasFreeformSupport(Context context) {
821         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
822                 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
823                 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", -1) == 1
824                 || (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
825                 && Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", -1) == 1));
826     }
827
828     public static boolean hasPartialFreeformSupport() {
829          return Build.MANUFACTURER.equalsIgnoreCase("Samsung");
830     }
831
832     public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
833         return isServiceRunning(context, cls.getName());
834     }
835
836     public static boolean isServiceRunning(Context context, String className) {
837         ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
838         for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
839             if(className.equals(service.service.getClassName()))
840                 return true;
841         }
842
843         return false;
844     }
845
846     public static int getBackgroundTint(Context context) {
847         SharedPreferences pref = getSharedPreferences(context);
848
849         // Import old background tint preference
850         if(pref.contains("show_background")) {
851             SharedPreferences.Editor editor = pref.edit();
852
853             if(!pref.getBoolean("show_background", true))
854                 editor.putInt("background_tint", Color.TRANSPARENT).apply();
855
856             editor.remove("show_background");
857             editor.apply();
858         }
859
860         return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
861     }
862
863     public static int getAccentColor(Context context) {
864         SharedPreferences pref = getSharedPreferences(context);
865         return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
866     }
867
868     @TargetApi(Build.VERSION_CODES.M)
869     public static boolean canDrawOverlays(Context context) {
870         return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
871     }
872
873     public static boolean isGame(Context context, String packageName) {
874         SharedPreferences pref = getSharedPreferences(context);
875         if(pref.getBoolean("launch_games_fullscreen", true)) {
876             PackageManager pm = context.getPackageManager();
877
878             try {
879                 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
880                 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
881             } catch (PackageManager.NameNotFoundException e) {
882                 return false;
883             }
884         } else
885             return false;
886     }
887
888     @TargetApi(Build.VERSION_CODES.M)
889     public static ActivityOptions getActivityOptions(ApplicationType applicationType) {
890         ActivityOptions options = ActivityOptions.makeBasic();
891         Integer stackId = null;
892
893         switch(applicationType) {
894             case APPLICATION:
895                 if(!FreeformHackHelper.getInstance().isFreeformHackActive())
896                     stackId = FULLSCREEN_WORKSPACE_STACK_ID;
897                 break;
898             case GAME:
899                 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
900                 break;
901             case FREEFORM_HACK:
902                 stackId = FREEFORM_WORKSPACE_STACK_ID;
903                 break;
904             case CONTEXT_MENU:
905                 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
906                     stackId = FULLSCREEN_WORKSPACE_STACK_ID;
907         }
908
909         if(stackId != null) {
910             try {
911                 Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
912                 method.invoke(options, stackId);
913             } catch (Exception e) { /* Gracefully fail */ }
914         }
915
916         return options;
917     }
918
919     private static ApplicationType getApplicationType(Context context, String packageName) {
920         return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
921     }
922
923     public static boolean isSystemApp(Context context) {
924         try {
925             ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
926             int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
927             return (info.flags & mask) != 0;
928         } catch (PackageManager.NameNotFoundException e) {
929             return false;
930         }
931     }
932
933     public static boolean isChromeOs(Context context) {
934         return context.getPackageManager().hasSystemFeature("org.chromium.arc");
935     }
936
937     public static boolean hasSupportLibrary(Context context) {
938         PackageManager pm = context.getPackageManager();
939         try {
940             pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
941             return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
942                     && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
943                     && isSystemApp(context);
944         } catch (PackageManager.NameNotFoundException e) {
945             return false;
946         }
947     }
948
949     public static int getBaseTaskbarSize(Context context) {
950         return Math.round(getBaseTaskbarSizeFloat(context));
951     }
952
953     private static float getBaseTaskbarSizeFloat(Context context) {
954         SharedPreferences pref = getSharedPreferences(context);
955         float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
956         boolean navbarButtonsEnabled = false;
957
958         if(pref.getBoolean("dashboard", false))
959             baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
960
961         if(pref.getBoolean("button_back", false)) {
962             navbarButtonsEnabled = true;
963             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
964         }
965
966         if(pref.getBoolean("button_home", false)) {
967             navbarButtonsEnabled = true;
968             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
969         }
970
971         if(pref.getBoolean("button_recents", false)) {
972             navbarButtonsEnabled = true;
973             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
974         }
975
976         if(navbarButtonsEnabled)
977             baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
978
979         return baseTaskbarSize;
980     }
981
982     private static void startTaskbarService(Context context, boolean fullRestart) {
983         context.startService(new Intent(context, TaskbarService.class));
984         context.startService(new Intent(context, StartMenuService.class));
985         context.startService(new Intent(context, DashboardService.class));
986         if(fullRestart) context.startService(new Intent(context, NotificationService.class));
987     }
988
989     private static void stopTaskbarService(Context context, boolean fullRestart) {
990         context.stopService(new Intent(context, TaskbarService.class));
991         context.stopService(new Intent(context, StartMenuService.class));
992         context.stopService(new Intent(context, DashboardService.class));
993         if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
994     }
995
996     public static void restartTaskbar(Context context) {
997         SharedPreferences pref = getSharedPreferences(context);
998         if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
999             pref.edit()
1000                     .putBoolean("is_restarting", true)
1001                     .putBoolean("skip_auto_hide_navbar", true)
1002                     .apply();
1003
1004             stopTaskbarService(context, true);
1005             startTaskbarService(context, true);
1006         } else if(isServiceRunning(context, StartMenuService.class)) {
1007             pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1008
1009             stopTaskbarService(context, false);
1010             startTaskbarService(context, false);
1011         }
1012     }
1013
1014     public static void restartNotificationService(Context context) {
1015         if(isServiceRunning(context, NotificationService.class)) {
1016             SharedPreferences pref = getSharedPreferences(context);
1017             pref.edit().putBoolean("is_restarting", true).apply();
1018
1019             Intent intent = new Intent(context, NotificationService.class);
1020             context.stopService(intent);
1021             context.startService(intent);
1022         }
1023     }
1024
1025     public static void showHideNavigationBar(Context context, boolean show) {
1026         // Show or hide the system navigation bar on Bliss-x86
1027         try {
1028             Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1029         } catch (Exception e) { /* Gracefully fail */ }
1030     }
1031
1032     public static void initPrefs(Context context) {
1033         // On smaller-screened devices, set "Grid" as the default start menu layout
1034         SharedPreferences pref = getSharedPreferences(context);
1035         if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 600
1036                 && pref.getString("start_menu_layout", "null").equals("null")) {
1037             pref.edit().putString("start_menu_layout", "grid").apply();
1038         }
1039
1040         // Enable freeform hack automatically on supported devices
1041         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
1042             if(!pref.getBoolean("freeform_hack_override", false)) {
1043                 pref.edit()
1044                         .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1045                         .putBoolean("save_window_sizes", false)
1046                         .putBoolean("freeform_hack_override", true)
1047                         .apply();
1048             } else if(!hasFreeformSupport(context)) {
1049                 pref.edit().putBoolean("freeform_hack", false).apply();
1050
1051                 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
1052             }
1053         }
1054
1055         // Customizations for Android-x86 devices (non-Bliss)
1056         if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1057                 && isSystemApp(context)
1058                 && !pref.getBoolean("android_x86_prefs", false)) {
1059             pref.edit()
1060                     .putString("recents_amount", "running_apps_only")
1061                     .putString("refresh_frequency", "0")
1062                     .putString("max_num_of_recents", "2147483647")
1063                     .putString("sort_order", "true")
1064                     .putBoolean("full_length", true)
1065                     .putBoolean("dashboard", true)
1066                     .putBoolean("android_x86_prefs", true)
1067                     .apply();
1068         }
1069     }
1070
1071     public static DisplayMetrics getRealDisplayMetrics(Context context) {
1072         DisplayMetrics metrics = new DisplayMetrics();
1073         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1074         Display disp = wm.getDefaultDisplay();
1075
1076         SharedPreferences pref = getSharedPreferences(context);
1077         if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1078             disp.getRealMetrics(metrics);
1079         else
1080             disp.getMetrics(metrics);
1081
1082         return metrics;
1083     }
1084
1085     public static int getOverlayType() {
1086         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
1087                 ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
1088                 : WindowManager.LayoutParams.TYPE_PHONE;
1089     }
1090
1091     public static boolean shouldCollapse(Context context) {
1092         SharedPreferences pref = getSharedPreferences(context);
1093         if(pref.getBoolean("hide_taskbar", true)) {
1094             if(isChromeOs(context))
1095                 return true;
1096             else {
1097                 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1098                 return !helper.isFreeformHackActive();
1099             }
1100         } else
1101             return false;
1102     }
1103 }