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.content.ActivityNotFoundException;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.SharedPreferences;
32 import android.content.pm.LauncherActivityInfo;
33 import android.content.pm.LauncherApps;
34 import android.content.pm.PackageManager;
35 import android.content.pm.ResolveInfo;
36 import android.graphics.Color;
37 import android.graphics.Point;
38 import android.graphics.Typeface;
39 import android.graphics.drawable.Drawable;
40 import android.os.Build;
41 import android.os.Bundle;
42 import android.os.Handler;
43 import android.os.PowerManager;
44 import android.os.SystemClock;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.speech.RecognizerIntent;
48 import android.support.v4.content.ContextCompat;
49 import android.support.v4.graphics.ColorUtils;
50 import android.view.Display;
51 import android.view.Gravity;
52 import android.view.LayoutInflater;
53 import android.view.MotionEvent;
54 import android.view.PointerIcon;
55 import android.view.View;
56 import android.view.ViewGroup;
57 import android.view.WindowManager;
58 import android.view.inputmethod.InputMethodManager;
59 import android.widget.Button;
60 import android.widget.FrameLayout;
61 import android.widget.ImageView;
63 import java.lang.reflect.Field;
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.List;
68 import android.support.v4.content.LocalBroadcastManager;
69 import android.widget.LinearLayout;
70 import android.widget.Space;
72 import com.farmerbb.taskbar.BuildConfig;
73 import com.farmerbb.taskbar.activity.HomeActivityDelegate;
74 import com.farmerbb.taskbar.activity.MainActivity;
75 import com.farmerbb.taskbar.R;
76 import com.farmerbb.taskbar.activity.HomeActivity;
77 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
78 import com.farmerbb.taskbar.util.AppEntry;
79 import com.farmerbb.taskbar.util.DisplayInfo;
80 import com.farmerbb.taskbar.util.FreeformHackHelper;
81 import com.farmerbb.taskbar.util.IconCache;
82 import com.farmerbb.taskbar.util.LauncherHelper;
83 import com.farmerbb.taskbar.util.PinnedBlockedApps;
84 import com.farmerbb.taskbar.util.MenuHelper;
85 import com.farmerbb.taskbar.util.U;
87 public class TaskbarController implements Controller {
89 private Context context;
90 private LinearLayout layout;
91 private ImageView startButton;
92 private LinearLayout taskbar;
93 private FrameLayout scrollView;
94 private Button button;
96 private FrameLayout dashboardButton;
97 private LinearLayout navbarButtons;
99 private Handler handler;
100 private Handler handler2;
101 private Thread thread;
102 private Thread thread2;
104 private boolean isShowingRecents = true;
105 private boolean shouldRefreshRecents = true;
106 private boolean taskbarShownTemporarily = false;
107 private boolean taskbarHiddenTemporarily = false;
108 private boolean isRefreshingRecents = false;
109 private boolean isFirstStart = true;
111 private boolean startThread2 = false;
112 private boolean stopThread2 = false;
114 private int refreshInterval = -1;
115 private long searchInterval = -1;
116 private String sortOrder = "false";
117 private boolean runningAppsOnly = false;
119 private int layoutId = R.layout.taskbar_left;
120 private int currentTaskbarPosition = 0;
121 private boolean showHideAutomagically = false;
122 private boolean positionIsVertical = false;
123 private boolean dashboardEnabled = false;
124 private boolean navbarButtonsEnabled = false;
126 private List<String> currentTaskbarIds = new ArrayList<>();
127 private int numOfPinnedApps = -1;
129 private View.OnClickListener ocl = view -> {
130 Intent intent = new Intent("com.farmerbb.taskbar.TOGGLE_START_MENU");
131 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
134 private BroadcastReceiver showReceiver = new BroadcastReceiver() {
136 public void onReceive(Context context, Intent intent) {
141 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
143 public void onReceive(Context context, Intent intent) {
148 private BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
150 public void onReceive(Context context, Intent intent) {
155 private BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
157 public void onReceive(Context context, Intent intent) {
158 tempHideTaskbar(false);
162 private BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
164 public void onReceive(Context context, Intent intent) {
165 if(startButton.getVisibility() == View.GONE
166 && (!LauncherHelper.getInstance().isOnHomeScreen() || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
167 layout.setVisibility(View.GONE);
171 private BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
173 public void onReceive(Context context, Intent intent) {
174 if(startButton.getVisibility() == View.GONE)
175 layout.setVisibility(View.VISIBLE);
179 public TaskbarController(Context context) {
180 this.context = context;
183 @TargetApi(Build.VERSION_CODES.M)
185 public void onCreateHost(Host host) {
186 SharedPreferences pref = U.getSharedPreferences(context);
187 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
188 if(U.canDrawOverlays(context, host instanceof HomeActivityDelegate))
191 pref.edit().putBoolean("taskbar_active", false).apply();
195 } else host.terminate();
198 @SuppressLint("RtlHardcoded")
199 private void drawTaskbar(Host host) {
200 IconCache.getInstance(context).clearCache();
202 // Initialize layout params
203 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
204 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
206 final ViewParams params = new ViewParams(
207 WindowManager.LayoutParams.WRAP_CONTENT,
208 WindowManager.LayoutParams.WRAP_CONTENT,
210 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
213 // Determine where to show the taskbar on screen
214 switch(U.getTaskbarPosition(context)) {
216 layoutId = R.layout.taskbar_left;
217 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
218 positionIsVertical = false;
220 case "bottom_vertical_left":
221 layoutId = R.layout.taskbar_vertical;
222 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
223 positionIsVertical = true;
226 layoutId = R.layout.taskbar_right;
227 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
228 positionIsVertical = false;
230 case "bottom_vertical_right":
231 layoutId = R.layout.taskbar_vertical;
232 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
233 positionIsVertical = true;
236 layoutId = R.layout.taskbar_left;
237 params.gravity = Gravity.TOP | Gravity.LEFT;
238 positionIsVertical = false;
240 case "top_vertical_left":
241 layoutId = R.layout.taskbar_top_vertical;
242 params.gravity = Gravity.TOP | Gravity.LEFT;
243 positionIsVertical = true;
246 layoutId = R.layout.taskbar_right;
247 params.gravity = Gravity.TOP | Gravity.RIGHT;
248 positionIsVertical = false;
250 case "top_vertical_right":
251 layoutId = R.layout.taskbar_top_vertical;
252 params.gravity = Gravity.TOP | Gravity.RIGHT;
253 positionIsVertical = true;
258 SharedPreferences pref = U.getSharedPreferences(context);
259 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
261 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
262 taskbar = layout.findViewById(R.id.taskbar);
263 scrollView = layout.findViewById(R.id.taskbar_scrollview);
265 int backgroundTint = U.getBackgroundTint(context);
266 int accentColor = U.getAccentColor(context);
268 if(altButtonConfig) {
269 space = layout.findViewById(R.id.space_alt);
270 layout.findViewById(R.id.space).setVisibility(View.GONE);
272 space = layout.findViewById(R.id.space);
273 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
276 space.setOnClickListener(v -> toggleTaskbar(true));
278 startButton = layout.findViewById(R.id.start_button);
281 if(pref.getBoolean("app_drawer_icon", false)) {
284 if(U.isBlissOs(context)) {
285 drawable = ContextCompat.getDrawable(context, R.drawable.bliss);
286 drawable.setTint(accentColor);
288 drawable = ContextCompat.getDrawable(context, R.mipmap.ic_launcher);
290 startButton.setImageDrawable(drawable);
291 padding = context.getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding_alt);
293 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.all_apps_button_icon));
294 padding = context.getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding);
297 startButton.setPadding(padding, padding, padding, padding);
298 startButton.setOnClickListener(ocl);
299 startButton.setOnLongClickListener(view -> {
304 startButton.setOnGenericMotionListener((view, motionEvent) -> {
305 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
306 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
312 refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "2")) * 1000);
313 if(refreshInterval == 0)
314 refreshInterval = 100;
316 sortOrder = pref.getString("sort_order", "false");
317 runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
319 switch(pref.getString("recents_amount", "past_day")) {
321 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
324 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
325 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
327 searchInterval = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
334 Intent intent = new Intent("com.farmerbb.taskbar.HIDE_START_MENU");
335 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
337 if(altButtonConfig) {
338 button = layout.findViewById(R.id.hide_taskbar_button_alt);
339 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
341 button = layout.findViewById(R.id.hide_taskbar_button);
342 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
346 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
347 } catch (RuntimeException e) { /* Gracefully fail */ }
350 button.setOnClickListener(v -> toggleTaskbar(true));
352 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
353 ? R.id.hide_taskbar_button_layout_alt
354 : R.id.hide_taskbar_button_layout);
355 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
357 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
358 ? R.id.hide_taskbar_button_layout
359 : R.id.hide_taskbar_button_layout_alt);
360 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
362 dashboardButton = layout.findViewById(R.id.dashboard_button);
363 navbarButtons = layout.findViewById(R.id.navbar_buttons);
365 dashboardEnabled = pref.getBoolean("dashboard", false);
366 if(dashboardEnabled) {
367 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
368 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
369 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
370 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
371 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
372 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
374 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
376 dashboardButton.setVisibility(View.GONE);
378 if(pref.getBoolean("button_back", false)) {
379 navbarButtonsEnabled = true;
381 ImageView backButton = layout.findViewById(R.id.button_back);
382 backButton.setColorFilter(accentColor);
383 backButton.setVisibility(View.VISIBLE);
384 backButton.setOnClickListener(v -> {
385 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
386 if(U.shouldCollapse(context, false))
390 backButton.setOnLongClickListener(v -> {
391 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
392 imm.showInputMethodPicker();
394 if(U.shouldCollapse(context, false))
400 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
401 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
402 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
403 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
404 imm.showInputMethodPicker();
406 if(U.shouldCollapse(context, false))
413 if(pref.getBoolean("button_home", false)) {
414 navbarButtonsEnabled = true;
416 ImageView homeButton = layout.findViewById(R.id.button_home);
417 homeButton.setColorFilter(accentColor);
418 homeButton.setVisibility(View.VISIBLE);
419 homeButton.setOnClickListener(v -> {
420 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
421 if(U.shouldCollapse(context, false))
425 homeButton.setOnLongClickListener(v -> {
426 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
427 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
430 context.startActivity(voiceSearchIntent);
431 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
433 if(U.shouldCollapse(context, false))
439 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
440 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
441 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
442 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
443 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
446 context.startActivity(voiceSearchIntent);
447 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
449 if(U.shouldCollapse(context, false))
456 if(pref.getBoolean("button_recents", false)) {
457 navbarButtonsEnabled = true;
459 ImageView recentsButton = layout.findViewById(R.id.button_recents);
460 recentsButton.setColorFilter(accentColor);
461 recentsButton.setVisibility(View.VISIBLE);
462 recentsButton.setOnClickListener(v -> {
463 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
464 if(U.shouldCollapse(context, false))
468 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
469 recentsButton.setOnLongClickListener(v -> {
470 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
471 if(U.shouldCollapse(context, false))
477 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
478 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
479 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
480 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
481 if(U.shouldCollapse(context, false))
489 if(!navbarButtonsEnabled)
490 navbarButtons.setVisibility(View.GONE);
492 layout.setBackgroundColor(backgroundTint);
493 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
494 button.setTextColor(accentColor);
496 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
498 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
499 toggleTaskbar(false);
501 if(pref.getBoolean("auto_hide_navbar", false))
502 U.showHideNavigationBar(context, false);
504 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
506 if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
507 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
509 new Handler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
512 lbm.unregisterReceiver(showReceiver);
513 lbm.unregisterReceiver(hideReceiver);
514 lbm.unregisterReceiver(tempShowReceiver);
515 lbm.unregisterReceiver(tempHideReceiver);
516 lbm.unregisterReceiver(startMenuAppearReceiver);
517 lbm.unregisterReceiver(startMenuDisappearReceiver);
519 lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
520 lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
521 lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
522 lbm.registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
523 lbm.registerReceiver(startMenuAppearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_APPEARING"));
524 lbm.registerReceiver(startMenuDisappearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
526 startRefreshingRecents();
528 host.addView(layout, params);
530 isFirstStart = false;
533 private void startRefreshingRecents() {
534 if(thread != null) thread.interrupt();
537 SharedPreferences pref = U.getSharedPreferences(context);
538 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
540 currentTaskbarIds.clear();
542 handler = new Handler();
543 thread = new Thread(() -> {
544 updateRecentApps(true);
546 if(!isRefreshingRecents) {
547 isRefreshingRecents = true;
549 while(shouldRefreshRecents) {
550 SystemClock.sleep(refreshInterval);
551 updateRecentApps(false);
553 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
556 int[] location = new int[2];
557 layout.getLocationOnScreen(location);
559 if(location[1] != 0) {
560 if(location[1] > currentTaskbarPosition) {
561 currentTaskbarPosition = location[1];
562 } else if(location[1] < currentTaskbarPosition) {
563 if(currentTaskbarPosition - location[1] == getNavBarSize())
564 currentTaskbarPosition = location[1];
565 else if(!startThread2) {
567 tempHideTaskbar(true);
575 isRefreshingRecents = false;
582 @SuppressWarnings("Convert2streamapi")
583 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
584 private void updateRecentApps(final boolean firstRefresh) {
585 if(isScreenOff()) return;
587 SharedPreferences pref = U.getSharedPreferences(context);
588 final PackageManager pm = context.getPackageManager();
589 final List<AppEntry> entries = new ArrayList<>();
590 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
591 int maxNumOfEntries = U.getMaxNumOfEntries(context);
592 int realNumOfPinnedApps = 0;
593 boolean fullLength = pref.getBoolean("full_length", false);
595 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
596 List<AppEntry> pinnedApps = pba.getPinnedApps();
597 List<AppEntry> blockedApps = pba.getBlockedApps();
598 List<String> applicationIdsToRemove = new ArrayList<>();
600 // Filter out anything on the pinned/blocked apps lists
601 if(pinnedApps.size() > 0) {
602 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
603 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
605 for(AppEntry entry : pinnedApps) {
606 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
607 userManager.getUserForSerialNumber(entry.getUserId(context)));
612 realNumOfPinnedApps--;
614 applicationIdsToRemove.add(entry.getPackageName());
617 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
620 if(blockedApps.size() > 0) {
621 for(AppEntry entry : blockedApps) {
622 applicationIdsToRemove.add(entry.getPackageName());
626 // Get list of all recently used apps
627 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
628 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
629 if(realNumOfPinnedApps < maxNumOfEntries) {
630 List<AppEntry> usageStatsList2 = new ArrayList<>();
631 List<AppEntry> usageStatsList3 = new ArrayList<>();
632 List<AppEntry> usageStatsList4 = new ArrayList<>();
633 List<AppEntry> usageStatsList5 = new ArrayList<>();
634 List<AppEntry> usageStatsList6;
636 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
637 homeIntent.addCategory(Intent.CATEGORY_HOME);
638 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
640 // Filter out apps without a launcher intent
641 // Also filter out the current launcher, and Taskbar itself
642 for(AppEntry packageInfo : usageStatsList) {
643 if(hasLauncherIntent(packageInfo.getPackageName())
644 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
645 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
646 usageStatsList2.add(packageInfo);
649 // Filter out apps that don't fall within our current search interval
650 for(AppEntry stats : usageStatsList2) {
651 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
652 usageStatsList3.add(stats);
655 // Sort apps by either most recently used, or most time used
656 if(!runningAppsOnly && sortOrder.contains("most_used")) {
657 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
659 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
662 // Filter out any duplicate entries
663 List<String> applicationIds = new ArrayList<>();
664 for(AppEntry stats : usageStatsList3) {
665 if(!applicationIds.contains(stats.getPackageName())) {
666 usageStatsList4.add(stats);
667 applicationIds.add(stats.getPackageName());
671 // Filter out the currently running foreground app, if requested by the user
672 if(pref.getBoolean("hide_foreground", false)) {
673 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
674 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
675 UsageEvents.Event eventCache = new UsageEvents.Event();
676 String currentForegroundApp = null;
678 while(events.hasNextEvent()) {
679 events.getNextEvent(eventCache);
681 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
682 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
683 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
684 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
685 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
686 currentForegroundApp = eventCache.getPackageName();
690 if(!applicationIdsToRemove.contains(currentForegroundApp))
691 applicationIdsToRemove.add(currentForegroundApp);
694 for(AppEntry stats : usageStatsList4) {
695 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
696 usageStatsList5.add(stats);
700 // Truncate list to a maximum length
701 if(usageStatsList5.size() > maxNumOfEntries)
702 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
704 usageStatsList6 = usageStatsList5;
706 // Determine if we need to reverse the order
707 boolean needToReverseOrder;
708 switch(U.getTaskbarPosition(context)) {
711 needToReverseOrder = sortOrder.contains("false");
714 needToReverseOrder = sortOrder.contains("true");
718 if(needToReverseOrder) {
719 Collections.reverse(usageStatsList6);
722 // Generate the AppEntries for the recent apps list
723 int number = usageStatsList6.size() == maxNumOfEntries
724 ? usageStatsList6.size() - realNumOfPinnedApps
725 : usageStatsList6.size();
727 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
728 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
730 final List<UserHandle> userHandles = userManager.getUserProfiles();
732 for(int i = 0; i < number; i++) {
733 for(UserHandle handle : userHandles) {
734 String packageName = usageStatsList6.get(i).getPackageName();
735 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
736 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
737 if(!list.isEmpty()) {
738 // Google App workaround
739 if(!packageName.equals("com.google.android.googlequicksearchbox"))
740 launcherAppCache.add(list.get(0));
742 boolean added = false;
743 for(LauncherActivityInfo info : list) {
744 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
745 launcherAppCache.add(info);
750 if(!added) launcherAppCache.add(list.get(0));
753 AppEntry newEntry = new AppEntry(
761 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
762 newEntry.setLastTimeUsed(lastTimeUsed);
763 entries.add(newEntry);
771 while(entries.size() > maxNumOfEntries) {
773 entries.remove(entries.size() - 1);
774 launcherAppCache.remove(launcherAppCache.size() - 1);
775 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
778 // Determine if we need to reverse the order again
779 if(U.getTaskbarPosition(context).contains("vertical")) {
780 Collections.reverse(entries);
781 Collections.reverse(launcherAppCache);
784 // Now that we've generated the list of apps,
785 // we need to determine if we need to redraw the Taskbar or not
786 boolean shouldRedrawTaskbar = firstRefresh;
788 List<String> finalApplicationIds = new ArrayList<>();
789 for(AppEntry entry : entries) {
790 finalApplicationIds.add(entry.getPackageName());
793 if(finalApplicationIds.size() != currentTaskbarIds.size()
794 || numOfPinnedApps != realNumOfPinnedApps)
795 shouldRedrawTaskbar = true;
797 for(int i = 0; i < finalApplicationIds.size(); i++) {
798 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
799 shouldRedrawTaskbar = true;
805 if(shouldRedrawTaskbar) {
806 currentTaskbarIds = finalApplicationIds;
807 numOfPinnedApps = realNumOfPinnedApps;
809 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
811 int launcherAppCachePos = -1;
812 for(int i = 0; i < entries.size(); i++) {
813 if(entries.get(i).getComponentName() == null) {
814 launcherAppCachePos++;
815 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
816 String packageName = entries.get(i).getPackageName();
817 long lastTimeUsed = entries.get(i).getLastTimeUsed();
821 AppEntry newEntry = new AppEntry(
823 appInfo.getComponentName().flattenToString(),
824 appInfo.getLabel().toString(),
825 IconCache.getInstance(context).getIcon(context, pm, appInfo),
828 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
829 newEntry.setLastTimeUsed(lastTimeUsed);
830 entries.add(i, newEntry);
834 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
837 if(numOfEntries > 0 || fullLength) {
838 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
839 DisplayInfo display = U.getDisplayInfo(context);
840 int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size) * numOfEntries;
841 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
843 if(U.getTaskbarPosition(context).contains("vertical")) {
844 int maxScreenSize = display.height
845 - U.getStatusBarHeight(context)
846 - U.getBaseTaskbarSize(context);
848 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
849 + context.getResources().getDimensionPixelSize(R.dimen.divider_size);
851 if(fullLength && U.getTaskbarPosition(context).contains("bottom")) {
853 Space whitespace = layout.findViewById(R.id.whitespace);
854 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
855 params2.height = maxScreenSize - recentsSize;
856 whitespace.setLayoutParams(params2);
857 } catch (NullPointerException e) { /* Gracefully fail */ }
860 int maxScreenSize = display.width - U.getBaseTaskbarSize(context);
862 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
863 + context.getResources().getDimensionPixelSize(R.dimen.divider_size);
865 if(fullLength && U.getTaskbarPosition(context).contains("right")) {
867 Space whitespace = layout.findViewById(R.id.whitespace);
868 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
869 params2.width = maxScreenSize - recentsSize;
870 whitespace.setLayoutParams(params2);
871 } catch (NullPointerException e) { /* Gracefully fail */ }
875 scrollView.setLayoutParams(params);
877 taskbar.removeAllViews();
878 for(int i = 0; i < entries.size(); i++) {
879 taskbar.addView(getView(entries, i));
882 isShowingRecents = true;
883 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
885 scrollView.setVisibility(View.INVISIBLE);
887 scrollView.setVisibility(View.VISIBLE);
890 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
891 new Handler().post(() -> {
892 switch(U.getTaskbarPosition(context)) {
897 if(sortOrder.contains("false"))
898 scrollView.scrollTo(0, 0);
899 else if(sortOrder.contains("true"))
900 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
902 case "bottom_vertical_left":
903 case "bottom_vertical_right":
904 case "top_vertical_left":
905 case "top_vertical_right":
906 if(sortOrder.contains("false"))
907 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
908 else if(sortOrder.contains("true"))
909 scrollView.scrollTo(0, 0);
913 if(shouldRefreshRecents) {
914 scrollView.setVisibility(View.VISIBLE);
918 isShowingRecents = false;
919 scrollView.setVisibility(View.GONE);
923 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
924 currentTaskbarIds.clear();
926 isShowingRecents = false;
927 scrollView.setVisibility(View.GONE);
932 private void toggleTaskbar(boolean userInitiated) {
933 if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
934 SharedPreferences pref = U.getSharedPreferences(context);
935 if(!pref.getBoolean("grip_rejection_toast_shown", false)) {
936 U.showToastLong(context, R.string.essential_phone_grip_rejection);
937 pref.edit().putBoolean("grip_rejection_toast_shown", true).apply();
941 if(startButton.getVisibility() == View.GONE)
947 private void showTaskbar(boolean clearVariables) {
949 taskbarShownTemporarily = false;
950 taskbarHiddenTemporarily = false;
953 if(startButton.getVisibility() == View.GONE) {
954 startButton.setVisibility(View.VISIBLE);
955 space.setVisibility(View.VISIBLE);
958 dashboardButton.setVisibility(View.VISIBLE);
960 if(navbarButtonsEnabled)
961 navbarButtons.setVisibility(View.VISIBLE);
963 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
964 scrollView.setVisibility(View.INVISIBLE);
966 shouldRefreshRecents = true;
967 startRefreshingRecents();
969 SharedPreferences pref = U.getSharedPreferences(context);
970 pref.edit().putBoolean("collapsed", true).apply();
974 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_START_MENU_SPACE")));
978 private void hideTaskbar(boolean clearVariables) {
980 taskbarShownTemporarily = false;
981 taskbarHiddenTemporarily = false;
984 if(startButton.getVisibility() == View.VISIBLE) {
985 startButton.setVisibility(View.GONE);
986 space.setVisibility(View.GONE);
989 dashboardButton.setVisibility(View.GONE);
991 if(navbarButtonsEnabled)
992 navbarButtons.setVisibility(View.GONE);
994 if(isShowingRecents) {
995 scrollView.setVisibility(View.GONE);
998 shouldRefreshRecents = false;
999 if(thread != null) thread.interrupt();
1001 SharedPreferences pref = U.getSharedPreferences(context);
1002 pref.edit().putBoolean("collapsed", false).apply();
1006 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
1007 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1009 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_SPACE")));
1013 private void tempShowTaskbar() {
1014 if(!taskbarHiddenTemporarily) {
1015 SharedPreferences pref = U.getSharedPreferences(context);
1016 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1021 if(taskbarHiddenTemporarily)
1022 taskbarHiddenTemporarily = false;
1025 private void tempHideTaskbar(boolean monitorPositionChanges) {
1026 if(!taskbarShownTemporarily) {
1027 SharedPreferences pref = U.getSharedPreferences(context);
1028 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1033 if(taskbarShownTemporarily)
1034 taskbarShownTemporarily = false;
1036 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1037 if(thread2 != null) thread2.interrupt();
1039 handler2 = new Handler();
1040 thread2 = new Thread(() -> {
1041 stopThread2 = false;
1043 while(!stopThread2) {
1044 SystemClock.sleep(refreshInterval);
1046 handler2.post(() -> stopThread2 = checkPositionChange());
1049 startThread2 = false;
1056 private boolean checkPositionChange() {
1057 if(!isScreenOff() && layout != null) {
1058 int[] location = new int[2];
1059 layout.getLocationOnScreen(location);
1061 if(location[1] == 0) {
1064 if(location[1] > currentTaskbarPosition) {
1065 currentTaskbarPosition = location[1];
1066 if(taskbarHiddenTemporarily) {
1070 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1073 } else if(location[1] < currentTaskbarPosition
1074 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1075 currentTaskbarPosition = location[1];
1083 private int getNavBarSize() {
1084 Point size = new Point();
1085 Point realSize = new Point();
1087 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1088 Display display = wm.getDefaultDisplay();
1089 display.getSize(size);
1090 display.getRealSize(realSize);
1092 return realSize.y - size.y;
1096 public void onDestroyHost(Host host) {
1097 shouldRefreshRecents = false;
1101 host.removeView(layout);
1102 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1104 SharedPreferences pref = U.getSharedPreferences(context);
1105 if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1106 pref.edit().remove("skip_auto_hide_navbar").apply();
1107 } else if(pref.getBoolean("auto_hide_navbar", false))
1108 U.showHideNavigationBar(context, true);
1110 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
1112 lbm.unregisterReceiver(showReceiver);
1113 lbm.unregisterReceiver(hideReceiver);
1114 lbm.unregisterReceiver(tempShowReceiver);
1115 lbm.unregisterReceiver(tempHideReceiver);
1116 lbm.unregisterReceiver(startMenuAppearReceiver);
1117 lbm.unregisterReceiver(startMenuDisappearReceiver);
1119 isFirstStart = true;
1122 @SuppressWarnings("deprecation")
1123 private void openContextMenu() {
1124 SharedPreferences pref = U.getSharedPreferences(context);
1126 Bundle args = new Bundle();
1127 args.putBoolean("dont_show_quit",
1128 LauncherHelper.getInstance().isOnHomeScreen()
1129 && !pref.getBoolean("taskbar_active", false));
1130 args.putBoolean("is_start_button", true);
1132 U.startContextMenuActivity(context, args);
1135 private void updateButton(boolean isCollapsed) {
1136 SharedPreferences pref = U.getSharedPreferences(context);
1137 boolean hide = pref.getBoolean("invisible_button", false);
1139 if(button != null) button.setText(context.getString(isCollapsed ? R.string.right_arrow : R.string.left_arrow));
1140 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1143 @TargetApi(Build.VERSION_CODES.M)
1145 public void onRecreateHost(Host host) {
1146 if(layout != null) {
1148 host.removeView(layout);
1149 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1151 currentTaskbarPosition = 0;
1153 if(U.canDrawOverlays(context, host instanceof HomeActivityDelegate))
1156 SharedPreferences pref = U.getSharedPreferences(context);
1157 pref.edit().putBoolean("taskbar_active", false).apply();
1164 private View getView(List<AppEntry> list, int position) {
1165 View convertView = View.inflate(context, R.layout.icon, null);
1167 final AppEntry entry = list.get(position);
1168 final SharedPreferences pref = U.getSharedPreferences(context);
1170 ImageView imageView = convertView.findViewById(R.id.icon);
1171 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1172 imageView.setImageDrawable(entry.getIcon(context));
1173 imageView2.setBackgroundColor(pref.getInt("accent_color", context.getResources().getInteger(R.integer.translucent_white)));
1175 String taskbarPosition = U.getTaskbarPosition(context);
1176 if(pref.getBoolean("shortcut_icon", true)) {
1177 boolean shouldShowShortcutIcon;
1178 if(taskbarPosition.contains("vertical"))
1179 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1181 shouldShowShortcutIcon = position < numOfPinnedApps;
1183 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1186 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1187 imageView.setRotationY(180);
1188 imageView2.setRotationY(180);
1191 FrameLayout layout = convertView.findViewById(R.id.entry);
1192 layout.setOnClickListener(view -> U.launchApp(
1194 entry.getPackageName(),
1195 entry.getComponentName(),
1196 entry.getUserId(context),
1202 layout.setOnLongClickListener(view -> {
1203 int[] location = new int[2];
1204 view.getLocationOnScreen(location);
1205 openContextMenu(entry, location);
1209 layout.setOnGenericMotionListener((view, motionEvent) -> {
1210 int action = motionEvent.getAction();
1212 if(action == MotionEvent.ACTION_BUTTON_PRESS
1213 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1214 int[] location = new int[2];
1215 view.getLocationOnScreen(location);
1216 openContextMenu(entry, location);
1219 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1220 view.setBackgroundColor(0);
1225 if(pref.getBoolean("visual_feedback", true)) {
1226 layout.setOnHoverListener((v, event) -> {
1227 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1228 int accentColor = U.getAccentColor(context);
1229 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1230 v.setBackgroundColor(accentColor);
1233 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1234 v.setBackgroundColor(0);
1236 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1237 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1242 layout.setOnTouchListener((v, event) -> {
1243 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1248 if(runningAppsOnly) {
1249 ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1250 if(entry.getLastTimeUsed() > 0) {
1251 runningAppIndicator.setVisibility(View.VISIBLE);
1252 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1254 runningAppIndicator.setVisibility(View.GONE);
1260 @SuppressWarnings("deprecation")
1261 private void openContextMenu(AppEntry entry, int[] location) {
1262 Bundle args = new Bundle();
1263 args.putString("package_name", entry.getPackageName());
1264 args.putString("app_name", entry.getLabel());
1265 args.putString("component_name", entry.getComponentName());
1266 args.putLong("user_id", entry.getUserId(context));
1267 args.putInt("x", location[0]);
1268 args.putInt("y", location[1]);
1270 U.startContextMenuActivity(context, args);
1273 private List<AppEntry> getAppEntries() {
1274 SharedPreferences pref = U.getSharedPreferences(context);
1276 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1278 return getAppEntriesUsingUsageStats();
1281 @SuppressWarnings("deprecation")
1282 @TargetApi(Build.VERSION_CODES.M)
1283 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1284 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1285 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1286 List<AppEntry> entries = new ArrayList<>();
1288 for(int i = 0; i < usageStatsList.size(); i++) {
1289 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1290 if(recentTaskInfo.id != -1) {
1291 String packageName = recentTaskInfo.baseActivity.getPackageName();
1292 AppEntry newEntry = new AppEntry(
1301 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1302 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1303 } catch (Exception e) {
1304 newEntry.setLastTimeUsed(i);
1307 entries.add(newEntry);
1314 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1315 private List<AppEntry> getAppEntriesUsingUsageStats() {
1316 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1317 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1318 List<AppEntry> entries = new ArrayList<>();
1320 for(UsageStats usageStats : usageStatsList) {
1321 AppEntry newEntry = new AppEntry(
1322 usageStats.getPackageName(),
1329 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1330 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1331 entries.add(newEntry);
1337 private boolean hasLauncherIntent(String packageName) {
1338 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1339 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1340 intentToResolve.setPackage(packageName);
1342 List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1343 return ris != null && ris.size() > 0;
1346 private boolean isScreenOff() {
1347 if(U.isChromeOs(context))
1350 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1351 return !pm.isInteractive();