OSDN Git Service

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