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.util.AppEntry;
99 import com.farmerbb.taskbar.util.DisplayInfo;
100 import com.farmerbb.taskbar.util.FreeformHackHelper;
101 import com.farmerbb.taskbar.util.IconCache;
102 import com.farmerbb.taskbar.util.LauncherHelper;
103 import com.farmerbb.taskbar.util.PinnedBlockedApps;
104 import com.farmerbb.taskbar.util.MenuHelper;
105 import com.farmerbb.taskbar.util.U;
107 public class TaskbarController implements UIController {
109 private Context context;
110 private LinearLayout layout;
111 private ImageView startButton;
112 private LinearLayout taskbar;
113 private FrameLayout scrollView;
114 private Button button;
116 private FrameLayout dashboardButton;
117 private LinearLayout navbarButtons;
118 private LinearLayout sysTrayLayout;
119 private FrameLayout sysTrayParentLayout;
120 private TextView time;
122 private Handler handler;
123 private Handler handler2;
124 private Thread thread;
125 private Thread thread2;
127 private boolean isShowingRecents = true;
128 private boolean shouldRefreshRecents = true;
129 private boolean taskbarShownTemporarily = false;
130 private boolean taskbarHiddenTemporarily = false;
131 private boolean isRefreshingRecents = false;
132 private boolean isFirstStart = true;
134 private boolean startThread2 = false;
135 private boolean stopThread2 = false;
137 private int refreshInterval = -1;
138 private long searchInterval = -1;
139 private String sortOrder = "false";
140 private boolean runningAppsOnly = false;
142 private int layoutId = R.layout.tb_taskbar_left;
143 private int currentTaskbarPosition = 0;
144 private boolean showHideAutomagically = false;
145 private boolean positionIsVertical = false;
146 private boolean dashboardEnabled = false;
147 private boolean navbarButtonsEnabled = false;
148 private boolean sysTrayEnabled = false;
150 private List<String> currentTaskbarIds = new ArrayList<>();
151 private List<String> currentRunningAppIds = new ArrayList<>();
152 private List<String> prevRunningAppIds = new ArrayList<>();
153 private int numOfPinnedApps = -1;
155 private int cellStrength = -1;
157 private View.OnClickListener ocl = view -> {
158 Intent intent = new Intent("com.farmerbb.taskbar.TOGGLE_START_MENU");
159 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
162 private BroadcastReceiver showReceiver = new BroadcastReceiver() {
164 public void onReceive(Context context, Intent intent) {
169 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
171 public void onReceive(Context context, Intent intent) {
176 private BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
178 public void onReceive(Context context, Intent intent) {
183 private BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
185 public void onReceive(Context context, Intent intent) {
186 tempHideTaskbar(false);
190 private BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
192 public void onReceive(Context context, Intent intent) {
193 if(startButton.getVisibility() == View.GONE
194 && (!LauncherHelper.getInstance().isOnHomeScreen() || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
195 layout.setVisibility(View.GONE);
199 private BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
201 public void onReceive(Context context, Intent intent) {
202 if(startButton.getVisibility() == View.GONE)
203 layout.setVisibility(View.VISIBLE);
207 @TargetApi(Build.VERSION_CODES.M)
208 private PhoneStateListener listener = new PhoneStateListener() {
210 public void onSignalStrengthsChanged(SignalStrength signalStrength) {
212 cellStrength = signalStrength.getLevel();
213 } catch (SecurityException e) {
219 public TaskbarController(Context context) {
220 this.context = context;
223 @TargetApi(Build.VERSION_CODES.M)
225 public void onCreateHost(UIHost host) {
226 SharedPreferences pref = U.getSharedPreferences(context);
227 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
228 if(U.canDrawOverlays(context))
231 pref.edit().putBoolean("taskbar_active", false).apply();
235 } else host.terminate();
238 @SuppressLint("RtlHardcoded")
239 private void drawTaskbar(UIHost host) {
240 IconCache.getInstance(context).clearCache();
242 // Initialize layout params
243 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
244 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
246 final ViewParams params = new ViewParams(
247 WindowManager.LayoutParams.WRAP_CONTENT,
248 WindowManager.LayoutParams.WRAP_CONTENT,
250 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
253 // Determine where to show the taskbar on screen
254 switch(U.getTaskbarPosition(context)) {
256 layoutId = R.layout.tb_taskbar_left;
257 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
258 positionIsVertical = false;
260 case "bottom_vertical_left":
261 layoutId = R.layout.tb_taskbar_vertical;
262 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
263 positionIsVertical = true;
266 layoutId = R.layout.tb_taskbar_right;
267 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
268 positionIsVertical = false;
270 case "bottom_vertical_right":
271 layoutId = R.layout.tb_taskbar_vertical;
272 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
273 positionIsVertical = true;
276 layoutId = R.layout.tb_taskbar_left;
277 params.gravity = Gravity.TOP | Gravity.LEFT;
278 positionIsVertical = false;
280 case "top_vertical_left":
281 layoutId = R.layout.tb_taskbar_top_vertical;
282 params.gravity = Gravity.TOP | Gravity.LEFT;
283 positionIsVertical = true;
286 layoutId = R.layout.tb_taskbar_right;
287 params.gravity = Gravity.TOP | Gravity.RIGHT;
288 positionIsVertical = false;
290 case "top_vertical_right":
291 layoutId = R.layout.tb_taskbar_top_vertical;
292 params.gravity = Gravity.TOP | Gravity.RIGHT;
293 positionIsVertical = true;
298 SharedPreferences pref = U.getSharedPreferences(context);
299 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
301 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
302 taskbar = layout.findViewById(R.id.taskbar);
303 scrollView = layout.findViewById(R.id.taskbar_scrollview);
305 int backgroundTint = U.getBackgroundTint(context);
306 int accentColor = U.getAccentColor(context);
308 if(altButtonConfig) {
309 space = layout.findViewById(R.id.space_alt);
310 layout.findViewById(R.id.space).setVisibility(View.GONE);
312 space = layout.findViewById(R.id.space);
313 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
316 space.setOnClickListener(v -> toggleTaskbar(true));
318 startButton = layout.findViewById(R.id.start_button);
321 switch(pref.getString("start_button_image", U.getDefaultStartButtonImage(context))) {
323 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
324 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
329 if(U.isBlissOs(context)) {
330 drawable = ContextCompat.getDrawable(context, R.drawable.tb_bliss);
331 drawable.setTint(accentColor);
333 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
334 LauncherActivityInfo info = launcherApps.getActivityList(context.getPackageName(), Process.myUserHandle()).get(0);
335 drawable = IconCache.getInstance(context).getIcon(context, context.getPackageManager(), info);
338 startButton.setImageDrawable(drawable);
339 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding_alt);
342 File file = new File(context.getFilesDir() + "/tb_images", "custom_image");
344 Handler handler = new Handler();
346 Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
349 BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
350 bitmapDrawable.setFilterBitmap(bitmap.getWidth() * bitmap.getHeight() > 2000);
351 startButton.setImageDrawable(bitmapDrawable);
353 U.showToastLong(context, R.string.tb_error_reading_custom_start_image);
354 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
359 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon));
361 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
365 startButton.setPadding(padding, padding, padding, padding);
366 startButton.setOnClickListener(ocl);
367 startButton.setOnLongClickListener(view -> {
372 startButton.setOnGenericMotionListener((view, motionEvent) -> {
373 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
374 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
380 refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "1")) * 1000);
381 if(refreshInterval == 0)
382 refreshInterval = 100;
384 sortOrder = pref.getString("sort_order", "false");
385 runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
387 switch(pref.getString("recents_amount", "past_day")) {
389 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
392 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
393 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
395 searchInterval = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
402 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
403 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
404 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.UPDATE_HOME_SCREEN_MARGINS"));
406 if(altButtonConfig) {
407 button = layout.findViewById(R.id.hide_taskbar_button_alt);
408 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
410 button = layout.findViewById(R.id.hide_taskbar_button);
411 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
415 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
416 } catch (RuntimeException e) { /* Gracefully fail */ }
419 button.setOnClickListener(v -> toggleTaskbar(true));
421 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
422 ? R.id.hide_taskbar_button_layout_alt
423 : R.id.hide_taskbar_button_layout);
424 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
426 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
427 ? R.id.hide_taskbar_button_layout
428 : R.id.hide_taskbar_button_layout_alt);
429 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
431 dashboardButton = layout.findViewById(R.id.dashboard_button);
432 navbarButtons = layout.findViewById(R.id.navbar_buttons);
434 dashboardEnabled = pref.getBoolean("dashboard", context.getResources().getBoolean(R.bool.tb_def_dashboard));
435 if(dashboardEnabled) {
436 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
437 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
438 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
439 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
440 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
441 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
443 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
445 dashboardButton.setVisibility(View.GONE);
447 if(pref.getBoolean("button_back", false)) {
448 navbarButtonsEnabled = true;
450 ImageView backButton = layout.findViewById(R.id.button_back);
451 backButton.setColorFilter(accentColor);
452 backButton.setVisibility(View.VISIBLE);
453 backButton.setOnClickListener(v -> {
454 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
455 if(U.shouldCollapse(context, false))
459 backButton.setOnLongClickListener(v -> {
460 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
461 imm.showInputMethodPicker();
463 if(U.shouldCollapse(context, false))
469 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
470 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
471 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
472 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
473 imm.showInputMethodPicker();
475 if(U.shouldCollapse(context, false))
482 if(pref.getBoolean("button_home", false)) {
483 navbarButtonsEnabled = true;
485 ImageView homeButton = layout.findViewById(R.id.button_home);
486 homeButton.setColorFilter(accentColor);
487 homeButton.setVisibility(View.VISIBLE);
488 homeButton.setOnClickListener(v -> {
489 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
490 if(U.shouldCollapse(context, false))
494 homeButton.setOnLongClickListener(v -> {
495 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
496 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
499 context.startActivity(voiceSearchIntent);
500 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
502 if(U.shouldCollapse(context, false))
508 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
509 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
510 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
511 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
512 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
515 context.startActivity(voiceSearchIntent);
516 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
518 if(U.shouldCollapse(context, false))
525 if(pref.getBoolean("button_recents", false)) {
526 navbarButtonsEnabled = true;
528 ImageView recentsButton = layout.findViewById(R.id.button_recents);
529 recentsButton.setColorFilter(accentColor);
530 recentsButton.setVisibility(View.VISIBLE);
531 recentsButton.setOnClickListener(v -> {
532 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
533 if(U.shouldCollapse(context, false))
537 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
538 recentsButton.setOnLongClickListener(v -> {
539 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
540 if(U.shouldCollapse(context, false))
546 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
547 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
548 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
549 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
550 if(U.shouldCollapse(context, false))
558 if(!navbarButtonsEnabled)
559 navbarButtons.setVisibility(View.GONE);
561 sysTrayEnabled = U.isSystemTrayEnabled(context);
564 sysTrayLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.tb_system_tray, null);
566 FrameLayout.LayoutParams sysTrayParams = new FrameLayout.LayoutParams(
567 FrameLayout.LayoutParams.WRAP_CONTENT,
568 context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size)
571 if(layoutId == R.layout.tb_taskbar_right) {
572 time = sysTrayLayout.findViewById(R.id.time_left);
573 sysTrayParams.gravity = Gravity.START;
575 time = sysTrayLayout.findViewById(R.id.time_right);
576 sysTrayParams.gravity = Gravity.END;
579 time.setVisibility(View.VISIBLE);
580 sysTrayLayout.setLayoutParams(sysTrayParams);
582 if(!U.isLibrary(context)) {
583 sysTrayLayout.setOnClickListener(v -> {
584 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
585 if(U.shouldCollapse(context, false))
589 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
590 sysTrayLayout.setOnLongClickListener(v -> {
591 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
592 if(U.shouldCollapse(context, false))
598 sysTrayLayout.setOnGenericMotionListener((view, motionEvent) -> {
599 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
600 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
601 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
602 if (U.shouldCollapse(context, false))
610 sysTrayParentLayout = layout.findViewById(R.id.add_systray_here);
611 sysTrayParentLayout.setVisibility(View.VISIBLE);
612 sysTrayParentLayout.addView(sysTrayLayout);
614 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
615 manager.listen(listener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
618 layout.setBackgroundColor(backgroundTint);
619 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
620 button.setTextColor(accentColor);
622 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
624 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
625 toggleTaskbar(false);
627 if(pref.getBoolean("auto_hide_navbar", false))
628 U.showHideNavigationBar(context, false);
630 if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
631 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
633 new Handler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
636 lbm.unregisterReceiver(showReceiver);
637 lbm.unregisterReceiver(hideReceiver);
638 lbm.unregisterReceiver(tempShowReceiver);
639 lbm.unregisterReceiver(tempHideReceiver);
640 lbm.unregisterReceiver(startMenuAppearReceiver);
641 lbm.unregisterReceiver(startMenuDisappearReceiver);
643 lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
644 lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
645 lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
646 lbm.registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
647 lbm.registerReceiver(startMenuAppearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_APPEARING"));
648 lbm.registerReceiver(startMenuDisappearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
650 startRefreshingRecents();
652 host.addView(layout, params);
654 isFirstStart = false;
657 private void startRefreshingRecents() {
658 if(thread != null) thread.interrupt();
661 SharedPreferences pref = U.getSharedPreferences(context);
662 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
664 currentTaskbarIds.clear();
666 handler = new Handler();
667 thread = new Thread(() -> {
669 updateRecentApps(true);
671 if(!isRefreshingRecents) {
672 isRefreshingRecents = true;
674 while(shouldRefreshRecents) {
675 SystemClock.sleep(refreshInterval);
677 updateRecentApps(false);
679 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
682 int[] location = new int[2];
683 layout.getLocationOnScreen(location);
685 if(location[1] != 0) {
686 if(location[1] > currentTaskbarPosition) {
687 currentTaskbarPosition = location[1];
688 } else if(location[1] < currentTaskbarPosition) {
689 if(currentTaskbarPosition - location[1] == getNavBarSize())
690 currentTaskbarPosition = location[1];
691 else if(!startThread2) {
693 tempHideTaskbar(true);
701 isRefreshingRecents = false;
708 @SuppressWarnings("Convert2streamapi")
709 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
710 private void updateRecentApps(final boolean firstRefresh) {
711 if(isScreenOff()) return;
713 SharedPreferences pref = U.getSharedPreferences(context);
714 final PackageManager pm = context.getPackageManager();
715 final List<AppEntry> entries = new ArrayList<>();
716 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
717 int maxNumOfEntries = U.getMaxNumOfEntries(context);
718 int realNumOfPinnedApps = 0;
719 boolean fullLength = pref.getBoolean("full_length", context.getResources().getBoolean(R.bool.tb_def_full_length));
722 currentRunningAppIds.clear();
724 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
725 List<AppEntry> pinnedApps = setTimeLastUsedFor(pba.getPinnedApps());
726 List<AppEntry> blockedApps = pba.getBlockedApps();
727 List<String> applicationIdsToRemove = new ArrayList<>();
729 // Filter out anything on the pinned/blocked apps lists
730 if(pinnedApps.size() > 0) {
731 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
732 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
734 for(AppEntry entry : pinnedApps) {
735 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
736 userManager.getUserForSerialNumber(entry.getUserId(context)));
741 realNumOfPinnedApps--;
743 applicationIdsToRemove.add(entry.getPackageName());
746 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
749 if(blockedApps.size() > 0) {
750 for(AppEntry entry : blockedApps) {
751 applicationIdsToRemove.add(entry.getPackageName());
755 // Get list of all recently used apps
756 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
757 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
758 if(realNumOfPinnedApps < maxNumOfEntries) {
759 List<AppEntry> usageStatsList2 = new ArrayList<>();
760 List<AppEntry> usageStatsList3 = new ArrayList<>();
761 List<AppEntry> usageStatsList4 = new ArrayList<>();
762 List<AppEntry> usageStatsList5 = new ArrayList<>();
763 List<AppEntry> usageStatsList6;
765 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
766 homeIntent.addCategory(Intent.CATEGORY_HOME);
767 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
769 // Filter out apps without a launcher intent
770 // Also filter out the current launcher, and Taskbar itself
771 for(AppEntry packageInfo : usageStatsList) {
772 if(hasLauncherIntent(packageInfo.getPackageName())
773 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
774 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
775 usageStatsList2.add(packageInfo);
778 // Filter out apps that don't fall within our current search interval
779 for(AppEntry stats : usageStatsList2) {
780 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
781 usageStatsList3.add(stats);
784 // Sort apps by either most recently used, or most time used
785 if(!runningAppsOnly && sortOrder.contains("most_used")) {
786 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
788 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
791 // Filter out any duplicate entries
792 List<String> applicationIds = new ArrayList<>();
793 for(AppEntry stats : usageStatsList3) {
794 if(!applicationIds.contains(stats.getPackageName())) {
795 usageStatsList4.add(stats);
796 applicationIds.add(stats.getPackageName());
800 // Filter out the currently running foreground app, if requested by the user
801 if(pref.getBoolean("hide_foreground", false)) {
802 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
803 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
804 UsageEvents.Event eventCache = new UsageEvents.Event();
805 String currentForegroundApp = null;
807 while(events.hasNextEvent()) {
808 events.getNextEvent(eventCache);
810 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
811 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
812 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
813 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
814 && !eventCache.getClassName().equals(HomeActivityDelegate.class.getCanonicalName())
815 && !eventCache.getClassName().equals(SecondaryHomeActivity.class.getCanonicalName())
816 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
817 currentForegroundApp = eventCache.getPackageName();
821 if(!applicationIdsToRemove.contains(currentForegroundApp))
822 applicationIdsToRemove.add(currentForegroundApp);
825 for(AppEntry stats : usageStatsList4) {
826 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
827 usageStatsList5.add(stats);
831 // Truncate list to a maximum length
832 if(usageStatsList5.size() > maxNumOfEntries)
833 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
835 usageStatsList6 = usageStatsList5;
837 // Determine if we need to reverse the order
838 boolean needToReverseOrder;
839 switch(U.getTaskbarPosition(context)) {
842 needToReverseOrder = sortOrder.contains("false");
845 needToReverseOrder = sortOrder.contains("true");
849 if(needToReverseOrder) {
850 Collections.reverse(usageStatsList6);
853 // Generate the AppEntries for the recent apps list
854 int number = usageStatsList6.size() == maxNumOfEntries
855 ? usageStatsList6.size() - realNumOfPinnedApps
856 : usageStatsList6.size();
858 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
859 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
861 final List<UserHandle> userHandles = userManager.getUserProfiles();
863 for(int i = 0; i < number; i++) {
864 for(UserHandle handle : userHandles) {
865 String packageName = usageStatsList6.get(i).getPackageName();
866 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
867 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
868 if(!list.isEmpty()) {
869 // Google App workaround
870 if(!packageName.equals("com.google.android.googlequicksearchbox"))
871 launcherAppCache.add(list.get(0));
873 boolean added = false;
874 for(LauncherActivityInfo info : list) {
875 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
876 launcherAppCache.add(info);
881 if(!added) launcherAppCache.add(list.get(0));
884 AppEntry newEntry = new AppEntry(
892 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
893 newEntry.setLastTimeUsed(lastTimeUsed);
894 entries.add(newEntry);
902 while(entries.size() > maxNumOfEntries) {
904 entries.remove(entries.size() - 1);
905 launcherAppCache.remove(launcherAppCache.size() - 1);
906 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
909 // Determine if we need to reverse the order again
910 if(U.getTaskbarPosition(context).contains("vertical")) {
911 Collections.reverse(entries);
912 Collections.reverse(launcherAppCache);
915 // Now that we've generated the list of apps,
916 // we need to determine if we need to redraw the Taskbar or not
917 boolean shouldRedrawTaskbar = firstRefresh;
919 List<String> finalApplicationIds = new ArrayList<>();
920 for(AppEntry entry : entries) {
921 finalApplicationIds.add(entry.getPackageName());
924 if(finalApplicationIds.size() != currentTaskbarIds.size()
925 || (runningAppsOnly && currentRunningAppIds.size() != prevRunningAppIds.size())
926 || numOfPinnedApps != realNumOfPinnedApps)
927 shouldRedrawTaskbar = true;
929 for(int i = 0; i < finalApplicationIds.size(); i++) {
930 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
931 shouldRedrawTaskbar = true;
936 if(!shouldRedrawTaskbar && runningAppsOnly) {
937 for(int i = 0; i < currentRunningAppIds.size(); i++) {
938 if(!currentRunningAppIds.get(i).equals(prevRunningAppIds.get(i))) {
939 shouldRedrawTaskbar = true;
946 if(shouldRedrawTaskbar) {
947 currentTaskbarIds = finalApplicationIds;
948 numOfPinnedApps = realNumOfPinnedApps;
950 if(runningAppsOnly) {
951 prevRunningAppIds.clear();
952 prevRunningAppIds.addAll(currentRunningAppIds);
955 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
957 int launcherAppCachePos = -1;
958 for(int i = 0; i < entries.size(); i++) {
959 if(entries.get(i).getComponentName() == null) {
960 launcherAppCachePos++;
961 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
962 String packageName = entries.get(i).getPackageName();
963 long lastTimeUsed = entries.get(i).getLastTimeUsed();
967 AppEntry newEntry = new AppEntry(
969 appInfo.getComponentName().flattenToString(),
970 appInfo.getLabel().toString(),
971 IconCache.getInstance(context).getIcon(context, pm, appInfo),
974 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
975 newEntry.setLastTimeUsed(lastTimeUsed);
976 entries.add(i, newEntry);
980 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
983 if(numOfEntries > 0 || fullLength) {
984 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
985 DisplayInfo display = U.getDisplayInfo(context, true);
986 int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size) * numOfEntries;
987 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
989 if(U.getTaskbarPosition(context).contains("vertical")) {
990 int maxScreenSize = Math.max(0, display.height
991 - U.getStatusBarHeight(context)
992 - U.getBaseTaskbarSize(context));
994 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
995 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
999 Space whitespaceTop = layout.findViewById(R.id.whitespace_top);
1000 Space whitespaceBottom = layout.findViewById(R.id.whitespace_bottom);
1001 int height = maxScreenSize - recentsSize;
1003 if(pref.getBoolean("centered_icons", false)) {
1004 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
1005 topParams.height = height / 2;
1006 whitespaceTop.setLayoutParams(topParams);
1008 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
1009 bottomParams.height = height / 2;
1010 whitespaceBottom.setLayoutParams(bottomParams);
1011 } else if(U.getTaskbarPosition(context).contains("bottom")) {
1012 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
1013 topParams.height = height;
1014 whitespaceTop.setLayoutParams(topParams);
1016 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
1017 bottomParams.height = height;
1018 whitespaceBottom.setLayoutParams(bottomParams);
1020 } catch (NullPointerException e) { /* Gracefully fail */ }
1023 int maxScreenSize = Math.max(0, display.width - U.getBaseTaskbarSize(context));
1025 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
1026 + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1030 Space whitespaceLeft = layout.findViewById(R.id.whitespace_left);
1031 Space whitespaceRight = layout.findViewById(R.id.whitespace_right);
1032 int width = maxScreenSize - recentsSize;
1034 if(pref.getBoolean("centered_icons", false)) {
1035 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1036 leftParams.width = width / 2;
1037 whitespaceLeft.setLayoutParams(leftParams);
1039 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1040 rightParams.width = width / 2;
1041 whitespaceRight.setLayoutParams(rightParams);
1042 } else if(U.getTaskbarPosition(context).contains("right")) {
1043 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1044 leftParams.width = width;
1045 whitespaceLeft.setLayoutParams(leftParams);
1047 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1048 rightParams.width = width;
1049 whitespaceRight.setLayoutParams(rightParams);
1051 } catch (NullPointerException e) { /* Gracefully fail */ }
1055 scrollView.setLayoutParams(params);
1057 taskbar.removeAllViews();
1058 for(int i = 0; i < entries.size(); i++) {
1059 taskbar.addView(getView(entries, i));
1062 isShowingRecents = true;
1063 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
1065 scrollView.setVisibility(View.INVISIBLE);
1067 scrollView.setVisibility(View.VISIBLE);
1070 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
1071 new Handler().post(() -> {
1072 switch(U.getTaskbarPosition(context)) {
1074 case "bottom_right":
1077 if(sortOrder.contains("false"))
1078 scrollView.scrollTo(0, 0);
1079 else if(sortOrder.contains("true"))
1080 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1082 case "bottom_vertical_left":
1083 case "bottom_vertical_right":
1084 case "top_vertical_left":
1085 case "top_vertical_right":
1086 if(sortOrder.contains("false"))
1087 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1088 else if(sortOrder.contains("true"))
1089 scrollView.scrollTo(0, 0);
1093 if(shouldRefreshRecents) {
1094 scrollView.setVisibility(View.VISIBLE);
1098 isShowingRecents = false;
1099 scrollView.setVisibility(View.GONE);
1103 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
1104 currentTaskbarIds.clear();
1105 handler.post(() -> {
1106 isShowingRecents = false;
1107 scrollView.setVisibility(View.GONE);
1112 private void toggleTaskbar(boolean userInitiated) {
1113 if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
1114 SharedPreferences pref = U.getSharedPreferences(context);
1115 if(!pref.getBoolean("grip_rejection_toast_shown", false)) {
1116 U.showToastLong(context, R.string.tb_essential_phone_grip_rejection);
1117 pref.edit().putBoolean("grip_rejection_toast_shown", true).apply();
1121 if(startButton.getVisibility() == View.GONE)
1127 private void showTaskbar(boolean clearVariables) {
1128 if(clearVariables) {
1129 taskbarShownTemporarily = false;
1130 taskbarHiddenTemporarily = false;
1133 if(startButton.getVisibility() == View.GONE) {
1134 startButton.setVisibility(View.VISIBLE);
1135 space.setVisibility(View.VISIBLE);
1137 if(dashboardEnabled)
1138 dashboardButton.setVisibility(View.VISIBLE);
1140 if(navbarButtonsEnabled)
1141 navbarButtons.setVisibility(View.VISIBLE);
1143 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
1144 scrollView.setVisibility(View.INVISIBLE);
1147 sysTrayParentLayout.setVisibility(View.VISIBLE);
1149 shouldRefreshRecents = true;
1150 startRefreshingRecents();
1152 SharedPreferences pref = U.getSharedPreferences(context);
1153 pref.edit().putBoolean("collapsed", true).apply();
1155 updateButton(false);
1157 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_START_MENU_SPACE")));
1161 private void hideTaskbar(boolean clearVariables) {
1162 if(clearVariables) {
1163 taskbarShownTemporarily = false;
1164 taskbarHiddenTemporarily = false;
1167 if(startButton.getVisibility() == View.VISIBLE) {
1168 startButton.setVisibility(View.GONE);
1169 space.setVisibility(View.GONE);
1171 if(dashboardEnabled)
1172 dashboardButton.setVisibility(View.GONE);
1174 if(navbarButtonsEnabled)
1175 navbarButtons.setVisibility(View.GONE);
1177 if(isShowingRecents)
1178 scrollView.setVisibility(View.GONE);
1181 sysTrayParentLayout.setVisibility(View.GONE);
1183 shouldRefreshRecents = false;
1184 if(thread != null) thread.interrupt();
1186 SharedPreferences pref = U.getSharedPreferences(context);
1187 pref.edit().putBoolean("collapsed", false).apply();
1191 if(clearVariables) {
1192 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
1193 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1196 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_SPACE")));
1200 private void tempShowTaskbar() {
1201 if(!taskbarHiddenTemporarily) {
1202 SharedPreferences pref = U.getSharedPreferences(context);
1203 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1208 if(taskbarHiddenTemporarily)
1209 taskbarHiddenTemporarily = false;
1212 private void tempHideTaskbar(boolean monitorPositionChanges) {
1213 if(!taskbarShownTemporarily) {
1214 SharedPreferences pref = U.getSharedPreferences(context);
1215 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1220 if(taskbarShownTemporarily)
1221 taskbarShownTemporarily = false;
1223 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1224 if(thread2 != null) thread2.interrupt();
1226 handler2 = new Handler();
1227 thread2 = new Thread(() -> {
1228 stopThread2 = false;
1230 while(!stopThread2) {
1231 SystemClock.sleep(refreshInterval);
1233 handler2.post(() -> stopThread2 = checkPositionChange());
1236 startThread2 = false;
1243 private boolean checkPositionChange() {
1244 if(!isScreenOff() && layout != null) {
1245 int[] location = new int[2];
1246 layout.getLocationOnScreen(location);
1248 if(location[1] == 0) {
1251 if(location[1] > currentTaskbarPosition) {
1252 currentTaskbarPosition = location[1];
1253 if(taskbarHiddenTemporarily) {
1257 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1260 } else if(location[1] < currentTaskbarPosition
1261 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1262 currentTaskbarPosition = location[1];
1270 private int getNavBarSize() {
1271 Point size = new Point();
1272 Point realSize = new Point();
1274 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1275 Display display = wm.getDefaultDisplay();
1276 display.getSize(size);
1277 display.getRealSize(realSize);
1279 return realSize.y - size.y;
1283 public void onDestroyHost(UIHost host) {
1284 shouldRefreshRecents = false;
1288 host.removeView(layout);
1289 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1291 SharedPreferences pref = U.getSharedPreferences(context);
1292 if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1293 pref.edit().remove("skip_auto_hide_navbar").apply();
1294 } else if(pref.getBoolean("auto_hide_navbar", false))
1295 U.showHideNavigationBar(context, true);
1297 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
1299 lbm.unregisterReceiver(showReceiver);
1300 lbm.unregisterReceiver(hideReceiver);
1301 lbm.unregisterReceiver(tempShowReceiver);
1302 lbm.unregisterReceiver(tempHideReceiver);
1303 lbm.unregisterReceiver(startMenuAppearReceiver);
1304 lbm.unregisterReceiver(startMenuDisappearReceiver);
1306 if(sysTrayEnabled) {
1307 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1308 manager.listen(listener, PhoneStateListener.LISTEN_NONE);
1311 isFirstStart = true;
1314 private void openContextMenu() {
1315 SharedPreferences pref = U.getSharedPreferences(context);
1317 Bundle args = new Bundle();
1318 args.putBoolean("dont_show_quit",
1319 LauncherHelper.getInstance().isOnHomeScreen()
1320 && !pref.getBoolean("taskbar_active", false));
1321 args.putBoolean("is_start_button", true);
1323 U.startContextMenuActivity(context, args);
1326 private void updateButton(boolean isCollapsed) {
1327 SharedPreferences pref = U.getSharedPreferences(context);
1328 boolean hide = pref.getBoolean("invisible_button", false);
1330 if(button != null) button.setText(context.getString(isCollapsed ? R.string.tb_right_arrow : R.string.tb_left_arrow));
1331 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1334 @TargetApi(Build.VERSION_CODES.M)
1336 public void onRecreateHost(UIHost host) {
1337 if(layout != null) {
1339 host.removeView(layout);
1340 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1342 currentTaskbarPosition = 0;
1344 if(U.canDrawOverlays(context))
1347 SharedPreferences pref = U.getSharedPreferences(context);
1348 pref.edit().putBoolean("taskbar_active", false).apply();
1355 private View getView(List<AppEntry> list, int position) {
1356 View convertView = View.inflate(context, R.layout.tb_icon, null);
1358 final AppEntry entry = list.get(position);
1359 final SharedPreferences pref = U.getSharedPreferences(context);
1361 ImageView imageView = convertView.findViewById(R.id.icon);
1362 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1363 imageView.setImageDrawable(entry.getIcon(context));
1364 imageView2.setBackgroundColor(U.getAccentColor(context));
1366 String taskbarPosition = U.getTaskbarPosition(context);
1367 if(pref.getBoolean("shortcut_icon", true)) {
1368 boolean shouldShowShortcutIcon;
1369 if(taskbarPosition.contains("vertical"))
1370 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1372 shouldShowShortcutIcon = position < numOfPinnedApps;
1374 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1377 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1378 imageView.setRotationY(180);
1379 imageView2.setRotationY(180);
1382 FrameLayout layout = convertView.findViewById(R.id.entry);
1383 layout.setOnClickListener(view -> U.launchApp(
1392 layout.setOnLongClickListener(view -> {
1393 int[] location = new int[2];
1394 view.getLocationOnScreen(location);
1395 openContextMenu(entry, location);
1399 layout.setOnGenericMotionListener((view, motionEvent) -> {
1400 int action = motionEvent.getAction();
1402 if(action == MotionEvent.ACTION_BUTTON_PRESS
1403 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1404 int[] location = new int[2];
1405 view.getLocationOnScreen(location);
1406 openContextMenu(entry, location);
1409 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1410 view.setBackgroundColor(0);
1415 if(pref.getBoolean("visual_feedback", true)) {
1416 layout.setOnHoverListener((v, event) -> {
1417 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1418 int accentColor = U.getAccentColor(context);
1419 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1420 v.setBackgroundColor(accentColor);
1423 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1424 v.setBackgroundColor(0);
1426 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1427 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1432 layout.setOnTouchListener((v, event) -> {
1433 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1438 if(runningAppsOnly) {
1439 ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1440 if(entry.getLastTimeUsed() > 0) {
1441 runningAppIndicator.setVisibility(View.VISIBLE);
1442 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1444 runningAppIndicator.setVisibility(View.GONE);
1450 private void openContextMenu(AppEntry entry, int[] location) {
1451 Bundle args = new Bundle();
1452 args.putSerializable("app_entry", entry);
1453 args.putInt("x", location[0]);
1454 args.putInt("y", location[1]);
1456 U.startContextMenuActivity(context, args);
1459 private List<AppEntry> getAppEntries() {
1460 SharedPreferences pref = U.getSharedPreferences(context);
1462 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1464 return getAppEntriesUsingUsageStats();
1467 @SuppressWarnings("deprecation")
1468 @TargetApi(Build.VERSION_CODES.M)
1469 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1470 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1471 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1472 List<AppEntry> entries = new ArrayList<>();
1474 for(int i = 0; i < usageStatsList.size(); i++) {
1475 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1476 if(recentTaskInfo.id != -1) {
1477 String packageName = recentTaskInfo.baseActivity.getPackageName();
1478 AppEntry newEntry = new AppEntry(
1487 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1488 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1489 currentRunningAppIds.add(packageName);
1490 } catch (Exception e) {
1491 newEntry.setLastTimeUsed(i);
1494 entries.add(newEntry);
1501 @SuppressWarnings("deprecation")
1502 @TargetApi(Build.VERSION_CODES.M)
1503 private List<AppEntry> setTimeLastUsedFor(List<AppEntry> pinnedApps) {
1504 if(!runningAppsOnly || pinnedApps.size() == 0)
1507 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1508 List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasks(Integer.MAX_VALUE, 0);
1510 for(AppEntry entry : pinnedApps) {
1511 for(ActivityManager.RecentTaskInfo task : recentTasks) {
1512 if(task.id != -1 && task.baseActivity.getPackageName().equals(entry.getPackageName())) {
1514 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1515 entry.setLastTimeUsed(field.getLong(task));
1516 currentRunningAppIds.add(entry.getPackageName());
1518 } catch (Exception e) { /* Gracefully fail */ }
1526 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1527 private List<AppEntry> getAppEntriesUsingUsageStats() {
1528 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1529 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1530 List<AppEntry> entries = new ArrayList<>();
1532 for(UsageStats usageStats : usageStatsList) {
1533 AppEntry newEntry = new AppEntry(
1534 usageStats.getPackageName(),
1541 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1542 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1543 entries.add(newEntry);
1549 private boolean hasLauncherIntent(String packageName) {
1550 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1551 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1552 intentToResolve.setPackage(packageName);
1554 List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1555 return ris != null && ris.size() > 0;
1558 private boolean isScreenOff() {
1559 if(U.isChromeOs(context))
1562 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1563 return !pm.isInteractive();
1566 private void updateSystemTray() {
1567 if(!sysTrayEnabled || isScreenOff()) return;
1569 handler.post(() -> {
1570 ImageView battery = sysTrayLayout.findViewById(R.id.battery);
1571 battery.setImageDrawable(getBatteryDrawable());
1573 ImageView wifi = sysTrayLayout.findViewById(R.id.wifi);
1574 wifi.setImageDrawable(getWifiDrawable());
1576 ImageView bluetooth = sysTrayLayout.findViewById(R.id.bluetooth);
1577 bluetooth.setImageDrawable(getBluetoothDrawable());
1579 ImageView cellular = sysTrayLayout.findViewById(R.id.cellular);
1580 cellular.setImageDrawable(getCellularDrawable());
1582 time.setText(context.getString(R.string.tb_systray_clock,
1583 DateFormat.getTimeFormat(context).format(new Date()),
1584 DateFormat.getDateFormat(context).format(new Date())));
1585 time.setTextColor(U.getAccentColor(context));
1589 @TargetApi(Build.VERSION_CODES.M)
1590 private Drawable getBatteryDrawable() {
1591 BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
1592 int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1594 if(batLevel == Integer.MIN_VALUE)
1597 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
1598 Intent batteryStatus = context.registerReceiver(null, ifilter);
1600 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
1601 boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
1602 status == BatteryManager.BATTERY_STATUS_FULL;
1605 if(batLevel < 10 && !isCharging)
1606 batDrawable = "alert";
1607 else if(batLevel < 25)
1609 else if(batLevel < 40)
1611 else if(batLevel < 55)
1613 else if(batLevel < 70)
1615 else if(batLevel < 85)
1617 else if(batLevel < 95)
1620 batDrawable = "full";
1624 charging = "charging_";
1628 String batRes = "tb_battery_" + charging + batDrawable;
1629 int id = getResourceIdFor(batRes);
1631 return getDrawableForSysTray(id);
1634 @TargetApi(Build.VERSION_CODES.M)
1635 private Drawable getWifiDrawable() {
1636 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1638 NetworkInfo ethernet = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
1639 if(ethernet != null && ethernet.isConnected())
1640 return getDrawableForSysTray(R.drawable.tb_settings_ethernet);
1642 NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
1643 if(wifi == null || !wifi.isConnected())
1646 WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
1647 int numberOfLevels = 5;
1649 WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1650 int level = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), numberOfLevels);
1652 String wifiRes = "tb_signal_wifi_" + level + "_bar";
1653 int id = getResourceIdFor(wifiRes);
1655 return getDrawableForSysTray(id);
1658 private Drawable getBluetoothDrawable() {
1659 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1660 if(adapter != null && adapter.isEnabled())
1661 return getDrawableForSysTray(R.drawable.tb_bluetooth);
1666 @TargetApi(Build.VERSION_CODES.M)
1667 private Drawable getCellularDrawable() {
1668 if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0)
1669 return getDrawableForSysTray(R.drawable.tb_airplanemode_active);
1671 if(cellStrength == -1)
1674 String cellRes = "tb_signal_cellular_" + cellStrength + "_bar";
1675 int id = getResourceIdFor(cellRes);
1677 return getDrawableForSysTray(id);
1680 private Drawable getDrawableForSysTray(int id) {
1681 Drawable drawable = null;
1683 drawable = ContextCompat.getDrawable(context, id);
1684 } catch (Resources.NotFoundException e) { /* Gracefully fail */ }
1686 if(drawable == null) return null;
1688 drawable.setTint(U.getAccentColor(context));
1692 private int getResourceIdFor(String name) {
1693 String packageName = context.getResources().getResourcePackageName(R.drawable.tb_dummy);
1694 return context.getResources().getIdentifier(name, "drawable", packageName);