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.Bitmap;
39 import android.graphics.BitmapFactory;
40 import android.graphics.Color;
41 import android.graphics.Point;
42 import android.graphics.Typeface;
43 import android.graphics.drawable.BitmapDrawable;
44 import android.graphics.drawable.Drawable;
45 import android.net.ConnectivityManager;
46 import android.net.NetworkInfo;
47 import android.net.wifi.WifiInfo;
48 import android.net.wifi.WifiManager;
49 import android.os.BatteryManager;
50 import android.os.Build;
51 import android.os.Bundle;
52 import android.os.Handler;
53 import android.os.PowerManager;
54 import android.os.Process;
55 import android.os.SystemClock;
56 import android.os.UserHandle;
57 import android.os.UserManager;
58 import android.provider.Settings;
59 import android.speech.RecognizerIntent;
60 import androidx.core.content.ContextCompat;
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;
80 import java.lang.reflect.Field;
81 import java.util.ArrayList;
82 import java.util.Collections;
83 import java.util.Date;
84 import java.util.List;
86 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
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.content.TaskbarIntent;
99 import com.farmerbb.taskbar.util.AppEntry;
100 import com.farmerbb.taskbar.util.DisplayInfo;
101 import com.farmerbb.taskbar.util.FreeformHackHelper;
102 import com.farmerbb.taskbar.util.IconCache;
103 import com.farmerbb.taskbar.util.LauncherHelper;
104 import com.farmerbb.taskbar.util.PinnedBlockedApps;
105 import com.farmerbb.taskbar.util.MenuHelper;
106 import com.farmerbb.taskbar.util.U;
108 public class TaskbarController implements UIController {
110 private Context context;
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;
123 private Handler handler;
124 private Handler handler2;
125 private Thread thread;
126 private Thread thread2;
128 private boolean isShowingRecents = true;
129 private boolean shouldRefreshRecents = true;
130 private boolean taskbarShownTemporarily = false;
131 private boolean taskbarHiddenTemporarily = false;
132 private boolean isRefreshingRecents = false;
133 private boolean isFirstStart = true;
135 private boolean startThread2 = false;
136 private boolean stopThread2 = false;
138 private int refreshInterval = -1;
139 private long searchInterval = -1;
140 private String sortOrder = "false";
141 private boolean runningAppsOnly = false;
143 private int layoutId = R.layout.tb_taskbar_left;
144 private int currentTaskbarPosition = 0;
145 private boolean showHideAutomagically = false;
146 private boolean positionIsVertical = false;
147 private boolean dashboardEnabled = false;
148 private boolean navbarButtonsEnabled = false;
149 private boolean sysTrayEnabled = false;
151 private List<String> currentTaskbarIds = new ArrayList<>();
152 private int numOfPinnedApps = -1;
154 private int cellStrength = -1;
156 private View.OnClickListener ocl = view -> {
157 Intent intent = new Intent(TaskbarIntent.ACTION_TOGGLE_START_MENU);
158 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
161 private BroadcastReceiver showReceiver = new BroadcastReceiver() {
163 public void onReceive(Context context, Intent intent) {
168 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
170 public void onReceive(Context context, Intent intent) {
175 private BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
177 public void onReceive(Context context, Intent intent) {
182 private BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
184 public void onReceive(Context context, Intent intent) {
185 tempHideTaskbar(false);
189 private BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
191 public void onReceive(Context context, Intent intent) {
192 if(startButton.getVisibility() == View.GONE
193 && (!LauncherHelper.getInstance().isOnHomeScreen() || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
194 layout.setVisibility(View.GONE);
198 private BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
200 public void onReceive(Context context, Intent intent) {
201 if(startButton.getVisibility() == View.GONE)
202 layout.setVisibility(View.VISIBLE);
206 @TargetApi(Build.VERSION_CODES.M)
207 private PhoneStateListener listener = new PhoneStateListener() {
209 public void onSignalStrengthsChanged(SignalStrength signalStrength) {
211 cellStrength = signalStrength.getLevel();
212 } catch (SecurityException e) {
218 public TaskbarController(Context context) {
219 this.context = context;
222 @TargetApi(Build.VERSION_CODES.M)
224 public void onCreateHost(UIHost host) {
225 SharedPreferences pref = U.getSharedPreferences(context);
226 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
227 if(U.canDrawOverlays(context))
230 pref.edit().putBoolean("taskbar_active", false).apply();
234 } else host.terminate();
237 @SuppressLint("RtlHardcoded")
238 private void drawTaskbar(UIHost host) {
239 IconCache.getInstance(context).clearCache();
241 // Initialize layout params
242 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
243 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
245 final ViewParams params = new ViewParams(
246 WindowManager.LayoutParams.WRAP_CONTENT,
247 WindowManager.LayoutParams.WRAP_CONTENT,
249 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
252 // Determine where to show the taskbar on screen
253 switch(U.getTaskbarPosition(context)) {
255 layoutId = R.layout.tb_taskbar_left;
256 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
257 positionIsVertical = false;
259 case "bottom_vertical_left":
260 layoutId = R.layout.tb_taskbar_vertical;
261 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
262 positionIsVertical = true;
265 layoutId = R.layout.tb_taskbar_right;
266 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
267 positionIsVertical = false;
269 case "bottom_vertical_right":
270 layoutId = R.layout.tb_taskbar_vertical;
271 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
272 positionIsVertical = true;
275 layoutId = R.layout.tb_taskbar_left;
276 params.gravity = Gravity.TOP | Gravity.LEFT;
277 positionIsVertical = false;
279 case "top_vertical_left":
280 layoutId = R.layout.tb_taskbar_top_vertical;
281 params.gravity = Gravity.TOP | Gravity.LEFT;
282 positionIsVertical = true;
285 layoutId = R.layout.tb_taskbar_right;
286 params.gravity = Gravity.TOP | Gravity.RIGHT;
287 positionIsVertical = false;
289 case "top_vertical_right":
290 layoutId = R.layout.tb_taskbar_top_vertical;
291 params.gravity = Gravity.TOP | Gravity.RIGHT;
292 positionIsVertical = true;
297 SharedPreferences pref = U.getSharedPreferences(context);
298 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
300 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
301 taskbar = layout.findViewById(R.id.taskbar);
302 scrollView = layout.findViewById(R.id.taskbar_scrollview);
304 int backgroundTint = U.getBackgroundTint(context);
305 int accentColor = U.getAccentColor(context);
307 if(altButtonConfig) {
308 space = layout.findViewById(R.id.space_alt);
309 layout.findViewById(R.id.space).setVisibility(View.GONE);
311 space = layout.findViewById(R.id.space);
312 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
315 space.setOnClickListener(v -> toggleTaskbar(true));
317 startButton = layout.findViewById(R.id.start_button);
320 switch(pref.getString("start_button_image", U.getDefaultStartButtonImage(context))) {
322 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
323 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
328 if(U.isBlissOs(context)) {
329 drawable = ContextCompat.getDrawable(context, R.drawable.tb_bliss);
330 drawable.setTint(accentColor);
332 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
333 LauncherActivityInfo info = launcherApps.getActivityList(context.getPackageName(), Process.myUserHandle()).get(0);
334 drawable = IconCache.getInstance(context).getIcon(context, context.getPackageManager(), info);
337 startButton.setImageDrawable(drawable);
338 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding_alt);
341 File file = new File(context.getFilesDir() + "/tb_images", "custom_image");
343 Handler handler = new Handler();
345 Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
348 BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
349 bitmapDrawable.setFilterBitmap(bitmap.getWidth() * bitmap.getHeight() > 2000);
350 startButton.setImageDrawable(bitmapDrawable);
352 U.showToastLong(context, R.string.tb_error_reading_custom_start_image);
353 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
358 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
360 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
364 startButton.setPadding(padding, padding, padding, padding);
365 startButton.setOnClickListener(ocl);
366 startButton.setOnLongClickListener(view -> {
371 startButton.setOnGenericMotionListener((view, motionEvent) -> {
372 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
373 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
379 refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "1")) * 1000);
380 if(refreshInterval == 0)
381 refreshInterval = 100;
383 sortOrder = pref.getString("sort_order", "false");
384 runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
386 switch(pref.getString("recents_amount", "past_day")) {
388 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
391 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
392 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
394 searchInterval = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
401 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
402 lbm.sendBroadcast(new Intent(TaskbarIntent.ACTION_HIDE_START_MENU));
403 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.UPDATE_HOME_SCREEN_MARGINS"));
405 if(altButtonConfig) {
406 button = layout.findViewById(R.id.hide_taskbar_button_alt);
407 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
409 button = layout.findViewById(R.id.hide_taskbar_button);
410 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
414 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
415 } catch (RuntimeException e) { /* Gracefully fail */ }
418 button.setOnClickListener(v -> toggleTaskbar(true));
420 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
421 ? R.id.hide_taskbar_button_layout_alt
422 : R.id.hide_taskbar_button_layout);
423 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
425 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
426 ? R.id.hide_taskbar_button_layout
427 : R.id.hide_taskbar_button_layout_alt);
428 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
430 dashboardButton = layout.findViewById(R.id.dashboard_button);
431 navbarButtons = layout.findViewById(R.id.navbar_buttons);
433 dashboardEnabled = pref.getBoolean("dashboard", context.getResources().getBoolean(R.bool.tb_def_dashboard));
434 if(dashboardEnabled) {
435 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
436 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
437 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
438 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
439 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
440 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
442 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
444 dashboardButton.setVisibility(View.GONE);
446 if(pref.getBoolean("button_back", false)) {
447 navbarButtonsEnabled = true;
449 ImageView backButton = layout.findViewById(R.id.button_back);
450 backButton.setColorFilter(accentColor);
451 backButton.setVisibility(View.VISIBLE);
452 backButton.setOnClickListener(v -> {
453 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
454 if(U.shouldCollapse(context, false))
458 backButton.setOnLongClickListener(v -> {
459 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
460 imm.showInputMethodPicker();
462 if(U.shouldCollapse(context, false))
468 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
469 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
470 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
471 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
472 imm.showInputMethodPicker();
474 if(U.shouldCollapse(context, false))
481 if(pref.getBoolean("button_home", false)) {
482 navbarButtonsEnabled = true;
484 ImageView homeButton = layout.findViewById(R.id.button_home);
485 homeButton.setColorFilter(accentColor);
486 homeButton.setVisibility(View.VISIBLE);
487 homeButton.setOnClickListener(v -> {
488 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
489 if(U.shouldCollapse(context, false))
493 homeButton.setOnLongClickListener(v -> {
494 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
495 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
498 context.startActivity(voiceSearchIntent);
499 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
501 if(U.shouldCollapse(context, false))
507 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
508 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
509 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
510 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
511 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
514 context.startActivity(voiceSearchIntent);
515 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
517 if(U.shouldCollapse(context, false))
524 if(pref.getBoolean("button_recents", false)) {
525 navbarButtonsEnabled = true;
527 ImageView recentsButton = layout.findViewById(R.id.button_recents);
528 recentsButton.setColorFilter(accentColor);
529 recentsButton.setVisibility(View.VISIBLE);
530 recentsButton.setOnClickListener(v -> {
531 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
532 if(U.shouldCollapse(context, false))
536 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
537 recentsButton.setOnLongClickListener(v -> {
538 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
539 if(U.shouldCollapse(context, false))
545 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
546 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
547 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
548 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
549 if(U.shouldCollapse(context, false))
557 if(!navbarButtonsEnabled)
558 navbarButtons.setVisibility(View.GONE);
560 sysTrayEnabled = U.isSystemTrayEnabled(context);
563 sysTrayLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.tb_system_tray, null);
565 FrameLayout.LayoutParams sysTrayParams = new FrameLayout.LayoutParams(
566 FrameLayout.LayoutParams.WRAP_CONTENT,
567 context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size)
570 if(layoutId == R.layout.tb_taskbar_right) {
571 time = sysTrayLayout.findViewById(R.id.time_left);
572 sysTrayParams.gravity = Gravity.START;
574 time = sysTrayLayout.findViewById(R.id.time_right);
575 sysTrayParams.gravity = Gravity.END;
578 time.setVisibility(View.VISIBLE);
579 sysTrayLayout.setLayoutParams(sysTrayParams);
581 if(!U.isLibrary(context)) {
582 sysTrayLayout.setOnClickListener(v -> {
583 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
584 if(U.shouldCollapse(context, false))
588 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
589 sysTrayLayout.setOnLongClickListener(v -> {
590 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
591 if(U.shouldCollapse(context, false))
597 sysTrayLayout.setOnGenericMotionListener((view, motionEvent) -> {
598 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
599 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
600 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
601 if (U.shouldCollapse(context, false))
609 sysTrayParentLayout = layout.findViewById(R.id.add_systray_here);
610 sysTrayParentLayout.setVisibility(View.VISIBLE);
611 sysTrayParentLayout.addView(sysTrayLayout);
613 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
614 manager.listen(listener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
617 layout.setBackgroundColor(backgroundTint);
618 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
619 button.setTextColor(accentColor);
621 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
623 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
624 toggleTaskbar(false);
626 if(pref.getBoolean("auto_hide_navbar", false))
627 U.showHideNavigationBar(context, false);
629 if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
630 lbm.sendBroadcast(new Intent(TaskbarIntent.ACTION_FINISH_FREEFORM_ACTIVITY));
632 new Handler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
635 lbm.unregisterReceiver(showReceiver);
636 lbm.unregisterReceiver(hideReceiver);
637 lbm.unregisterReceiver(tempShowReceiver);
638 lbm.unregisterReceiver(tempHideReceiver);
639 lbm.unregisterReceiver(startMenuAppearReceiver);
640 lbm.unregisterReceiver(startMenuDisappearReceiver);
642 lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
643 lbm.registerReceiver(hideReceiver, new IntentFilter(TaskbarIntent.ACTION_HIDE_TASKBAR));
644 lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
645 lbm.registerReceiver(
647 new IntentFilter(TaskbarIntent.ACTION_TEMP_HIDE_TASKBAR)
649 lbm.registerReceiver(
650 startMenuAppearReceiver,
651 new IntentFilter(TaskbarIntent.ACTION_START_MENU_APPEARING)
653 lbm.registerReceiver(
654 startMenuDisappearReceiver,
655 new IntentFilter(TaskbarIntent.ACTION_START_MENU_DISAPPEARING)
658 startRefreshingRecents();
660 host.addView(layout, params);
662 isFirstStart = false;
665 private void startRefreshingRecents() {
666 if(thread != null) thread.interrupt();
669 SharedPreferences pref = U.getSharedPreferences(context);
670 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
672 currentTaskbarIds.clear();
674 handler = new Handler();
675 thread = new Thread(() -> {
677 updateRecentApps(true);
679 if(!isRefreshingRecents) {
680 isRefreshingRecents = true;
682 while(shouldRefreshRecents) {
683 SystemClock.sleep(refreshInterval);
685 updateRecentApps(false);
687 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
690 int[] location = new int[2];
691 layout.getLocationOnScreen(location);
693 if(location[1] != 0) {
694 if(location[1] > currentTaskbarPosition) {
695 currentTaskbarPosition = location[1];
696 } else if(location[1] < currentTaskbarPosition) {
697 if(currentTaskbarPosition - location[1] == getNavBarSize())
698 currentTaskbarPosition = location[1];
699 else if(!startThread2) {
701 tempHideTaskbar(true);
709 isRefreshingRecents = false;
716 @SuppressWarnings("Convert2streamapi")
717 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
718 private void updateRecentApps(final boolean firstRefresh) {
719 if(isScreenOff()) return;
721 SharedPreferences pref = U.getSharedPreferences(context);
722 final PackageManager pm = context.getPackageManager();
723 final List<AppEntry> entries = new ArrayList<>();
724 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
725 int maxNumOfEntries = U.getMaxNumOfEntries(context);
726 int realNumOfPinnedApps = 0;
727 boolean fullLength = pref.getBoolean("full_length", context.getResources().getBoolean(R.bool.tb_def_full_length));
729 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
730 List<AppEntry> pinnedApps = pba.getPinnedApps();
731 List<AppEntry> blockedApps = pba.getBlockedApps();
732 List<String> applicationIdsToRemove = new ArrayList<>();
734 // Filter out anything on the pinned/blocked apps lists
735 if(pinnedApps.size() > 0) {
736 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
737 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
739 for(AppEntry entry : pinnedApps) {
740 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
741 userManager.getUserForSerialNumber(entry.getUserId(context)));
746 realNumOfPinnedApps--;
748 applicationIdsToRemove.add(entry.getPackageName());
751 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
754 if(blockedApps.size() > 0) {
755 for(AppEntry entry : blockedApps) {
756 applicationIdsToRemove.add(entry.getPackageName());
760 // Get list of all recently used apps
761 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
762 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
763 if(realNumOfPinnedApps < maxNumOfEntries) {
764 List<AppEntry> usageStatsList2 = new ArrayList<>();
765 List<AppEntry> usageStatsList3 = new ArrayList<>();
766 List<AppEntry> usageStatsList4 = new ArrayList<>();
767 List<AppEntry> usageStatsList5 = new ArrayList<>();
768 List<AppEntry> usageStatsList6;
770 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
771 homeIntent.addCategory(Intent.CATEGORY_HOME);
772 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
774 // Filter out apps without a launcher intent
775 // Also filter out the current launcher, and Taskbar itself
776 for(AppEntry packageInfo : usageStatsList) {
777 if(hasLauncherIntent(packageInfo.getPackageName())
778 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
779 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
780 usageStatsList2.add(packageInfo);
783 // Filter out apps that don't fall within our current search interval
784 for(AppEntry stats : usageStatsList2) {
785 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
786 usageStatsList3.add(stats);
789 // Sort apps by either most recently used, or most time used
790 if(!runningAppsOnly && sortOrder.contains("most_used")) {
791 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
793 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
796 // Filter out any duplicate entries
797 List<String> applicationIds = new ArrayList<>();
798 for(AppEntry stats : usageStatsList3) {
799 if(!applicationIds.contains(stats.getPackageName())) {
800 usageStatsList4.add(stats);
801 applicationIds.add(stats.getPackageName());
805 // Filter out the currently running foreground app, if requested by the user
806 if(pref.getBoolean("hide_foreground", false)) {
807 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
808 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
809 UsageEvents.Event eventCache = new UsageEvents.Event();
810 String currentForegroundApp = null;
812 while(events.hasNextEvent()) {
813 events.getNextEvent(eventCache);
815 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
816 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
817 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
818 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
819 && !eventCache.getClassName().equals(HomeActivityDelegate.class.getCanonicalName())
820 && !eventCache.getClassName().equals(SecondaryHomeActivity.class.getCanonicalName())
821 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
822 currentForegroundApp = eventCache.getPackageName();
826 if(!applicationIdsToRemove.contains(currentForegroundApp))
827 applicationIdsToRemove.add(currentForegroundApp);
830 for(AppEntry stats : usageStatsList4) {
831 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
832 usageStatsList5.add(stats);
836 // Truncate list to a maximum length
837 if(usageStatsList5.size() > maxNumOfEntries)
838 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
840 usageStatsList6 = usageStatsList5;
842 // Determine if we need to reverse the order
843 boolean needToReverseOrder;
844 switch(U.getTaskbarPosition(context)) {
847 needToReverseOrder = sortOrder.contains("false");
850 needToReverseOrder = sortOrder.contains("true");
854 if(needToReverseOrder) {
855 Collections.reverse(usageStatsList6);
858 // Generate the AppEntries for the recent apps list
859 int number = usageStatsList6.size() == maxNumOfEntries
860 ? usageStatsList6.size() - realNumOfPinnedApps
861 : usageStatsList6.size();
863 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
864 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
866 final List<UserHandle> userHandles = userManager.getUserProfiles();
868 for(int i = 0; i < number; i++) {
869 for(UserHandle handle : userHandles) {
870 String packageName = usageStatsList6.get(i).getPackageName();
871 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
872 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
873 if(!list.isEmpty()) {
874 // Google App workaround
875 if(!packageName.equals("com.google.android.googlequicksearchbox"))
876 launcherAppCache.add(list.get(0));
878 boolean added = false;
879 for(LauncherActivityInfo info : list) {
880 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
881 launcherAppCache.add(info);
886 if(!added) launcherAppCache.add(list.get(0));
889 AppEntry newEntry = new AppEntry(
897 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
898 newEntry.setLastTimeUsed(lastTimeUsed);
899 entries.add(newEntry);
907 while(entries.size() > maxNumOfEntries) {
909 entries.remove(entries.size() - 1);
910 launcherAppCache.remove(launcherAppCache.size() - 1);
911 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
914 // Determine if we need to reverse the order again
915 if(U.getTaskbarPosition(context).contains("vertical")) {
916 Collections.reverse(entries);
917 Collections.reverse(launcherAppCache);
920 // Now that we've generated the list of apps,
921 // we need to determine if we need to redraw the Taskbar or not
922 boolean shouldRedrawTaskbar = firstRefresh;
924 List<String> finalApplicationIds = new ArrayList<>();
925 for(AppEntry entry : entries) {
926 finalApplicationIds.add(entry.getPackageName());
929 if(finalApplicationIds.size() != currentTaskbarIds.size()
930 || numOfPinnedApps != realNumOfPinnedApps)
931 shouldRedrawTaskbar = true;
933 for(int i = 0; i < finalApplicationIds.size(); i++) {
934 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
935 shouldRedrawTaskbar = true;
941 if(shouldRedrawTaskbar) {
942 currentTaskbarIds = finalApplicationIds;
943 numOfPinnedApps = realNumOfPinnedApps;
945 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
947 int launcherAppCachePos = -1;
948 for(int i = 0; i < entries.size(); i++) {
949 if(entries.get(i).getComponentName() == null) {
950 launcherAppCachePos++;
951 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
952 String packageName = entries.get(i).getPackageName();
953 long lastTimeUsed = entries.get(i).getLastTimeUsed();
957 AppEntry newEntry = new AppEntry(
959 appInfo.getComponentName().flattenToString(),
960 appInfo.getLabel().toString(),
961 IconCache.getInstance(context).getIcon(context, pm, appInfo),
964 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
965 newEntry.setLastTimeUsed(lastTimeUsed);
966 entries.add(i, newEntry);
970 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
973 if(numOfEntries > 0 || fullLength) {
974 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
975 DisplayInfo display = U.getDisplayInfo(context, true);
976 int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size) * numOfEntries;
977 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
979 if(U.getTaskbarPosition(context).contains("vertical")) {
980 int maxScreenSize = Math.max(0, display.height
981 - U.getStatusBarHeight(context)
982 - U.getBaseTaskbarSize(context));
984 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
985 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
989 Space whitespaceTop = layout.findViewById(R.id.whitespace_top);
990 Space whitespaceBottom = layout.findViewById(R.id.whitespace_bottom);
991 int height = maxScreenSize - recentsSize;
993 if(pref.getBoolean("centered_icons", false)) {
994 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
995 topParams.height = height / 2;
996 whitespaceTop.setLayoutParams(topParams);
998 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
999 bottomParams.height = height / 2;
1000 whitespaceBottom.setLayoutParams(bottomParams);
1001 } else if(U.getTaskbarPosition(context).contains("bottom")) {
1002 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
1003 topParams.height = height;
1004 whitespaceTop.setLayoutParams(topParams);
1006 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
1007 bottomParams.height = height;
1008 whitespaceBottom.setLayoutParams(bottomParams);
1010 } catch (NullPointerException e) { /* Gracefully fail */ }
1013 int maxScreenSize = Math.max(0, display.width - U.getBaseTaskbarSize(context));
1015 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
1016 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1020 Space whitespaceLeft = layout.findViewById(R.id.whitespace_left);
1021 Space whitespaceRight = layout.findViewById(R.id.whitespace_right);
1022 int width = maxScreenSize - recentsSize;
1024 if(pref.getBoolean("centered_icons", false)) {
1025 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1026 leftParams.width = width / 2;
1027 whitespaceLeft.setLayoutParams(leftParams);
1029 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1030 rightParams.width = width / 2;
1031 whitespaceRight.setLayoutParams(rightParams);
1032 } else if(U.getTaskbarPosition(context).contains("right")) {
1033 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1034 leftParams.width = width;
1035 whitespaceLeft.setLayoutParams(leftParams);
1037 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1038 rightParams.width = width;
1039 whitespaceRight.setLayoutParams(rightParams);
1041 } catch (NullPointerException e) { /* Gracefully fail */ }
1045 scrollView.setLayoutParams(params);
1047 taskbar.removeAllViews();
1048 for(int i = 0; i < entries.size(); i++) {
1049 taskbar.addView(getView(entries, i));
1053 updateRunningAppIndicators(pinnedApps, usageStatsList, entries);
1055 isShowingRecents = true;
1056 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
1058 scrollView.setVisibility(View.INVISIBLE);
1060 scrollView.setVisibility(View.VISIBLE);
1063 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
1064 new Handler().post(() -> {
1065 switch(U.getTaskbarPosition(context)) {
1067 case "bottom_right":
1070 if(sortOrder.contains("false"))
1071 scrollView.scrollTo(0, 0);
1072 else if(sortOrder.contains("true"))
1073 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1075 case "bottom_vertical_left":
1076 case "bottom_vertical_right":
1077 case "top_vertical_left":
1078 case "top_vertical_right":
1079 if(sortOrder.contains("false"))
1080 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1081 else if(sortOrder.contains("true"))
1082 scrollView.scrollTo(0, 0);
1086 if(shouldRefreshRecents) {
1087 scrollView.setVisibility(View.VISIBLE);
1091 isShowingRecents = false;
1092 scrollView.setVisibility(View.GONE);
1095 } else if(runningAppsOnly)
1096 handler.post(() -> updateRunningAppIndicators(pinnedApps, usageStatsList, entries));
1097 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
1098 currentTaskbarIds.clear();
1099 handler.post(() -> {
1100 isShowingRecents = false;
1101 scrollView.setVisibility(View.GONE);
1106 private void updateRunningAppIndicators(List<AppEntry> pinnedApps, List<AppEntry> usageStatsList, List<AppEntry> entries) {
1107 if(taskbar.getChildCount() != entries.size())
1110 List<String> pinnedPackageList = new ArrayList<>();
1111 List<String> runningPackageList = new ArrayList<>();
1113 for(AppEntry entry : pinnedApps)
1114 pinnedPackageList.add(entry.getPackageName());
1116 for(AppEntry entry : usageStatsList)
1117 runningPackageList.add(entry.getPackageName());
1119 for(int i = 0; i < taskbar.getChildCount(); i++) {
1120 View convertView = taskbar.getChildAt(i);
1121 String packageName = entries.get(i).getPackageName();
1123 ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1124 if(pinnedPackageList.contains(packageName) && !runningPackageList.contains(packageName))
1125 runningAppIndicator.setVisibility(View.GONE);
1127 runningAppIndicator.setVisibility(View.VISIBLE);
1128 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1133 private void toggleTaskbar(boolean userInitiated) {
1134 if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
1135 SharedPreferences pref = U.getSharedPreferences(context);
1136 if(!pref.getBoolean("grip_rejection_toast_shown", false)) {
1137 U.showToastLong(context, R.string.tb_essential_phone_grip_rejection);
1138 pref.edit().putBoolean("grip_rejection_toast_shown", true).apply();
1142 if(startButton.getVisibility() == View.GONE)
1148 private void showTaskbar(boolean clearVariables) {
1149 if(clearVariables) {
1150 taskbarShownTemporarily = false;
1151 taskbarHiddenTemporarily = false;
1154 if(startButton.getVisibility() == View.GONE) {
1155 startButton.setVisibility(View.VISIBLE);
1156 space.setVisibility(View.VISIBLE);
1158 if(dashboardEnabled)
1159 dashboardButton.setVisibility(View.VISIBLE);
1161 if(navbarButtonsEnabled)
1162 navbarButtons.setVisibility(View.VISIBLE);
1164 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
1165 scrollView.setVisibility(View.INVISIBLE);
1168 sysTrayParentLayout.setVisibility(View.VISIBLE);
1170 shouldRefreshRecents = true;
1171 startRefreshingRecents();
1173 SharedPreferences pref = U.getSharedPreferences(context);
1174 pref.edit().putBoolean("collapsed", true).apply();
1176 updateButton(false);
1178 new Handler().post(() ->
1179 LocalBroadcastManager
1180 .getInstance(context)
1181 .sendBroadcast(new Intent(TaskbarIntent.ACTION_SHOW_START_MENU_SPACE))
1186 private void hideTaskbar(boolean clearVariables) {
1187 if(clearVariables) {
1188 taskbarShownTemporarily = false;
1189 taskbarHiddenTemporarily = false;
1192 if(startButton.getVisibility() == View.VISIBLE) {
1193 startButton.setVisibility(View.GONE);
1194 space.setVisibility(View.GONE);
1196 if(dashboardEnabled)
1197 dashboardButton.setVisibility(View.GONE);
1199 if(navbarButtonsEnabled)
1200 navbarButtons.setVisibility(View.GONE);
1202 if(isShowingRecents)
1203 scrollView.setVisibility(View.GONE);
1206 sysTrayParentLayout.setVisibility(View.GONE);
1208 shouldRefreshRecents = false;
1209 if(thread != null) thread.interrupt();
1211 SharedPreferences pref = U.getSharedPreferences(context);
1212 pref.edit().putBoolean("collapsed", false).apply();
1216 if(clearVariables) {
1217 LocalBroadcastManager
1218 .getInstance(context)
1219 .sendBroadcast(new Intent(TaskbarIntent.ACTION_HIDE_START_MENU));
1220 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1223 new Handler().post(() ->
1224 LocalBroadcastManager
1225 .getInstance(context)
1226 .sendBroadcast(new Intent(TaskbarIntent.ACTION_HIDE_START_MENU_SPACE))
1231 private void tempShowTaskbar() {
1232 if(!taskbarHiddenTemporarily) {
1233 SharedPreferences pref = U.getSharedPreferences(context);
1234 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1239 if(taskbarHiddenTemporarily)
1240 taskbarHiddenTemporarily = false;
1243 private void tempHideTaskbar(boolean monitorPositionChanges) {
1244 if(!taskbarShownTemporarily) {
1245 SharedPreferences pref = U.getSharedPreferences(context);
1246 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1251 if(taskbarShownTemporarily)
1252 taskbarShownTemporarily = false;
1254 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1255 if(thread2 != null) thread2.interrupt();
1257 handler2 = new Handler();
1258 thread2 = new Thread(() -> {
1259 stopThread2 = false;
1261 while(!stopThread2) {
1262 SystemClock.sleep(refreshInterval);
1264 handler2.post(() -> stopThread2 = checkPositionChange());
1267 startThread2 = false;
1274 private boolean checkPositionChange() {
1275 if(!isScreenOff() && layout != null) {
1276 int[] location = new int[2];
1277 layout.getLocationOnScreen(location);
1279 if(location[1] == 0) {
1282 if(location[1] > currentTaskbarPosition) {
1283 currentTaskbarPosition = location[1];
1284 if(taskbarHiddenTemporarily) {
1288 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1291 } else if(location[1] < currentTaskbarPosition
1292 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1293 currentTaskbarPosition = location[1];
1301 private int getNavBarSize() {
1302 Point size = new Point();
1303 Point realSize = new Point();
1305 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1306 Display display = wm.getDefaultDisplay();
1307 display.getSize(size);
1308 display.getRealSize(realSize);
1310 return realSize.y - size.y;
1314 public void onDestroyHost(UIHost host) {
1315 shouldRefreshRecents = false;
1319 host.removeView(layout);
1320 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1322 SharedPreferences pref = U.getSharedPreferences(context);
1323 if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1324 pref.edit().remove("skip_auto_hide_navbar").apply();
1325 } else if(pref.getBoolean("auto_hide_navbar", false))
1326 U.showHideNavigationBar(context, true);
1328 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
1330 lbm.unregisterReceiver(showReceiver);
1331 lbm.unregisterReceiver(hideReceiver);
1332 lbm.unregisterReceiver(tempShowReceiver);
1333 lbm.unregisterReceiver(tempHideReceiver);
1334 lbm.unregisterReceiver(startMenuAppearReceiver);
1335 lbm.unregisterReceiver(startMenuDisappearReceiver);
1337 if(sysTrayEnabled) {
1338 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1339 manager.listen(listener, PhoneStateListener.LISTEN_NONE);
1342 isFirstStart = true;
1345 private void openContextMenu() {
1346 SharedPreferences pref = U.getSharedPreferences(context);
1348 Bundle args = new Bundle();
1349 args.putBoolean("dont_show_quit",
1350 LauncherHelper.getInstance().isOnHomeScreen()
1351 && !pref.getBoolean("taskbar_active", false));
1352 args.putBoolean("is_start_button", true);
1354 U.startContextMenuActivity(context, args);
1357 private void updateButton(boolean isCollapsed) {
1358 SharedPreferences pref = U.getSharedPreferences(context);
1359 boolean hide = pref.getBoolean("invisible_button", false);
1361 if(button != null) button.setText(context.getString(isCollapsed ? R.string.tb_right_arrow : R.string.tb_left_arrow));
1362 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1365 @TargetApi(Build.VERSION_CODES.M)
1367 public void onRecreateHost(UIHost host) {
1368 if(layout != null) {
1370 host.removeView(layout);
1371 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1373 currentTaskbarPosition = 0;
1375 if(U.canDrawOverlays(context))
1378 SharedPreferences pref = U.getSharedPreferences(context);
1379 pref.edit().putBoolean("taskbar_active", false).apply();
1386 private View getView(List<AppEntry> list, int position) {
1387 View convertView = View.inflate(context, R.layout.tb_icon, null);
1389 final AppEntry entry = list.get(position);
1390 final SharedPreferences pref = U.getSharedPreferences(context);
1392 ImageView imageView = convertView.findViewById(R.id.icon);
1393 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1394 imageView.setImageDrawable(entry.getIcon(context));
1395 imageView2.setBackgroundColor(U.getAccentColor(context));
1397 String taskbarPosition = U.getTaskbarPosition(context);
1398 if(pref.getBoolean("shortcut_icon", true)) {
1399 boolean shouldShowShortcutIcon;
1400 if(taskbarPosition.contains("vertical"))
1401 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1403 shouldShowShortcutIcon = position < numOfPinnedApps;
1405 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1408 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1409 imageView.setRotationY(180);
1410 imageView2.setRotationY(180);
1413 FrameLayout layout = convertView.findViewById(R.id.entry);
1414 layout.setOnClickListener(view -> U.launchApp(
1423 layout.setOnLongClickListener(view -> {
1424 int[] location = new int[2];
1425 view.getLocationOnScreen(location);
1426 openContextMenu(entry, location);
1430 layout.setOnGenericMotionListener((view, motionEvent) -> {
1431 int action = motionEvent.getAction();
1433 if(action == MotionEvent.ACTION_BUTTON_PRESS
1434 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1435 int[] location = new int[2];
1436 view.getLocationOnScreen(location);
1437 openContextMenu(entry, location);
1440 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1441 view.setBackgroundColor(0);
1446 if(pref.getBoolean("visual_feedback", true)) {
1447 layout.setOnHoverListener((v, event) -> {
1448 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1449 int accentColor = U.getAccentColor(context);
1450 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1451 v.setBackgroundColor(accentColor);
1454 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1455 v.setBackgroundColor(0);
1457 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1458 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1463 layout.setOnTouchListener((v, event) -> {
1464 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1472 private void openContextMenu(AppEntry entry, int[] location) {
1473 Bundle args = new Bundle();
1474 args.putSerializable("app_entry", entry);
1475 args.putInt("x", location[0]);
1476 args.putInt("y", location[1]);
1478 U.startContextMenuActivity(context, args);
1481 private List<AppEntry> getAppEntries() {
1482 SharedPreferences pref = U.getSharedPreferences(context);
1484 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1486 return getAppEntriesUsingUsageStats();
1489 @SuppressWarnings("deprecation")
1490 @TargetApi(Build.VERSION_CODES.M)
1491 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1492 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1493 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1494 List<AppEntry> entries = new ArrayList<>();
1496 for(int i = 0; i < usageStatsList.size(); i++) {
1497 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1498 if(recentTaskInfo.id != -1) {
1499 String packageName = recentTaskInfo.baseActivity.getPackageName();
1500 AppEntry newEntry = new AppEntry(
1509 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1510 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1511 } catch (Exception e) {
1512 newEntry.setLastTimeUsed(i);
1515 entries.add(newEntry);
1522 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1523 private List<AppEntry> getAppEntriesUsingUsageStats() {
1524 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1525 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1526 List<AppEntry> entries = new ArrayList<>();
1528 for(UsageStats usageStats : usageStatsList) {
1529 AppEntry newEntry = new AppEntry(
1530 usageStats.getPackageName(),
1537 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1538 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1539 entries.add(newEntry);
1545 private boolean hasLauncherIntent(String packageName) {
1546 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1547 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1548 intentToResolve.setPackage(packageName);
1550 List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1551 return ris != null && ris.size() > 0;
1554 private boolean isScreenOff() {
1555 if(U.isChromeOs(context))
1558 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1559 return !pm.isInteractive();
1562 private void updateSystemTray() {
1563 if(!sysTrayEnabled || isScreenOff()) return;
1565 handler.post(() -> {
1566 ImageView battery = sysTrayLayout.findViewById(R.id.battery);
1567 battery.setImageDrawable(getBatteryDrawable());
1569 ImageView wifi = sysTrayLayout.findViewById(R.id.wifi);
1570 wifi.setImageDrawable(getWifiDrawable());
1572 ImageView bluetooth = sysTrayLayout.findViewById(R.id.bluetooth);
1573 bluetooth.setImageDrawable(getBluetoothDrawable());
1575 ImageView cellular = sysTrayLayout.findViewById(R.id.cellular);
1576 cellular.setImageDrawable(getCellularDrawable());
1578 time.setText(context.getString(R.string.tb_systray_clock,
1579 DateFormat.getTimeFormat(context).format(new Date()),
1580 DateFormat.getDateFormat(context).format(new Date())));
1581 time.setTextColor(U.getAccentColor(context));
1585 @TargetApi(Build.VERSION_CODES.M)
1586 private Drawable getBatteryDrawable() {
1587 BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
1588 int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1590 if(batLevel == Integer.MIN_VALUE)
1593 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
1594 Intent batteryStatus = context.registerReceiver(null, ifilter);
1596 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
1597 boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
1598 status == BatteryManager.BATTERY_STATUS_FULL;
1601 if(batLevel < 10 && !isCharging)
1602 batDrawable = "alert";
1603 else if(batLevel < 25)
1605 else if(batLevel < 40)
1607 else if(batLevel < 55)
1609 else if(batLevel < 70)
1611 else if(batLevel < 85)
1613 else if(batLevel < 95)
1616 batDrawable = "full";
1620 charging = "charging_";
1624 String batRes = "tb_battery_" + charging + batDrawable;
1625 int id = getResourceIdFor(batRes);
1627 return getDrawableForSysTray(id);
1630 @TargetApi(Build.VERSION_CODES.M)
1631 private Drawable getWifiDrawable() {
1632 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1634 NetworkInfo ethernet = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
1635 if(ethernet != null && ethernet.isConnected())
1636 return getDrawableForSysTray(R.drawable.tb_settings_ethernet);
1638 NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
1639 if(wifi == null || !wifi.isConnected())
1642 WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
1643 int numberOfLevels = 5;
1645 WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1646 int level = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), numberOfLevels);
1648 String wifiRes = "tb_signal_wifi_" + level + "_bar";
1649 int id = getResourceIdFor(wifiRes);
1651 return getDrawableForSysTray(id);
1654 private Drawable getBluetoothDrawable() {
1655 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1656 if(adapter != null && adapter.isEnabled())
1657 return getDrawableForSysTray(R.drawable.tb_bluetooth);
1662 @TargetApi(Build.VERSION_CODES.M)
1663 private Drawable getCellularDrawable() {
1664 if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0)
1665 return getDrawableForSysTray(R.drawable.tb_airplanemode_active);
1667 if(cellStrength == -1)
1670 String cellRes = "tb_signal_cellular_" + cellStrength + "_bar";
1671 int id = getResourceIdFor(cellRes);
1673 return getDrawableForSysTray(id);
1676 private Drawable getDrawableForSysTray(int id) {
1677 Drawable drawable = null;
1679 drawable = ContextCompat.getDrawable(context, id);
1680 } catch (Resources.NotFoundException e) { /* Gracefully fail */ }
1682 if(drawable == null) return null;
1684 drawable.setTint(U.getAccentColor(context));
1688 private int getResourceIdFor(String name) {
1689 String packageName = context.getResources().getResourcePackageName(R.drawable.tb_dummy);
1690 return context.getResources().getIdentifier(name, "drawable", packageName);