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.content.res.ResourcesCompat;
61 import androidx.core.graphics.ColorUtils;
62 import android.telephony.PhoneStateListener;
63 import android.telephony.SignalStrength;
64 import android.telephony.TelephonyManager;
65 import android.text.format.DateFormat;
66 import android.view.Display;
67 import android.view.Gravity;
68 import android.view.LayoutInflater;
69 import android.view.MotionEvent;
70 import android.view.PointerIcon;
71 import android.view.View;
72 import android.view.ViewGroup;
73 import android.view.WindowManager;
74 import android.view.inputmethod.InputMethodManager;
75 import android.widget.Button;
76 import android.widget.FrameLayout;
77 import android.widget.ImageView;
79 import java.lang.reflect.Field;
80 import java.util.ArrayList;
81 import java.util.Collections;
82 import java.util.Date;
83 import java.util.HashMap;
84 import java.util.List;
87 import android.widget.LinearLayout;
88 import android.widget.Space;
89 import android.widget.TextView;
91 import com.farmerbb.taskbar.BuildConfig;
92 import com.farmerbb.taskbar.activity.HomeActivityDelegate;
93 import com.farmerbb.taskbar.activity.MainActivity;
94 import com.farmerbb.taskbar.R;
95 import com.farmerbb.taskbar.activity.HomeActivity;
96 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
97 import com.farmerbb.taskbar.activity.SecondaryHomeActivity;
98 import com.farmerbb.taskbar.util.TaskbarPosition;
99 import com.farmerbb.taskbar.util.AppEntry;
100 import com.farmerbb.taskbar.util.DisplayInfo;
101 import com.farmerbb.taskbar.helper.FreeformHackHelper;
102 import com.farmerbb.taskbar.util.IconCache;
103 import com.farmerbb.taskbar.helper.LauncherHelper;
104 import com.farmerbb.taskbar.util.PinnedBlockedApps;
105 import com.farmerbb.taskbar.helper.MenuHelper;
106 import com.farmerbb.taskbar.util.U;
108 import static com.farmerbb.taskbar.util.Constants.*;
110 public class TaskbarController extends UIController {
112 private LinearLayout layout;
113 private ImageView startButton;
114 private LinearLayout taskbar;
115 private FrameLayout scrollView;
116 private Button button;
118 private FrameLayout dashboardButton;
119 private LinearLayout navbarButtons;
120 private LinearLayout sysTrayLayout;
121 private FrameLayout sysTrayParentLayout;
122 private TextView time;
123 private ImageView notificationCountCircle;
124 private TextView notificationCountText;
126 private Handler handler;
127 private Handler handler2;
128 private Thread thread;
129 private Thread thread2;
131 private boolean isShowingRecents = true;
132 private boolean shouldRefreshRecents = true;
133 private boolean taskbarShownTemporarily = false;
134 private boolean taskbarHiddenTemporarily = false;
135 private boolean isRefreshingRecents = false;
136 private boolean isFirstStart = true;
138 private boolean startThread2 = false;
139 private boolean stopThread2 = false;
141 private int refreshInterval = -1;
142 private long searchInterval = -1;
143 private String sortOrder = "false";
144 private boolean runningAppsOnly = false;
146 private int currentTaskbarPosition = 0;
147 private boolean showHideAutomagically = false;
148 private boolean positionIsVertical = false;
149 private boolean dashboardEnabled = false;
150 private boolean navbarButtonsEnabled = false;
151 private boolean sysTrayEnabled = false;
153 private List<String> currentTaskbarIds = new ArrayList<>();
154 private int numOfPinnedApps = -1;
156 private int cellStrength = -1;
157 private int notificationCount = 0;
158 private int numOfSysTrayIcons = 0;
160 private boolean matchParent;
161 private Runnable updateParamsRunnable;
163 private final Map<Integer, Boolean> sysTrayIconStates = new HashMap<>();
165 private final View.OnClickListener ocl = view ->
166 U.sendBroadcast(context, ACTION_TOGGLE_START_MENU);
168 private final BroadcastReceiver showReceiver = new BroadcastReceiver() {
170 public void onReceive(Context context, Intent intent) {
175 private final BroadcastReceiver hideReceiver = new BroadcastReceiver() {
177 public void onReceive(Context context, Intent intent) {
182 private final BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
184 public void onReceive(Context context, Intent intent) {
189 private final BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
191 public void onReceive(Context context, Intent intent) {
192 tempHideTaskbar(false);
196 private final BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
198 public void onReceive(Context context, Intent intent) {
199 if(startButton.getVisibility() == View.GONE
200 && (!LauncherHelper.getInstance().isOnHomeScreen(context) || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
201 layout.setVisibility(View.GONE);
205 private final BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
207 public void onReceive(Context context, Intent intent) {
208 if(startButton.getVisibility() == View.GONE)
209 layout.setVisibility(View.VISIBLE);
213 private final BroadcastReceiver notificationCountReceiver = new BroadcastReceiver() {
215 public void onReceive(Context context, Intent intent) {
216 notificationCount = intent.getIntExtra(EXTRA_COUNT, 0);
220 @TargetApi(Build.VERSION_CODES.M)
221 private final PhoneStateListener listener = new PhoneStateListener() {
223 public void onSignalStrengthsChanged(SignalStrength signalStrength) {
225 cellStrength = signalStrength.getLevel();
226 } catch (SecurityException e) {
232 public TaskbarController(Context context) {
237 public void onCreateHost(UIHost host) {
238 init(context, host, () -> drawTaskbar(host));
241 private void drawTaskbar(UIHost host) {
242 IconCache.getInstance(context).clearCache();
244 // Initialize layout params
245 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
246 TaskbarPosition.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
248 final ViewParams params = new ViewParams(
249 WindowManager.LayoutParams.WRAP_CONTENT,
250 WindowManager.LayoutParams.WRAP_CONTENT,
252 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
253 getBottomMargin(context, host)
256 // Determine where to show the taskbar on screen
257 String taskbarPosition = TaskbarPosition.getTaskbarPosition(context);
258 params.gravity = getTaskbarGravity(taskbarPosition);
259 int layoutId = getTaskbarLayoutId(taskbarPosition);
260 positionIsVertical = TaskbarPosition.isVertical(taskbarPosition);
263 SharedPreferences pref = U.getSharedPreferences(context);
264 boolean altButtonConfig = pref.getBoolean(PREF_ALT_BUTTON_CONFIG, false);
266 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
267 taskbar = layout.findViewById(R.id.taskbar);
268 scrollView = layout.findViewById(R.id.taskbar_scrollview);
270 int backgroundTint = U.getBackgroundTint(context);
271 int accentColor = U.getAccentColor(context);
273 if(altButtonConfig) {
274 space = layout.findViewById(R.id.space_alt);
275 layout.findViewById(R.id.space).setVisibility(View.GONE);
277 space = layout.findViewById(R.id.space);
278 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
281 space.setOnClickListener(v -> toggleTaskbar(true));
283 startButton = layout.findViewById(R.id.start_button);
284 drawStartButton(context, startButton, pref, accentColor);
286 refreshInterval = (int) (Float.parseFloat(pref.getString(PREF_REFRESH_FREQUENCY, "1")) * 1000);
287 if(refreshInterval == 0)
288 refreshInterval = 100;
290 sortOrder = pref.getString(PREF_SORT_ORDER, "false");
292 PREF_RECENTS_AMOUNT_RUNNING_APPS_ONLY
293 .equals(pref.getString(PREF_RECENTS_AMOUNT, PREF_RECENTS_AMOUNT_PAST_DAY));
294 searchInterval = getSearchInterval(pref);
296 U.sendBroadcast(context, ACTION_HIDE_START_MENU);
297 U.sendBroadcast(context, ACTION_UPDATE_HOME_SCREEN_MARGINS);
299 if(altButtonConfig) {
300 button = layout.findViewById(R.id.hide_taskbar_button_alt);
301 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
303 button = layout.findViewById(R.id.hide_taskbar_button);
304 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
308 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
309 } catch (RuntimeException ignored) {}
312 button.setOnClickListener(v -> toggleTaskbar(true));
314 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
315 ? R.id.hide_taskbar_button_layout_alt
316 : R.id.hide_taskbar_button_layout);
317 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
319 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
320 ? R.id.hide_taskbar_button_layout
321 : R.id.hide_taskbar_button_layout_alt);
322 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
324 dashboardButton = layout.findViewById(R.id.dashboard_button);
325 navbarButtons = layout.findViewById(R.id.navbar_buttons);
326 dashboardEnabled = drawDashboardButton(context, layout, dashboardButton, accentColor);
327 navbarButtonsEnabled = drawNavbarButtons(context, layout, pref, accentColor);
328 if(!navbarButtonsEnabled)
329 navbarButtons.setVisibility(View.GONE);
331 sysTrayEnabled = U.isSystemTrayEnabled(context);
334 drawSysTray(context, layoutId, layout);
337 layout.setBackgroundColor(backgroundTint);
338 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
339 button.setTextColor(accentColor);
341 applyMarginFix(host, layout, params);
343 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
345 else if(!pref.getBoolean(PREF_COLLAPSED, false) && pref.getBoolean(PREF_TASKBAR_ACTIVE, false))
346 toggleTaskbar(false);
348 if(pref.getBoolean(PREF_AUTO_HIDE_NAVBAR, false))
349 U.showHideNavigationBar(context, false);
351 if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
352 U.sendBroadcast(context, ACTION_FINISH_FREEFORM_ACTIVITY);
354 U.newHandler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
357 U.registerReceiver(context, showReceiver, ACTION_SHOW_TASKBAR);
358 U.registerReceiver(context, hideReceiver, ACTION_HIDE_TASKBAR);
359 U.registerReceiver(context, tempShowReceiver, ACTION_TEMP_SHOW_TASKBAR);
360 U.registerReceiver(context, tempHideReceiver, ACTION_TEMP_HIDE_TASKBAR);
361 U.registerReceiver(context, startMenuAppearReceiver, ACTION_START_MENU_APPEARING);
362 U.registerReceiver(context, startMenuDisappearReceiver, ACTION_START_MENU_DISAPPEARING);
365 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
366 manager.listen(listener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
368 U.registerReceiver(context, notificationCountReceiver, ACTION_NOTIFICATION_COUNT_CHANGED);
369 U.sendBroadcast(context, ACTION_REQUEST_NOTIFICATION_COUNT);
373 updateParamsRunnable = () -> {
374 ViewParams newParams;
375 if(TaskbarPosition.isVertical(context)) {
376 newParams = matchParent
377 ? params.updateHeight(WindowManager.LayoutParams.MATCH_PARENT)
378 : params.updateHeight(WindowManager.LayoutParams.WRAP_CONTENT);
380 newParams = matchParent
381 ? params.updateWidth(WindowManager.LayoutParams.MATCH_PARENT)
382 : params.updateWidth(WindowManager.LayoutParams.WRAP_CONTENT);
385 host.updateViewLayout(layout, newParams);
388 startRefreshingRecents();
390 host.addView(layout, params);
392 isFirstStart = false;
395 @SuppressLint("RtlHardcoded")
397 int getTaskbarGravity(String taskbarPosition) {
398 int gravity = Gravity.BOTTOM | Gravity.LEFT;
399 switch(taskbarPosition) {
400 case POSITION_BOTTOM_LEFT:
401 case POSITION_BOTTOM_VERTICAL_LEFT:
402 gravity = Gravity.BOTTOM | Gravity.LEFT;
404 case POSITION_BOTTOM_RIGHT:
405 case POSITION_BOTTOM_VERTICAL_RIGHT:
406 gravity = Gravity.BOTTOM | Gravity.RIGHT;
408 case POSITION_TOP_LEFT:
409 case POSITION_TOP_VERTICAL_LEFT:
410 gravity = Gravity.TOP | Gravity.LEFT;
412 case POSITION_TOP_RIGHT:
413 case POSITION_TOP_VERTICAL_RIGHT:
414 gravity = Gravity.TOP | Gravity.RIGHT;
421 int getTaskbarLayoutId(String taskbarPosition) {
422 int layoutId = R.layout.tb_taskbar_left;
423 switch(taskbarPosition) {
424 case POSITION_BOTTOM_LEFT:
425 case POSITION_TOP_LEFT:
426 layoutId = R.layout.tb_taskbar_left;
428 case POSITION_BOTTOM_VERTICAL_LEFT:
429 case POSITION_BOTTOM_VERTICAL_RIGHT:
430 layoutId = R.layout.tb_taskbar_vertical;
432 case POSITION_BOTTOM_RIGHT:
433 case POSITION_TOP_RIGHT:
434 layoutId = R.layout.tb_taskbar_right;
436 case POSITION_TOP_VERTICAL_LEFT:
437 case POSITION_TOP_VERTICAL_RIGHT:
438 layoutId = R.layout.tb_taskbar_top_vertical;
445 void drawStartButton(Context context, ImageView startButton, SharedPreferences pref, int accentColor) {
446 Drawable allAppsIcon = ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon);
449 switch(pref.getString(PREF_START_BUTTON_IMAGE, U.getDefaultStartButtonImage(context))) {
450 case PREF_START_BUTTON_IMAGE_DEFAULT:
451 startButton.setImageDrawable(allAppsIcon);
452 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
454 case PREF_START_BUTTON_IMAGE_APP_LOGO:
457 if(U.isAndroidGeneric(context)) {
459 String bdPackageName = "com.boringdroid.systemui";
460 Resources res = context.getPackageManager().getResourcesForApplication(bdPackageName);
461 int id = res.getIdentifier("bt_all_apps", "drawable", bdPackageName);
462 drawable = ResourcesCompat.getDrawable(res, id, null);
463 } catch (Exception e) {
464 drawable = ContextCompat.getDrawable(context, R.drawable.tb_bliss);
465 drawable.setTint(accentColor);
468 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
469 LauncherActivityInfo info = launcherApps.getActivityList(context.getPackageName(), Process.myUserHandle()).get(0);
470 drawable = IconCache.getInstance(context).getIcon(context, context.getPackageManager(), info);
473 startButton.setImageDrawable(drawable);
474 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding_alt);
476 case PREF_START_BUTTON_IMAGE_CUSTOM:
477 U.applyCustomImage(context, "custom_image", startButton, allAppsIcon);
478 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
482 startButton.setPadding(padding, padding, padding, padding);
483 startButton.setOnClickListener(ocl);
484 startButton.setOnLongClickListener(view -> {
489 startButton.setOnGenericMotionListener((view, motionEvent) -> {
490 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
491 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
499 boolean drawDashboardButton(Context context,
501 FrameLayout dashboardButton,
503 boolean dashboardEnabled = U.getBooleanPrefWithDefault(context, PREF_DASHBOARD);
504 if(dashboardEnabled) {
505 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
506 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
507 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
508 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
509 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
510 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
512 dashboardButton.setOnClickListener(v -> U.sendBroadcast(context, ACTION_TOGGLE_DASHBOARD));
513 dashboardButton.setVisibility(View.VISIBLE);
515 dashboardButton.setVisibility(View.GONE);
517 return dashboardEnabled;
521 boolean drawNavbarButtons(Context context,
523 SharedPreferences pref,
525 boolean navbarButtonsEnabled = false;
526 if(pref.getBoolean(PREF_BUTTON_BACK, false)) {
527 navbarButtonsEnabled = true;
529 ImageView backButton = layout.findViewById(R.id.button_back);
530 backButton.setColorFilter(accentColor);
531 backButton.setVisibility(View.VISIBLE);
532 backButton.setOnClickListener(v -> {
533 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
534 if(U.shouldCollapse(context, false))
538 backButton.setOnLongClickListener(v -> {
539 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
540 imm.showInputMethodPicker();
542 if(U.shouldCollapse(context, false))
548 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
549 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
550 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
551 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
552 imm.showInputMethodPicker();
554 if(U.shouldCollapse(context, false))
561 if(pref.getBoolean(PREF_BUTTON_HOME, false)) {
562 navbarButtonsEnabled = true;
564 ImageView homeButton = layout.findViewById(R.id.button_home);
565 homeButton.setColorFilter(accentColor);
566 homeButton.setVisibility(View.VISIBLE);
567 homeButton.setOnClickListener(v -> {
568 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
569 if(U.shouldCollapse(context, false))
573 homeButton.setOnLongClickListener(v -> {
574 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
575 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
578 context.startActivity(voiceSearchIntent);
579 } catch (ActivityNotFoundException ignored) {}
581 if(U.shouldCollapse(context, false))
587 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
588 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
589 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
590 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
591 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
594 context.startActivity(voiceSearchIntent);
595 } catch (ActivityNotFoundException ignored) {}
597 if(U.shouldCollapse(context, false))
604 if(pref.getBoolean(PREF_BUTTON_RECENTS, false)) {
605 navbarButtonsEnabled = true;
607 ImageView recentsButton = layout.findViewById(R.id.button_recents);
608 recentsButton.setColorFilter(accentColor);
609 recentsButton.setVisibility(View.VISIBLE);
610 recentsButton.setOnClickListener(v -> {
611 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
612 if(U.shouldCollapse(context, false))
616 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
617 recentsButton.setOnLongClickListener(v -> {
618 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
619 if(U.shouldCollapse(context, false))
625 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
626 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
627 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
628 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
629 if(U.shouldCollapse(context, false))
636 return navbarButtonsEnabled;
640 long getSearchInterval(SharedPreferences pref) {
641 long searchInterval = -1;
642 switch(pref.getString(PREF_RECENTS_AMOUNT, PREF_RECENTS_AMOUNT_PAST_DAY)) {
643 case PREF_RECENTS_AMOUNT_PAST_DAY:
644 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
646 case PREF_RECENTS_AMOUNT_APP_START:
647 long appStartTime = pref.getLong(PREF_TIME_OF_SERVICE_START, System.currentTimeMillis());
648 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
649 searchInterval = Math.max(deviceStartTime, appStartTime);
651 case PREF_RECENTS_AMOUNT_SHOW_ALL:
655 return searchInterval;
659 void drawSysTray(Context context, int layoutId, LinearLayout layout) {
660 sysTrayLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.tb_system_tray, null);
662 FrameLayout.LayoutParams sysTrayParams = new FrameLayout.LayoutParams(
663 FrameLayout.LayoutParams.WRAP_CONTENT,
664 context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size)
667 if(layoutId == R.layout.tb_taskbar_right) {
668 time = sysTrayLayout.findViewById(R.id.time_left);
669 sysTrayParams.gravity = Gravity.START;
670 sysTrayLayout.findViewById(R.id.space_right).setVisibility(View.VISIBLE);
672 time = sysTrayLayout.findViewById(R.id.time_right);
673 sysTrayParams.gravity = Gravity.END;
674 sysTrayLayout.findViewById(R.id.space_left).setVisibility(View.VISIBLE);
677 time.setVisibility(View.VISIBLE);
678 sysTrayLayout.setLayoutParams(sysTrayParams);
680 if(!U.isLibrary(context)) {
681 sysTrayLayout.setOnClickListener(v -> {
682 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, () -> {
683 if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
684 U.showToast(context, R.string.tb_opening_notification_tray);
685 U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
689 if(U.shouldCollapse(context, false))
693 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
694 sysTrayLayout.setOnLongClickListener(v -> {
695 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, () -> {
696 if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
697 U.showToast(context, R.string.tb_opening_quick_settings);
698 U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
702 if(U.shouldCollapse(context, false))
708 sysTrayLayout.setOnGenericMotionListener((view, motionEvent) -> {
709 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
710 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
711 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, () -> {
712 if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
713 U.showToast(context, R.string.tb_opening_quick_settings);
714 U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
718 if(U.shouldCollapse(context, false))
726 notificationCountCircle = sysTrayLayout.findViewById(R.id.notification_count_circle);
727 notificationCountText = sysTrayLayout.findViewById(R.id.notification_count_text);
729 sysTrayParentLayout = layout.findViewById(R.id.add_systray_here);
730 sysTrayParentLayout.setVisibility(View.VISIBLE);
731 sysTrayParentLayout.addView(sysTrayLayout);
733 sysTrayIconStates.clear();
734 sysTrayIconStates.put(R.id.cellular, false);
735 sysTrayIconStates.put(R.id.bluetooth, false);
736 sysTrayIconStates.put(R.id.wifi, false);
737 sysTrayIconStates.put(R.id.battery, false);
738 sysTrayIconStates.put(R.id.notification_count, false);
741 private void startRefreshingRecents() {
742 if(thread != null) thread.interrupt();
745 SharedPreferences pref = U.getSharedPreferences(context);
746 showHideAutomagically = pref.getBoolean(PREF_HIDE_WHEN_KEYBOARD_SHOWN, false);
748 currentTaskbarIds.clear();
750 handler = U.newHandler();
751 thread = new Thread(() -> {
752 updateRecentApps(true);
754 if(!isRefreshingRecents) {
755 isRefreshingRecents = true;
757 while(shouldRefreshRecents) {
758 updateRecentApps(false);
760 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen()) {
763 int[] location = new int[2];
764 layout.getLocationOnScreen(location);
766 if(location[1] != 0) {
767 if(location[1] > currentTaskbarPosition) {
768 currentTaskbarPosition = location[1];
769 } else if(location[1] < currentTaskbarPosition) {
770 if(currentTaskbarPosition - location[1] == getNavBarSize())
771 currentTaskbarPosition = location[1];
772 else if(!startThread2) {
774 tempHideTaskbar(true);
782 SystemClock.sleep(refreshInterval);
785 isRefreshingRecents = false;
792 @SuppressWarnings("Convert2streamapi")
793 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
794 private void updateRecentApps(final boolean firstRefresh) {
795 if(isScreenOff()) return;
799 SharedPreferences pref = U.getSharedPreferences(context);
800 final PackageManager pm = context.getPackageManager();
801 final List<AppEntry> entries = new ArrayList<>();
802 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
803 int maxNumOfEntries = firstRefresh ? 0 : U.getMaxNumOfEntries(context);
804 boolean fullLength = pref.getBoolean(PREF_FULL_LENGTH, true);
806 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
807 List<AppEntry> pinnedApps = pba.getPinnedApps();
808 List<AppEntry> blockedApps = pba.getBlockedApps();
809 List<String> applicationIdsToRemove = new ArrayList<>();
811 // Filter out anything on the pinned/blocked apps lists
812 int realNumOfPinnedApps = filterRealPinnedApps(context, pinnedApps, entries, applicationIdsToRemove);
814 if(blockedApps.size() > 0) {
815 //noinspection SynchronizationOnLocalVariableOrMethodParameter
816 synchronized(blockedApps) {
817 for(AppEntry entry : blockedApps) {
818 applicationIdsToRemove.add(entry.getPackageName());
823 // Get list of all recently used apps
824 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
825 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
826 if(realNumOfPinnedApps < maxNumOfEntries) {
827 List<AppEntry> usageStatsList2 = new ArrayList<>();
828 List<AppEntry> usageStatsList3 = new ArrayList<>();
829 List<AppEntry> usageStatsList4 = new ArrayList<>();
830 List<AppEntry> usageStatsList5 = new ArrayList<>();
831 List<AppEntry> usageStatsList6;
833 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
834 homeIntent.addCategory(Intent.CATEGORY_HOME);
835 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
837 // Filter out apps without a launcher intent
838 // Also filter out the current launcher, and Taskbar itself
839 for(AppEntry packageInfo : usageStatsList) {
840 if(hasLauncherIntent(packageInfo.getPackageName())
841 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
842 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName)
843 && (!(U.launcherIsDefault(context) && pref.getBoolean(PREF_DESKTOP_MODE, false))
844 || !packageInfo.getPackageName().equals(pref.getString(PREF_HSL_ID, "null"))))
845 usageStatsList2.add(packageInfo);
848 // Filter out apps that don't fall within our current search interval
849 for(AppEntry stats : usageStatsList2) {
850 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
851 usageStatsList3.add(stats);
854 // Sort apps by either most recently used, or most time used
855 if(!runningAppsOnly && sortOrder.contains("most_used")) {
856 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
858 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
861 // Filter out any duplicate entries
862 List<String> applicationIds = new ArrayList<>();
863 for(AppEntry stats : usageStatsList3) {
864 if(!applicationIds.contains(stats.getPackageName())) {
865 usageStatsList4.add(stats);
866 applicationIds.add(stats.getPackageName());
870 // Filter out the currently running foreground app, if requested by the user
871 filterForegroundApp(context, pref, searchInterval, applicationIdsToRemove);
873 for(AppEntry stats : usageStatsList4) {
874 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
875 usageStatsList5.add(stats);
879 // Truncate list to a maximum length
880 if(usageStatsList5.size() > maxNumOfEntries)
881 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
883 usageStatsList6 = usageStatsList5;
885 // Determine if we need to reverse the order
886 if(needToReverseOrder(context, sortOrder)) {
887 Collections.reverse(usageStatsList6);
890 // Generate the AppEntries for the recent apps list
891 int number = usageStatsList6.size() == maxNumOfEntries
892 ? usageStatsList6.size() - realNumOfPinnedApps
893 : usageStatsList6.size();
895 generateAppEntries(context, number, usageStatsList6, entries, launcherAppCache);
898 while(entries.size() > maxNumOfEntries) {
900 entries.remove(entries.size() - 1);
901 launcherAppCache.remove(launcherAppCache.size() - 1);
902 } catch (ArrayIndexOutOfBoundsException ignored) {}
905 // Determine if we need to reverse the order again
906 if(TaskbarPosition.isVertical(context)) {
907 Collections.reverse(entries);
908 Collections.reverse(launcherAppCache);
911 // Now that we've generated the list of apps,
912 // we need to determine if we need to redraw the Taskbar or not
913 boolean shouldRedrawTaskbar = firstRefresh;
915 List<String> finalApplicationIds = new ArrayList<>();
916 for(AppEntry entry : entries) {
917 finalApplicationIds.add(entry.getPackageName());
920 int realNumOfSysTrayIcons = 0;
921 for(Integer key : sysTrayIconStates.keySet()) {
922 if(sysTrayIconStates.get(key))
923 realNumOfSysTrayIcons++;
926 if(finalApplicationIds.size() != currentTaskbarIds.size()
927 || numOfPinnedApps != realNumOfPinnedApps
928 || numOfSysTrayIcons != realNumOfSysTrayIcons)
929 shouldRedrawTaskbar = true;
931 for(int i = 0; i < finalApplicationIds.size(); i++) {
932 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
933 shouldRedrawTaskbar = true;
939 if(shouldRedrawTaskbar) {
940 currentTaskbarIds = finalApplicationIds;
941 numOfPinnedApps = realNumOfPinnedApps;
942 numOfSysTrayIcons = realNumOfSysTrayIcons;
944 populateAppEntries(context, pm, entries, launcherAppCache);
946 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
949 if(numOfEntries > 0 || fullLength) {
950 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
951 calculateScrollViewParams(context, pref, params, fullLength, numOfEntries);
952 scrollView.setLayoutParams(params);
954 for(Integer key : sysTrayIconStates.keySet()) {
955 sysTrayLayout.findViewById(key).setVisibility(
956 sysTrayIconStates.get(key) ? View.VISIBLE : View.GONE
960 taskbar.removeAllViews();
961 for(int i = 0; i < entries.size(); i++) {
962 taskbar.addView(getView(entries, i));
966 updateRunningAppIndicators(pinnedApps, usageStatsList, entries);
968 isShowingRecents = true;
969 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
971 scrollView.setVisibility(View.INVISIBLE);
973 scrollView.setVisibility(View.VISIBLE);
976 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE) {
981 TaskbarPosition.getTaskbarPosition(context),
988 isShowingRecents = false;
989 scrollView.setVisibility(View.GONE);
992 } else if(runningAppsOnly)
993 handler.post(() -> updateRunningAppIndicators(pinnedApps, usageStatsList, entries));
994 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
995 currentTaskbarIds.clear();
997 isShowingRecents = false;
998 scrollView.setVisibility(View.GONE);
1004 void calculateScrollViewParams(Context context,
1005 SharedPreferences pref,
1006 ViewGroup.LayoutParams params,
1009 DisplayInfo display = U.getDisplayInfo(context, true);
1010 int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size) * numOfEntries;
1011 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
1014 float baseStart = U.getBaseTaskbarSizeStart(context);
1015 float baseEnd = U.getBaseTaskbarSizeEnd(context, sysTrayIconStates);
1016 int baseTotal = Math.round(baseStart + baseEnd);
1018 int diff = Math.round(Math.max(baseStart, baseEnd) - Math.min(baseStart, baseEnd));
1019 boolean startIsBigger = Math.max(baseStart, baseEnd) == baseStart;
1021 if(TaskbarPosition.isVertical(context)) {
1022 maxScreenSize = Math.max(0, display.height
1023 - U.getStatusBarHeight(context)
1026 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
1027 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1031 Space whitespaceStart = layout.findViewById(R.id.whitespace_start);
1032 Space whitespaceEnd = layout.findViewById(R.id.whitespace_end);
1033 int height = maxScreenSize - recentsSize;
1035 if(pref.getBoolean(PREF_CENTERED_ICONS, false)) {
1036 int startHeight = (height / 2) + (diff / (startIsBigger ? -2 : 2));
1037 int endHeight = (height / 2) + (diff / (startIsBigger ? 2 : -2));
1039 if(startHeight < 0) {
1045 startHeight = height;
1049 ViewGroup.LayoutParams startParams = whitespaceStart.getLayoutParams();
1050 startParams.height = startHeight;
1051 whitespaceStart.setLayoutParams(startParams);
1053 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1054 endParams.height = endHeight;
1055 whitespaceEnd.setLayoutParams(endParams);
1057 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1058 endParams.height = height;
1059 whitespaceEnd.setLayoutParams(endParams);
1061 } catch (NullPointerException ignored) {}
1064 maxScreenSize = Math.max(0, display.width - baseTotal);
1066 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
1067 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1071 Space whitespaceStart = layout.findViewById(R.id.whitespace_start);
1072 Space whitespaceEnd = layout.findViewById(R.id.whitespace_end);
1073 int width = maxScreenSize - recentsSize;
1075 if(pref.getBoolean(PREF_CENTERED_ICONS, false)) {
1076 int startWidth = (width / 2) + (diff / (startIsBigger ? -2 : 2));
1077 int endWidth = (width / 2) + (diff / (startIsBigger ? 2 : -2));
1079 if(startWidth < 0) {
1089 ViewGroup.LayoutParams startParams = whitespaceStart.getLayoutParams();
1090 startParams.width = startWidth;
1091 whitespaceStart.setLayoutParams(startParams);
1093 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1094 endParams.width = endWidth;
1095 whitespaceEnd.setLayoutParams(endParams);
1097 ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1098 endParams.width = width;
1099 whitespaceEnd.setLayoutParams(endParams);
1101 } catch (NullPointerException ignored) {}
1105 boolean realMatchParent = maxRecentsSize >= maxScreenSize
1106 && pref.getBoolean(PREF_COLLAPSED, false)
1107 && !(TaskbarPosition.isVertical(context) && U.isChromeOs(context));
1109 if(realMatchParent != matchParent) {
1110 matchParent = realMatchParent;
1111 U.newHandler().post(updateParamsRunnable);
1116 void scrollTaskbar(FrameLayout scrollView,
1117 LinearLayout taskbar,
1118 String taskbarPosition,
1120 boolean shouldRefreshRecents) {
1121 if(TaskbarPosition.isVertical(taskbarPosition)) {
1122 if(sortOrder.contains("false")) {
1123 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1124 } else if(sortOrder.contains("true")) {
1125 scrollView.scrollTo(0, 0);
1128 if(sortOrder.contains("false")) {
1129 scrollView.scrollTo(0, 0);
1130 } else if(sortOrder.contains("true")) {
1131 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1135 if(shouldRefreshRecents) {
1136 scrollView.setVisibility(View.VISIBLE);
1140 @TargetApi(Build.VERSION_CODES.M)
1142 void filterForegroundApp(Context context,
1143 SharedPreferences pref,
1144 long searchInterval,
1145 List<String> applicationIdsToRemove) {
1146 if(pref.getBoolean(PREF_HIDE_FOREGROUND, false)) {
1147 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1148 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
1149 UsageEvents.Event eventCache = new UsageEvents.Event();
1150 String currentForegroundApp = null;
1152 while (events.hasNextEvent()) {
1153 events.getNextEvent(eventCache);
1155 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
1156 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
1157 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
1158 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
1159 && !eventCache.getClassName().equals(HomeActivityDelegate.class.getCanonicalName())
1160 && !eventCache.getClassName().equals(SecondaryHomeActivity.class.getCanonicalName())
1161 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName()))) {
1162 currentForegroundApp = eventCache.getPackageName();
1167 if(!applicationIdsToRemove.contains(currentForegroundApp)) {
1168 applicationIdsToRemove.add(currentForegroundApp);
1174 boolean needToReverseOrder(Context context, String sortOrder) {
1175 switch(TaskbarPosition.getTaskbarPosition(context)) {
1176 case POSITION_BOTTOM_RIGHT:
1177 case POSITION_TOP_RIGHT:
1178 return sortOrder.contains("false");
1180 return sortOrder.contains("true");
1185 int filterRealPinnedApps(Context context,
1186 List<AppEntry> pinnedApps,
1187 List<AppEntry> entries,
1188 List<String> applicationIdsToRemove) {
1189 int realNumOfPinnedApps = 0;
1190 if(pinnedApps.size() > 0) {
1191 //noinspection SynchronizationOnLocalVariableOrMethodParameter
1192 synchronized(pinnedApps) {
1193 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1194 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
1196 for(AppEntry entry : pinnedApps) {
1197 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
1198 userManager.getUserForSerialNumber(entry.getUserId(context)));
1203 realNumOfPinnedApps--;
1205 applicationIdsToRemove.add(entry.getPackageName());
1208 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
1212 return realNumOfPinnedApps;
1216 void generateAppEntries(Context context,
1218 List<AppEntry> usageStatsList6,
1219 List<AppEntry> entries,
1220 List<LauncherActivityInfo> launcherAppCache) {
1221 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1222 LauncherApps launcherApps =
1223 (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
1225 final List<UserHandle> userHandles = userManager.getUserProfiles();
1227 final String googleSearchBoxPackage = "com.google.android.googlequicksearchbox";
1228 final String googleSearchBoxActivity =
1229 "com.google.android.googlequicksearchbox.SearchActivity";
1230 for(int i = 0; i < number; i++) {
1231 for(UserHandle handle : userHandles) {
1232 String packageName = usageStatsList6.get(i).getPackageName();
1233 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
1234 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
1235 if(!list.isEmpty()) {
1236 // Google App workaround
1237 if(!packageName.equals(googleSearchBoxPackage)) {
1238 launcherAppCache.add(list.get(0));
1240 boolean added = false;
1241 for(LauncherActivityInfo info : list) {
1242 if(info.getName().equals(googleSearchBoxActivity)) {
1243 launcherAppCache.add(info);
1249 launcherAppCache.add(list.get(0));
1253 AppEntry newEntry = new AppEntry(packageName, null, null, null, false);
1255 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
1256 newEntry.setLastTimeUsed(lastTimeUsed);
1257 entries.add(newEntry);
1265 @SuppressWarnings("SuspiciousListRemoveInLoop")
1267 void populateAppEntries(Context context,
1269 List<AppEntry> entries,
1270 List<LauncherActivityInfo> launcherAppCache) {
1271 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1273 int launcherAppCachePos = -1;
1274 for(int i = 0; i < entries.size(); i++) {
1275 if(entries.get(i).getComponentName() == null) {
1276 launcherAppCachePos++;
1277 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
1278 String packageName = entries.get(i).getPackageName();
1279 long lastTimeUsed = entries.get(i).getLastTimeUsed();
1283 AppEntry newEntry = new AppEntry(
1285 appInfo.getComponentName().flattenToString(),
1286 appInfo.getLabel().toString(),
1287 IconCache.getInstance(context).getIcon(context, pm, appInfo),
1290 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
1291 newEntry.setLastTimeUsed(lastTimeUsed);
1292 entries.add(i, newEntry);
1297 private void updateRunningAppIndicators(List<AppEntry> pinnedApps, List<AppEntry> usageStatsList, List<AppEntry> entries) {
1298 if(taskbar.getChildCount() != entries.size())
1301 List<String> pinnedPackageList = new ArrayList<>();
1302 List<String> runningPackageList = new ArrayList<>();
1304 for(AppEntry entry : pinnedApps)
1305 pinnedPackageList.add(entry.getPackageName());
1307 for(AppEntry entry : usageStatsList)
1308 runningPackageList.add(entry.getPackageName());
1310 for(int i = 0; i < taskbar.getChildCount(); i++) {
1311 View convertView = taskbar.getChildAt(i);
1312 String packageName = entries.get(i).getPackageName();
1314 ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1315 if(pinnedPackageList.contains(packageName) && !runningPackageList.contains(packageName))
1316 runningAppIndicator.setVisibility(View.GONE);
1318 runningAppIndicator.setVisibility(View.VISIBLE);
1319 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1324 private void toggleTaskbar(boolean userInitiated) {
1325 if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
1326 SharedPreferences pref = U.getSharedPreferences(context);
1327 LauncherHelper helper = LauncherHelper.getInstance();
1329 if(!pref.getBoolean(PREF_GRIP_REJECTION_TOAST_SHOWN, false)
1330 && !helper.isOnSecondaryHomeScreen(context)) {
1331 U.showToastLong(context, R.string.tb_essential_phone_grip_rejection);
1332 pref.edit().putBoolean(PREF_GRIP_REJECTION_TOAST_SHOWN, true).apply();
1336 if(startButton.getVisibility() == View.GONE)
1342 private void showTaskbar(boolean clearVariables) {
1343 if(clearVariables) {
1344 taskbarShownTemporarily = false;
1345 taskbarHiddenTemporarily = false;
1348 if(startButton.getVisibility() == View.GONE) {
1349 startButton.setVisibility(View.VISIBLE);
1350 space.setVisibility(View.VISIBLE);
1352 if(dashboardEnabled)
1353 dashboardButton.setVisibility(View.VISIBLE);
1355 if(navbarButtonsEnabled)
1356 navbarButtons.setVisibility(View.VISIBLE);
1358 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
1359 scrollView.setVisibility(View.INVISIBLE);
1362 sysTrayParentLayout.setVisibility(View.VISIBLE);
1364 shouldRefreshRecents = true;
1365 startRefreshingRecents();
1367 SharedPreferences pref = U.getSharedPreferences(context);
1368 pref.edit().putBoolean(PREF_COLLAPSED, true).apply();
1370 updateButton(false);
1372 U.newHandler().post(() -> U.sendBroadcast(context, ACTION_SHOW_START_MENU_SPACE));
1376 private void hideTaskbar(boolean clearVariables) {
1377 if(clearVariables) {
1378 taskbarShownTemporarily = false;
1379 taskbarHiddenTemporarily = false;
1382 if(startButton.getVisibility() == View.VISIBLE) {
1383 startButton.setVisibility(View.GONE);
1384 space.setVisibility(View.GONE);
1386 if(dashboardEnabled)
1387 dashboardButton.setVisibility(View.GONE);
1389 if(navbarButtonsEnabled)
1390 navbarButtons.setVisibility(View.GONE);
1392 if(isShowingRecents)
1393 scrollView.setVisibility(View.GONE);
1396 sysTrayParentLayout.setVisibility(View.GONE);
1398 shouldRefreshRecents = false;
1399 if(thread != null) thread.interrupt();
1401 SharedPreferences pref = U.getSharedPreferences(context);
1402 pref.edit().putBoolean(PREF_COLLAPSED, false).apply();
1406 if(clearVariables) {
1407 U.sendBroadcast(context, ACTION_HIDE_START_MENU);
1408 U.sendBroadcast(context, ACTION_HIDE_DASHBOARD);
1412 matchParent = false;
1413 U.newHandler().post(updateParamsRunnable);
1416 U.newHandler().post(() -> U.sendBroadcast(context, ACTION_HIDE_START_MENU_SPACE));
1420 private void tempShowTaskbar() {
1421 if(!taskbarHiddenTemporarily) {
1422 SharedPreferences pref = U.getSharedPreferences(context);
1423 if(!pref.getBoolean(PREF_COLLAPSED, false)) taskbarShownTemporarily = true;
1428 if(taskbarHiddenTemporarily)
1429 taskbarHiddenTemporarily = false;
1432 private void tempHideTaskbar(boolean monitorPositionChanges) {
1433 if(!taskbarShownTemporarily) {
1434 SharedPreferences pref = U.getSharedPreferences(context);
1435 if(pref.getBoolean(PREF_COLLAPSED, false)) taskbarHiddenTemporarily = true;
1440 if(taskbarShownTemporarily)
1441 taskbarShownTemporarily = false;
1443 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1444 if(thread2 != null) thread2.interrupt();
1446 handler2 = U.newHandler();
1447 thread2 = new Thread(() -> {
1448 stopThread2 = false;
1450 while(!stopThread2) {
1451 SystemClock.sleep(refreshInterval);
1453 handler2.post(() -> stopThread2 = checkPositionChange());
1456 startThread2 = false;
1463 private boolean checkPositionChange() {
1464 if(!isScreenOff() && layout != null) {
1465 int[] location = new int[2];
1466 layout.getLocationOnScreen(location);
1468 if(location[1] == 0) {
1471 if(location[1] > currentTaskbarPosition) {
1472 currentTaskbarPosition = location[1];
1473 if(taskbarHiddenTemporarily) {
1477 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1480 } else if(location[1] < currentTaskbarPosition
1481 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1482 currentTaskbarPosition = location[1];
1490 private int getNavBarSize() {
1491 Point size = new Point();
1492 Point realSize = new Point();
1494 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1495 Display display = wm.getDefaultDisplay();
1496 display.getSize(size);
1497 display.getRealSize(realSize);
1499 return realSize.y - size.y;
1503 public void onDestroyHost(UIHost host) {
1504 shouldRefreshRecents = false;
1508 host.removeView(layout);
1509 } catch (IllegalArgumentException ignored) {}
1511 SharedPreferences pref = U.getSharedPreferences(context);
1512 if(pref.getBoolean(PREF_SKIP_AUTO_HIDE_NAVBAR, false)) {
1513 pref.edit().remove(PREF_SKIP_AUTO_HIDE_NAVBAR).apply();
1514 } else if(pref.getBoolean(PREF_AUTO_HIDE_NAVBAR, false))
1515 U.showHideNavigationBar(context, true);
1517 U.unregisterReceiver(context, showReceiver);
1518 U.unregisterReceiver(context, hideReceiver);
1519 U.unregisterReceiver(context, tempShowReceiver);
1520 U.unregisterReceiver(context, tempHideReceiver);
1521 U.unregisterReceiver(context, startMenuAppearReceiver);
1522 U.unregisterReceiver(context, startMenuDisappearReceiver);
1524 if(sysTrayEnabled) {
1525 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1526 manager.listen(listener, PhoneStateListener.LISTEN_NONE);
1528 U.unregisterReceiver(context, notificationCountReceiver);
1531 isFirstStart = true;
1534 private void openContextMenu() {
1535 SharedPreferences pref = U.getSharedPreferences(context);
1537 Bundle args = new Bundle();
1538 args.putBoolean("dont_show_quit",
1539 LauncherHelper.getInstance().isOnHomeScreen(context)
1540 && !pref.getBoolean(PREF_TASKBAR_ACTIVE, false));
1541 args.putBoolean("is_start_button", true);
1543 U.startContextMenuActivity(context, args);
1546 private void updateButton(boolean isCollapsed) {
1547 SharedPreferences pref = U.getSharedPreferences(context);
1548 boolean hide = pref.getBoolean(PREF_INVISIBLE_BUTTON, false);
1550 if(button != null) button.setText(context.getString(isCollapsed ? R.string.tb_right_arrow : R.string.tb_left_arrow));
1551 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1554 @TargetApi(Build.VERSION_CODES.M)
1556 public void onRecreateHost(UIHost host) {
1557 if(layout != null) {
1559 host.removeView(layout);
1560 } catch (IllegalArgumentException ignored) {}
1562 currentTaskbarPosition = 0;
1564 if(U.canDrawOverlays(context))
1567 SharedPreferences pref = U.getSharedPreferences(context);
1568 pref.edit().putBoolean(PREF_TASKBAR_ACTIVE, false).apply();
1575 private View getView(List<AppEntry> list, int position) {
1576 View convertView = View.inflate(context, R.layout.tb_icon, null);
1578 final AppEntry entry = list.get(position);
1579 final SharedPreferences pref = U.getSharedPreferences(context);
1581 ImageView imageView = convertView.findViewById(R.id.icon);
1582 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1583 imageView.setImageDrawable(entry.getIcon(context));
1584 imageView2.setBackgroundColor(U.getAccentColor(context));
1586 String taskbarPosition = TaskbarPosition.getTaskbarPosition(context);
1587 if(pref.getBoolean(PREF_SHORTCUT_ICON, true)) {
1588 boolean shouldShowShortcutIcon;
1589 if(taskbarPosition.contains("vertical"))
1590 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1592 shouldShowShortcutIcon = position < numOfPinnedApps;
1594 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1597 if(POSITION_BOTTOM_RIGHT.equals(taskbarPosition) || POSITION_TOP_RIGHT.equals(taskbarPosition)) {
1598 imageView.setRotationY(180);
1599 imageView2.setRotationY(180);
1602 FrameLayout layout = convertView.findViewById(R.id.entry);
1603 layout.setOnClickListener(view -> U.launchApp(
1612 layout.setOnLongClickListener(view -> {
1613 int[] location = new int[2];
1614 view.getLocationOnScreen(location);
1615 openContextMenu(entry, location);
1619 layout.setOnGenericMotionListener((view, motionEvent) -> {
1620 int action = motionEvent.getAction();
1622 if(action == MotionEvent.ACTION_BUTTON_PRESS
1623 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1624 int[] location = new int[2];
1625 view.getLocationOnScreen(location);
1626 openContextMenu(entry, location);
1629 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean(PREF_VISUAL_FEEDBACK, true))
1630 view.setBackgroundColor(0);
1635 if(pref.getBoolean(PREF_VISUAL_FEEDBACK, true)) {
1636 layout.setOnHoverListener((v, event) -> {
1637 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1638 int accentColor = U.getAccentColor(context);
1639 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1640 v.setBackgroundColor(accentColor);
1643 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1644 v.setBackgroundColor(0);
1646 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1647 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1652 layout.setOnTouchListener((v, event) -> {
1653 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1661 private void openContextMenu(AppEntry entry, int[] location) {
1662 Bundle args = new Bundle();
1663 args.putSerializable("app_entry", entry);
1664 args.putInt("x", location[0]);
1665 args.putInt("y", location[1]);
1667 U.startContextMenuActivity(context, args);
1670 private List<AppEntry> getAppEntries() {
1671 SharedPreferences pref = U.getSharedPreferences(context);
1673 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString(PREF_MAX_NUM_OF_RECENTS, "10")));
1675 return getAppEntriesUsingUsageStats();
1678 @SuppressWarnings({"deprecation", "JavaReflectionMemberAccess"})
1679 @TargetApi(Build.VERSION_CODES.M)
1680 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1681 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1682 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1683 List<AppEntry> entries = new ArrayList<>();
1685 for(int i = 0; i < usageStatsList.size(); i++) {
1686 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1687 if(recentTaskInfo.id != -1) {
1688 String packageName = recentTaskInfo.baseActivity.getPackageName();
1689 AppEntry newEntry = new AppEntry(
1697 U.allowReflection();
1699 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1700 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1701 } catch (Exception e) {
1702 newEntry.setLastTimeUsed(i);
1705 entries.add(newEntry);
1712 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1713 private List<AppEntry> getAppEntriesUsingUsageStats() {
1714 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1715 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1716 List<AppEntry> entries = new ArrayList<>();
1718 for(UsageStats usageStats : usageStatsList) {
1719 AppEntry newEntry = new AppEntry(
1720 usageStats.getPackageName(),
1727 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1728 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1729 entries.add(newEntry);
1735 private boolean hasLauncherIntent(String packageName) {
1736 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1737 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1738 intentToResolve.setPackage(packageName);
1740 List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1741 return ris != null && ris.size() > 0;
1744 private boolean isScreenOff() {
1745 if(U.isChromeOs(context))
1748 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1749 return !pm.isInteractive();
1752 @SuppressLint("SetTextI18n")
1753 private void updateSystemTray() {
1754 if(!sysTrayEnabled) return;
1756 handler.post(() -> {
1757 Map<Integer, Drawable> drawables = new HashMap<>();
1758 drawables.put(R.id.battery, getBatteryDrawable());
1759 drawables.put(R.id.wifi, getWifiDrawable());
1760 drawables.put(R.id.bluetooth, getBluetoothDrawable());
1761 drawables.put(R.id.cellular, getCellularDrawable());
1763 for(Integer key : drawables.keySet()) {
1764 ImageView view = sysTrayLayout.findViewById(key);
1765 Drawable drawable = drawables.get(key);
1767 if(drawable != null) view.setImageDrawable(drawable);
1768 sysTrayIconStates.put(key, drawable != null);
1771 if(notificationCount > 0) {
1772 int color = ColorUtils.setAlphaComponent(U.getBackgroundTint(context), 255);
1773 notificationCountText.setTextColor(color);
1775 Drawable drawable = ContextCompat.getDrawable(context, R.drawable.tb_circle);
1776 drawable.setTint(U.getAccentColor(context));
1778 notificationCountCircle.setImageDrawable(drawable);
1779 notificationCountText.setText(Integer.toString(notificationCount));
1780 sysTrayIconStates.put(R.id.notification_count, true);
1782 sysTrayIconStates.put(R.id.notification_count, false);
1784 time.setText(context.getString(R.string.tb_systray_clock,
1785 DateFormat.getTimeFormat(context).format(new Date()),
1786 DateFormat.getDateFormat(context).format(new Date())));
1787 time.setTextColor(U.getAccentColor(context));
1791 @TargetApi(Build.VERSION_CODES.M)
1792 private Drawable getBatteryDrawable() {
1793 BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
1794 int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1796 if(batLevel == Integer.MIN_VALUE)
1799 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
1800 Intent batteryStatus = context.registerReceiver(null, ifilter);
1802 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
1803 boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
1804 status == BatteryManager.BATTERY_STATUS_FULL;
1807 if(batLevel < 10 && !isCharging)
1808 batDrawable = "alert";
1809 else if(batLevel < 25)
1811 else if(batLevel < 40)
1813 else if(batLevel < 55)
1815 else if(batLevel < 70)
1817 else if(batLevel < 85)
1819 else if(batLevel < 95)
1822 batDrawable = "full";
1826 charging = "charging_";
1830 String batRes = "tb_battery_" + charging + batDrawable;
1831 int id = getResourceIdFor(batRes);
1833 return getDrawableForSysTray(id);
1836 @TargetApi(Build.VERSION_CODES.M)
1837 private Drawable getWifiDrawable() {
1838 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1840 NetworkInfo ethernet = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
1841 if(ethernet != null && ethernet.isConnected())
1842 return getDrawableForSysTray(R.drawable.tb_settings_ethernet);
1844 NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
1845 if(wifi == null || !wifi.isConnected())
1848 WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
1849 int numberOfLevels = 5;
1851 WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1852 int level = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), numberOfLevels);
1854 String wifiRes = "tb_signal_wifi_" + level + "_bar";
1855 int id = getResourceIdFor(wifiRes);
1857 return getDrawableForSysTray(id);
1860 private Drawable getBluetoothDrawable() {
1861 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1862 if(adapter != null && adapter.isEnabled())
1863 return getDrawableForSysTray(R.drawable.tb_bluetooth);
1868 @TargetApi(Build.VERSION_CODES.M)
1869 private Drawable getCellularDrawable() {
1870 if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0)
1871 return getDrawableForSysTray(R.drawable.tb_airplanemode_active);
1873 if(cellStrength == -1)
1876 String cellRes = "tb_signal_cellular_" + cellStrength + "_bar";
1877 int id = getResourceIdFor(cellRes);
1879 return getDrawableForSysTray(id);
1882 private Drawable getDrawableForSysTray(int id) {
1883 Drawable drawable = null;
1885 drawable = ContextCompat.getDrawable(context, id);
1886 } catch (Resources.NotFoundException ignored) {}
1888 if(drawable == null) return null;
1890 drawable.setTint(U.getAccentColor(context));
1894 private int getResourceIdFor(String name) {
1895 String packageName = context.getResources().getResourcePackageName(R.drawable.tb_dummy);
1896 return context.getResources().getIdentifier(name, "drawable", packageName);