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.service;
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.Service;
24 import android.app.usage.UsageEvents;
25 import android.app.usage.UsageStats;
26 import android.app.usage.UsageStatsManager;
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.Configuration;
38 import android.graphics.Color;
39 import android.graphics.PixelFormat;
40 import android.graphics.Point;
41 import android.graphics.Rect;
42 import android.graphics.Typeface;
43 import android.graphics.drawable.Drawable;
44 import android.os.Build;
45 import android.os.Handler;
46 import android.os.IBinder;
47 import android.os.PowerManager;
48 import android.os.SystemClock;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.speech.RecognizerIntent;
52 import android.support.v4.content.ContextCompat;
53 import android.support.v4.graphics.ColorUtils;
54 import android.util.DisplayMetrics;
55 import android.view.Display;
56 import android.view.Gravity;
57 import android.view.LayoutInflater;
58 import android.view.MotionEvent;
59 import android.view.PointerIcon;
60 import android.view.View;
61 import android.view.ViewGroup;
62 import android.view.WindowManager;
63 import android.view.inputmethod.InputMethodManager;
64 import android.widget.Button;
65 import android.widget.FrameLayout;
66 import android.widget.ImageView;
68 import java.lang.reflect.Field;
69 import java.util.ArrayList;
70 import java.util.Collections;
71 import java.util.List;
73 import android.support.v4.content.LocalBroadcastManager;
74 import android.widget.LinearLayout;
75 import android.widget.Space;
77 import com.farmerbb.taskbar.BuildConfig;
78 import com.farmerbb.taskbar.activity.MainActivity;
79 import com.farmerbb.taskbar.R;
80 import com.farmerbb.taskbar.activity.ContextMenuActivity;
81 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
82 import com.farmerbb.taskbar.activity.HomeActivity;
83 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
84 import com.farmerbb.taskbar.util.AppEntry;
85 import com.farmerbb.taskbar.util.ApplicationType;
86 import com.farmerbb.taskbar.util.FreeformHackHelper;
87 import com.farmerbb.taskbar.util.IconCache;
88 import com.farmerbb.taskbar.util.LauncherHelper;
89 import com.farmerbb.taskbar.util.CompatUtils;
90 import com.farmerbb.taskbar.util.PinnedBlockedApps;
91 import com.farmerbb.taskbar.util.MenuHelper;
92 import com.farmerbb.taskbar.util.U;
94 public class TaskbarService extends Service {
96 private WindowManager windowManager;
97 private LinearLayout layout;
98 private ImageView startButton;
99 private LinearLayout taskbar;
100 private FrameLayout scrollView;
101 private Button button;
103 private FrameLayout dashboardButton;
104 private LinearLayout navbarButtons;
106 private Handler handler;
107 private Handler handler2;
108 private Thread thread;
109 private Thread thread2;
111 private boolean isShowingRecents = true;
112 private boolean shouldRefreshRecents = true;
113 private boolean taskbarShownTemporarily = false;
114 private boolean taskbarHiddenTemporarily = false;
115 private boolean isRefreshingRecents = false;
116 private boolean isFirstStart = true;
118 private boolean startThread2 = false;
119 private boolean stopThread2 = false;
121 private int refreshInterval = -1;
122 private long searchInterval = -1;
123 private String sortOrder = "false";
124 private boolean runningAppsOnly = false;
126 private int layoutId = R.layout.taskbar_left;
127 private int currentTaskbarPosition = 0;
128 private boolean showHideAutomagically = false;
129 private boolean positionIsVertical = false;
130 private boolean dashboardEnabled = false;
131 private boolean navbarButtonsEnabled = false;
133 private List<String> currentTaskbarIds = new ArrayList<>();
134 private int numOfPinnedApps = -1;
136 private View.OnClickListener ocl = view -> {
137 Intent intent = new Intent("com.farmerbb.taskbar.TOGGLE_START_MENU");
138 LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
141 private BroadcastReceiver showReceiver = new BroadcastReceiver() {
143 public void onReceive(Context context, Intent intent) {
148 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
150 public void onReceive(Context context, Intent intent) {
155 private BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
157 public void onReceive(Context context, Intent intent) {
162 private BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
164 public void onReceive(Context context, Intent intent) {
165 tempHideTaskbar(false);
169 private BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
171 public void onReceive(Context context, Intent intent) {
172 if(startButton.getVisibility() == View.GONE
173 && (!LauncherHelper.getInstance().isOnHomeScreen() || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
174 layout.setVisibility(View.GONE);
178 private BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
180 public void onReceive(Context context, Intent intent) {
181 if(startButton.getVisibility() == View.GONE)
182 layout.setVisibility(View.VISIBLE);
187 public IBinder onBind(Intent intent) {
192 public int onStartCommand(Intent intent, int flags, int startId) {
196 @TargetApi(Build.VERSION_CODES.M)
198 public void onCreate() {
201 SharedPreferences pref = U.getSharedPreferences(this);
202 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
203 if(U.canDrawOverlays(this))
206 pref.edit().putBoolean("taskbar_active", false).apply();
213 @SuppressLint("RtlHardcoded")
214 private void drawTaskbar() {
215 IconCache.getInstance(this).clearCache();
217 // Initialize layout params
218 windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
219 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
221 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
222 WindowManager.LayoutParams.WRAP_CONTENT,
223 WindowManager.LayoutParams.WRAP_CONTENT,
224 CompatUtils.getOverlayType(),
225 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
226 PixelFormat.TRANSLUCENT);
228 // Determine where to show the taskbar on screen
229 switch(U.getTaskbarPosition(this)) {
231 layoutId = R.layout.taskbar_left;
232 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
233 positionIsVertical = false;
235 case "bottom_vertical_left":
236 layoutId = R.layout.taskbar_vertical;
237 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
238 positionIsVertical = true;
241 layoutId = R.layout.taskbar_right;
242 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
243 positionIsVertical = false;
245 case "bottom_vertical_right":
246 layoutId = R.layout.taskbar_vertical;
247 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
248 positionIsVertical = true;
251 layoutId = R.layout.taskbar_left;
252 params.gravity = Gravity.TOP | Gravity.LEFT;
253 positionIsVertical = false;
255 case "top_vertical_left":
256 layoutId = R.layout.taskbar_top_vertical;
257 params.gravity = Gravity.TOP | Gravity.LEFT;
258 positionIsVertical = true;
261 layoutId = R.layout.taskbar_right;
262 params.gravity = Gravity.TOP | Gravity.RIGHT;
263 positionIsVertical = false;
265 case "top_vertical_right":
266 layoutId = R.layout.taskbar_top_vertical;
267 params.gravity = Gravity.TOP | Gravity.RIGHT;
268 positionIsVertical = true;
273 SharedPreferences pref = U.getSharedPreferences(this);
274 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
276 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(this)).inflate(layoutId, null);
277 taskbar = U.findViewById(layout, R.id.taskbar);
278 scrollView = U.findViewById(layout, R.id.taskbar_scrollview);
280 int backgroundTint = U.getBackgroundTint(this);
281 int accentColor = U.getAccentColor(this);
283 if(altButtonConfig) {
284 space = U.findViewById(layout, R.id.space_alt);
285 layout.findViewById(R.id.space).setVisibility(View.GONE);
287 space = U.findViewById(layout, R.id.space);
288 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
291 space.setOnClickListener(v -> toggleTaskbar());
293 startButton = U.findViewById(layout, R.id.start_button);
296 if(pref.getBoolean("app_drawer_icon", false)) {
299 if(U.isBlissOs(this)) {
300 drawable = ContextCompat.getDrawable(this, R.drawable.bliss);
301 drawable.setTint(accentColor);
303 drawable = ContextCompat.getDrawable(this, R.mipmap.ic_launcher);
305 startButton.setImageDrawable(drawable);
306 padding = getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding_alt);
308 startButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.all_apps_button_icon));
309 padding = getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding);
312 startButton.setPadding(padding, padding, padding, padding);
313 startButton.setOnClickListener(ocl);
314 startButton.setOnLongClickListener(view -> {
319 startButton.setOnGenericMotionListener((view, motionEvent) -> {
320 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
321 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
327 refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "2")) * 1000);
328 if(refreshInterval == 0)
329 refreshInterval = 100;
331 sortOrder = pref.getString("sort_order", "false");
332 runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
334 switch(pref.getString("recents_amount", "past_day")) {
336 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
339 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
340 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
342 searchInterval = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
349 Intent intent = new Intent("com.farmerbb.taskbar.HIDE_START_MENU");
350 LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
352 if(altButtonConfig) {
353 button = U.findViewById(layout, R.id.hide_taskbar_button_alt);
354 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
356 button = U.findViewById(layout, R.id.hide_taskbar_button);
357 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
361 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
362 } catch (RuntimeException e) { /* Gracefully fail */ }
365 button.setOnClickListener(v -> toggleTaskbar());
367 LinearLayout buttonLayout = U.findViewById(layout, altButtonConfig
368 ? R.id.hide_taskbar_button_layout_alt
369 : R.id.hide_taskbar_button_layout);
370 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar());
372 LinearLayout buttonLayoutToHide = U.findViewById(layout, altButtonConfig
373 ? R.id.hide_taskbar_button_layout
374 : R.id.hide_taskbar_button_layout_alt);
375 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
377 dashboardButton = U.findViewById(layout, R.id.dashboard_button);
378 navbarButtons = U.findViewById(layout, R.id.navbar_buttons);
380 dashboardEnabled = pref.getBoolean("dashboard", false);
381 if(dashboardEnabled) {
382 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
383 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
384 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
385 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
386 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
387 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
389 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
391 dashboardButton.setVisibility(View.GONE);
393 if(pref.getBoolean("button_back", false)) {
394 navbarButtonsEnabled = true;
396 ImageView backButton = U.findViewById(layout, R.id.button_back);
397 backButton.setColorFilter(accentColor);
398 backButton.setVisibility(View.VISIBLE);
399 backButton.setOnClickListener(v -> {
400 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_BACK);
401 if(U.shouldCollapse(this, false))
405 backButton.setOnLongClickListener(v -> {
406 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
407 imm.showInputMethodPicker();
409 if(U.shouldCollapse(this, false))
415 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
416 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
417 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
418 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
419 imm.showInputMethodPicker();
421 if(U.shouldCollapse(this, false))
428 if(pref.getBoolean("button_home", false)) {
429 navbarButtonsEnabled = true;
431 ImageView homeButton = U.findViewById(layout, R.id.button_home);
432 homeButton.setColorFilter(accentColor);
433 homeButton.setVisibility(View.VISIBLE);
434 homeButton.setOnClickListener(v -> {
435 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_HOME);
436 if(U.shouldCollapse(this, false))
440 homeButton.setOnLongClickListener(v -> {
441 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
442 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
445 startActivity(voiceSearchIntent);
446 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
448 if(U.shouldCollapse(this, false))
454 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
455 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
456 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
457 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
458 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
461 startActivity(voiceSearchIntent);
462 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
464 if(U.shouldCollapse(this, false))
471 if(pref.getBoolean("button_recents", false)) {
472 navbarButtonsEnabled = true;
474 ImageView recentsButton = U.findViewById(layout, R.id.button_recents);
475 recentsButton.setColorFilter(accentColor);
476 recentsButton.setVisibility(View.VISIBLE);
477 recentsButton.setOnClickListener(v -> {
478 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_RECENTS);
479 if(U.shouldCollapse(this, false))
483 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
484 recentsButton.setOnLongClickListener(v -> {
485 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
486 if(U.shouldCollapse(this, false))
492 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
493 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
494 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
495 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
496 if(U.shouldCollapse(this, false))
504 if(!navbarButtonsEnabled)
505 navbarButtons.setVisibility(View.GONE);
507 layout.setBackgroundColor(backgroundTint);
508 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
509 button.setTextColor(accentColor);
511 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
513 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
516 if(pref.getBoolean("auto_hide_navbar", false))
517 U.showHideNavigationBar(this, false);
519 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
521 lbm.unregisterReceiver(showReceiver);
522 lbm.unregisterReceiver(hideReceiver);
523 lbm.unregisterReceiver(tempShowReceiver);
524 lbm.unregisterReceiver(tempHideReceiver);
525 lbm.unregisterReceiver(startMenuAppearReceiver);
526 lbm.unregisterReceiver(startMenuDisappearReceiver);
528 lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
529 lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
530 lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
531 lbm.registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
532 lbm.registerReceiver(startMenuAppearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_APPEARING"));
533 lbm.registerReceiver(startMenuDisappearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
535 startRefreshingRecents();
537 windowManager.addView(layout, params);
539 isFirstStart = false;
542 private void startRefreshingRecents() {
543 if(thread != null) thread.interrupt();
546 SharedPreferences pref = U.getSharedPreferences(this);
547 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
549 currentTaskbarIds.clear();
551 handler = new Handler();
552 thread = new Thread(() -> {
553 updateRecentApps(true);
555 if(!isRefreshingRecents) {
556 isRefreshingRecents = true;
558 while(shouldRefreshRecents) {
559 SystemClock.sleep(refreshInterval);
560 updateRecentApps(false);
562 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
565 int[] location = new int[2];
566 layout.getLocationOnScreen(location);
568 if(location[1] != 0) {
569 if(location[1] > currentTaskbarPosition) {
570 currentTaskbarPosition = location[1];
571 } else if(location[1] < currentTaskbarPosition) {
572 if(currentTaskbarPosition - location[1] == getNavBarSize())
573 currentTaskbarPosition = location[1];
574 else if(!startThread2) {
576 tempHideTaskbar(true);
584 isRefreshingRecents = false;
591 @SuppressWarnings("Convert2streamapi")
592 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
593 private void updateRecentApps(final boolean firstRefresh) {
594 if(isScreenOff()) return;
596 SharedPreferences pref = U.getSharedPreferences(this);
597 final PackageManager pm = getPackageManager();
598 final List<AppEntry> entries = new ArrayList<>();
599 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
600 int maxNumOfEntries = U.getMaxNumOfEntries(this);
601 int realNumOfPinnedApps = 0;
602 boolean fullLength = pref.getBoolean("full_length", false);
604 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(this);
605 List<AppEntry> pinnedApps = pba.getPinnedApps();
606 List<AppEntry> blockedApps = pba.getBlockedApps();
607 List<String> applicationIdsToRemove = new ArrayList<>();
609 // Filter out anything on the pinned/blocked apps lists
610 if(pinnedApps.size() > 0) {
611 UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
612 LauncherApps launcherApps = (LauncherApps) getSystemService(LAUNCHER_APPS_SERVICE);
614 for(AppEntry entry : pinnedApps) {
615 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
616 userManager.getUserForSerialNumber(entry.getUserId(this)));
621 realNumOfPinnedApps--;
623 applicationIdsToRemove.add(entry.getPackageName());
626 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
629 if(blockedApps.size() > 0) {
630 for(AppEntry entry : blockedApps) {
631 applicationIdsToRemove.add(entry.getPackageName());
635 // Get list of all recently used apps
636 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
637 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
638 if(realNumOfPinnedApps < maxNumOfEntries) {
639 List<AppEntry> usageStatsList2 = new ArrayList<>();
640 List<AppEntry> usageStatsList3 = new ArrayList<>();
641 List<AppEntry> usageStatsList4 = new ArrayList<>();
642 List<AppEntry> usageStatsList5 = new ArrayList<>();
643 List<AppEntry> usageStatsList6;
645 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
646 homeIntent.addCategory(Intent.CATEGORY_HOME);
647 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
649 // Filter out apps without a launcher intent
650 // Also filter out the current launcher, and Taskbar itself
651 for(AppEntry packageInfo : usageStatsList) {
652 if(hasLauncherIntent(packageInfo.getPackageName())
653 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
654 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
655 usageStatsList2.add(packageInfo);
658 // Filter out apps that don't fall within our current search interval
659 for(AppEntry stats : usageStatsList2) {
660 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
661 usageStatsList3.add(stats);
664 // Sort apps by either most recently used, or most time used
665 if(!runningAppsOnly && sortOrder.contains("most_used")) {
666 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
668 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
671 // Filter out any duplicate entries
672 List<String> applicationIds = new ArrayList<>();
673 for(AppEntry stats : usageStatsList3) {
674 if(!applicationIds.contains(stats.getPackageName())) {
675 usageStatsList4.add(stats);
676 applicationIds.add(stats.getPackageName());
680 // Filter out the currently running foreground app, if requested by the user
681 if(pref.getBoolean("hide_foreground", false)) {
682 UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
683 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
684 UsageEvents.Event eventCache = new UsageEvents.Event();
685 String currentForegroundApp = null;
687 while(events.hasNextEvent()) {
688 events.getNextEvent(eventCache);
690 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
691 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
692 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
693 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
694 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
695 currentForegroundApp = eventCache.getPackageName();
699 if(!applicationIdsToRemove.contains(currentForegroundApp))
700 applicationIdsToRemove.add(currentForegroundApp);
703 for(AppEntry stats : usageStatsList4) {
704 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
705 usageStatsList5.add(stats);
709 // Truncate list to a maximum length
710 if(usageStatsList5.size() > maxNumOfEntries)
711 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
713 usageStatsList6 = usageStatsList5;
715 // Determine if we need to reverse the order
716 boolean needToReverseOrder;
717 switch(U.getTaskbarPosition(this)) {
720 needToReverseOrder = sortOrder.contains("false");
723 needToReverseOrder = sortOrder.contains("true");
727 if(needToReverseOrder) {
728 Collections.reverse(usageStatsList6);
731 // Generate the AppEntries for TaskbarAdapter
732 int number = usageStatsList6.size() == maxNumOfEntries
733 ? usageStatsList6.size() - realNumOfPinnedApps
734 : usageStatsList6.size();
736 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
737 LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
739 final List<UserHandle> userHandles = userManager.getUserProfiles();
741 for(int i = 0; i < number; i++) {
742 for(UserHandle handle : userHandles) {
743 String packageName = usageStatsList6.get(i).getPackageName();
744 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
745 if(!list.isEmpty()) {
746 // Google App workaround
747 if(!packageName.equals("com.google.android.googlequicksearchbox"))
748 launcherAppCache.add(list.get(0));
750 boolean added = false;
751 for(LauncherActivityInfo info : list) {
752 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
753 launcherAppCache.add(info);
758 if(!added) launcherAppCache.add(list.get(0));
761 AppEntry newEntry = new AppEntry(
769 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
770 entries.add(newEntry);
778 while(entries.size() > maxNumOfEntries) {
780 entries.remove(entries.size() - 1);
781 launcherAppCache.remove(launcherAppCache.size() - 1);
782 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
785 // Determine if we need to reverse the order again
786 if(U.getTaskbarPosition(this).contains("vertical")) {
787 Collections.reverse(entries);
788 Collections.reverse(launcherAppCache);
791 // Now that we've generated the list of apps,
792 // we need to determine if we need to redraw the Taskbar or not
793 boolean shouldRedrawTaskbar = firstRefresh;
795 List<String> finalApplicationIds = new ArrayList<>();
796 for(AppEntry entry : entries) {
797 finalApplicationIds.add(entry.getPackageName());
800 if(finalApplicationIds.size() != currentTaskbarIds.size()
801 || numOfPinnedApps != realNumOfPinnedApps)
802 shouldRedrawTaskbar = true;
804 for(int i = 0; i < finalApplicationIds.size(); i++) {
805 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
806 shouldRedrawTaskbar = true;
812 if(shouldRedrawTaskbar) {
813 currentTaskbarIds = finalApplicationIds;
814 numOfPinnedApps = realNumOfPinnedApps;
816 UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
818 int launcherAppCachePos = -1;
819 for(int i = 0; i < entries.size(); i++) {
820 if(entries.get(i).getComponentName() == null) {
821 launcherAppCachePos++;
822 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
823 String packageName = entries.get(i).getPackageName();
827 AppEntry newEntry = new AppEntry(
829 appInfo.getComponentName().flattenToString(),
830 appInfo.getLabel().toString(),
831 IconCache.getInstance(this).getIcon(this, pm, appInfo),
834 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
835 entries.add(i, newEntry);
839 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
842 if(numOfEntries > 0 || fullLength) {
843 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
844 DisplayMetrics metrics = U.getRealDisplayMetrics(this);
845 int recentsSize = getResources().getDimensionPixelSize(R.dimen.icon_size) * numOfEntries;
846 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
848 if(U.getTaskbarPosition(this).contains("vertical")) {
849 int maxScreenSize = metrics.heightPixels
850 - U.getStatusBarHeight(this)
851 - U.getBaseTaskbarSize(this);
853 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
854 + getResources().getDimensionPixelSize(R.dimen.divider_size);
856 if(fullLength && U.getTaskbarPosition(this).contains("bottom")) {
858 Space whitespace = U.findViewById(layout, R.id.whitespace);
859 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
860 params2.height = maxScreenSize - recentsSize;
861 whitespace.setLayoutParams(params2);
862 } catch (NullPointerException e) { /* Gracefully fail */ }
865 int maxScreenSize = metrics.widthPixels
866 - U.getBaseTaskbarSize(this);
868 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
869 + getResources().getDimensionPixelSize(R.dimen.divider_size);
871 if(fullLength && U.getTaskbarPosition(this).contains("right")) {
873 Space whitespace = U.findViewById(layout, R.id.whitespace);
874 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
875 params2.width = maxScreenSize - recentsSize;
876 whitespace.setLayoutParams(params2);
877 } catch (NullPointerException e) { /* Gracefully fail */ }
881 scrollView.setLayoutParams(params);
883 taskbar.removeAllViews();
884 for(int i = 0; i < entries.size(); i++) {
885 taskbar.addView(getView(entries, i));
888 isShowingRecents = true;
889 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
891 scrollView.setVisibility(View.INVISIBLE);
893 scrollView.setVisibility(View.VISIBLE);
896 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
897 new Handler().post(() -> {
898 switch(U.getTaskbarPosition(this)) {
903 if(sortOrder.contains("false"))
904 scrollView.scrollTo(0, 0);
905 else if(sortOrder.contains("true"))
906 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
908 case "bottom_vertical_left":
909 case "bottom_vertical_right":
910 case "top_vertical_left":
911 case "top_vertical_right":
912 if(sortOrder.contains("false"))
913 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
914 else if(sortOrder.contains("true"))
915 scrollView.scrollTo(0, 0);
919 if(shouldRefreshRecents) {
920 scrollView.setVisibility(View.VISIBLE);
924 isShowingRecents = false;
925 scrollView.setVisibility(View.GONE);
929 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
930 currentTaskbarIds.clear();
932 isShowingRecents = false;
933 scrollView.setVisibility(View.GONE);
938 private void toggleTaskbar() {
939 if(startButton.getVisibility() == View.GONE)
945 private void showTaskbar(boolean clearVariables) {
947 taskbarShownTemporarily = false;
948 taskbarHiddenTemporarily = false;
951 if(startButton.getVisibility() == View.GONE) {
952 startButton.setVisibility(View.VISIBLE);
953 space.setVisibility(View.VISIBLE);
956 dashboardButton.setVisibility(View.VISIBLE);
958 if(navbarButtonsEnabled)
959 navbarButtons.setVisibility(View.VISIBLE);
961 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
962 scrollView.setVisibility(View.INVISIBLE);
964 shouldRefreshRecents = true;
965 startRefreshingRecents();
967 SharedPreferences pref = U.getSharedPreferences(this);
968 pref.edit().putBoolean("collapsed", true).apply();
972 new Handler().post(() -> LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_START_MENU_SPACE")));
976 private void hideTaskbar(boolean clearVariables) {
978 taskbarShownTemporarily = false;
979 taskbarHiddenTemporarily = false;
982 if(startButton.getVisibility() == View.VISIBLE) {
983 startButton.setVisibility(View.GONE);
984 space.setVisibility(View.GONE);
987 dashboardButton.setVisibility(View.GONE);
989 if(navbarButtonsEnabled)
990 navbarButtons.setVisibility(View.GONE);
992 if(isShowingRecents) {
993 scrollView.setVisibility(View.GONE);
996 shouldRefreshRecents = false;
997 if(thread != null) thread.interrupt();
999 SharedPreferences pref = U.getSharedPreferences(this);
1000 pref.edit().putBoolean("collapsed", false).apply();
1004 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
1005 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1007 new Handler().post(() -> LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_SPACE")));
1011 private void tempShowTaskbar() {
1012 if(!taskbarHiddenTemporarily) {
1013 SharedPreferences pref = U.getSharedPreferences(this);
1014 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1019 if(taskbarHiddenTemporarily)
1020 taskbarHiddenTemporarily = false;
1023 private void tempHideTaskbar(boolean monitorPositionChanges) {
1024 if(!taskbarShownTemporarily) {
1025 SharedPreferences pref = U.getSharedPreferences(this);
1026 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1031 if(taskbarShownTemporarily)
1032 taskbarShownTemporarily = false;
1034 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1035 if(thread2 != null) thread2.interrupt();
1037 handler2 = new Handler();
1038 thread2 = new Thread(() -> {
1039 stopThread2 = false;
1041 while(!stopThread2) {
1042 SystemClock.sleep(refreshInterval);
1044 handler2.post(() -> stopThread2 = checkPositionChange());
1047 startThread2 = false;
1054 private boolean checkPositionChange() {
1055 if(!isScreenOff() && layout != null) {
1056 int[] location = new int[2];
1057 layout.getLocationOnScreen(location);
1059 if(location[1] == 0) {
1062 if(location[1] > currentTaskbarPosition) {
1063 currentTaskbarPosition = location[1];
1064 if(taskbarHiddenTemporarily) {
1068 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1071 } else if(location[1] < currentTaskbarPosition
1072 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1073 currentTaskbarPosition = location[1];
1081 private int getNavBarSize() {
1082 Point size = new Point();
1083 Point realSize = new Point();
1085 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
1086 Display display = wm.getDefaultDisplay();
1087 display.getSize(size);
1088 display.getRealSize(realSize);
1090 return realSize.y - size.y;
1094 public void onDestroy() {
1095 shouldRefreshRecents = false;
1100 windowManager.removeView(layout);
1101 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1103 SharedPreferences pref = U.getSharedPreferences(this);
1104 if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1105 pref.edit().remove("skip_auto_hide_navbar").apply();
1106 } else if(pref.getBoolean("auto_hide_navbar", false))
1107 U.showHideNavigationBar(this, true);
1109 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
1111 lbm.unregisterReceiver(showReceiver);
1112 lbm.unregisterReceiver(hideReceiver);
1113 lbm.unregisterReceiver(tempShowReceiver);
1114 lbm.unregisterReceiver(tempHideReceiver);
1115 lbm.unregisterReceiver(startMenuAppearReceiver);
1116 lbm.unregisterReceiver(startMenuDisappearReceiver);
1118 isFirstStart = true;
1121 @SuppressWarnings("deprecation")
1122 private void openContextMenu() {
1123 SharedPreferences pref = U.getSharedPreferences(this);
1124 Intent intent = null;
1126 switch(pref.getString("theme", "light")) {
1128 intent = new Intent(this, ContextMenuActivity.class);
1131 intent = new Intent(this, ContextMenuActivityDark.class);
1135 if(intent != null) {
1136 intent.putExtra("dont_show_quit", LauncherHelper.getInstance().isOnHomeScreen() && !pref.getBoolean("taskbar_active", false));
1137 intent.putExtra("is_start_button", true);
1138 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1141 if(U.hasFreeformSupport(this) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1142 DisplayMetrics metrics = U.getRealDisplayMetrics(this);
1144 if(intent != null && U.hasBrokenSetLaunchBoundsApi())
1145 intent.putExtra("context_menu_fix", true);
1147 startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)).toBundle());
1149 startActivity(intent);
1152 private void updateButton(boolean isCollapsed) {
1153 SharedPreferences pref = U.getSharedPreferences(this);
1154 boolean hide = pref.getBoolean("invisible_button", false);
1156 if(button != null) button.setText(getString(isCollapsed ? R.string.right_arrow : R.string.left_arrow));
1157 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1160 @TargetApi(Build.VERSION_CODES.M)
1162 public void onConfigurationChanged(Configuration newConfig) {
1163 super.onConfigurationChanged(newConfig);
1165 if(layout != null) {
1167 windowManager.removeView(layout);
1168 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1170 currentTaskbarPosition = 0;
1172 if(U.canDrawOverlays(this))
1175 SharedPreferences pref = U.getSharedPreferences(this);
1176 pref.edit().putBoolean("taskbar_active", false).apply();
1183 private View getView(List<AppEntry> list, int position) {
1184 View convertView = View.inflate(this, R.layout.icon, null);
1186 final AppEntry entry = list.get(position);
1187 final SharedPreferences pref = U.getSharedPreferences(this);
1189 ImageView imageView = U.findViewById(convertView, R.id.icon);
1190 ImageView imageView2 = U.findViewById(convertView, R.id.shortcut_icon);
1191 imageView.setImageDrawable(entry.getIcon(this));
1192 imageView2.setBackgroundColor(pref.getInt("accent_color", getResources().getInteger(R.integer.translucent_white)));
1194 String taskbarPosition = U.getTaskbarPosition(this);
1195 if(pref.getBoolean("shortcut_icon", true)) {
1196 boolean shouldShowShortcutIcon;
1197 if(taskbarPosition.contains("vertical"))
1198 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1200 shouldShowShortcutIcon = position < numOfPinnedApps;
1202 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1205 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1206 imageView.setRotationY(180);
1207 imageView2.setRotationY(180);
1210 FrameLayout layout = U.findViewById(convertView, R.id.entry);
1211 layout.setOnClickListener(view -> U.launchApp(this, entry.getPackageName(), entry.getComponentName(), entry.getUserId(this), null, true, false));
1213 layout.setOnLongClickListener(view -> {
1214 int[] location = new int[2];
1215 view.getLocationOnScreen(location);
1216 openContextMenu(entry, location);
1220 layout.setOnGenericMotionListener((view, motionEvent) -> {
1221 int action = motionEvent.getAction();
1223 if(action == MotionEvent.ACTION_BUTTON_PRESS
1224 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1225 int[] location = new int[2];
1226 view.getLocationOnScreen(location);
1227 openContextMenu(entry, location);
1230 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1231 view.setBackgroundColor(0);
1236 if(pref.getBoolean("visual_feedback", true)) {
1237 layout.setOnHoverListener((v, event) -> {
1238 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1239 int accentColor = U.getAccentColor(this);
1240 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1241 v.setBackgroundColor(accentColor);
1244 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1245 v.setBackgroundColor(0);
1247 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1248 v.setPointerIcon(PointerIcon.getSystemIcon(this, PointerIcon.TYPE_DEFAULT));
1253 layout.setOnTouchListener((v, event) -> {
1254 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1262 @SuppressWarnings("deprecation")
1263 private void openContextMenu(AppEntry entry, int[] location) {
1264 SharedPreferences pref = U.getSharedPreferences(this);
1265 Intent intent = null;
1267 switch(pref.getString("theme", "light")) {
1269 intent = new Intent(this, ContextMenuActivity.class);
1272 intent = new Intent(this, ContextMenuActivityDark.class);
1276 if(intent != null) {
1277 intent.putExtra("package_name", entry.getPackageName());
1278 intent.putExtra("app_name", entry.getLabel());
1279 intent.putExtra("component_name", entry.getComponentName());
1280 intent.putExtra("user_id", entry.getUserId(this));
1281 intent.putExtra("x", location[0]);
1282 intent.putExtra("y", location[1]);
1283 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1286 if(U.hasFreeformSupport(this) && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1287 DisplayMetrics metrics = U.getRealDisplayMetrics(this);
1289 if(intent != null && U.hasBrokenSetLaunchBoundsApi())
1290 intent.putExtra("context_menu_fix", true);
1292 startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)).toBundle());
1294 startActivity(intent);
1297 private List<AppEntry> getAppEntries() {
1298 SharedPreferences pref = U.getSharedPreferences(this);
1300 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1302 return getAppEntriesUsingUsageStats();
1305 @SuppressWarnings("deprecation")
1306 @TargetApi(Build.VERSION_CODES.M)
1307 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1308 ActivityManager mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1309 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1310 List<AppEntry> entries = new ArrayList<>();
1312 for(int i = 0; i < usageStatsList.size(); i++) {
1313 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1314 if(recentTaskInfo.id != -1) {
1315 String packageName = recentTaskInfo.baseActivity.getPackageName();
1316 AppEntry newEntry = new AppEntry(
1325 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1326 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1327 } catch (Exception e) {
1328 newEntry.setLastTimeUsed(i);
1331 entries.add(newEntry);
1338 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1339 private List<AppEntry> getAppEntriesUsingUsageStats() {
1340 UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
1341 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1342 List<AppEntry> entries = new ArrayList<>();
1344 for(UsageStats usageStats : usageStatsList) {
1345 AppEntry newEntry = new AppEntry(
1346 usageStats.getPackageName(),
1353 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1354 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1355 entries.add(newEntry);
1361 private boolean hasLauncherIntent(String packageName) {
1362 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1363 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1364 intentToResolve.setPackage(packageName);
1366 List<ResolveInfo> ris = getPackageManager().queryIntentActivities(intentToResolve, 0);
1367 return ris != null && ris.size() > 0;
1370 private boolean isScreenOff() {
1371 if(U.isChromeOs(this))
1374 PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
1375 return !pm.isInteractive();