OSDN Git Service

Recheck for shortcut host permission when launching app shortcuts
[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.ActivityManager;
20 import android.app.ActivityOptions;
21 import android.app.AlertDialog;
22 import android.app.Service;
23 import android.app.admin.DevicePolicyManager;
24 import android.content.ActivityNotFoundException;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.SharedPreferences;
29 import android.content.pm.ActivityInfo;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.LauncherActivityInfo;
32 import android.content.pm.LauncherApps;
33 import android.content.pm.PackageManager;
34 import android.content.pm.ResolveInfo;
35 import android.content.pm.ShortcutInfo;
36 import android.content.res.Configuration;
37 import android.graphics.Rect;
38 import android.graphics.drawable.BitmapDrawable;
39 import android.hardware.display.DisplayManager;
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.WindowManager;
53 import android.widget.Toast;
54
55 import com.farmerbb.taskbar.BuildConfig;
56 import com.farmerbb.taskbar.R;
57 import com.farmerbb.taskbar.activity.DummyActivity;
58 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
59 import com.farmerbb.taskbar.activity.ShortcutActivity;
60 import com.farmerbb.taskbar.activity.StartTaskbarActivity;
61 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
62 import com.farmerbb.taskbar.service.PowerMenuService;
63
64 import java.lang.reflect.Method;
65 import java.util.ArrayList;
66 import java.util.List;
67
68 import moe.banana.support.ToastCompat;
69
70 public class U {
71
72     private U() {}
73
74     private static SharedPreferences pref;
75     private static Integer cachedRotation;
76
77     private static final int MAXIMIZED = 0;
78     private static final int LEFT = -1;
79     private static final int RIGHT = 1;
80
81     public static final int HIDDEN = 0;
82     public static final int TOP_APPS = 1;
83
84     // From android.app.ActivityManager.StackId
85     private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
86     private static final int FREEFORM_WORKSPACE_STACK_ID = 2;
87
88     public static SharedPreferences getSharedPreferences(Context context) {
89         if(pref == null) pref = context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
90         return pref;
91     }
92
93     @TargetApi(Build.VERSION_CODES.M)
94     public static void showPermissionDialog(final Context context) {
95         AlertDialog.Builder builder = new AlertDialog.Builder(context);
96         builder.setTitle(R.string.permission_dialog_title)
97                 .setMessage(R.string.permission_dialog_message)
98                 .setPositiveButton(R.string.action_grant_permission, (dialog, which) -> {
99                     try {
100                         context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
101                                 Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
102                     } catch (ActivityNotFoundException e) {
103                         showErrorDialog(context, "SYSTEM_ALERT_WINDOW");
104                     }
105                 });
106
107         AlertDialog dialog = builder.create();
108         dialog.show();
109         dialog.setCancelable(false);
110     }
111
112     public static void showErrorDialog(final Context context, String appopCmd) {
113         AlertDialog.Builder builder = new AlertDialog.Builder(context);
114         builder.setTitle(R.string.error_dialog_title)
115                 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
116                 .setPositiveButton(R.string.action_ok, null);
117
118         AlertDialog dialog = builder.create();
119         dialog.show();
120     }
121
122     public static void lockDevice(Context context) {
123         ComponentName component = new ComponentName(context, LockDeviceReceiver.class);
124         context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
125                 PackageManager.DONT_KILL_APP);
126
127         DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
128         if(mDevicePolicyManager.isAdminActive(component))
129             mDevicePolicyManager.lockNow();
130         else {
131             Intent intent = new Intent(context, DummyActivity.class);
132             intent.putExtra("device_admin", true);
133             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
134             context.startActivity(intent);
135         }
136     }
137
138     public static void showPowerMenu(Context context) {
139         ComponentName component = new ComponentName(context, PowerMenuService.class);
140         context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
141                 PackageManager.DONT_KILL_APP);
142
143         if(isAccessibilityServiceEnabled(context))
144             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_POWER_MENU"));
145         else {
146             Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
147             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
148
149             try {
150                 context.startActivity(intent);
151                 showToastLong(context, R.string.enable_accessibility);
152             } catch (ActivityNotFoundException e) {
153                 showToast(context, R.string.lock_device_not_supported);
154             }
155         }
156     }
157
158     private static boolean isAccessibilityServiceEnabled(Context context) {
159         String accessibilityServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
160         ComponentName component = new ComponentName(context, PowerMenuService.class);
161
162         return accessibilityServices != null
163                 && (accessibilityServices.contains(component.flattenToString())
164                 || accessibilityServices.contains(component.flattenToShortString()));
165     }
166
167     public static void showToast(Context context, int message) {
168         showToast(context, context.getString(message), Toast.LENGTH_SHORT);
169     }
170
171     public static void showToastLong(Context context, int message) {
172         showToast(context, context.getString(message), Toast.LENGTH_LONG);
173     }
174
175     public static void showToast(Context context, String message, int length) {
176         cancelToast();
177
178         ToastCompat toast = ToastCompat.makeText(context.getApplicationContext(), message, length);
179         toast.show();
180
181         ToastHelper.getInstance().setLastToast(toast);
182     }
183
184     public static void cancelToast() {
185         ToastCompat toast = ToastHelper.getInstance().getLastToast();
186         if(toast != null) toast.cancel();
187     }
188
189     public static void startShortcut(Context context, String packageName, String componentName, ShortcutInfo shortcut) {
190         launchApp(context,
191                 packageName,
192                 componentName,
193                 0,
194                 null,
195                 false,
196                 false,
197                 shortcut);
198     }
199
200     public static void launchApp(final Context context,
201                                  final String packageName,
202                                  final String componentName,
203                                  final long userId, final String windowSize,
204                                  final boolean launchedFromTaskbar,
205                                  final boolean openInNewWindow) {
206         launchApp(context,
207                 packageName,
208                 componentName,
209                 userId,
210                 windowSize,
211                 launchedFromTaskbar,
212                 openInNewWindow,
213                 null);
214     }
215
216     private static void launchApp(final Context context,
217                                  final String packageName,
218                                  final String componentName,
219                                  final long userId, final String windowSize,
220                                  final boolean launchedFromTaskbar,
221                                  final boolean openInNewWindow,
222                                  final ShortcutInfo shortcut) {
223         boolean shouldDelay = false;
224
225         SharedPreferences pref = getSharedPreferences(context);
226         FreeformHackHelper helper = FreeformHackHelper.getInstance();
227         boolean freeformHackActive = openInNewWindow
228                 ? helper.isInFreeformWorkspace()
229                 : helper.isFreeformHackActive();
230
231         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
232                 && pref.getBoolean("freeform_hack", false)
233                 && !freeformHackActive) {
234             shouldDelay = true;
235
236             new Handler().postDelayed(() -> {
237                 startFreeformHack(context, true, launchedFromTaskbar);
238
239                 new Handler().postDelayed(() -> continueLaunchingApp(context, packageName, componentName, userId,
240                         windowSize, launchedFromTaskbar, openInNewWindow, shortcut), 100);
241             }, launchedFromTaskbar ? 0 : 100);
242         }
243
244         if(!helper.isFreeformHackActive()) {
245             if(!shouldDelay)
246                 continueLaunchingApp(context, packageName, componentName, userId,
247                         windowSize, launchedFromTaskbar, openInNewWindow, shortcut);
248         } else
249             continueLaunchingApp(context, packageName, componentName, userId,
250                     windowSize, launchedFromTaskbar, openInNewWindow, shortcut);
251     }
252
253     @SuppressWarnings("deprecation")
254     @TargetApi(Build.VERSION_CODES.N)
255     public static void startFreeformHack(Context context, boolean checkMultiWindow, boolean launchedFromTaskbar) {
256         Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
257         freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
258
259         if(checkMultiWindow)
260             freeformHackIntent.putExtra("check_multiwindow", true);
261
262         if(launchedFromTaskbar) {
263             SharedPreferences pref = getSharedPreferences(context);
264             if(pref.getBoolean("disable_animations", false))
265                 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
266         }
267
268         launchAppLowerRight(context, freeformHackIntent);
269     }
270
271     @TargetApi(Build.VERSION_CODES.N)
272     private static void continueLaunchingApp(Context context,
273                                              String packageName,
274                                              String componentName,
275                                              long userId,
276                                              String windowSize,
277                                              boolean launchedFromTaskbar,
278                                              boolean openInNewWindow,
279                                              ShortcutInfo shortcut) {
280         SharedPreferences pref = getSharedPreferences(context);
281         Intent intent = new Intent();
282         intent.setComponent(ComponentName.unflattenFromString(componentName));
283         intent.setAction(Intent.ACTION_MAIN);
284         intent.addCategory(Intent.CATEGORY_LAUNCHER);
285         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
286         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
287
288         if(FreeformHackHelper.getInstance().isInFreeformWorkspace())
289             intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
290
291         if(launchedFromTaskbar) {
292             if(pref.getBoolean("disable_animations", false))
293                 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
294         }
295
296         if(openInNewWindow || pref.getBoolean("force_new_window", false)) {
297             intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
298
299             ActivityInfo activityInfo = intent.resolveActivityInfo(context.getPackageManager(), 0);
300             if(activityInfo != null) {
301                 switch(activityInfo.launchMode) {
302                     case ActivityInfo.LAUNCH_SINGLE_TASK:
303                     case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
304                         intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
305                         break;
306                 }
307             }
308         }
309
310         if(windowSize == null) {
311             if(pref.getBoolean("save_window_sizes", true))
312                 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
313             else
314                 windowSize = pref.getString("window_size", "standard");
315         }
316
317         if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !pref.getBoolean("freeform_hack", false)) {
318             if(shortcut == null) {
319                 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
320                 if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
321                     try {
322                         context.startActivity(intent, null);
323                     } catch (ActivityNotFoundException e) {
324                         launchAndroidForWork(context, intent.getComponent(), null, userId);
325                     } catch (IllegalArgumentException e) { /* Gracefully fail */ }
326                 } else
327                     launchAndroidForWork(context, intent.getComponent(), null, userId);
328             } else
329                 launchShortcut(context, shortcut, null);
330         } else switch(windowSize) {
331             case "standard":
332                 if(FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
333                     Bundle bundle = getActivityOptions(isGame(context, packageName)).toBundle();
334                     if(shortcut == null) {
335                         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
336                         if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
337                             try {
338                                 context.startActivity(intent, bundle);
339                             } catch (ActivityNotFoundException e) {
340                                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
341                             } catch (IllegalArgumentException e) { /* Gracefully fail */ }
342                         } else
343                             launchAndroidForWork(context, intent.getComponent(), bundle, userId);
344                     } else
345                         launchShortcut(context, shortcut, bundle);
346                 } else
347                     launchMode1(context, intent, 1, userId, shortcut, isGame(context, packageName));
348                 break;
349             case "large":
350                 launchMode1(context, intent, 2, userId, shortcut, isGame(context, packageName));
351                 break;
352             case "fullscreen":
353                 launchMode2(context, intent, MAXIMIZED, userId, shortcut, isGame(context, packageName));
354                 break;
355             case "half_left":
356                 launchMode2(context, intent, LEFT, userId, shortcut, isGame(context, packageName));
357                 break;
358             case "half_right":
359                 launchMode2(context, intent, RIGHT, userId, shortcut, isGame(context, packageName));
360                 break;
361             case "phone_size":
362                 launchMode3(context, intent, userId, shortcut, isGame(context, packageName));
363                 break;
364         }
365
366         if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
367             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
368         else
369             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
370     }
371     
372     @SuppressWarnings("deprecation")
373     @TargetApi(Build.VERSION_CODES.N)
374     private static void launchMode1(Context context, Intent intent, int factor, long userId, ShortcutInfo shortcut, Boolean isGame) {
375         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
376         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
377
378         int width1 = display.getWidth() / (4 * factor);
379         int width2 = display.getWidth() - width1;
380         int height1 = display.getHeight() / (4 * factor);
381         int height2 = display.getHeight() - height1;
382
383         Bundle bundle = getActivityOptions(isGame).setLaunchBounds(new Rect(
384                 width1,
385                 height1,
386                 width2,
387                 height2
388         )).toBundle();
389
390         if(shortcut == null) {
391             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
392             if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
393                 try {
394                     context.startActivity(intent, bundle);
395                 } catch (ActivityNotFoundException e) {
396                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
397                 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
398             } else
399                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
400         } else
401             launchShortcut(context, shortcut, bundle);
402     }
403
404     @SuppressWarnings("deprecation")
405     @TargetApi(Build.VERSION_CODES.N)
406     private static void launchMode2(Context context, Intent intent, int launchType, long userId, ShortcutInfo shortcut, Boolean isGame) {
407         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
408         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
409
410         int statusBarHeight = getStatusBarHeight(context);
411         String position = getTaskbarPosition(context);
412
413         boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
414         boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
415
416         int left = launchType == RIGHT && isLandscape
417                 ? display.getWidth() / 2
418                 : 0;
419
420         int top = launchType == RIGHT && isPortrait
421                 ? display.getHeight() / 2
422                 : statusBarHeight;
423
424         int right = launchType == LEFT && isLandscape
425                 ? display.getWidth() / 2
426                 : display.getWidth();
427
428         int bottom = launchType == LEFT && isPortrait
429                 ? display.getHeight() / 2
430                 : display.getHeight();
431
432         int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
433
434         if(position.contains("vertical_left")) {
435             if(launchType != RIGHT || isPortrait) left = left + iconSize;
436         } else if(position.contains("vertical_right")) {
437             if(launchType != LEFT || isPortrait) right = right - iconSize;
438         } else if(position.contains("bottom")) {
439             if(isLandscape || (launchType != LEFT && isPortrait))
440                 bottom = bottom - iconSize;
441         } else if(isLandscape || (launchType != RIGHT && isPortrait))
442             top = top + iconSize;
443
444         Bundle bundle = getActivityOptions(isGame).setLaunchBounds(new Rect(
445                 left,
446                 top,
447                 right,
448                 bottom
449         )).toBundle();
450
451         if(shortcut == null) {
452             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
453             if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
454                 try {
455                     context.startActivity(intent, bundle);
456                 } catch (ActivityNotFoundException e) {
457                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
458                 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
459             } else
460                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
461         } else
462             launchShortcut(context, shortcut, bundle);
463     }
464
465     @SuppressWarnings("deprecation")
466     @TargetApi(Build.VERSION_CODES.N)
467     private static void launchMode3(Context context, Intent intent, long userId, ShortcutInfo shortcut, Boolean isGame) {
468         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
469         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
470
471         int width1 = display.getWidth() / 2;
472         int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
473         int height1 = display.getHeight() / 2;
474         int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
475
476         Bundle bundle = getActivityOptions(isGame).setLaunchBounds(new Rect(
477                 width1 - width2,
478                 height1 - height2,
479                 width1 + width2,
480                 height1 + height2
481         )).toBundle();
482
483         if(shortcut == null) {
484             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
485             if(userId == userManager.getSerialNumberForUser(Process.myUserHandle())) {
486                 try {
487                     context.startActivity(intent, bundle);
488                 } catch (ActivityNotFoundException e) {
489                     launchAndroidForWork(context, intent.getComponent(), bundle, userId);
490                 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
491             } else
492                 launchAndroidForWork(context, intent.getComponent(), bundle, userId);
493         } else
494             launchShortcut(context, shortcut, bundle);
495     }
496
497     private static void launchAndroidForWork(Context context, ComponentName componentName, Bundle bundle, long userId) {
498         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
499         LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
500
501         try {
502             launcherApps.startMainActivity(componentName, userManager.getUserForSerialNumber(userId), null, bundle);
503         } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
504     }
505
506     @TargetApi(Build.VERSION_CODES.N_MR1)
507     private static void launchShortcut(Context context, ShortcutInfo shortcut, Bundle bundle) {
508         LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
509
510         if(launcherApps.hasShortcutHostPermission()) {
511             try {
512                 launcherApps.startShortcut(shortcut, null, bundle);
513             } catch (ActivityNotFoundException | NullPointerException e) { /* Gracefully fail */ }
514         }
515     }
516
517     public static void launchAppMaximized(Context context, Intent intent) {
518         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
519         long userId = userManager.getSerialNumberForUser(Process.myUserHandle());
520
521         launchMode2(context, intent, MAXIMIZED, userId, null, null);
522     }
523
524     @SuppressWarnings("deprecation")
525     @TargetApi(Build.VERSION_CODES.N)
526     public static void launchAppLowerRight(Context context, Intent intent) {
527         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
528         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
529         try {
530             context.startActivity(intent, getActivityOptions(false).setLaunchBounds(new Rect(
531                     display.getWidth(),
532                     display.getHeight(),
533                     display.getWidth() + 1,
534                     display.getHeight() + 1
535             )).toBundle());
536         } catch (IllegalArgumentException e) { /* Gracefully fail */ }
537     }
538
539     public static void checkForUpdates(Context context) {
540         if(!BuildConfig.DEBUG) {
541             String url;
542             try {
543                 context.getPackageManager().getPackageInfo("com.android.vending", 0);
544                 url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
545             } catch (PackageManager.NameNotFoundException e) {
546                 url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.BASE_APPLICATION_ID;
547             }
548
549             Intent intent = new Intent(Intent.ACTION_VIEW);
550             intent.setData(Uri.parse(url));
551             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
552
553             try {
554                 context.startActivity(intent);
555             } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
556         } else
557             showToast(context, R.string.debug_build);
558     }
559
560     public static boolean launcherIsDefault(Context context) {
561         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
562         homeIntent.addCategory(Intent.CATEGORY_HOME);
563         ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
564
565         return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
566     }
567
568     public static void setCachedRotation(int cachedRotation) {
569         U.cachedRotation = cachedRotation;
570     }
571
572     public static String getTaskbarPosition(Context context) {
573         SharedPreferences pref = getSharedPreferences(context);
574         String position = pref.getString("position", "bottom_left");
575
576         if(pref.getBoolean("anchor", false)) {
577             WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
578             int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
579
580             switch(position) {
581                 case "bottom_left":
582                     switch(rotation) {
583                         case Surface.ROTATION_0:
584                             return "bottom_left";
585                         case Surface.ROTATION_90:
586                             return "bottom_vertical_right";
587                         case Surface.ROTATION_180:
588                             return "top_right";
589                         case Surface.ROTATION_270:
590                             return "top_vertical_left";
591                     }
592                     break;
593                 case "bottom_vertical_left":
594                     switch(rotation) {
595                         case Surface.ROTATION_0:
596                             return "bottom_vertical_left";
597                         case Surface.ROTATION_90:
598                             return "bottom_right";
599                         case Surface.ROTATION_180:
600                             return "top_vertical_right";
601                         case Surface.ROTATION_270:
602                             return "top_left";
603                     }
604                     break;
605                 case "bottom_right":
606                     switch(rotation) {
607                         case Surface.ROTATION_0:
608                             return "bottom_right";
609                         case Surface.ROTATION_90:
610                             return "top_vertical_right";
611                         case Surface.ROTATION_180:
612                             return "top_left";
613                         case Surface.ROTATION_270:
614                             return "bottom_vertical_left";
615                     }
616                     break;
617                 case "bottom_vertical_right":
618                     switch(rotation) {
619                         case Surface.ROTATION_0:
620                             return "bottom_vertical_right";
621                         case Surface.ROTATION_90:
622                             return "top_right";
623                         case Surface.ROTATION_180:
624                             return "top_vertical_left";
625                         case Surface.ROTATION_270:
626                             return "bottom_left";
627                     }
628                     break;
629                 case "top_left":
630                     switch(rotation) {
631                         case Surface.ROTATION_0:
632                             return "top_left";
633                         case Surface.ROTATION_90:
634                             return "bottom_vertical_left";
635                         case Surface.ROTATION_180:
636                             return "bottom_right";
637                         case Surface.ROTATION_270:
638                             return "top_vertical_right";
639                     }
640                     break;
641                 case "top_vertical_left":
642                     switch(rotation) {
643                         case Surface.ROTATION_0:
644                             return "top_vertical_left";
645                         case Surface.ROTATION_90:
646                             return "bottom_left";
647                         case Surface.ROTATION_180:
648                             return "bottom_vertical_right";
649                         case Surface.ROTATION_270:
650                             return "top_right";
651                     }
652                     break;
653                 case "top_right":
654                     switch(rotation) {
655                         case Surface.ROTATION_0:
656                             return "top_right";
657                         case Surface.ROTATION_90:
658                             return "top_vertical_left";
659                         case Surface.ROTATION_180:
660                             return "bottom_left";
661                         case Surface.ROTATION_270:
662                             return "bottom_vertical_right";
663                     }
664                     break;
665                 case "top_vertical_right":
666                     switch(rotation) {
667                         case Surface.ROTATION_0:
668                             return "top_vertical_right";
669                         case Surface.ROTATION_90:
670                             return "top_left";
671                         case Surface.ROTATION_180:
672                             return "bottom_vertical_left";
673                         case Surface.ROTATION_270:
674                             return "bottom_right";
675                     }
676                     break;
677             }
678         }
679
680         return position;
681     }
682
683     private static int getMaxNumOfColumns(Context context) {
684         SharedPreferences pref = getSharedPreferences(context);
685         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
686         float baseTaskbarSize = context.getResources().getDimension(pref.getBoolean("dashboard", false) ? R.dimen.base_taskbar_size_dashboard : R.dimen.base_taskbar_size) / metrics.density;
687         int numOfColumns = 0;
688
689         float maxScreenSize = getTaskbarPosition(context).contains("vertical")
690                 ? (metrics.heightPixels - getStatusBarHeight(context)) / metrics.density
691                 : metrics.widthPixels / metrics.density;
692
693         float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
694
695         int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
696
697         while(baseTaskbarSize + iconSize < maxScreenSize
698                 && numOfColumns < userMaxNumOfColumns) {
699             baseTaskbarSize = baseTaskbarSize + iconSize;
700             numOfColumns++;
701         }
702
703         return numOfColumns;
704     }
705
706     public static int getMaxNumOfEntries(Context context) {
707         SharedPreferences pref = getSharedPreferences(context);
708         return pref.getBoolean("disable_scrolling_list", false)
709                 ? getMaxNumOfColumns(context)
710                 : Integer.valueOf(pref.getString("max_num_of_recents", "10"));
711     }
712
713     public static int getStatusBarHeight(Context context) {
714         int statusBarHeight = 0;
715         int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
716         if(resourceId > 0)
717             statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
718
719         return statusBarHeight;
720     }
721
722     public static void refreshPinnedIcons(Context context) {
723         IconCache.getInstance(context).clearCache();
724
725         PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
726         List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
727         List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
728         PackageManager pm = context.getPackageManager();
729
730         pba.clear(context);
731
732         for(AppEntry entry : pinnedAppsList) {
733             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
734             LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
735
736             final List<UserHandle> userHandles = userManager.getUserProfiles();
737             LauncherActivityInfo appInfo = null;
738
739             for(UserHandle handle : userHandles) {
740                 List<LauncherActivityInfo> list = launcherApps.getActivityList(entry.getPackageName(), handle);
741                 if(!list.isEmpty()) {
742                     // Google App workaround
743                     if(!entry.getPackageName().equals("com.google.android.googlequicksearchbox"))
744                         appInfo = list.get(0);
745                     else {
746                         boolean added = false;
747                         for(LauncherActivityInfo info : list) {
748                             if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
749                                 appInfo = info;
750                                 added = true;
751                             }
752                         }
753
754                         if(!added) appInfo = list.get(0);
755                     }
756
757                     break;
758                 }
759             }
760
761             if(appInfo != null) {
762                 AppEntry newEntry = new AppEntry(
763                         entry.getPackageName(),
764                         entry.getComponentName(),
765                         entry.getLabel(),
766                         IconCache.getInstance(context).getIcon(context, pm, appInfo),
767                         true);
768
769                 newEntry.setUserId(entry.getUserId(context));
770                 pba.addPinnedApp(context, newEntry);
771             }
772         }
773
774         for(AppEntry entry : blockedAppsList) {
775             pba.addBlockedApp(context, entry);
776         }
777     }
778
779     public static Intent getShortcutIntent(Context context) {
780         Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
781         shortcutIntent.setAction(Intent.ACTION_MAIN);
782         shortcutIntent.putExtra("is_launching_shortcut", true);
783
784         BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_freeform_mode);
785
786         Intent intent = new Intent();
787         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
788         if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
789         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.pref_header_freeform));
790
791         return intent;
792     }
793
794     public static Intent getStartStopIntent(Context context) {
795         Intent shortcutIntent = new Intent(context, StartTaskbarActivity.class);
796         shortcutIntent.setAction(Intent.ACTION_MAIN);
797         shortcutIntent.putExtra("is_launching_shortcut", true);
798
799         BitmapDrawable drawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_launcher);
800
801         Intent intent = new Intent();
802         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
803         if(drawable != null) intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawable.getBitmap());
804         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.start_taskbar));
805
806         return intent;
807     }
808
809     public static boolean hasFreeformSupport(Context context) {
810         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
811                 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
812                 || Settings.Global.getInt(context.getContentResolver(), "enable_freeform_support", -1) == 1
813                 || Settings.Global.getInt(context.getContentResolver(), "force_resizable_activities", -1) == 1);
814     }
815
816     public static boolean isServiceRunning(Context context, Class<? extends Service> cls) {
817         return isServiceRunning(context, cls.getName());
818     }
819
820     public static boolean isServiceRunning(Context context, String className) {
821         ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
822         for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
823             if(className.equals(service.service.getClassName()))
824                 return true;
825         }
826
827         return false;
828     }
829
830     public static int getBackgroundTint(Context context) {
831         SharedPreferences pref = getSharedPreferences(context);
832
833         // Import old background tint preference
834         if(pref.contains("show_background")) {
835             SharedPreferences.Editor editor = pref.edit();
836
837             if(!pref.getBoolean("show_background", true))
838                 editor.putInt("background_tint", 0).apply();
839
840             editor.remove("show_background");
841             editor.apply();
842         }
843
844         return pref.getInt("background_tint", context.getResources().getInteger(R.integer.translucent_gray));
845     }
846
847     public static int getAccentColor(Context context) {
848         SharedPreferences pref = getSharedPreferences(context);
849         return pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white));
850     }
851
852     @TargetApi(Build.VERSION_CODES.M)
853     public static boolean canDrawOverlays(Context context) {
854         return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
855     }
856
857     public static boolean isGame(Context context, String packageName) {
858         SharedPreferences pref = getSharedPreferences(context);
859         if(pref.getBoolean("launch_games_fullscreen", true)) {
860             PackageManager pm = context.getPackageManager();
861
862             try {
863                 ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
864                 return (info.flags & ApplicationInfo.FLAG_IS_GAME) != 0 || (info.metaData != null && info.metaData.getBoolean("isGame", false));
865             } catch (PackageManager.NameNotFoundException e) {
866                 return false;
867             }
868         } else
869             return false;
870     }
871
872     @TargetApi(Build.VERSION_CODES.M)
873     private static ActivityOptions getActivityOptions(Boolean isGame) {
874         ActivityOptions options = ActivityOptions.makeBasic();
875
876         if(isGame != null) {
877             try {
878                 Method method = ActivityOptions.class.getMethod("setLaunchStackId", int.class);
879                 method.invoke(options, isGame ? FULLSCREEN_WORKSPACE_STACK_ID : FREEFORM_WORKSPACE_STACK_ID);
880             } catch (Exception e) { /* Gracefully fail */ }
881         }
882
883         return options;
884     }
885 }