OSDN Git Service

Fix launching windows with "Save window sizes" preference disabled
[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.ActivityOptions;
20 import android.app.AlertDialog;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.ActivityNotFoundException;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.SharedPreferences;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ResolveInfo;
31 import android.content.res.Configuration;
32 import android.graphics.Rect;
33 import android.hardware.display.DisplayManager;
34 import android.net.Uri;
35 import android.os.Build;
36 import android.os.Handler;
37 import android.provider.Settings;
38 import android.support.v4.content.LocalBroadcastManager;
39 import android.util.DisplayMetrics;
40 import android.view.Display;
41 import android.view.Surface;
42 import android.view.WindowManager;
43 import android.widget.Toast;
44
45 import com.farmerbb.taskbar.BuildConfig;
46 import com.farmerbb.taskbar.R;
47 import com.farmerbb.taskbar.activity.DummyActivity;
48 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
49 import com.farmerbb.taskbar.receiver.LockDeviceReceiver;
50
51 import java.util.ArrayList;
52 import java.util.List;
53
54 public class U {
55
56     private U() {}
57
58     private static SharedPreferences pref;
59     private static Toast toast;
60     private static Integer cachedRotation;
61
62     private static final int FULLSCREEN = 0;
63     private static final int LEFT = -1;
64     private static final int RIGHT = 1;
65
66     public static final int HIDDEN = 0;
67     public static final int TOP_APPS = 1;
68
69     public static SharedPreferences getSharedPreferences(Context context) {
70         if(pref == null) pref = context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
71         return pref;
72     }
73
74     @TargetApi(Build.VERSION_CODES.M)
75     public static void showPermissionDialog(final Context context) {
76         AlertDialog.Builder builder = new AlertDialog.Builder(context);
77         builder.setTitle(R.string.permission_dialog_title)
78                 .setMessage(R.string.permission_dialog_message)
79                 .setPositiveButton(R.string.action_grant_permission, new DialogInterface.OnClickListener() {
80                     @Override
81                     public void onClick(DialogInterface dialog, int which) {
82                         try {
83                             context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
84                         } catch (ActivityNotFoundException e) {
85                             showErrorDialog(context, "SYSTEM_ALERT_WINDOW");
86                         }
87                     }
88                 });
89
90         AlertDialog dialog = builder.create();
91         dialog.show();
92         dialog.setCancelable(false);
93     }
94
95     public static void showErrorDialog(final Context context, String appopCmd) {
96         AlertDialog.Builder builder = new AlertDialog.Builder(context);
97         builder.setTitle(R.string.error_dialog_title)
98                 .setMessage(context.getString(R.string.error_dialog_message, BuildConfig.APPLICATION_ID, appopCmd))
99                 .setPositiveButton(R.string.action_ok, null);
100
101         AlertDialog dialog = builder.create();
102         dialog.show();
103     }
104
105     public static void lockDevice(Context context) {
106         ComponentName component = new ComponentName(BuildConfig.APPLICATION_ID, LockDeviceReceiver.class.getName());
107         context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
108                 PackageManager.DONT_KILL_APP);
109
110         DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
111         if(mDevicePolicyManager.isAdminActive(component))
112             mDevicePolicyManager.lockNow();
113         else {
114             Intent intent = new Intent(context, DummyActivity.class);
115             intent.putExtra("device_admin", true);
116             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
117             context.startActivity(intent);
118         }
119     }
120
121     public static void showToast(Context context, int message) {
122         showToast(context, context.getString(message), Toast.LENGTH_SHORT);
123     }
124
125     public static void showToastLong(Context context, int message) {
126         showToast(context, context.getString(message), Toast.LENGTH_LONG);
127     }
128
129     public static void showToast(Context context, String message, int length) {
130         if(toast != null) toast.cancel();
131
132         toast = Toast.makeText(context, message, length);
133         toast.show();
134     }
135
136     public static void launchApp(final Context context,
137                                  final String packageName,
138                                  final String componentName,
139                                  final String windowSize,
140                                  final boolean launchedFromTaskbar,
141                                  final boolean padStatusBar,
142                                  final boolean openInNewWindow) {
143         boolean shouldDelay = false;
144
145         SharedPreferences pref = getSharedPreferences(context);
146         boolean openInFullscreen = pref.getBoolean("open_in_fullscreen", true);
147         boolean freeformHackActive = openInNewWindow
148                 ? FreeformHackHelper.getInstance().isInFreeformWorkspace()
149                 : (openInFullscreen
150                     ? FreeformHackHelper.getInstance().isInFreeformWorkspace()
151                     : FreeformHackHelper.getInstance().isFreeformHackActive());
152
153         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
154                 && pref.getBoolean("freeform_hack", false)
155                 && !freeformHackActive) {
156             shouldDelay = true;
157
158             new Handler().postDelayed(new Runnable() {
159                 @Override
160                 public void run() {
161                     startFreeformHack(context, launchedFromTaskbar);
162
163                     new Handler().postDelayed(new Runnable() {
164                         @Override
165                         public void run() {
166                             continueLaunchingApp(context, packageName, componentName, windowSize,
167                                     launchedFromTaskbar, padStatusBar, openInNewWindow);
168                         }
169                     }, 100);
170                 }
171             }, launchedFromTaskbar ? 0 : 100);
172         }
173
174         if(!FreeformHackHelper.getInstance().isFreeformHackActive()) {
175             if(!shouldDelay)
176                 continueLaunchingApp(context, packageName, componentName, windowSize,
177                         launchedFromTaskbar, padStatusBar, openInNewWindow);
178         } else if(FreeformHackHelper.getInstance().isInFreeformWorkspace() || !openInFullscreen)
179             continueLaunchingApp(context, packageName, componentName, windowSize,
180                     launchedFromTaskbar, padStatusBar, openInNewWindow);
181     }
182
183     @SuppressWarnings("deprecation")
184     @TargetApi(Build.VERSION_CODES.N)
185     private static void startFreeformHack(Context context, boolean launchedFromTaskbar) {
186         Intent freeformHackIntent = new Intent(context, InvisibleActivityFreeform.class);
187         freeformHackIntent.putExtra("check_multiwindow", true);
188         freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
189
190         if(launchedFromTaskbar) {
191             SharedPreferences pref = getSharedPreferences(context);
192             if(pref.getBoolean("disable_animations", false))
193                 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
194         }
195
196         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
197         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
198         try {
199             context.startActivity(freeformHackIntent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
200                     display.getWidth(),
201                     display.getHeight(),
202                     display.getWidth() + 1,
203                     display.getHeight() + 1
204             )).toBundle());
205         } catch (IllegalArgumentException e) { /* Gracefully fail */ }
206     }
207
208     @TargetApi(Build.VERSION_CODES.N)
209     private static void continueLaunchingApp(Context context,
210                                              String packageName,
211                                              String componentName,
212                                              String windowSize,
213                                              boolean launchedFromTaskbar,
214                                              boolean padStatusBar,
215                                              boolean openInNewWindow) {
216         SharedPreferences pref = getSharedPreferences(context);
217         Intent intent = new Intent();
218         intent.setComponent(ComponentName.unflattenFromString(componentName));
219         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
220         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
221         
222         if(launchedFromTaskbar) {
223             if(pref.getBoolean("disable_animations", false))
224                 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
225         }
226
227         if(openInNewWindow) {
228             intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
229
230             switch(intent.resolveActivityInfo(context.getPackageManager(), 0).launchMode) {
231                 case ActivityInfo.LAUNCH_SINGLE_TASK:
232                 case ActivityInfo.LAUNCH_SINGLE_INSTANCE:
233                     intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
234                     break;
235             }
236         }
237
238         if(windowSize == null) {
239             if(pref.getBoolean("save_window_sizes", true))
240                 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
241             else
242                 windowSize = pref.getString("window_size", "standard");
243         }
244
245         if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !pref.getBoolean("freeform_hack", false)) {
246             try {
247                 context.startActivity(intent);
248             } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
249         } else switch(windowSize) {
250             case "standard":
251                 if(FreeformHackHelper.getInstance().isInFreeformWorkspace())
252                     try {
253                         context.startActivity(intent);
254                     } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
255                 else
256                     launchMode1(context, intent, 1);
257                 break;
258             case "large":
259                 launchMode1(context, intent, 2);
260                 break;
261             case "fullscreen":
262                 launchMode2(context, intent, padStatusBar, FULLSCREEN);
263                 break;
264             case "half_left":
265                 launchMode2(context, intent, padStatusBar, LEFT);
266                 break;
267             case "half_right":
268                 launchMode2(context, intent, padStatusBar, RIGHT);
269                 break;
270             case "phone_size":
271                 launchMode3(context, intent);
272                 break;
273         }
274
275         if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
276             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
277         else
278             LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
279     }
280     
281     @SuppressWarnings("deprecation")
282     @TargetApi(Build.VERSION_CODES.N)
283     private static void launchMode1(Context context, Intent intent, int factor) {
284         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
285         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
286
287         int width1 = display.getWidth() / (4 * factor);
288         int width2 = display.getWidth() - width1;
289         int height1 = display.getHeight() / (4 * factor);
290         int height2 = display.getHeight() - height1;
291
292         try {
293             context.startActivity(intent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
294                     width1,
295                     height1,
296                     width2,
297                     height2
298             )).toBundle());
299         } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
300     }
301
302     @SuppressWarnings("deprecation")
303     @TargetApi(Build.VERSION_CODES.N)
304     private static void launchMode2(Context context, Intent intent, boolean padStatusBar, int launchType) {
305         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
306         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
307
308         int statusBarHeight = getStatusBarHeight(context);
309
310         String position = getTaskbarPosition(context);
311         boolean overridePad = position.equals("top_left") || position.equals("top_right");
312
313         boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
314         boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
315
316         int left = launchType == RIGHT && isLandscape
317                 ? display.getWidth() / 2
318                 : 0;
319
320         int top;
321         if(launchType == RIGHT && isPortrait) {
322             top = (display.getHeight() / 2)
323                     + (!padStatusBar && overridePad ? statusBarHeight / 2 : 0)
324                     + (!padStatusBar && !overridePad ? statusBarHeight / 2 : 0);
325         } else {
326             top = padStatusBar || overridePad ? statusBarHeight : 0;
327         }
328
329         int right = launchType == LEFT && isLandscape
330                 ? display.getWidth() / 2
331                 : display.getWidth();
332
333         int bottom;
334         if(launchType == LEFT && isPortrait) {
335             bottom = display.getHeight() / 2
336                     + (!padStatusBar && overridePad ? statusBarHeight / 2 : 0)
337                     - (!padStatusBar && !overridePad ? statusBarHeight / 2 : 0);
338         } else {
339             bottom = display.getHeight()
340                     + ((!padStatusBar && overridePad) || (!padStatusBar && launchType == RIGHT && isPortrait)
341                     ? statusBarHeight
342                     : 0);
343         }
344
345         int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
346
347         if(position.contains("vertical_left")) {
348             if(launchType != RIGHT || isPortrait) left = left + iconSize;
349         } else if(position.contains("vertical_right")) {
350             if(launchType != LEFT || isPortrait) right = right - iconSize;
351         } else if(position.contains("bottom")) {
352             if(isLandscape || (launchType != LEFT && isPortrait))
353                 bottom = bottom - iconSize;
354         } else if(isLandscape || (launchType != RIGHT && isPortrait))
355             top = top + iconSize;
356
357         try {
358             context.startActivity(intent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
359                     left,
360                     top,
361                     right,
362                     bottom
363             )).toBundle());
364         } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
365     }
366
367     @SuppressWarnings("deprecation")
368     @TargetApi(Build.VERSION_CODES.N)
369     private static void launchMode3(Context context, Intent intent) {
370         DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
371         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
372
373         int width1 = display.getWidth() / 2;
374         int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
375         int height1 = display.getHeight() / 2;
376         int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
377
378         try {
379             context.startActivity(intent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
380                     width1 - width2,
381                     height1 - height2,
382                     width1 + width2,
383                     height1 + height2
384             )).toBundle());
385         } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
386     }
387
388     public static void checkForUpdates(Context context) {
389         String url;
390         try {
391             context.getPackageManager().getPackageInfo("com.android.vending", 0);
392             url = "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID;
393         } catch (PackageManager.NameNotFoundException e) {
394             url = "https://f-droid.org/repository/browse/?fdid=" + BuildConfig.BASE_APPLICATION_ID;
395         }
396
397         Intent intent = new Intent(Intent.ACTION_VIEW);
398         intent.setData(Uri.parse(url));
399         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
400
401         try {
402             context.startActivity(intent);
403         } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
404     }
405
406     public static boolean launcherIsDefault(Context context) {
407         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
408         homeIntent.addCategory(Intent.CATEGORY_HOME);
409         ResolveInfo defaultLauncher = context.getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
410
411         return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
412     }
413
414     public static void setCachedRotation(int cachedRotation) {
415         U.cachedRotation = cachedRotation;
416     }
417
418     public static String getTaskbarPosition(Context context) {
419         SharedPreferences pref = getSharedPreferences(context);
420         String position = pref.getString("position", "bottom_left");
421
422         if(pref.getBoolean("anchor", false)) {
423             WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
424             int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
425
426             switch(position) {
427                 case "bottom_left":
428                     switch(rotation) {
429                         case Surface.ROTATION_0:
430                             return "bottom_left";
431                         case Surface.ROTATION_90:
432                             return "bottom_vertical_right";
433                         case Surface.ROTATION_180:
434                             return "top_right";
435                         case Surface.ROTATION_270:
436                             return "top_vertical_left";
437                     }
438                     break;
439                 case "bottom_vertical_left":
440                     switch(rotation) {
441                         case Surface.ROTATION_0:
442                             return "bottom_vertical_left";
443                         case Surface.ROTATION_90:
444                             return "bottom_right";
445                         case Surface.ROTATION_180:
446                             return "top_vertical_right";
447                         case Surface.ROTATION_270:
448                             return "top_left";
449                     }
450                     break;
451                 case "bottom_right":
452                     switch(rotation) {
453                         case Surface.ROTATION_0:
454                             return "bottom_right";
455                         case Surface.ROTATION_90:
456                             return "top_vertical_right";
457                         case Surface.ROTATION_180:
458                             return "top_left";
459                         case Surface.ROTATION_270:
460                             return "bottom_vertical_left";
461                     }
462                     break;
463                 case "bottom_vertical_right":
464                     switch(rotation) {
465                         case Surface.ROTATION_0:
466                             return "bottom_vertical_right";
467                         case Surface.ROTATION_90:
468                             return "top_right";
469                         case Surface.ROTATION_180:
470                             return "top_vertical_left";
471                         case Surface.ROTATION_270:
472                             return "bottom_left";
473                     }
474                     break;
475                 case "top_left":
476                     switch(rotation) {
477                         case Surface.ROTATION_0:
478                             return "top_left";
479                         case Surface.ROTATION_90:
480                             return "bottom_vertical_left";
481                         case Surface.ROTATION_180:
482                             return "bottom_right";
483                         case Surface.ROTATION_270:
484                             return "top_vertical_right";
485                     }
486                     break;
487                 case "top_vertical_left":
488                     switch(rotation) {
489                         case Surface.ROTATION_0:
490                             return "top_vertical_left";
491                         case Surface.ROTATION_90:
492                             return "bottom_left";
493                         case Surface.ROTATION_180:
494                             return "bottom_vertical_right";
495                         case Surface.ROTATION_270:
496                             return "top_right";
497                     }
498                     break;
499                 case "top_right":
500                     switch(rotation) {
501                         case Surface.ROTATION_0:
502                             return "top_right";
503                         case Surface.ROTATION_90:
504                             return "top_vertical_left";
505                         case Surface.ROTATION_180:
506                             return "bottom_left";
507                         case Surface.ROTATION_270:
508                             return "bottom_vertical_right";
509                     }
510                     break;
511                 case "top_vertical_right":
512                     switch(rotation) {
513                         case Surface.ROTATION_0:
514                             return "top_vertical_right";
515                         case Surface.ROTATION_90:
516                             return "top_left";
517                         case Surface.ROTATION_180:
518                             return "bottom_vertical_left";
519                         case Surface.ROTATION_270:
520                             return "bottom_right";
521                     }
522                     break;
523             }
524         }
525
526         return position;
527     }
528
529     public static int getMaxNumOfColumns(Context context) {
530         // The base Taskbar size without any recent apps added.
531         // Someday this might be automatically calculated, but today is not that day.
532         float baseTaskbarSize = 92;
533         int numOfColumns = 0;
534
535         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
536         float maxScreenSize = getTaskbarPosition(context).contains("vertical")
537                 ? (metrics.heightPixels - getStatusBarHeight(context)) / metrics.density
538                 : metrics.widthPixels / metrics.density;
539
540         float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
541
542         SharedPreferences pref = getSharedPreferences(context);
543         int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
544
545         while(baseTaskbarSize + iconSize < maxScreenSize
546                 && numOfColumns < userMaxNumOfColumns) {
547             baseTaskbarSize = baseTaskbarSize + iconSize;
548             numOfColumns++;
549         }
550
551         return numOfColumns;
552     }
553
554     private static int getStatusBarHeight(Context context) {
555         int statusBarHeight = 0;
556         int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
557         if(resourceId > 0)
558             statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
559
560         return statusBarHeight;
561     }
562
563     public static void refreshPinnedIcons(Context context) {
564         IconCache.getInstance(context).clearCache();
565
566         PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
567         List<AppEntry> pinnedAppsList = new ArrayList<>(pba.getPinnedApps());
568         List<AppEntry> blockedAppsList = new ArrayList<>(pba.getBlockedApps());
569         PackageManager pm = context.getPackageManager();
570
571         pba.clear(context);
572
573         for(AppEntry entry : pinnedAppsList) {
574             Intent throwaway = new Intent();
575             throwaway.setComponent(ComponentName.unflattenFromString(entry.getComponentName()));
576
577             AppEntry newEntry = new AppEntry(
578                     entry.getPackageName(),
579                     entry.getComponentName(),
580                     entry.getLabel(),
581                     IconCache.getInstance(context).getIcon(context, pm, throwaway.resolveActivityInfo(pm, 0)),
582                     true);
583
584             pba.addPinnedApp(context, newEntry);
585         }
586
587         for(AppEntry entry : blockedAppsList) {
588             pba.addBlockedApp(context, entry);
589         }
590     }
591 }