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.ui;
18 import android.accessibilityservice.AccessibilityService;
19 import android.annotation.SuppressLint;
20 import android.annotation.TargetApi;
21 import android.app.ActivityManager;
22 import android.app.AlarmManager;
23 import android.app.usage.UsageEvents;
24 import android.app.usage.UsageStats;
25 import android.app.usage.UsageStatsManager;
26 import android.bluetooth.BluetoothAdapter;
27 import android.content.ActivityNotFoundException;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.SharedPreferences;
33 import android.content.pm.LauncherActivityInfo;
34 import android.content.pm.LauncherApps;
35 import android.content.pm.PackageManager;
36 import android.content.pm.ResolveInfo;
37 import android.content.res.Resources;
38 import android.graphics.Color;
39 import android.graphics.Point;
40 import android.graphics.Typeface;
41 import android.graphics.drawable.Drawable;
42 import android.net.ConnectivityManager;
43 import android.net.NetworkInfo;
44 import android.net.wifi.WifiInfo;
45 import android.net.wifi.WifiManager;
46 import android.os.BatteryManager;
47 import android.os.Build;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.os.PowerManager;
51 import android.os.Process;
52 import android.os.SystemClock;
53 import android.os.UserHandle;
54 import android.os.UserManager;
55 import android.provider.Settings;
56 import android.speech.RecognizerIntent;
58 import androidx.annotation.VisibleForTesting;
59 import androidx.core.content.ContextCompat;
60 import androidx.core.graphics.ColorUtils;
61 import android.telephony.PhoneStateListener;
62 import android.telephony.SignalStrength;
63 import android.telephony.TelephonyManager;
64 import android.text.format.DateFormat;
65 import android.view.Display;
66 import android.view.Gravity;
67 import android.view.LayoutInflater;
68 import android.view.MotionEvent;
69 import android.view.PointerIcon;
70 import android.view.View;
71 import android.view.ViewGroup;
72 import android.view.WindowManager;
73 import android.view.inputmethod.InputMethodManager;
74 import android.widget.Button;
75 import android.widget.FrameLayout;
76 import android.widget.ImageView;
78 import java.lang.reflect.Field;
79 import java.util.ArrayList;
80 import java.util.Collections;
81 import java.util.Date;
82 import java.util.HashMap;
83 import java.util.List;
86 import android.widget.LinearLayout;
87 import android.widget.Space;
88 import android.widget.TextView;
90 import com.farmerbb.taskbar.BuildConfig;
91 import com.farmerbb.taskbar.activity.HomeActivityDelegate;
92 import com.farmerbb.taskbar.activity.MainActivity;
93 import com.farmerbb.taskbar.R;
94 import com.farmerbb.taskbar.activity.HomeActivity;
95 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
96 import com.farmerbb.taskbar.activity.SecondaryHomeActivity;
97 import com.farmerbb.taskbar.util.TaskbarPosition;
98 import com.farmerbb.taskbar.util.AppEntry;
99 import com.farmerbb.taskbar.util.DisplayInfo;
100 import com.farmerbb.taskbar.helper.FreeformHackHelper;
101 import com.farmerbb.taskbar.util.IconCache;
102 import com.farmerbb.taskbar.helper.LauncherHelper;
103 import com.farmerbb.taskbar.util.PinnedBlockedApps;
104 import com.farmerbb.taskbar.helper.MenuHelper;
105 import com.farmerbb.taskbar.util.U;
107 import static com.farmerbb.taskbar.util.Constants.*;
109 public class TaskbarController extends UIController {
111 private LinearLayout layout;
112 private ImageView startButton;
113 private LinearLayout taskbar;
114 private FrameLayout scrollView;
115 private Button button;
117 private FrameLayout dashboardButton;
118 private LinearLayout navbarButtons;
119 private LinearLayout sysTrayLayout;
120 private FrameLayout sysTrayParentLayout;
121 private TextView time;
122 private ImageView notificationCountCircle;
123 private TextView notificationCountText;
125 private Handler handler;
126 private Handler handler2;
127 private Thread thread;
128 private Thread thread2;
130 private boolean isShowingRecents = true;
131 private boolean shouldRefreshRecents = true;
132 private boolean taskbarShownTemporarily = false;
133 private boolean taskbarHiddenTemporarily = false;
134 private boolean isRefreshingRecents = false;
135 private boolean isFirstStart = true;
137 private boolean startThread2 = false;
138 private boolean stopThread2 = false;
140 private int refreshInterval = -1;
141 private long searchInterval = -1;
142 private String sortOrder = "false";
143 private boolean runningAppsOnly = false;
145 private int currentTaskbarPosition = 0;
146 private boolean showHideAutomagically = false;
147 private boolean positionIsVertical = false;
148 private boolean dashboardEnabled = false;
149 private boolean navbarButtonsEnabled = false;
150 private boolean sysTrayEnabled = false;
152 private List<String> currentTaskbarIds = new ArrayList<>();
153 private int numOfPinnedApps = -1;
155 private int cellStrength = -1;
156 private int notificationCount = 0;
157 private int numOfSysTrayIcons = 0;
159 private boolean matchParent;
160 private Runnable updateParamsRunnable;
162 private final Map<Integer, Boolean> sysTrayIconStates = new HashMap<>();
164 private final View.OnClickListener ocl = view ->
165 U.sendBroadcast(context, ACTION_TOGGLE_START_MENU);
167 private final BroadcastReceiver showReceiver = new BroadcastReceiver() {
169 public void onReceive(Context context, Intent intent) {
174 private final BroadcastReceiver hideReceiver = new BroadcastReceiver() {
176 public void onReceive(Context context, Intent intent) {
181 private final BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
183 public void onReceive(Context context, Intent intent) {
188 private final BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
190 public void onReceive(Context context, Intent intent) {
191 tempHideTaskbar(false);
195 private final BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
197 public void onReceive(Context context, Intent intent) {
198 if(startButton.getVisibility() == View.GONE
199 && (!LauncherHelper.getInstance().isOnHomeScreen(context) || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
200 layout.setVisibility(View.GONE);
204 private final BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
206 public void onReceive(Context context, Intent intent) {
207 if(startButton.getVisibility() == View.GONE)
208 layout.setVisibility(View.VISIBLE);
212 private final BroadcastReceiver notificationCountReceiver = new BroadcastReceiver() {
214 public void onReceive(Context context, Intent intent) {
215 notificationCount = intent.getIntExtra(EXTRA_COUNT, 0);
219 @TargetApi(Build.VERSION_CODES.M)
220 private final PhoneStateListener listener = new PhoneStateListener() {
222 public void onSignalStrengthsChanged(SignalStrength signalStrength) {
224 cellStrength = signalStrength.getLevel();
225 } catch (SecurityException e) {
231 public TaskbarController(Context context) {
236 public void onCreateHost(UIHost host) {
237 init(context, host, () -> drawTaskbar(host));
240 private void drawTaskbar(UIHost host) {
241 IconCache.getInstance(context).clearCache();
243 // Initialize layout params
244 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
245 TaskbarPosition.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
247 final ViewParams params = new ViewParams(
248 WindowManager.LayoutParams.WRAP_CONTENT,
249 WindowManager.LayoutParams.WRAP_CONTENT,
251 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
252 getBottomMargin(context, host)
255 // Determine where to show the taskbar on screen
256 String taskbarPosition = TaskbarPosition.getTaskbarPosition(context);
257 params.gravity = getTaskbarGravity(taskbarPosition);
258 int layoutId = getTaskbarLayoutId(taskbarPosition);
259 positionIsVertical = TaskbarPosition.isVertical(taskbarPosition);
262 SharedPreferences pref = U.getSharedPreferences(context);
263 boolean altButtonConfig = pref.getBoolean(PREF_ALT_BUTTON_CONFIG, false);
265 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
266 taskbar = layout.findViewById(R.id.taskbar);
267 scrollView = layout.findViewById(R.id.taskbar_scrollview);
269 int backgroundTint = U.getBackgroundTint(context);
270 int accentColor = U.getAccentColor(context);
272 if(altButtonConfig) {
273 space = layout.findViewById(R.id.space_alt);
274 layout.findViewById(R.id.space).setVisibility(View.GONE);
276 space = layout.findViewById(R.id.space);
277 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
280 space.setOnClickListener(v -> toggleTaskbar(true));
282 startButton = layout.findViewById(R.id.start_button);
283 drawStartButton(context, startButton, pref, accentColor);
285 refreshInterval = (int) (Float.parseFloat(pref.getString(PREF_REFRESH_FREQUENCY, "1")) * 1000);
286 if(refreshInterval == 0)
287 refreshInterval = 100;
289 sortOrder = pref.getString(PREF_SORT_ORDER, "false");
291 PREF_RECENTS_AMOUNT_RUNNING_APPS_ONLY
292 .equals(pref.getString(PREF_RECENTS_AMOUNT, PREF_RECENTS_AMOUNT_PAST_DAY));
293 searchInterval = getSearchInterval(pref);
295 U.sendBroadcast(context, ACTION_HIDE_START_MENU);
296 U.sendBroadcast(context, ACTION_UPDATE_HOME_SCREEN_MARGINS);
298 if(altButtonConfig) {
299 button = layout.findViewById(R.id.hide_taskbar_button_alt);
300 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
302 button = layout.findViewById(R.id.hide_taskbar_button);
303 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
307 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
308 } catch (RuntimeException ignored) {}
311 button.setOnClickListener(v -> toggleTaskbar(true));
313 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
314 ? R.id.hide_taskbar_button_layout_alt
315 : R.id.hide_taskbar_button_layout);
316 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
318 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
319 ? R.id.hide_taskbar_button_layout
320 : R.id.hide_taskbar_button_layout_alt);
321 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
323 dashboardButton = layout.findViewById(R.id.dashboard_button);
324 navbarButtons = layout.findViewById(R.id.navbar_buttons);
325 dashboardEnabled = drawDashboardButton(context, layout, dashboardButton, accentColor);
326 navbarButtonsEnabled = drawNavbarButtons(context, layout, pref, accentColor);
327 if(!navbarButtonsEnabled)
328 navbarButtons.setVisibility(View.GONE);
330 sysTrayEnabled = U.isSystemTrayEnabled(context);
333 drawSysTray(context, layoutId, layout);
336 layout.setBackgroundColor(backgroundTint);
337 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
338 button.setTextColor(accentColor);
340 applyMarginFix(host, layout, params);
342 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
344 else if(!pref.getBoolean(PREF_COLLAPSED, false) && pref.getBoolean(PREF_TASKBAR_ACTIVE, false))
345 toggleTaskbar(false);
347 if(pref.getBoolean(PREF_AUTO_HIDE_NAVBAR, false))
348 U.showHideNavigationBar(context, false);
350 if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
351 U.sendBroadcast(context, ACTION_FINISH_FREEFORM_ACTIVITY);
353 new Handler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
356 U.registerReceiver(context, showReceiver, ACTION_SHOW_TASKBAR);
357 U.registerReceiver(context, hideReceiver, ACTION_HIDE_TASKBAR);
358 U.registerReceiver(context, tempShowReceiver, ACTION_TEMP_SHOW_TASKBAR);
359 U.registerReceiver(context, tempHideReceiver, ACTION_TEMP_HIDE_TASKBAR);
360 U.registerReceiver(context, startMenuAppearReceiver, ACTION_START_MENU_APPEARING);
361 U.registerReceiver(context, startMenuDisappearReceiver, ACTION_START_MENU_DISAPPEARING);
364 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
365 manager.listen(listener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
367 U.registerReceiver(context, notificationCountReceiver, ACTION_NOTIFICATION_COUNT_CHANGED);
368 U.sendBroadcast(context, ACTION_REQUEST_NOTIFICATION_COUNT);
372 updateParamsRunnable = () -> {
373 ViewParams newParams;
374 if(TaskbarPosition.isVertical(context)) {
375 newParams = matchParent
376 ? params.updateHeight(WindowManager.LayoutParams.MATCH_PARENT)
377 : params.updateHeight(WindowManager.LayoutParams.WRAP_CONTENT);
379 newParams = matchParent
380 ? params.updateWidth(WindowManager.LayoutParams.MATCH_PARENT)
381 : params.updateWidth(WindowManager.LayoutParams.WRAP_CONTENT);
384 host.updateViewLayout(layout, newParams);
387 startRefreshingRecents();
389 host.addView(layout, params);
391 isFirstStart = false;
394 @SuppressLint("RtlHardcoded")
396 int getTaskbarGravity(String taskbarPosition) {
397 int gravity = Gravity.BOTTOM | Gravity.LEFT;
398 switch(taskbarPosition) {
399 case POSITION_BOTTOM_LEFT:
400 case POSITION_BOTTOM_VERTICAL_LEFT:
401 gravity = Gravity.BOTTOM | Gravity.LEFT;
403 case POSITION_BOTTOM_RIGHT:
404 case POSITION_BOTTOM_VERTICAL_RIGHT:
405 gravity = Gravity.BOTTOM | Gravity.RIGHT;
407 case POSITION_TOP_LEFT:
408 case POSITION_TOP_VERTICAL_LEFT:
409 gravity = Gravity.TOP | Gravity.LEFT;
411 case POSITION_TOP_RIGHT:
412 case POSITION_TOP_VERTICAL_RIGHT:
413 gravity = Gravity.TOP | Gravity.RIGHT;
420 int getTaskbarLayoutId(String taskbarPosition) {
421 int layoutId = R.layout.tb_taskbar_left;
422 switch(taskbarPosition) {
423 case POSITION_BOTTOM_LEFT:
424 case POSITION_TOP_LEFT:
425 layoutId = R.layout.tb_taskbar_left;
427 case POSITION_BOTTOM_VERTICAL_LEFT:
428 case POSITION_BOTTOM_VERTICAL_RIGHT:
429 layoutId = R.layout.tb_taskbar_vertical;
431 case POSITION_BOTTOM_RIGHT:
432 case POSITION_TOP_RIGHT:
433 layoutId = R.layout.tb_taskbar_right;
435 case POSITION_TOP_VERTICAL_LEFT:
436 case POSITION_TOP_VERTICAL_RIGHT:
437 layoutId = R.layout.tb_taskbar_top_vertical;
444 void drawStartButton(Context context, ImageView startButton, SharedPreferences pref, int accentColor) {
445 Drawable allAppsIcon = ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon);
448 switch(pref.getString(PREF_START_BUTTON_IMAGE, U.getDefaultStartButtonImage(context))) {
449 case PREF_START_BUTTON_IMAGE_DEFAULT:
450 startButton.setImageDrawable(allAppsIcon);
451 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
453 case PREF_START_BUTTON_IMAGE_APP_LOGO:
456 if(U.isBlissOs(context)) {
457 drawable = ContextCompat.getDrawable(context, R.drawable.tb_bliss);
458 drawable.setTint(accentColor);
460 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
461 LauncherActivityInfo info = launcherApps.getActivityList(context.getPackageName(), Process.myUserHandle()).get(0);
462 drawable = IconCache.getInstance(context).getIcon(context, context.getPackageManager(), info);
465 startButton.setImageDrawable(drawable);
466 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding_alt);
468 case PREF_START_BUTTON_IMAGE_CUSTOM:
469 U.applyCustomImage(context, "custom_image", startButton, allAppsIcon);
470 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
474 startButton.setPadding(padding, padding, padding, padding);
475 startButton.setOnClickListener(ocl);
476 startButton.setOnLongClickListener(view -> {
481 startButton.setOnGenericMotionListener((view, motionEvent) -> {
482 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
483 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
491 boolean drawDashboardButton(Context context,
493 FrameLayout dashboardButton,
495 boolean dashboardEnabled = U.getBooleanPrefWithDefault(context, PREF_DASHBOARD);
496 if(dashboardEnabled) {
497 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
498 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
499 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
500 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
501 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
502 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
504 dashboardButton.setOnClickListener(v -> U.sendBroadcast(context, ACTION_TOGGLE_DASHBOARD));
505 dashboardButton.setVisibility(View.VISIBLE);
507 dashboardButton.setVisibility(View.GONE);
509 return dashboardEnabled;
513 boolean drawNavbarButtons(Context context,
515 SharedPreferences pref,
517 boolean navbarButtonsEnabled = false;
518 if(pref.getBoolean(PREF_BUTTON_BACK, false)) {
519 navbarButtonsEnabled = true;
521 ImageView backButton = layout.findViewById(R.id.button_back);
522 backButton.setColorFilter(accentColor);
523 backButton.setVisibility(View.VISIBLE);
524 backButton.setOnClickListener(v -> {
525 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
526 if(U.shouldCollapse(context, false))
530 backButton.setOnLongClickListener(v -> {
531 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
532 imm.showInputMethodPicker();
534 if(U.shouldCollapse(context, false))
540 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
541 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
542 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
543 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
544 imm.showInputMethodPicker();
546 if(U.shouldCollapse(context, false))
553 if(pref.getBoolean(PREF_BUTTON_HOME, false)) {
554 navbarButtonsEnabled = true;
556 ImageView homeButton = layout.findViewById(R.id.button_home);
557 homeButton.setColorFilter(accentColor);
558 homeButton.setVisibility(View.VISIBLE);
559 homeButton.setOnClickListener(v -> {
560 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
561 if(U.shouldCollapse(context, false))
565 homeButton.setOnLongClickListener(v -> {
566 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
567 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
570 context.startActivity(voiceSearchIntent);
571 } catch (ActivityNotFoundException ignored) {}
573 if(U.shouldCollapse(context, false))
579 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
580 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
581 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
582 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
583 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
586 context.startActivity(voiceSearchIntent);
587 } catch (ActivityNotFoundException ignored) {}
589 if(U.shouldCollapse(context, false))
596 if(pref.getBoolean(PREF_BUTTON_RECENTS, false)) {
597 navbarButtonsEnabled = true;
599 ImageView recentsButton = layout.findViewById(R.id.button_recents);
600 recentsButton.setColorFilter(accentColor);
601 recentsButton.setVisibility(View.VISIBLE);
602 recentsButton.setOnClickListener(v -> {
603 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
604 if(U.shouldCollapse(context, false))
608 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
609 recentsButton.setOnLongClickListener(v -> {
610 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
611 if(U.shouldCollapse(context, false))
617 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
618 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
619 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
620 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
621 if(U.shouldCollapse(context, false))
628 return navbarButtonsEnabled;
632 long getSearchInterval(SharedPreferences pref) {
633 long searchInterval = -1;
634 switch(pref.getString(PREF_RECENTS_AMOUNT, PREF_RECENTS_AMOUNT_PAST_DAY)) {
635 case PREF_RECENTS_AMOUNT_PAST_DAY:
636 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
638 case PREF_RECENTS_AMOUNT_APP_START:
639 long appStartTime = pref.getLong(PREF_TIME_OF_SERVICE_START, System.currentTimeMillis());
640 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
641 searchInterval = Math.max(deviceStartTime, appStartTime);
643 case PREF_RECENTS_AMOUNT_SHOW_ALL:
647 return searchInterval;
651 void drawSysTray(Context context, int layoutId, LinearLayout layout) {
652 sysTrayLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.tb_system_tray, null);
654 FrameLayout.LayoutParams sysTrayParams = new FrameLayout.LayoutParams(
655 FrameLayout.LayoutParams.WRAP_CONTENT,
656 context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size)
659 if(layoutId == R.layout.tb_taskbar_right) {
660 time = sysTrayLayout.findViewById(R.id.time_left);
661 sysTrayParams.gravity = Gravity.START;
662 sysTrayLayout.findViewById(R.id.space_right).setVisibility(View.VISIBLE);
664 time = sysTrayLayout.findViewById(R.id.time_right);
665 sysTrayParams.gravity = Gravity.END;
666 sysTrayLayout.findViewById(R.id.space_left).setVisibility(View.VISIBLE);
669 time.setVisibility(View.VISIBLE);
670 sysTrayLayout.setLayoutParams(sysTrayParams);
672 if(!U.isLibrary(context)) {
673 sysTrayLayout.setOnClickListener(v -> {
674 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, () -> {
675 if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
676 U.showToast(context, R.string.tb_opening_notification_tray);
677 U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
681 if(U.shouldCollapse(context, false))
685 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
686 sysTrayLayout.setOnLongClickListener(v -> {
687 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, () -> {
688 if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
689 U.showToast(context, R.string.tb_opening_quick_settings);
690 U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
694 if(U.shouldCollapse(context, false))
700 sysTrayLayout.setOnGenericMotionListener((view, motionEvent) -> {
701 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
702 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
703 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, () -> {
704 if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
705 U.showToast(context, R.string.tb_opening_quick_settings);
706 U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
710 if(U.shouldCollapse(context, false))
718 notificationCountCircle = sysTrayLayout.findViewById(R.id.notification_count_circle);
719 notificationCountText = sysTrayLayout.findViewById(R.id.notification_count_text);
721 sysTrayParentLayout = layout.findViewById(R.id.add_systray_here);
722 sysTrayParentLayout.setVisibility(View.VISIBLE);
723 sysTrayParentLayout.addView(sysTrayLayout);
725 sysTrayIconStates.clear();
726 sysTrayIconStates.put(R.id.cellular, false);
727 sysTrayIconStates.put(R.id.bluetooth, false);
728 sysTrayIconStates.put(R.id.wifi, false);
729 sysTrayIconStates.put(R.id.battery, false);
730 sysTrayIconStates.put(R.id.notification_count, false);
733 private void startRefreshingRecents() {
734 if(thread != null) thread.interrupt();
737 SharedPreferences pref = U.getSharedPreferences(context);
738 showHideAutomagically = pref.getBoolean(PREF_HIDE_WHEN_KEYBOARD_SHOWN, false);
740 currentTaskbarIds.clear();
742 handler = new Handler();
743 thread = new Thread(() -> {
744 updateRecentApps(true);
746 if(!isRefreshingRecents) {
747 isRefreshingRecents = true;
749 while(shouldRefreshRecents) {
750 updateRecentApps(false);
752 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen()) {
755 int[] location = new int[2];
756 layout.getLocationOnScreen(location);
758 if(location[1] != 0) {
759 if(location[1] > currentTaskbarPosition) {
760 currentTaskbarPosition = location[1];
761 } else if(location[1] < currentTaskbarPosition) {
762 if(currentTaskbarPosition - location[1] == getNavBarSize())
763 currentTaskbarPosition = location[1];
764 else if(!startThread2) {
766 tempHideTaskbar(true);
774 SystemClock.sleep(refreshInterval);
777 isRefreshingRecents = false;
784 @SuppressWarnings("Convert2streamapi")
785 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
786 private void updateRecentApps(final boolean firstRefresh) {
787 if(isScreenOff()) return;
791 SharedPreferences pref = U.getSharedPreferences(context);
792 final PackageManager pm = context.getPackageManager();
793 final List<AppEntry> entries = new ArrayList<>();
794 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
795 int maxNumOfEntries = firstRefresh ? 0 : U.getMaxNumOfEntries(context);
796 boolean fullLength = pref.getBoolean(PREF_FULL_LENGTH, true);
798 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
799 List<AppEntry> pinnedApps = pba.getPinnedApps();
800 List<AppEntry> blockedApps = pba.getBlockedApps();
801 List<String> applicationIdsToRemove = new ArrayList<>();
803 // Filter out anything on the pinned/blocked apps lists
804 int realNumOfPinnedApps = filterRealPinnedApps(context, pinnedApps, entries, applicationIdsToRemove);
806 if(blockedApps.size() > 0) {
807 //noinspection SynchronizationOnLocalVariableOrMethodParameter
808 synchronized(blockedApps) {
809 for(AppEntry entry : blockedApps) {
810 applicationIdsToRemove.add(entry.getPackageName());
815 // Get list of all recently used apps
816 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
817 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
818 if(realNumOfPinnedApps < maxNumOfEntries) {
819 List<AppEntry> usageStatsList2 = new ArrayList<>();
820 List<AppEntry> usageStatsList3 = new ArrayList<>();
821 List<AppEntry> usageStatsList4 = new ArrayList<>();
822 List<AppEntry> usageStatsList5 = new ArrayList<>();
823 List<AppEntry> usageStatsList6;
825 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
826 homeIntent.addCategory(Intent.CATEGORY_HOME);
827 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
829 // Filter out apps without a launcher intent
830 // Also filter out the current launcher, and Taskbar itself
831 for(AppEntry packageInfo : usageStatsList) {
832 if(hasLauncherIntent(packageInfo.getPackageName())
833 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
834 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName)
835 && (!(U.launcherIsDefault(context) && pref.getBoolean(PREF_DESKTOP_MODE, false))
836 || !packageInfo.getPackageName().equals(pref.getString(PREF_HSL_ID, "null"))))
837 usageStatsList2.add(packageInfo);
840 // Filter out apps that don't fall within our current search interval
841 for(AppEntry stats : usageStatsList2) {
842 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
843 usageStatsList3.add(stats);
846 // Sort apps by either most recently used, or most time used
847 if(!runningAppsOnly && sortOrder.contains("most_used")) {
848 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
850 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
853 // Filter out any duplicate entries
854 List<String> applicationIds = new ArrayList<>();
855 for(AppEntry stats : usageStatsList3) {
856 if(!applicationIds.contains(stats.getPackageName())) {
857 usageStatsList4.add(stats);
858 applicationIds.add(stats.getPackageName());
862 // Filter out the currently running foreground app, if requested by the user
863 filterForegroundApp(context, pref, searchInterval, applicationIdsToRemove);
865 for(AppEntry stats : usageStatsList4) {
866 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
867 usageStatsList5.add(stats);
871 // Truncate list to a maximum length
872 if(usageStatsList5.size() > maxNumOfEntries)
873 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
875 usageStatsList6 = usageStatsList5;
877 // Determine if we need to reverse the order
878 if(needToReverseOrder(context, sortOrder)) {
879 Collections.reverse(usageStatsList6);
882 // Generate the AppEntries for the recent apps list
883 int number = usageStatsList6.size() == maxNumOfEntries
884 ? usageStatsList6.size() - realNumOfPinnedApps
885 : usageStatsList6.size();
887 generateAppEntries(context, number, usageStatsList6, entries, launcherAppCache);
890 while(entries.size() > maxNumOfEntries) {
892 entries.remove(entries.size() - 1);
893 launcherAppCache.remove(launcherAppCache.size() - 1);
894 } catch (ArrayIndexOutOfBoundsException ignored) {}
897 // Determine if we need to reverse the order again
898 if(TaskbarPosition.isVertical(context)) {
899 Collections.reverse(entries);
900 Collections.reverse(launcherAppCache);
903 // Now that we've generated the list of apps,
904 // we need to determine if we need to redraw the Taskbar or not
905 boolean shouldRedrawTaskbar = firstRefresh;
907 List<String> finalApplicationIds = new ArrayList<>();
908 for(AppEntry entry : entries) {
909 finalApplicationIds.add(entry.getPackageName());
912 int realNumOfSysTrayIcons = 0;
913 for(Integer key : sysTrayIconStates.keySet()) {
914 if(sysTrayIconStates.get(key))
915 realNumOfSysTrayIcons++;
918 if(finalApplicationIds.size() != currentTaskbarIds.size()
919 || numOfPinnedApps != realNumOfPinnedApps
920 || numOfSysTrayIcons != realNumOfSysTrayIcons)
921 shouldRedrawTaskbar = true;
923 for(int i = 0; i < finalApplicationIds.size(); i++) {
924 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
925 shouldRedrawTaskbar = true;
931 if(shouldRedrawTaskbar) {
932 currentTaskbarIds = finalApplicationIds;
933 numOfPinnedApps = realNumOfPinnedApps;
934 numOfSysTrayIcons = realNumOfSysTrayIcons;
936 populateAppEntries(context, pm, entries, launcherAppCache);
938 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
941 if(numOfEntries > 0 || fullLength) {
942 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
943 calculateScrollViewParams(context, pref, params, fullLength, numOfEntries);
944 scrollView.setLayoutParams(params);
946 for(Integer key : sysTrayIconStates.keySet()) {
947 sysTrayLayout.findViewById(key).setVisibility(
948 sysTrayIconStates.get(key) ? View.VISIBLE : View.GONE
952 taskbar.removeAllViews();
953 for(int i = 0; i < entries.size(); i++) {
954 taskbar.addView(getView(entries, i));
958 updateRunningAppIndicators(pinnedApps, usageStatsList, entries);
960 isShowingRecents = true;
961 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
963 scrollView.setVisibility(View.INVISIBLE);
965 scrollView.setVisibility(View.VISIBLE);
968 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE) {
973 TaskbarPosition.getTaskbarPosition(context),
980 isShowingRecents = false;
981 scrollView.setVisibility(View.GONE);
984 } else if(runningAppsOnly)
985 handler.post(() -> updateRunningAppIndicators(pinnedApps, usageStatsList, entries));
986 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
987 currentTaskbarIds.clear();
989 isShowingRecents = false;
990 scrollView.setVisibility(View.GONE);
996 void calculateScrollViewParams(Context context,
997 SharedPreferences pref,
998 ViewGroup.LayoutParams params,
1001 DisplayInfo display = U.getDisplayInfo(context, true);
1002 int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size) * numOfEntries;
1003 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
1006 float baseStart = U.getBaseTaskbarSizeStart(context);
1007 float baseEnd = U.getBaseTaskbarSizeEnd(context, sysTrayIconStates);
1008 int baseTotal = Math.round(baseStart + baseEnd);
1010 int diff = Math.round(Math.max(baseStart, baseEnd) - Math.min(baseStart, baseEnd));
1011 boolean startIsBigger = Math.max(baseStart, baseEnd) == baseStart;
1013 if(TaskbarPosition.isVertical(context)) {
1014 maxScreenSize = Math.max(0, display.height
1015 - U.getStatusBarHeight(context)
1018 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
1019 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1023 Space whitespaceStart = layout.findViewById(R.id.whitespace_start);
1024 Space whitespaceEnd = layout.findViewById(R.id.whitespace_end);
1025 int height = maxScreenSize - recentsSize;
1027 if(pref.getBoolean(PREF_CENTERED_ICONS, false)) {
1028 int startHeight = (height / 2) + (diff / (startIsBigger ? -2 : 2));
1029 int endHeight = (height / 2) + (diff / (startIsBigger ? 2 : -2));
1031 if(startHeight < 0) {
1037 startHeight = height;
1041 ViewGroup.LayoutParams startParams = whitespaceStart.getLayoutParams();
1042 startParams.height = startHeight;
1043 whitespaceStart.setLayoutParams(startParams);
1045 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1046 endParams.height = endHeight;
1047 whitespaceEnd.setLayoutParams(endParams);
1049 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1050 endParams.height = height;
1051 whitespaceEnd.setLayoutParams(endParams);
1053 } catch (NullPointerException ignored) {}
1056 maxScreenSize = Math.max(0, display.width - baseTotal);
1058 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
1059 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1063 Space whitespaceStart = layout.findViewById(R.id.whitespace_start);
1064 Space whitespaceEnd = layout.findViewById(R.id.whitespace_end);
1065 int width = maxScreenSize - recentsSize;
1067 if(pref.getBoolean(PREF_CENTERED_ICONS, false)) {
1068 int startWidth = (width / 2) + (diff / (startIsBigger ? -2 : 2));
1069 int endWidth = (width / 2) + (diff / (startIsBigger ? 2 : -2));
1071 if(startWidth < 0) {
1081 ViewGroup.LayoutParams startParams = whitespaceStart.getLayoutParams();
1082 startParams.width = startWidth;
1083 whitespaceStart.setLayoutParams(startParams);
1085 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1086 endParams.width = endWidth;
1087 whitespaceEnd.setLayoutParams(endParams);
1089 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1090 endParams.width = width;
1091 whitespaceEnd.setLayoutParams(endParams);
1093 } catch (NullPointerException ignored) {}
1097 boolean realMatchParent = maxRecentsSize >= maxScreenSize
1098 && pref.getBoolean(PREF_COLLAPSED, false)
1099 && !(TaskbarPosition.isVertical(context) && U.isChromeOs(context));
1101 if(realMatchParent != matchParent) {
1102 matchParent = realMatchParent;
1103 new Handler().post(updateParamsRunnable);
1108 void scrollTaskbar(FrameLayout scrollView,
1109 LinearLayout taskbar,
1110 String taskbarPosition,
1112 boolean shouldRefreshRecents) {
1113 if(TaskbarPosition.isVertical(taskbarPosition)) {
1114 if(sortOrder.contains("false")) {
1115 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1116 } else if(sortOrder.contains("true")) {
1117 scrollView.scrollTo(0, 0);
1120 if(sortOrder.contains("false")) {
1121 scrollView.scrollTo(0, 0);
1122 } else if(sortOrder.contains("true")) {
1123 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1127 if(shouldRefreshRecents) {
1128 scrollView.setVisibility(View.VISIBLE);
1132 @TargetApi(Build.VERSION_CODES.M)
1134 void filterForegroundApp(Context context,
1135 SharedPreferences pref,
1136 long searchInterval,
1137 List<String> applicationIdsToRemove) {
1138 if(pref.getBoolean(PREF_HIDE_FOREGROUND, false)) {
1139 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1140 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
1141 UsageEvents.Event eventCache = new UsageEvents.Event();
1142 String currentForegroundApp = null;
1144 while (events.hasNextEvent()) {
1145 events.getNextEvent(eventCache);
1147 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
1148 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
1149 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
1150 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
1151 && !eventCache.getClassName().equals(HomeActivityDelegate.class.getCanonicalName())
1152 && !eventCache.getClassName().equals(SecondaryHomeActivity.class.getCanonicalName())
1153 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName()))) {
1154 currentForegroundApp = eventCache.getPackageName();
1159 if(!applicationIdsToRemove.contains(currentForegroundApp)) {
1160 applicationIdsToRemove.add(currentForegroundApp);
1166 boolean needToReverseOrder(Context context, String sortOrder) {
1167 switch(TaskbarPosition.getTaskbarPosition(context)) {
1168 case POSITION_BOTTOM_RIGHT:
1169 case POSITION_TOP_RIGHT:
1170 return sortOrder.contains("false");
1172 return sortOrder.contains("true");
1177 int filterRealPinnedApps(Context context,
1178 List<AppEntry> pinnedApps,
1179 List<AppEntry> entries,
1180 List<String> applicationIdsToRemove) {
1181 int realNumOfPinnedApps = 0;
1182 if(pinnedApps.size() > 0) {
1183 //noinspection SynchronizationOnLocalVariableOrMethodParameter
1184 synchronized(pinnedApps) {
1185 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1186 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
1188 for(AppEntry entry : pinnedApps) {
1189 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
1190 userManager.getUserForSerialNumber(entry.getUserId(context)));
1195 realNumOfPinnedApps--;
1197 applicationIdsToRemove.add(entry.getPackageName());
1200 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
1204 return realNumOfPinnedApps;
1208 void generateAppEntries(Context context,
1210 List<AppEntry> usageStatsList6,
1211 List<AppEntry> entries,
1212 List<LauncherActivityInfo> launcherAppCache) {
1213 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1214 LauncherApps launcherApps =
1215 (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
1217 final List<UserHandle> userHandles = userManager.getUserProfiles();
1219 final String googleSearchBoxPackage = "com.google.android.googlequicksearchbox";
1220 final String googleSearchBoxActivity =
1221 "com.google.android.googlequicksearchbox.SearchActivity";
1222 for(int i = 0; i < number; i++) {
1223 for(UserHandle handle : userHandles) {
1224 String packageName = usageStatsList6.get(i).getPackageName();
1225 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
1226 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
1227 if(!list.isEmpty()) {
1228 // Google App workaround
1229 if(!packageName.equals(googleSearchBoxPackage)) {
1230 launcherAppCache.add(list.get(0));
1232 boolean added = false;
1233 for(LauncherActivityInfo info : list) {
1234 if(info.getName().equals(googleSearchBoxActivity)) {
1235 launcherAppCache.add(info);
1241 launcherAppCache.add(list.get(0));
1245 AppEntry newEntry = new AppEntry(packageName, null, null, null, false);
1247 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
1248 newEntry.setLastTimeUsed(lastTimeUsed);
1249 entries.add(newEntry);
1257 @SuppressWarnings("SuspiciousListRemoveInLoop")
1259 void populateAppEntries(Context context,
1261 List<AppEntry> entries,
1262 List<LauncherActivityInfo> launcherAppCache) {
1263 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1265 int launcherAppCachePos = -1;
1266 for(int i = 0; i < entries.size(); i++) {
1267 if(entries.get(i).getComponentName() == null) {
1268 launcherAppCachePos++;
1269 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
1270 String packageName = entries.get(i).getPackageName();
1271 long lastTimeUsed = entries.get(i).getLastTimeUsed();
1275 AppEntry newEntry = new AppEntry(
1277 appInfo.getComponentName().flattenToString(),
1278 appInfo.getLabel().toString(),
1279 IconCache.getInstance(context).getIcon(context, pm, appInfo),
1282 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
1283 newEntry.setLastTimeUsed(lastTimeUsed);
1284 entries.add(i, newEntry);
1289 private void updateRunningAppIndicators(List<AppEntry> pinnedApps, List<AppEntry> usageStatsList, List<AppEntry> entries) {
1290 if(taskbar.getChildCount() != entries.size())
1293 List<String> pinnedPackageList = new ArrayList<>();
1294 List<String> runningPackageList = new ArrayList<>();
1296 for(AppEntry entry : pinnedApps)
1297 pinnedPackageList.add(entry.getPackageName());
1299 for(AppEntry entry : usageStatsList)
1300 runningPackageList.add(entry.getPackageName());
1302 for(int i = 0; i < taskbar.getChildCount(); i++) {
1303 View convertView = taskbar.getChildAt(i);
1304 String packageName = entries.get(i).getPackageName();
1306 ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1307 if(pinnedPackageList.contains(packageName) && !runningPackageList.contains(packageName))
1308 runningAppIndicator.setVisibility(View.GONE);
1310 runningAppIndicator.setVisibility(View.VISIBLE);
1311 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1316 private void toggleTaskbar(boolean userInitiated) {
1317 if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
1318 SharedPreferences pref = U.getSharedPreferences(context);
1319 LauncherHelper helper = LauncherHelper.getInstance();
1321 if(!pref.getBoolean(PREF_GRIP_REJECTION_TOAST_SHOWN, false)
1322 && !helper.isOnSecondaryHomeScreen(context)) {
1323 U.showToastLong(context, R.string.tb_essential_phone_grip_rejection);
1324 pref.edit().putBoolean(PREF_GRIP_REJECTION_TOAST_SHOWN, true).apply();
1328 if(startButton.getVisibility() == View.GONE)
1334 private void showTaskbar(boolean clearVariables) {
1335 if(clearVariables) {
1336 taskbarShownTemporarily = false;
1337 taskbarHiddenTemporarily = false;
1340 if(startButton.getVisibility() == View.GONE) {
1341 startButton.setVisibility(View.VISIBLE);
1342 space.setVisibility(View.VISIBLE);
1344 if(dashboardEnabled)
1345 dashboardButton.setVisibility(View.VISIBLE);
1347 if(navbarButtonsEnabled)
1348 navbarButtons.setVisibility(View.VISIBLE);
1350 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
1351 scrollView.setVisibility(View.INVISIBLE);
1354 sysTrayParentLayout.setVisibility(View.VISIBLE);
1356 shouldRefreshRecents = true;
1357 startRefreshingRecents();
1359 SharedPreferences pref = U.getSharedPreferences(context);
1360 pref.edit().putBoolean(PREF_COLLAPSED, true).apply();
1362 updateButton(false);
1364 new Handler().post(() -> U.sendBroadcast(context, ACTION_SHOW_START_MENU_SPACE));
1368 private void hideTaskbar(boolean clearVariables) {
1369 if(clearVariables) {
1370 taskbarShownTemporarily = false;
1371 taskbarHiddenTemporarily = false;
1374 if(startButton.getVisibility() == View.VISIBLE) {
1375 startButton.setVisibility(View.GONE);
1376 space.setVisibility(View.GONE);
1378 if(dashboardEnabled)
1379 dashboardButton.setVisibility(View.GONE);
1381 if(navbarButtonsEnabled)
1382 navbarButtons.setVisibility(View.GONE);
1384 if(isShowingRecents)
1385 scrollView.setVisibility(View.GONE);
1388 sysTrayParentLayout.setVisibility(View.GONE);
1390 shouldRefreshRecents = false;
1391 if(thread != null) thread.interrupt();
1393 SharedPreferences pref = U.getSharedPreferences(context);
1394 pref.edit().putBoolean(PREF_COLLAPSED, false).apply();
1398 if(clearVariables) {
1399 U.sendBroadcast(context, ACTION_HIDE_START_MENU);
1400 U.sendBroadcast(context, ACTION_HIDE_DASHBOARD);
1404 matchParent = false;
1405 new Handler().post(updateParamsRunnable);
1408 new Handler().post(() -> U.sendBroadcast(context, ACTION_HIDE_START_MENU_SPACE));
1412 private void tempShowTaskbar() {
1413 if(!taskbarHiddenTemporarily) {
1414 SharedPreferences pref = U.getSharedPreferences(context);
1415 if(!pref.getBoolean(PREF_COLLAPSED, false)) taskbarShownTemporarily = true;
1420 if(taskbarHiddenTemporarily)
1421 taskbarHiddenTemporarily = false;
1424 private void tempHideTaskbar(boolean monitorPositionChanges) {
1425 if(!taskbarShownTemporarily) {
1426 SharedPreferences pref = U.getSharedPreferences(context);
1427 if(pref.getBoolean(PREF_COLLAPSED, false)) taskbarHiddenTemporarily = true;
1432 if(taskbarShownTemporarily)
1433 taskbarShownTemporarily = false;
1435 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1436 if(thread2 != null) thread2.interrupt();
1438 handler2 = new Handler();
1439 thread2 = new Thread(() -> {
1440 stopThread2 = false;
1442 while(!stopThread2) {
1443 SystemClock.sleep(refreshInterval);
1445 handler2.post(() -> stopThread2 = checkPositionChange());
1448 startThread2 = false;
1455 private boolean checkPositionChange() {
1456 if(!isScreenOff() && layout != null) {
1457 int[] location = new int[2];
1458 layout.getLocationOnScreen(location);
1460 if(location[1] == 0) {
1463 if(location[1] > currentTaskbarPosition) {
1464 currentTaskbarPosition = location[1];
1465 if(taskbarHiddenTemporarily) {
1469 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1472 } else if(location[1] < currentTaskbarPosition
1473 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1474 currentTaskbarPosition = location[1];
1482 private int getNavBarSize() {
1483 Point size = new Point();
1484 Point realSize = new Point();
1486 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1487 Display display = wm.getDefaultDisplay();
1488 display.getSize(size);
1489 display.getRealSize(realSize);
1491 return realSize.y - size.y;
1495 public void onDestroyHost(UIHost host) {
1496 shouldRefreshRecents = false;
1500 host.removeView(layout);
1501 } catch (IllegalArgumentException ignored) {}
1503 SharedPreferences pref = U.getSharedPreferences(context);
1504 if(pref.getBoolean(PREF_SKIP_AUTO_HIDE_NAVBAR, false)) {
1505 pref.edit().remove(PREF_SKIP_AUTO_HIDE_NAVBAR).apply();
1506 } else if(pref.getBoolean(PREF_AUTO_HIDE_NAVBAR, false))
1507 U.showHideNavigationBar(context, true);
1509 U.unregisterReceiver(context, showReceiver);
1510 U.unregisterReceiver(context, hideReceiver);
1511 U.unregisterReceiver(context, tempShowReceiver);
1512 U.unregisterReceiver(context, tempHideReceiver);
1513 U.unregisterReceiver(context, startMenuAppearReceiver);
1514 U.unregisterReceiver(context, startMenuDisappearReceiver);
1516 if(sysTrayEnabled) {
1517 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1518 manager.listen(listener, PhoneStateListener.LISTEN_NONE);
1520 U.unregisterReceiver(context, notificationCountReceiver);
1523 isFirstStart = true;
1526 private void openContextMenu() {
1527 SharedPreferences pref = U.getSharedPreferences(context);
1529 Bundle args = new Bundle();
1530 args.putBoolean("dont_show_quit",
1531 LauncherHelper.getInstance().isOnHomeScreen(context)
1532 && !pref.getBoolean(PREF_TASKBAR_ACTIVE, false));
1533 args.putBoolean("is_start_button", true);
1535 U.startContextMenuActivity(context, args);
1538 private void updateButton(boolean isCollapsed) {
1539 SharedPreferences pref = U.getSharedPreferences(context);
1540 boolean hide = pref.getBoolean(PREF_INVISIBLE_BUTTON, false);
1542 if(button != null) button.setText(context.getString(isCollapsed ? R.string.tb_right_arrow : R.string.tb_left_arrow));
1543 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1546 @TargetApi(Build.VERSION_CODES.M)
1548 public void onRecreateHost(UIHost host) {
1549 if(layout != null) {
1551 host.removeView(layout);
1552 } catch (IllegalArgumentException ignored) {}
1554 currentTaskbarPosition = 0;
1556 if(U.canDrawOverlays(context))
1559 SharedPreferences pref = U.getSharedPreferences(context);
1560 pref.edit().putBoolean(PREF_TASKBAR_ACTIVE, false).apply();
1567 private View getView(List<AppEntry> list, int position) {
1568 View convertView = View.inflate(context, R.layout.tb_icon, null);
1570 final AppEntry entry = list.get(position);
1571 final SharedPreferences pref = U.getSharedPreferences(context);
1573 ImageView imageView = convertView.findViewById(R.id.icon);
1574 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1575 imageView.setImageDrawable(entry.getIcon(context));
1576 imageView2.setBackgroundColor(U.getAccentColor(context));
1578 String taskbarPosition = TaskbarPosition.getTaskbarPosition(context);
1579 if(pref.getBoolean(PREF_SHORTCUT_ICON, true)) {
1580 boolean shouldShowShortcutIcon;
1581 if(taskbarPosition.contains("vertical"))
1582 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1584 shouldShowShortcutIcon = position < numOfPinnedApps;
1586 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1589 if(POSITION_BOTTOM_RIGHT.equals(taskbarPosition) || POSITION_TOP_RIGHT.equals(taskbarPosition)) {
1590 imageView.setRotationY(180);
1591 imageView2.setRotationY(180);
1594 FrameLayout layout = convertView.findViewById(R.id.entry);
1595 layout.setOnClickListener(view -> U.launchApp(
1604 layout.setOnLongClickListener(view -> {
1605 int[] location = new int[2];
1606 view.getLocationOnScreen(location);
1607 openContextMenu(entry, location);
1611 layout.setOnGenericMotionListener((view, motionEvent) -> {
1612 int action = motionEvent.getAction();
1614 if(action == MotionEvent.ACTION_BUTTON_PRESS
1615 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1616 int[] location = new int[2];
1617 view.getLocationOnScreen(location);
1618 openContextMenu(entry, location);
1621 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean(PREF_VISUAL_FEEDBACK, true))
1622 view.setBackgroundColor(0);
1627 if(pref.getBoolean(PREF_VISUAL_FEEDBACK, true)) {
1628 layout.setOnHoverListener((v, event) -> {
1629 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1630 int accentColor = U.getAccentColor(context);
1631 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1632 v.setBackgroundColor(accentColor);
1635 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1636 v.setBackgroundColor(0);
1638 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1639 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1644 layout.setOnTouchListener((v, event) -> {
1645 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1653 private void openContextMenu(AppEntry entry, int[] location) {
1654 Bundle args = new Bundle();
1655 args.putSerializable("app_entry", entry);
1656 args.putInt("x", location[0]);
1657 args.putInt("y", location[1]);
1659 U.startContextMenuActivity(context, args);
1662 private List<AppEntry> getAppEntries() {
1663 SharedPreferences pref = U.getSharedPreferences(context);
1665 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString(PREF_MAX_NUM_OF_RECENTS, "10")));
1667 return getAppEntriesUsingUsageStats();
1670 @SuppressWarnings({"deprecation", "JavaReflectionMemberAccess"})
1671 @TargetApi(Build.VERSION_CODES.M)
1672 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1673 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1674 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1675 List<AppEntry> entries = new ArrayList<>();
1677 for(int i = 0; i < usageStatsList.size(); i++) {
1678 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1679 if(recentTaskInfo.id != -1) {
1680 String packageName = recentTaskInfo.baseActivity.getPackageName();
1681 AppEntry newEntry = new AppEntry(
1689 U.allowReflection();
1691 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1692 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1693 } catch (Exception e) {
1694 newEntry.setLastTimeUsed(i);
1697 entries.add(newEntry);
1704 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1705 private List<AppEntry> getAppEntriesUsingUsageStats() {
1706 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1707 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1708 List<AppEntry> entries = new ArrayList<>();
1710 for(UsageStats usageStats : usageStatsList) {
1711 AppEntry newEntry = new AppEntry(
1712 usageStats.getPackageName(),
1719 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1720 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1721 entries.add(newEntry);
1727 private boolean hasLauncherIntent(String packageName) {
1728 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1729 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1730 intentToResolve.setPackage(packageName);
1732 List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1733 return ris != null && ris.size() > 0;
1736 private boolean isScreenOff() {
1737 if(U.isChromeOs(context))
1740 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1741 return !pm.isInteractive();
1744 @SuppressLint("SetTextI18n")
1745 private void updateSystemTray() {
1746 if(!sysTrayEnabled) return;
1748 handler.post(() -> {
1749 Map<Integer, Drawable> drawables = new HashMap<>();
1750 drawables.put(R.id.battery, getBatteryDrawable());
1751 drawables.put(R.id.wifi, getWifiDrawable());
1752 drawables.put(R.id.bluetooth, getBluetoothDrawable());
1753 drawables.put(R.id.cellular, getCellularDrawable());
1755 for(Integer key : drawables.keySet()) {
1756 ImageView view = sysTrayLayout.findViewById(key);
1757 Drawable drawable = drawables.get(key);
1759 if(drawable != null) view.setImageDrawable(drawable);
1760 sysTrayIconStates.put(key, drawable != null);
1763 if(notificationCount > 0) {
1764 int color = ColorUtils.setAlphaComponent(U.getBackgroundTint(context), 255);
1765 notificationCountText.setTextColor(color);
1767 Drawable drawable = ContextCompat.getDrawable(context, R.drawable.tb_circle);
1768 drawable.setTint(U.getAccentColor(context));
1770 notificationCountCircle.setImageDrawable(drawable);
1771 notificationCountText.setText(Integer.toString(notificationCount));
1772 sysTrayIconStates.put(R.id.notification_count, true);
1774 sysTrayIconStates.put(R.id.notification_count, false);
1776 time.setText(context.getString(R.string.tb_systray_clock,
1777 DateFormat.getTimeFormat(context).format(new Date()),
1778 DateFormat.getDateFormat(context).format(new Date())));
1779 time.setTextColor(U.getAccentColor(context));
1783 @TargetApi(Build.VERSION_CODES.M)
1784 private Drawable getBatteryDrawable() {
1785 BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
1786 int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1788 if(batLevel == Integer.MIN_VALUE)
1791 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
1792 Intent batteryStatus = context.registerReceiver(null, ifilter);
1794 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
1795 boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
1796 status == BatteryManager.BATTERY_STATUS_FULL;
1799 if(batLevel < 10 && !isCharging)
1800 batDrawable = "alert";
1801 else if(batLevel < 25)
1803 else if(batLevel < 40)
1805 else if(batLevel < 55)
1807 else if(batLevel < 70)
1809 else if(batLevel < 85)
1811 else if(batLevel < 95)
1814 batDrawable = "full";
1818 charging = "charging_";
1822 String batRes = "tb_battery_" + charging + batDrawable;
1823 int id = getResourceIdFor(batRes);
1825 return getDrawableForSysTray(id);
1828 @TargetApi(Build.VERSION_CODES.M)
1829 private Drawable getWifiDrawable() {
1830 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1832 NetworkInfo ethernet = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
1833 if(ethernet != null && ethernet.isConnected())
1834 return getDrawableForSysTray(R.drawable.tb_settings_ethernet);
1836 NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
1837 if(wifi == null || !wifi.isConnected())
1840 WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
1841 int numberOfLevels = 5;
1843 WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1844 int level = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), numberOfLevels);
1846 String wifiRes = "tb_signal_wifi_" + level + "_bar";
1847 int id = getResourceIdFor(wifiRes);
1849 return getDrawableForSysTray(id);
1852 private Drawable getBluetoothDrawable() {
1853 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1854 if(adapter != null && adapter.isEnabled())
1855 return getDrawableForSysTray(R.drawable.tb_bluetooth);
1860 @TargetApi(Build.VERSION_CODES.M)
1861 private Drawable getCellularDrawable() {
1862 if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0)
1863 return getDrawableForSysTray(R.drawable.tb_airplanemode_active);
1865 if(cellStrength == -1)
1868 String cellRes = "tb_signal_cellular_" + cellStrength + "_bar";
1869 int id = getResourceIdFor(cellRes);
1871 return getDrawableForSysTray(id);
1874 private Drawable getDrawableForSysTray(int id) {
1875 Drawable drawable = null;
1877 drawable = ContextCompat.getDrawable(context, id);
1878 } catch (Resources.NotFoundException ignored) {}
1880 if(drawable == null) return null;
1882 drawable.setTint(U.getAccentColor(context));
1886 private int getResourceIdFor(String name) {
1887 String packageName = context.getResources().getResourcePackageName(R.drawable.tb_dummy);
1888 return context.getResources().getIdentifier(name, "drawable", packageName);