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.graphics.Bitmap;
38 import android.graphics.BitmapFactory;
39 import android.graphics.Color;
40 import android.graphics.Point;
41 import android.graphics.Typeface;
42 import android.graphics.drawable.BitmapDrawable;
43 import android.graphics.drawable.Drawable;
44 import android.net.ConnectivityManager;
45 import android.net.NetworkInfo;
46 import android.net.wifi.WifiInfo;
47 import android.net.wifi.WifiManager;
48 import android.os.BatteryManager;
49 import android.os.Build;
50 import android.os.Bundle;
51 import android.os.Handler;
52 import android.os.PowerManager;
53 import android.os.Process;
54 import android.os.SystemClock;
55 import android.os.UserHandle;
56 import android.os.UserManager;
57 import android.provider.Settings;
58 import android.speech.RecognizerIntent;
59 import androidx.core.content.ContextCompat;
60 import androidx.core.graphics.ColorUtils;
61 import android.telephony.PhoneStateListener;
62 import android.telephony.SignalStrength;
63 import android.telephony.TelephonyManager;
64 import android.text.format.DateFormat;
65 import android.view.Display;
66 import android.view.Gravity;
67 import android.view.LayoutInflater;
68 import android.view.MotionEvent;
69 import android.view.PointerIcon;
70 import android.view.View;
71 import android.view.ViewGroup;
72 import android.view.WindowManager;
73 import android.view.inputmethod.InputMethodManager;
74 import android.widget.Button;
75 import android.widget.FrameLayout;
76 import android.widget.ImageView;
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.List;
85 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
86 import android.widget.LinearLayout;
87 import android.widget.Space;
88 import android.widget.TextView;
90 import com.farmerbb.taskbar.BuildConfig;
91 import com.farmerbb.taskbar.activity.HomeActivityDelegate;
92 import com.farmerbb.taskbar.activity.MainActivity;
93 import com.farmerbb.taskbar.R;
94 import com.farmerbb.taskbar.activity.HomeActivity;
95 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
96 import com.farmerbb.taskbar.activity.SecondaryHomeActivity;
97 import com.farmerbb.taskbar.util.AppEntry;
98 import com.farmerbb.taskbar.util.DisplayInfo;
99 import com.farmerbb.taskbar.util.FreeformHackHelper;
100 import com.farmerbb.taskbar.util.IconCache;
101 import com.farmerbb.taskbar.util.LauncherHelper;
102 import com.farmerbb.taskbar.util.PinnedBlockedApps;
103 import com.farmerbb.taskbar.util.MenuHelper;
104 import com.farmerbb.taskbar.util.U;
106 public class TaskbarController implements UIController {
108 private Context context;
109 private LinearLayout layout;
110 private ImageView startButton;
111 private LinearLayout taskbar;
112 private FrameLayout scrollView;
113 private Button button;
115 private FrameLayout dashboardButton;
116 private LinearLayout navbarButtons;
117 private LinearLayout sysTrayLayout;
118 private FrameLayout sysTrayParentLayout;
119 private TextView time;
121 private Handler handler;
122 private Handler handler2;
123 private Thread thread;
124 private Thread thread2;
126 private boolean isShowingRecents = true;
127 private boolean shouldRefreshRecents = true;
128 private boolean taskbarShownTemporarily = false;
129 private boolean taskbarHiddenTemporarily = false;
130 private boolean isRefreshingRecents = false;
131 private boolean isFirstStart = true;
133 private boolean startThread2 = false;
134 private boolean stopThread2 = false;
136 private int refreshInterval = -1;
137 private long searchInterval = -1;
138 private String sortOrder = "false";
139 private boolean runningAppsOnly = false;
141 private int layoutId = R.layout.tb_taskbar_left;
142 private int currentTaskbarPosition = 0;
143 private boolean showHideAutomagically = false;
144 private boolean positionIsVertical = false;
145 private boolean dashboardEnabled = false;
146 private boolean navbarButtonsEnabled = false;
147 private boolean sysTrayEnabled = false;
149 private List<String> currentTaskbarIds = new ArrayList<>();
150 private List<String> currentRunningAppIds = new ArrayList<>();
151 private List<String> prevRunningAppIds = new ArrayList<>();
152 private int numOfPinnedApps = -1;
154 private int cellStrength = -1;
156 private View.OnClickListener ocl = view -> {
157 Intent intent = new Intent("com.farmerbb.taskbar.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) {
210 cellStrength = signalStrength.getLevel();
214 public TaskbarController(Context context) {
215 this.context = context;
218 @TargetApi(Build.VERSION_CODES.M)
220 public void onCreateHost(UIHost host) {
221 SharedPreferences pref = U.getSharedPreferences(context);
222 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
223 if(U.canDrawOverlays(context))
226 pref.edit().putBoolean("taskbar_active", false).apply();
230 } else host.terminate();
233 @SuppressLint("RtlHardcoded")
234 private void drawTaskbar(UIHost host) {
235 IconCache.getInstance(context).clearCache();
237 // Initialize layout params
238 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
239 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
241 final ViewParams params = new ViewParams(
242 WindowManager.LayoutParams.WRAP_CONTENT,
243 WindowManager.LayoutParams.WRAP_CONTENT,
245 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
248 // Determine where to show the taskbar on screen
249 switch(U.getTaskbarPosition(context)) {
251 layoutId = R.layout.tb_taskbar_left;
252 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
253 positionIsVertical = false;
255 case "bottom_vertical_left":
256 layoutId = R.layout.tb_taskbar_vertical;
257 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
258 positionIsVertical = true;
261 layoutId = R.layout.tb_taskbar_right;
262 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
263 positionIsVertical = false;
265 case "bottom_vertical_right":
266 layoutId = R.layout.tb_taskbar_vertical;
267 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
268 positionIsVertical = true;
271 layoutId = R.layout.tb_taskbar_left;
272 params.gravity = Gravity.TOP | Gravity.LEFT;
273 positionIsVertical = false;
275 case "top_vertical_left":
276 layoutId = R.layout.tb_taskbar_top_vertical;
277 params.gravity = Gravity.TOP | Gravity.LEFT;
278 positionIsVertical = true;
281 layoutId = R.layout.tb_taskbar_right;
282 params.gravity = Gravity.TOP | Gravity.RIGHT;
283 positionIsVertical = false;
285 case "top_vertical_right":
286 layoutId = R.layout.tb_taskbar_top_vertical;
287 params.gravity = Gravity.TOP | Gravity.RIGHT;
288 positionIsVertical = true;
293 SharedPreferences pref = U.getSharedPreferences(context);
294 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
296 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
297 taskbar = layout.findViewById(R.id.taskbar);
298 scrollView = layout.findViewById(R.id.taskbar_scrollview);
300 int backgroundTint = U.getBackgroundTint(context);
301 int accentColor = U.getAccentColor(context);
303 if(altButtonConfig) {
304 space = layout.findViewById(R.id.space_alt);
305 layout.findViewById(R.id.space).setVisibility(View.GONE);
307 space = layout.findViewById(R.id.space);
308 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
311 space.setOnClickListener(v -> toggleTaskbar(true));
313 startButton = layout.findViewById(R.id.start_button);
316 switch(pref.getString("start_button_image", U.getDefaultStartButtonImage(context))) {
318 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
319 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
324 if(U.isBlissOs(context)) {
325 drawable = ContextCompat.getDrawable(context, R.drawable.tb_bliss);
326 drawable.setTint(accentColor);
328 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
329 LauncherActivityInfo info = launcherApps.getActivityList(context.getPackageName(), Process.myUserHandle()).get(0);
330 drawable = IconCache.getInstance(context).getIcon(context, context.getPackageManager(), info);
333 startButton.setImageDrawable(drawable);
334 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding_alt);
337 File file = new File(context.getFilesDir() + "/tb_images", "custom_image");
339 Handler handler = new Handler();
341 Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
344 BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
345 bitmapDrawable.setFilterBitmap(bitmap.getWidth() * bitmap.getHeight() > 2000);
346 startButton.setImageDrawable(bitmapDrawable);
348 U.showToastLong(context, R.string.tb_error_reading_custom_start_image);
349 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
354 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
356 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
360 startButton.setPadding(padding, padding, padding, padding);
361 startButton.setOnClickListener(ocl);
362 startButton.setOnLongClickListener(view -> {
367 startButton.setOnGenericMotionListener((view, motionEvent) -> {
368 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
369 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
375 refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "1")) * 1000);
376 if(refreshInterval == 0)
377 refreshInterval = 100;
379 sortOrder = pref.getString("sort_order", "false");
380 runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
382 switch(pref.getString("recents_amount", "past_day")) {
384 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
387 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
388 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
390 searchInterval = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
397 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
398 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
399 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.UPDATE_HOME_SCREEN_MARGINS"));
401 if(altButtonConfig) {
402 button = layout.findViewById(R.id.hide_taskbar_button_alt);
403 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
405 button = layout.findViewById(R.id.hide_taskbar_button);
406 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
410 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
411 } catch (RuntimeException e) { /* Gracefully fail */ }
414 button.setOnClickListener(v -> toggleTaskbar(true));
416 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
417 ? R.id.hide_taskbar_button_layout_alt
418 : R.id.hide_taskbar_button_layout);
419 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
421 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
422 ? R.id.hide_taskbar_button_layout
423 : R.id.hide_taskbar_button_layout_alt);
424 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
426 dashboardButton = layout.findViewById(R.id.dashboard_button);
427 navbarButtons = layout.findViewById(R.id.navbar_buttons);
429 dashboardEnabled = pref.getBoolean("dashboard", context.getResources().getBoolean(R.bool.tb_def_dashboard));
430 if(dashboardEnabled) {
431 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
432 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
433 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
434 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
435 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
436 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
438 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
440 dashboardButton.setVisibility(View.GONE);
442 if(pref.getBoolean("button_back", false)) {
443 navbarButtonsEnabled = true;
445 ImageView backButton = layout.findViewById(R.id.button_back);
446 backButton.setColorFilter(accentColor);
447 backButton.setVisibility(View.VISIBLE);
448 backButton.setOnClickListener(v -> {
449 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
450 if(U.shouldCollapse(context, false))
454 backButton.setOnLongClickListener(v -> {
455 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
456 imm.showInputMethodPicker();
458 if(U.shouldCollapse(context, false))
464 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
465 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
466 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
467 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
468 imm.showInputMethodPicker();
470 if(U.shouldCollapse(context, false))
477 if(pref.getBoolean("button_home", false)) {
478 navbarButtonsEnabled = true;
480 ImageView homeButton = layout.findViewById(R.id.button_home);
481 homeButton.setColorFilter(accentColor);
482 homeButton.setVisibility(View.VISIBLE);
483 homeButton.setOnClickListener(v -> {
484 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
485 if(U.shouldCollapse(context, false))
489 homeButton.setOnLongClickListener(v -> {
490 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
491 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
494 context.startActivity(voiceSearchIntent);
495 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
497 if(U.shouldCollapse(context, false))
503 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
504 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
505 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
506 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
507 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
510 context.startActivity(voiceSearchIntent);
511 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
513 if(U.shouldCollapse(context, false))
520 if(pref.getBoolean("button_recents", false)) {
521 navbarButtonsEnabled = true;
523 ImageView recentsButton = layout.findViewById(R.id.button_recents);
524 recentsButton.setColorFilter(accentColor);
525 recentsButton.setVisibility(View.VISIBLE);
526 recentsButton.setOnClickListener(v -> {
527 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
528 if(U.shouldCollapse(context, false))
532 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
533 recentsButton.setOnLongClickListener(v -> {
534 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
535 if(U.shouldCollapse(context, false))
541 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
542 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
543 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
544 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
545 if(U.shouldCollapse(context, false))
553 if(!navbarButtonsEnabled)
554 navbarButtons.setVisibility(View.GONE);
556 sysTrayEnabled = U.isSystemTrayEnabled(context);
559 sysTrayLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.tb_system_tray, null);
561 FrameLayout.LayoutParams sysTrayParams = new FrameLayout.LayoutParams(
562 FrameLayout.LayoutParams.WRAP_CONTENT,
563 context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size)
566 if(layoutId == R.layout.tb_taskbar_right) {
567 time = sysTrayLayout.findViewById(R.id.time_left);
568 sysTrayParams.gravity = Gravity.START;
570 time = sysTrayLayout.findViewById(R.id.time_right);
571 sysTrayParams.gravity = Gravity.END;
574 time.setVisibility(View.VISIBLE);
575 sysTrayLayout.setLayoutParams(sysTrayParams);
577 if(!U.isLibrary(context)) {
578 sysTrayLayout.setOnClickListener(v -> {
579 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
580 if(U.shouldCollapse(context, false))
584 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
585 sysTrayLayout.setOnLongClickListener(v -> {
586 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
587 if(U.shouldCollapse(context, false))
593 sysTrayLayout.setOnGenericMotionListener((view, motionEvent) -> {
594 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
595 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
596 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
597 if (U.shouldCollapse(context, false))
605 sysTrayParentLayout = layout.findViewById(R.id.add_systray_here);
606 sysTrayParentLayout.setVisibility(View.VISIBLE);
607 sysTrayParentLayout.addView(sysTrayLayout);
609 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
610 manager.listen(listener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
613 layout.setBackgroundColor(backgroundTint);
614 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
615 button.setTextColor(accentColor);
617 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
619 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
620 toggleTaskbar(false);
622 if(pref.getBoolean("auto_hide_navbar", false))
623 U.showHideNavigationBar(context, false);
625 if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
626 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
628 new Handler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
631 lbm.unregisterReceiver(showReceiver);
632 lbm.unregisterReceiver(hideReceiver);
633 lbm.unregisterReceiver(tempShowReceiver);
634 lbm.unregisterReceiver(tempHideReceiver);
635 lbm.unregisterReceiver(startMenuAppearReceiver);
636 lbm.unregisterReceiver(startMenuDisappearReceiver);
638 lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
639 lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
640 lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
641 lbm.registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
642 lbm.registerReceiver(startMenuAppearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_APPEARING"));
643 lbm.registerReceiver(startMenuDisappearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
645 startRefreshingRecents();
647 host.addView(layout, params);
649 isFirstStart = false;
652 private void startRefreshingRecents() {
653 if(thread != null) thread.interrupt();
656 SharedPreferences pref = U.getSharedPreferences(context);
657 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
659 currentTaskbarIds.clear();
661 handler = new Handler();
662 thread = new Thread(() -> {
664 updateRecentApps(true);
666 if(!isRefreshingRecents) {
667 isRefreshingRecents = true;
669 while(shouldRefreshRecents) {
670 SystemClock.sleep(refreshInterval);
672 updateRecentApps(false);
674 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
677 int[] location = new int[2];
678 layout.getLocationOnScreen(location);
680 if(location[1] != 0) {
681 if(location[1] > currentTaskbarPosition) {
682 currentTaskbarPosition = location[1];
683 } else if(location[1] < currentTaskbarPosition) {
684 if(currentTaskbarPosition - location[1] == getNavBarSize())
685 currentTaskbarPosition = location[1];
686 else if(!startThread2) {
688 tempHideTaskbar(true);
696 isRefreshingRecents = false;
703 @SuppressWarnings("Convert2streamapi")
704 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
705 private void updateRecentApps(final boolean firstRefresh) {
706 if(isScreenOff()) return;
708 SharedPreferences pref = U.getSharedPreferences(context);
709 final PackageManager pm = context.getPackageManager();
710 final List<AppEntry> entries = new ArrayList<>();
711 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
712 int maxNumOfEntries = U.getMaxNumOfEntries(context);
713 int realNumOfPinnedApps = 0;
714 boolean fullLength = pref.getBoolean("full_length", context.getResources().getBoolean(R.bool.tb_def_full_length));
717 currentRunningAppIds.clear();
719 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
720 List<AppEntry> pinnedApps = setTimeLastUsedFor(pba.getPinnedApps());
721 List<AppEntry> blockedApps = pba.getBlockedApps();
722 List<String> applicationIdsToRemove = new ArrayList<>();
724 // Filter out anything on the pinned/blocked apps lists
725 if(pinnedApps.size() > 0) {
726 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
727 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
729 for(AppEntry entry : pinnedApps) {
730 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
731 userManager.getUserForSerialNumber(entry.getUserId(context)));
736 realNumOfPinnedApps--;
738 applicationIdsToRemove.add(entry.getPackageName());
741 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
744 if(blockedApps.size() > 0) {
745 for(AppEntry entry : blockedApps) {
746 applicationIdsToRemove.add(entry.getPackageName());
750 // Get list of all recently used apps
751 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
752 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
753 if(realNumOfPinnedApps < maxNumOfEntries) {
754 List<AppEntry> usageStatsList2 = new ArrayList<>();
755 List<AppEntry> usageStatsList3 = new ArrayList<>();
756 List<AppEntry> usageStatsList4 = new ArrayList<>();
757 List<AppEntry> usageStatsList5 = new ArrayList<>();
758 List<AppEntry> usageStatsList6;
760 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
761 homeIntent.addCategory(Intent.CATEGORY_HOME);
762 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
764 // Filter out apps without a launcher intent
765 // Also filter out the current launcher, and Taskbar itself
766 for(AppEntry packageInfo : usageStatsList) {
767 if(hasLauncherIntent(packageInfo.getPackageName())
768 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
769 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
770 usageStatsList2.add(packageInfo);
773 // Filter out apps that don't fall within our current search interval
774 for(AppEntry stats : usageStatsList2) {
775 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
776 usageStatsList3.add(stats);
779 // Sort apps by either most recently used, or most time used
780 if(!runningAppsOnly && sortOrder.contains("most_used")) {
781 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
783 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
786 // Filter out any duplicate entries
787 List<String> applicationIds = new ArrayList<>();
788 for(AppEntry stats : usageStatsList3) {
789 if(!applicationIds.contains(stats.getPackageName())) {
790 usageStatsList4.add(stats);
791 applicationIds.add(stats.getPackageName());
795 // Filter out the currently running foreground app, if requested by the user
796 if(pref.getBoolean("hide_foreground", false)) {
797 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
798 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
799 UsageEvents.Event eventCache = new UsageEvents.Event();
800 String currentForegroundApp = null;
802 while(events.hasNextEvent()) {
803 events.getNextEvent(eventCache);
805 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
806 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
807 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
808 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
809 && !eventCache.getClassName().equals(HomeActivityDelegate.class.getCanonicalName())
810 && !eventCache.getClassName().equals(SecondaryHomeActivity.class.getCanonicalName())
811 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
812 currentForegroundApp = eventCache.getPackageName();
816 if(!applicationIdsToRemove.contains(currentForegroundApp))
817 applicationIdsToRemove.add(currentForegroundApp);
820 for(AppEntry stats : usageStatsList4) {
821 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
822 usageStatsList5.add(stats);
826 // Truncate list to a maximum length
827 if(usageStatsList5.size() > maxNumOfEntries)
828 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
830 usageStatsList6 = usageStatsList5;
832 // Determine if we need to reverse the order
833 boolean needToReverseOrder;
834 switch(U.getTaskbarPosition(context)) {
837 needToReverseOrder = sortOrder.contains("false");
840 needToReverseOrder = sortOrder.contains("true");
844 if(needToReverseOrder) {
845 Collections.reverse(usageStatsList6);
848 // Generate the AppEntries for the recent apps list
849 int number = usageStatsList6.size() == maxNumOfEntries
850 ? usageStatsList6.size() - realNumOfPinnedApps
851 : usageStatsList6.size();
853 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
854 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
856 final List<UserHandle> userHandles = userManager.getUserProfiles();
858 for(int i = 0; i < number; i++) {
859 for(UserHandle handle : userHandles) {
860 String packageName = usageStatsList6.get(i).getPackageName();
861 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
862 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
863 if(!list.isEmpty()) {
864 // Google App workaround
865 if(!packageName.equals("com.google.android.googlequicksearchbox"))
866 launcherAppCache.add(list.get(0));
868 boolean added = false;
869 for(LauncherActivityInfo info : list) {
870 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
871 launcherAppCache.add(info);
876 if(!added) launcherAppCache.add(list.get(0));
879 AppEntry newEntry = new AppEntry(
887 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
888 newEntry.setLastTimeUsed(lastTimeUsed);
889 entries.add(newEntry);
897 while(entries.size() > maxNumOfEntries) {
899 entries.remove(entries.size() - 1);
900 launcherAppCache.remove(launcherAppCache.size() - 1);
901 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
904 // Determine if we need to reverse the order again
905 if(U.getTaskbarPosition(context).contains("vertical")) {
906 Collections.reverse(entries);
907 Collections.reverse(launcherAppCache);
910 // Now that we've generated the list of apps,
911 // we need to determine if we need to redraw the Taskbar or not
912 boolean shouldRedrawTaskbar = firstRefresh;
914 List<String> finalApplicationIds = new ArrayList<>();
915 for(AppEntry entry : entries) {
916 finalApplicationIds.add(entry.getPackageName());
919 if(finalApplicationIds.size() != currentTaskbarIds.size()
920 || (runningAppsOnly && currentRunningAppIds.size() != prevRunningAppIds.size())
921 || numOfPinnedApps != realNumOfPinnedApps)
922 shouldRedrawTaskbar = true;
924 for(int i = 0; i < finalApplicationIds.size(); i++) {
925 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
926 shouldRedrawTaskbar = true;
931 if(!shouldRedrawTaskbar && runningAppsOnly) {
932 for(int i = 0; i < currentRunningAppIds.size(); i++) {
933 if(!currentRunningAppIds.get(i).equals(prevRunningAppIds.get(i))) {
934 shouldRedrawTaskbar = true;
941 if(shouldRedrawTaskbar) {
942 currentTaskbarIds = finalApplicationIds;
943 numOfPinnedApps = realNumOfPinnedApps;
945 if(runningAppsOnly) {
946 prevRunningAppIds.clear();
947 prevRunningAppIds.addAll(currentRunningAppIds);
950 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
952 int launcherAppCachePos = -1;
953 for(int i = 0; i < entries.size(); i++) {
954 if(entries.get(i).getComponentName() == null) {
955 launcherAppCachePos++;
956 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
957 String packageName = entries.get(i).getPackageName();
958 long lastTimeUsed = entries.get(i).getLastTimeUsed();
962 AppEntry newEntry = new AppEntry(
964 appInfo.getComponentName().flattenToString(),
965 appInfo.getLabel().toString(),
966 IconCache.getInstance(context).getIcon(context, pm, appInfo),
969 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
970 newEntry.setLastTimeUsed(lastTimeUsed);
971 entries.add(i, newEntry);
975 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
978 if(numOfEntries > 0 || fullLength) {
979 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
980 DisplayInfo display = U.getDisplayInfo(context, true);
981 int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size) * numOfEntries;
982 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
984 if(U.getTaskbarPosition(context).contains("vertical")) {
985 int maxScreenSize = Math.max(0, display.height
986 - U.getStatusBarHeight(context)
987 - U.getBaseTaskbarSize(context));
989 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
990 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
994 Space whitespaceTop = layout.findViewById(R.id.whitespace_top);
995 Space whitespaceBottom = layout.findViewById(R.id.whitespace_bottom);
996 int height = maxScreenSize - recentsSize;
998 if(pref.getBoolean("centered_icons", false)) {
999 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
1000 topParams.height = height / 2;
1001 whitespaceTop.setLayoutParams(topParams);
1003 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
1004 bottomParams.height = height / 2;
1005 whitespaceBottom.setLayoutParams(bottomParams);
1006 } else if(U.getTaskbarPosition(context).contains("bottom")) {
1007 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
1008 topParams.height = height;
1009 whitespaceTop.setLayoutParams(topParams);
1011 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
1012 bottomParams.height = height;
1013 whitespaceBottom.setLayoutParams(bottomParams);
1015 } catch (NullPointerException e) { /* Gracefully fail */ }
1018 int maxScreenSize = Math.max(0, display.width - U.getBaseTaskbarSize(context));
1020 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
1021 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1025 Space whitespaceLeft = layout.findViewById(R.id.whitespace_left);
1026 Space whitespaceRight = layout.findViewById(R.id.whitespace_right);
1027 int width = maxScreenSize - recentsSize;
1029 if(pref.getBoolean("centered_icons", false)) {
1030 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1031 leftParams.width = width / 2;
1032 whitespaceLeft.setLayoutParams(leftParams);
1034 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1035 rightParams.width = width / 2;
1036 whitespaceRight.setLayoutParams(rightParams);
1037 } else if(U.getTaskbarPosition(context).contains("right")) {
1038 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1039 leftParams.width = width;
1040 whitespaceLeft.setLayoutParams(leftParams);
1042 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1043 rightParams.width = width;
1044 whitespaceRight.setLayoutParams(rightParams);
1046 } catch (NullPointerException e) { /* Gracefully fail */ }
1050 scrollView.setLayoutParams(params);
1052 taskbar.removeAllViews();
1053 for(int i = 0; i < entries.size(); i++) {
1054 taskbar.addView(getView(entries, i));
1057 isShowingRecents = true;
1058 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
1060 scrollView.setVisibility(View.INVISIBLE);
1062 scrollView.setVisibility(View.VISIBLE);
1065 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
1066 new Handler().post(() -> {
1067 switch(U.getTaskbarPosition(context)) {
1069 case "bottom_right":
1072 if(sortOrder.contains("false"))
1073 scrollView.scrollTo(0, 0);
1074 else if(sortOrder.contains("true"))
1075 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1077 case "bottom_vertical_left":
1078 case "bottom_vertical_right":
1079 case "top_vertical_left":
1080 case "top_vertical_right":
1081 if(sortOrder.contains("false"))
1082 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1083 else if(sortOrder.contains("true"))
1084 scrollView.scrollTo(0, 0);
1088 if(shouldRefreshRecents) {
1089 scrollView.setVisibility(View.VISIBLE);
1093 isShowingRecents = false;
1094 scrollView.setVisibility(View.GONE);
1098 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
1099 currentTaskbarIds.clear();
1100 handler.post(() -> {
1101 isShowingRecents = false;
1102 scrollView.setVisibility(View.GONE);
1107 private void toggleTaskbar(boolean userInitiated) {
1108 if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
1109 SharedPreferences pref = U.getSharedPreferences(context);
1110 if(!pref.getBoolean("grip_rejection_toast_shown", false)) {
1111 U.showToastLong(context, R.string.tb_essential_phone_grip_rejection);
1112 pref.edit().putBoolean("grip_rejection_toast_shown", true).apply();
1116 if(startButton.getVisibility() == View.GONE)
1122 private void showTaskbar(boolean clearVariables) {
1123 if(clearVariables) {
1124 taskbarShownTemporarily = false;
1125 taskbarHiddenTemporarily = false;
1128 if(startButton.getVisibility() == View.GONE) {
1129 startButton.setVisibility(View.VISIBLE);
1130 space.setVisibility(View.VISIBLE);
1132 if(dashboardEnabled)
1133 dashboardButton.setVisibility(View.VISIBLE);
1135 if(navbarButtonsEnabled)
1136 navbarButtons.setVisibility(View.VISIBLE);
1138 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
1139 scrollView.setVisibility(View.INVISIBLE);
1142 sysTrayParentLayout.setVisibility(View.VISIBLE);
1144 shouldRefreshRecents = true;
1145 startRefreshingRecents();
1147 SharedPreferences pref = U.getSharedPreferences(context);
1148 pref.edit().putBoolean("collapsed", true).apply();
1150 updateButton(false);
1152 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_START_MENU_SPACE")));
1156 private void hideTaskbar(boolean clearVariables) {
1157 if(clearVariables) {
1158 taskbarShownTemporarily = false;
1159 taskbarHiddenTemporarily = false;
1162 if(startButton.getVisibility() == View.VISIBLE) {
1163 startButton.setVisibility(View.GONE);
1164 space.setVisibility(View.GONE);
1166 if(dashboardEnabled)
1167 dashboardButton.setVisibility(View.GONE);
1169 if(navbarButtonsEnabled)
1170 navbarButtons.setVisibility(View.GONE);
1172 if(isShowingRecents)
1173 scrollView.setVisibility(View.GONE);
1176 sysTrayParentLayout.setVisibility(View.GONE);
1178 shouldRefreshRecents = false;
1179 if(thread != null) thread.interrupt();
1181 SharedPreferences pref = U.getSharedPreferences(context);
1182 pref.edit().putBoolean("collapsed", false).apply();
1186 if(clearVariables) {
1187 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
1188 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1191 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_SPACE")));
1195 private void tempShowTaskbar() {
1196 if(!taskbarHiddenTemporarily) {
1197 SharedPreferences pref = U.getSharedPreferences(context);
1198 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1203 if(taskbarHiddenTemporarily)
1204 taskbarHiddenTemporarily = false;
1207 private void tempHideTaskbar(boolean monitorPositionChanges) {
1208 if(!taskbarShownTemporarily) {
1209 SharedPreferences pref = U.getSharedPreferences(context);
1210 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1215 if(taskbarShownTemporarily)
1216 taskbarShownTemporarily = false;
1218 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1219 if(thread2 != null) thread2.interrupt();
1221 handler2 = new Handler();
1222 thread2 = new Thread(() -> {
1223 stopThread2 = false;
1225 while(!stopThread2) {
1226 SystemClock.sleep(refreshInterval);
1228 handler2.post(() -> stopThread2 = checkPositionChange());
1231 startThread2 = false;
1238 private boolean checkPositionChange() {
1239 if(!isScreenOff() && layout != null) {
1240 int[] location = new int[2];
1241 layout.getLocationOnScreen(location);
1243 if(location[1] == 0) {
1246 if(location[1] > currentTaskbarPosition) {
1247 currentTaskbarPosition = location[1];
1248 if(taskbarHiddenTemporarily) {
1252 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1255 } else if(location[1] < currentTaskbarPosition
1256 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1257 currentTaskbarPosition = location[1];
1265 private int getNavBarSize() {
1266 Point size = new Point();
1267 Point realSize = new Point();
1269 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1270 Display display = wm.getDefaultDisplay();
1271 display.getSize(size);
1272 display.getRealSize(realSize);
1274 return realSize.y - size.y;
1278 public void onDestroyHost(UIHost host) {
1279 shouldRefreshRecents = false;
1283 host.removeView(layout);
1284 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1286 SharedPreferences pref = U.getSharedPreferences(context);
1287 if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1288 pref.edit().remove("skip_auto_hide_navbar").apply();
1289 } else if(pref.getBoolean("auto_hide_navbar", false))
1290 U.showHideNavigationBar(context, true);
1292 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
1294 lbm.unregisterReceiver(showReceiver);
1295 lbm.unregisterReceiver(hideReceiver);
1296 lbm.unregisterReceiver(tempShowReceiver);
1297 lbm.unregisterReceiver(tempHideReceiver);
1298 lbm.unregisterReceiver(startMenuAppearReceiver);
1299 lbm.unregisterReceiver(startMenuDisappearReceiver);
1301 if(sysTrayEnabled) {
1302 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1303 manager.listen(listener, PhoneStateListener.LISTEN_NONE);
1306 isFirstStart = true;
1309 private void openContextMenu() {
1310 SharedPreferences pref = U.getSharedPreferences(context);
1312 Bundle args = new Bundle();
1313 args.putBoolean("dont_show_quit",
1314 LauncherHelper.getInstance().isOnHomeScreen()
1315 && !pref.getBoolean("taskbar_active", false));
1316 args.putBoolean("is_start_button", true);
1318 U.startContextMenuActivity(context, args);
1321 private void updateButton(boolean isCollapsed) {
1322 SharedPreferences pref = U.getSharedPreferences(context);
1323 boolean hide = pref.getBoolean("invisible_button", false);
1325 if(button != null) button.setText(context.getString(isCollapsed ? R.string.tb_right_arrow : R.string.tb_left_arrow));
1326 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1329 @TargetApi(Build.VERSION_CODES.M)
1331 public void onRecreateHost(UIHost host) {
1332 if(layout != null) {
1334 host.removeView(layout);
1335 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1337 currentTaskbarPosition = 0;
1339 if(U.canDrawOverlays(context))
1342 SharedPreferences pref = U.getSharedPreferences(context);
1343 pref.edit().putBoolean("taskbar_active", false).apply();
1350 private View getView(List<AppEntry> list, int position) {
1351 View convertView = View.inflate(context, R.layout.tb_icon, null);
1353 final AppEntry entry = list.get(position);
1354 final SharedPreferences pref = U.getSharedPreferences(context);
1356 ImageView imageView = convertView.findViewById(R.id.icon);
1357 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1358 imageView.setImageDrawable(entry.getIcon(context));
1359 imageView2.setBackgroundColor(U.getAccentColor(context));
1361 String taskbarPosition = U.getTaskbarPosition(context);
1362 if(pref.getBoolean("shortcut_icon", true)) {
1363 boolean shouldShowShortcutIcon;
1364 if(taskbarPosition.contains("vertical"))
1365 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1367 shouldShowShortcutIcon = position < numOfPinnedApps;
1369 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1372 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1373 imageView.setRotationY(180);
1374 imageView2.setRotationY(180);
1377 FrameLayout layout = convertView.findViewById(R.id.entry);
1378 layout.setOnClickListener(view -> U.launchApp(
1387 layout.setOnLongClickListener(view -> {
1388 int[] location = new int[2];
1389 view.getLocationOnScreen(location);
1390 openContextMenu(entry, location);
1394 layout.setOnGenericMotionListener((view, motionEvent) -> {
1395 int action = motionEvent.getAction();
1397 if(action == MotionEvent.ACTION_BUTTON_PRESS
1398 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1399 int[] location = new int[2];
1400 view.getLocationOnScreen(location);
1401 openContextMenu(entry, location);
1404 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1405 view.setBackgroundColor(0);
1410 if(pref.getBoolean("visual_feedback", true)) {
1411 layout.setOnHoverListener((v, event) -> {
1412 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1413 int accentColor = U.getAccentColor(context);
1414 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1415 v.setBackgroundColor(accentColor);
1418 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1419 v.setBackgroundColor(0);
1421 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1422 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1427 layout.setOnTouchListener((v, event) -> {
1428 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1433 if(runningAppsOnly) {
1434 ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1435 if(entry.getLastTimeUsed() > 0) {
1436 runningAppIndicator.setVisibility(View.VISIBLE);
1437 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1439 runningAppIndicator.setVisibility(View.GONE);
1445 private void openContextMenu(AppEntry entry, int[] location) {
1446 Bundle args = new Bundle();
1447 args.putSerializable("app_entry", entry);
1448 args.putInt("x", location[0]);
1449 args.putInt("y", location[1]);
1451 U.startContextMenuActivity(context, args);
1454 private List<AppEntry> getAppEntries() {
1455 SharedPreferences pref = U.getSharedPreferences(context);
1457 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1459 return getAppEntriesUsingUsageStats();
1462 @SuppressWarnings("deprecation")
1463 @TargetApi(Build.VERSION_CODES.M)
1464 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1465 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1466 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1467 List<AppEntry> entries = new ArrayList<>();
1469 for(int i = 0; i < usageStatsList.size(); i++) {
1470 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1471 if(recentTaskInfo.id != -1) {
1472 String packageName = recentTaskInfo.baseActivity.getPackageName();
1473 AppEntry newEntry = new AppEntry(
1482 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1483 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1484 currentRunningAppIds.add(packageName);
1485 } catch (Exception e) {
1486 newEntry.setLastTimeUsed(i);
1489 entries.add(newEntry);
1496 @SuppressWarnings("deprecation")
1497 @TargetApi(Build.VERSION_CODES.M)
1498 private List<AppEntry> setTimeLastUsedFor(List<AppEntry> pinnedApps) {
1499 if(!runningAppsOnly || pinnedApps.size() == 0)
1502 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1503 List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasks(Integer.MAX_VALUE, 0);
1505 for(AppEntry entry : pinnedApps) {
1506 for(ActivityManager.RecentTaskInfo task : recentTasks) {
1507 if(task.id != -1 && task.baseActivity.getPackageName().equals(entry.getPackageName())) {
1509 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1510 entry.setLastTimeUsed(field.getLong(task));
1511 currentRunningAppIds.add(entry.getPackageName());
1513 } catch (Exception e) { /* Gracefully fail */ }
1521 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1522 private List<AppEntry> getAppEntriesUsingUsageStats() {
1523 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1524 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1525 List<AppEntry> entries = new ArrayList<>();
1527 for(UsageStats usageStats : usageStatsList) {
1528 AppEntry newEntry = new AppEntry(
1529 usageStats.getPackageName(),
1536 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1537 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1538 entries.add(newEntry);
1544 private boolean hasLauncherIntent(String packageName) {
1545 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1546 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1547 intentToResolve.setPackage(packageName);
1549 List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1550 return ris != null && ris.size() > 0;
1553 private boolean isScreenOff() {
1554 if(U.isChromeOs(context))
1557 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1558 return !pm.isInteractive();
1561 private void updateSystemTray() {
1562 if(!sysTrayEnabled || isScreenOff()) return;
1564 handler.post(() -> {
1565 ImageView battery = sysTrayLayout.findViewById(R.id.battery);
1566 battery.setImageDrawable(getBatteryDrawable());
1568 ImageView wifi = sysTrayLayout.findViewById(R.id.wifi);
1569 wifi.setImageDrawable(getWifiDrawable());
1571 ImageView bluetooth = sysTrayLayout.findViewById(R.id.bluetooth);
1572 bluetooth.setImageDrawable(getBluetoothDrawable());
1574 ImageView cellular = sysTrayLayout.findViewById(R.id.cellular);
1575 cellular.setImageDrawable(getCellularDrawable());
1577 time.setText(context.getString(R.string.tb_systray_clock,
1578 DateFormat.getTimeFormat(context).format(new Date()),
1579 DateFormat.getDateFormat(context).format(new Date())));
1580 time.setTextColor(U.getAccentColor(context));
1584 @TargetApi(Build.VERSION_CODES.M)
1585 private Drawable getBatteryDrawable() {
1586 BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
1587 int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1589 if(batLevel == Integer.MIN_VALUE)
1592 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
1593 Intent batteryStatus = context.registerReceiver(null, ifilter);
1595 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
1596 boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
1597 status == BatteryManager.BATTERY_STATUS_FULL;
1600 if(batLevel < 10 && !isCharging)
1601 batDrawable = "alert";
1602 else if(batLevel < 25)
1604 else if(batLevel < 40)
1606 else if(batLevel < 55)
1608 else if(batLevel < 70)
1610 else if(batLevel < 85)
1612 else if(batLevel < 95)
1615 batDrawable = "full";
1619 charging = "charging_";
1623 String batRes = "tb_battery_" + charging + batDrawable;
1624 int id = getResourceIdFor(batRes);
1626 return applyTintTo(ContextCompat.getDrawable(context, id));
1629 @TargetApi(Build.VERSION_CODES.M)
1630 private Drawable getWifiDrawable() {
1631 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1633 NetworkInfo ethernet = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
1634 if(ethernet != null && ethernet.isConnected())
1635 return applyTintTo(ContextCompat.getDrawable(context, R.drawable.tb_settings_ethernet));
1637 NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
1638 if(wifi == null || !wifi.isConnected())
1641 WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
1642 int numberOfLevels = 5;
1644 WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1645 int level = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), numberOfLevels);
1647 String wifiRes = "tb_signal_wifi_" + level + "_bar";
1648 int id = getResourceIdFor(wifiRes);
1650 return applyTintTo(ContextCompat.getDrawable(context, id));
1653 private Drawable getBluetoothDrawable() {
1654 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1655 if(adapter != null && adapter.isEnabled())
1656 return applyTintTo(ContextCompat.getDrawable(context, R.drawable.tb_bluetooth));
1661 @TargetApi(Build.VERSION_CODES.M)
1662 private Drawable getCellularDrawable() {
1663 if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0)
1664 return applyTintTo(ContextCompat.getDrawable(context, R.drawable.tb_airplanemode_active));
1666 if(cellStrength == -1)
1669 String cellRes = "tb_signal_cellular_" + cellStrength + "_bar";
1670 int id = getResourceIdFor(cellRes);
1672 return applyTintTo(ContextCompat.getDrawable(context, id));
1675 private Drawable applyTintTo(Drawable drawable) {
1676 if(drawable == null) return null;
1678 drawable.setTint(U.getAccentColor(context));
1682 private int getResourceIdFor(String name) {
1683 String packageName = context.getResources().getResourcePackageName(R.drawable.tb_dummy);
1684 return context.getResources().getIdentifier(name, "drawable", packageName);