OSDN Git Service

3be54c8b2c60b042831c637ba23d80c976b0d79d
[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);
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     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.M)
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                 break;
894             case GAME:
895                 stackId = FULLSCREEN_WORKSPACE_STACK_ID;
896                 break;
897             case FREEFORM_HACK:
898                 stackId = FREEFORM_WORKSPACE_STACK_ID;
899                 break;
900             case CONTEXT_MENU:
901                 if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1)
902                     stackId = FULLSCREEN_WORKSPACE_STACK_ID;
903         }
904
905         if(stackId != null) {
906             try {
907                 Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
908                 method.invoke(options, stackId);
909             } catch (Exception e) { /* Gracefully fail */ }
910         }
911
912         return options;
913     }
914
915     private static ApplicationType getApplicationType(Context context, String packageName) {
916         return isGame(context, packageName) ? ApplicationType.GAME : ApplicationType.APPLICATION;
917     }
918
919     public static boolean isSystemApp(Context context) {
920         try {
921             ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
922             int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
923             return (info.flags & mask) != 0;
924         } catch (PackageManager.NameNotFoundException e) {
925             return false;
926         }
927     }
928
929     public static boolean isChromeOs(Context context) {
930         return context.getPackageManager().hasSystemFeature("org.chromium.arc");
931     }
932
933     public static boolean hasSupportLibrary(Context context) {
934         PackageManager pm = context.getPackageManager();
935         try {
936             pm.getPackageInfo(BuildConfig.SUPPORT_APPLICATION_ID, 0);
937             return pm.checkSignatures(BuildConfig.SUPPORT_APPLICATION_ID, BuildConfig.APPLICATION_ID) == PackageManager.SIGNATURE_MATCH
938                     && BuildConfig.APPLICATION_ID.equals(BuildConfig.BASE_APPLICATION_ID)
939                     && isSystemApp(context);
940         } catch (PackageManager.NameNotFoundException e) {
941             return false;
942         }
943     }
944
945     public static int getBaseTaskbarSize(Context context) {
946         return Math.round(getBaseTaskbarSizeFloat(context));
947     }
948
949     private static float getBaseTaskbarSizeFloat(Context context) {
950         SharedPreferences pref = getSharedPreferences(context);
951         float baseTaskbarSize = context.getResources().getDimension(R.dimen.base_taskbar_size);
952         boolean navbarButtonsEnabled = false;
953
954         if(pref.getBoolean("dashboard", false))
955             baseTaskbarSize += context.getResources().getDimension(R.dimen.dashboard_button_size);
956
957         if(pref.getBoolean("button_back", false)) {
958             navbarButtonsEnabled = true;
959             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
960         }
961
962         if(pref.getBoolean("button_home", false)) {
963             navbarButtonsEnabled = true;
964             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
965         }
966
967         if(pref.getBoolean("button_recents", false)) {
968             navbarButtonsEnabled = true;
969             baseTaskbarSize += context.getResources().getDimension(R.dimen.icon_size);
970         }
971
972         if(navbarButtonsEnabled)
973             baseTaskbarSize += context.getResources().getDimension(R.dimen.navbar_buttons_margin);
974
975         return baseTaskbarSize;
976     }
977
978     private static void startTaskbarService(Context context, boolean fullRestart) {
979         context.startService(new Intent(context, TaskbarService.class));
980         context.startService(new Intent(context, StartMenuService.class));
981         context.startService(new Intent(context, DashboardService.class));
982         if(fullRestart) context.startService(new Intent(context, NotificationService.class));
983     }
984
985     private static void stopTaskbarService(Context context, boolean fullRestart) {
986         context.stopService(new Intent(context, TaskbarService.class));
987         context.stopService(new Intent(context, StartMenuService.class));
988         context.stopService(new Intent(context, DashboardService.class));
989         if(fullRestart) context.stopService(new Intent(context, NotificationService.class));
990     }
991
992     public static void restartTaskbar(Context context) {
993         SharedPreferences pref = getSharedPreferences(context);
994         if(pref.getBoolean("taskbar_active", false) && !pref.getBoolean("is_hidden", false)) {
995             pref.edit()
996                     .putBoolean("is_restarting", true)
997                     .putBoolean("skip_auto_hide_navbar", true)
998                     .apply();
999
1000             stopTaskbarService(context, true);
1001             startTaskbarService(context, true);
1002         } else if(isServiceRunning(context, StartMenuService.class)) {
1003             pref.edit().putBoolean("skip_auto_hide_navbar", true).apply();
1004
1005             stopTaskbarService(context, false);
1006             startTaskbarService(context, false);
1007         }
1008     }
1009
1010     public static void restartNotificationService(Context context) {
1011         if(isServiceRunning(context, NotificationService.class)) {
1012             SharedPreferences pref = getSharedPreferences(context);
1013             pref.edit().putBoolean("is_restarting", true).apply();
1014
1015             Intent intent = new Intent(context, NotificationService.class);
1016             context.stopService(intent);
1017             context.startService(intent);
1018         }
1019     }
1020
1021     public static void showHideNavigationBar(Context context, boolean show) {
1022         // Show or hide the system navigation bar on Bliss-x86
1023         try {
1024             Settings.System.putInt(context.getContentResolver(), "navigation_bar_show", show ? 1 : 0);
1025         } catch (Exception e) { /* Gracefully fail */ }
1026     }
1027
1028     public static void initPrefs(Context context) {
1029         // On smaller-screened devices, set "Grid" as the default start menu layout
1030         SharedPreferences pref = getSharedPreferences(context);
1031         if(context.getApplicationContext().getResources().getConfiguration().smallestScreenWidthDp < 600
1032                 && pref.getString("start_menu_layout", "null").equals("null")) {
1033             pref.edit().putString("start_menu_layout", "grid").apply();
1034         }
1035
1036         // Enable freeform hack automatically on supported devices
1037         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
1038             if(!pref.getBoolean("freeform_hack_override", false)) {
1039                 pref.edit()
1040                         .putBoolean("freeform_hack", hasFreeformSupport(context) && !hasPartialFreeformSupport())
1041                         .putBoolean("save_window_sizes", false)
1042                         .putBoolean("freeform_hack_override", true)
1043                         .apply();
1044             } else if(!hasFreeformSupport(context)) {
1045                 pref.edit().putBoolean("freeform_hack", false).apply();
1046
1047                 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
1048             }
1049         }
1050
1051         // Customizations for Android-x86 devices (non-Bliss)
1052         if(BuildConfig.APPLICATION_ID.equals(BuildConfig.ANDROIDX86_APPLICATION_ID)
1053                 && isSystemApp(context)
1054                 && !pref.getBoolean("android_x86_prefs", false)) {
1055             pref.edit()
1056                     .putString("recents_amount", "running_apps_only")
1057                     .putString("refresh_frequency", "0")
1058                     .putString("max_num_of_recents", "2147483647")
1059                     .putString("sort_order", "true")
1060                     .putBoolean("full_length", true)
1061                     .putBoolean("dashboard", true)
1062                     .putBoolean("android_x86_prefs", true)
1063                     .apply();
1064         }
1065     }
1066
1067     public static DisplayMetrics getRealDisplayMetrics(Context context) {
1068         DisplayMetrics metrics = new DisplayMetrics();
1069         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1070         Display disp = wm.getDefaultDisplay();
1071
1072         SharedPreferences pref = getSharedPreferences(context);
1073         if(isChromeOs(context) && !pref.getBoolean("chrome_os_context_menu_fix", true))
1074             disp.getRealMetrics(metrics);
1075         else
1076             disp.getMetrics(metrics);
1077
1078         return metrics;
1079     }
1080
1081     static void pinAppShortcut(Context context) {
1082         Intent intent = getShortcutIntent(context);
1083         intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
1084         intent.putExtra("duplicate", false);
1085
1086         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
1087         homeIntent.addCategory(Intent.CATEGORY_HOME);
1088         ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
1089
1090         intent.setPackage(defaultLauncher.activityInfo.packageName);
1091         context.sendBroadcast(intent);
1092
1093         showToast(context, R.string.shortcut_created);
1094     }
1095
1096     public static boolean shouldCollapse(Context context, boolean pendingAppLaunch) {
1097         SharedPreferences pref = getSharedPreferences(context);
1098         if(pref.getBoolean("hide_taskbar", true)) {
1099             if(isChromeOs(context))
1100                 return true;
1101             else {
1102                 FreeformHackHelper helper = FreeformHackHelper.getInstance();
1103                 if(pendingAppLaunch)
1104                     return !helper.isFreeformHackActive();
1105                 else
1106                     return !helper.isInFreeformWorkspace();
1107             }
1108         } else
1109             return false;
1110     }
1111
1112     @SuppressWarnings("unchecked")
1113     public static <T extends View> T findViewById(Activity target, int id) {
1114         return (T) target.findViewById(id);
1115     }
1116
1117     @SuppressWarnings("unchecked")
1118     public static <T extends View> T findViewById(View target, int id) {
1119         return (T) target.findViewById(id);
1120     }
1121 }