1 /* Copyright 2016 Braden Farmer
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.farmerbb.taskbar.util;
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;
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;
51 import java.util.ArrayList;
52 import java.util.List;
58 private static SharedPreferences pref;
59 private static Toast toast;
60 private static Integer cachedRotation;
62 private static final int FULLSCREEN = 0;
63 private static final int LEFT = -1;
64 private static final int RIGHT = 1;
66 public static final int HIDDEN = 0;
67 public static final int TOP_APPS = 1;
69 public static SharedPreferences getSharedPreferences(Context context) {
70 if(pref == null) pref = context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", Context.MODE_PRIVATE);
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() {
81 public void onClick(DialogInterface dialog, int which) {
83 context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
84 } catch (ActivityNotFoundException e) {
85 showErrorDialog(context, "SYSTEM_ALERT_WINDOW");
90 AlertDialog dialog = builder.create();
92 dialog.setCancelable(false);
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);
101 AlertDialog dialog = builder.create();
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);
110 DevicePolicyManager mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
111 if(mDevicePolicyManager.isAdminActive(component))
112 mDevicePolicyManager.lockNow();
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);
121 public static void showToast(Context context, int message) {
122 showToast(context, context.getString(message), Toast.LENGTH_SHORT);
125 public static void showToastLong(Context context, int message) {
126 showToast(context, context.getString(message), Toast.LENGTH_LONG);
129 public static void showToast(Context context, String message, int length) {
130 if(toast != null) toast.cancel();
132 toast = Toast.makeText(context, message, length);
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;
145 SharedPreferences pref = getSharedPreferences(context);
146 boolean openInFullscreen = pref.getBoolean("open_in_fullscreen", true);
147 boolean freeformHackActive = openInNewWindow
148 ? FreeformHackHelper.getInstance().isInFreeformWorkspace()
150 ? FreeformHackHelper.getInstance().isInFreeformWorkspace()
151 : FreeformHackHelper.getInstance().isFreeformHackActive());
153 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
154 && pref.getBoolean("freeform_hack", false)
155 && !freeformHackActive) {
158 new Handler().postDelayed(new Runnable() {
161 startFreeformHack(context, launchedFromTaskbar);
163 new Handler().postDelayed(new Runnable() {
166 continueLaunchingApp(context, packageName, componentName, windowSize,
167 launchedFromTaskbar, padStatusBar, openInNewWindow);
171 }, launchedFromTaskbar ? 0 : 100);
174 if(!FreeformHackHelper.getInstance().isFreeformHackActive()) {
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);
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);
190 if(launchedFromTaskbar) {
191 SharedPreferences pref = getSharedPreferences(context);
192 if(pref.getBoolean("disable_animations", false))
193 freeformHackIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
196 DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
197 Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
199 context.startActivity(freeformHackIntent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
202 display.getWidth() + 1,
203 display.getHeight() + 1
205 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
208 @TargetApi(Build.VERSION_CODES.N)
209 private static void continueLaunchingApp(Context context,
211 String componentName,
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);
222 if(launchedFromTaskbar) {
223 if(pref.getBoolean("disable_animations", false))
224 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
227 if(openInNewWindow) {
228 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
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);
238 if(windowSize == null) {
239 if(pref.getBoolean("save_window_sizes", true))
240 windowSize = SavedWindowSizes.getInstance(context).getWindowSize(context, packageName);
242 windowSize = pref.getString("window_size", "standard");
245 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !pref.getBoolean("freeform_hack", false)) {
247 context.startActivity(intent);
248 } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
249 } else switch(windowSize) {
251 if(FreeformHackHelper.getInstance().isInFreeformWorkspace())
253 context.startActivity(intent);
254 } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
256 launchMode1(context, intent, 1);
259 launchMode1(context, intent, 2);
262 launchMode2(context, intent, padStatusBar, FULLSCREEN);
265 launchMode2(context, intent, padStatusBar, LEFT);
268 launchMode2(context, intent, padStatusBar, RIGHT);
271 launchMode3(context, intent);
275 if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
276 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
278 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
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);
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;
293 context.startActivity(intent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
299 } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
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);
308 int statusBarHeight = getStatusBarHeight(context);
310 String position = getTaskbarPosition(context);
311 boolean overridePad = position.equals("top_left") || position.equals("top_right");
313 boolean isPortrait = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
314 boolean isLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
316 int left = launchType == RIGHT && isLandscape
317 ? display.getWidth() / 2
321 if(launchType == RIGHT && isPortrait) {
322 top = (display.getHeight() / 2)
323 + (!padStatusBar && overridePad ? statusBarHeight / 2 : 0)
324 + (!padStatusBar && !overridePad ? statusBarHeight / 2 : 0);
326 top = padStatusBar || overridePad ? statusBarHeight : 0;
329 int right = launchType == LEFT && isLandscape
330 ? display.getWidth() / 2
331 : display.getWidth();
334 if(launchType == LEFT && isPortrait) {
335 bottom = display.getHeight() / 2
336 + (!padStatusBar && overridePad ? statusBarHeight / 2 : 0)
337 - (!padStatusBar && !overridePad ? statusBarHeight / 2 : 0);
339 bottom = display.getHeight()
340 + ((!padStatusBar && overridePad) || (!padStatusBar && launchType == RIGHT && isPortrait)
345 int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
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;
358 context.startActivity(intent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
364 } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
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);
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;
379 context.startActivity(intent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
385 } catch (ActivityNotFoundException | IllegalArgumentException e) { /* Gracefully fail */ }
388 public static void checkForUpdates(Context context) {
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;
397 Intent intent = new Intent(Intent.ACTION_VIEW);
398 intent.setData(Uri.parse(url));
399 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
402 context.startActivity(intent);
403 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
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);
411 return defaultLauncher.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID);
414 public static void setCachedRotation(int cachedRotation) {
415 U.cachedRotation = cachedRotation;
418 public static String getTaskbarPosition(Context context) {
419 SharedPreferences pref = getSharedPreferences(context);
420 String position = pref.getString("position", "bottom_left");
422 if(pref.getBoolean("anchor", false)) {
423 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
424 int rotation = cachedRotation != null ? cachedRotation : windowManager.getDefaultDisplay().getRotation();
429 case Surface.ROTATION_0:
430 return "bottom_left";
431 case Surface.ROTATION_90:
432 return "bottom_vertical_right";
433 case Surface.ROTATION_180:
435 case Surface.ROTATION_270:
436 return "top_vertical_left";
439 case "bottom_vertical_left":
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:
453 case Surface.ROTATION_0:
454 return "bottom_right";
455 case Surface.ROTATION_90:
456 return "top_vertical_right";
457 case Surface.ROTATION_180:
459 case Surface.ROTATION_270:
460 return "bottom_vertical_left";
463 case "bottom_vertical_right":
465 case Surface.ROTATION_0:
466 return "bottom_vertical_right";
467 case Surface.ROTATION_90:
469 case Surface.ROTATION_180:
470 return "top_vertical_left";
471 case Surface.ROTATION_270:
472 return "bottom_left";
477 case Surface.ROTATION_0:
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";
487 case "top_vertical_left":
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:
501 case Surface.ROTATION_0:
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";
511 case "top_vertical_right":
513 case Surface.ROTATION_0:
514 return "top_vertical_right";
515 case Surface.ROTATION_90:
517 case Surface.ROTATION_180:
518 return "bottom_vertical_left";
519 case Surface.ROTATION_270:
520 return "bottom_right";
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;
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;
540 float iconSize = context.getResources().getDimension(R.dimen.icon_size) / metrics.density;
542 SharedPreferences pref = getSharedPreferences(context);
543 int userMaxNumOfColumns = Integer.valueOf(pref.getString("max_num_of_recents", "10"));
545 while(baseTaskbarSize + iconSize < maxScreenSize
546 && numOfColumns < userMaxNumOfColumns) {
547 baseTaskbarSize = baseTaskbarSize + iconSize;
554 private static int getStatusBarHeight(Context context) {
555 int statusBarHeight = 0;
556 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
558 statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
560 return statusBarHeight;
563 public static void refreshPinnedIcons(Context context) {
564 IconCache.getInstance(context).clearCache();
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();
573 for(AppEntry entry : pinnedAppsList) {
574 Intent throwaway = new Intent();
575 throwaway.setComponent(ComponentName.unflattenFromString(entry.getComponentName()));
577 AppEntry newEntry = new AppEntry(
578 entry.getPackageName(),
579 entry.getComponentName(),
581 IconCache.getInstance(context).getIcon(context, pm, throwaway.resolveActivityInfo(pm, 0)),
584 pba.addPinnedApp(context, newEntry);
587 for(AppEntry entry : blockedAppsList) {
588 pba.addBlockedApp(context, entry);