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.os.Build;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.os.SystemClock;
47 import android.os.UserHandle;
48 import android.os.UserManager;
49 import android.provider.Settings;
50 import android.speech.RecognizerIntent;
51 import android.support.v4.content.ContextCompat;
52 import android.support.v4.graphics.ColorUtils;
53 import android.util.DisplayMetrics;
54 import android.view.ContextThemeWrapper;
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.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.PinnedBlockedApps;
90 import com.farmerbb.taskbar.util.MenuHelper;
91 import com.farmerbb.taskbar.util.U;
93 public class TaskbarService extends Service {
95 private WindowManager windowManager;
96 private LinearLayout layout;
97 private ImageView startButton;
98 private LinearLayout taskbar;
99 private FrameLayout scrollView;
100 private Button button;
102 private FrameLayout dashboardButton;
103 private LinearLayout navbarButtons;
105 private Handler handler;
106 private Handler handler2;
107 private Thread thread;
108 private Thread thread2;
110 private boolean isShowingRecents = true;
111 private boolean shouldRefreshRecents = true;
112 private boolean taskbarShownTemporarily = false;
113 private boolean taskbarHiddenTemporarily = false;
114 private boolean isRefreshingRecents = false;
115 private boolean isFirstStart = true;
117 private boolean startThread2 = false;
118 private boolean stopThread2 = false;
120 private int refreshInterval = -1;
121 private long searchInterval = -1;
122 private String sortOrder = "false";
123 private boolean runningAppsOnly = false;
125 private int layoutId = R.layout.taskbar_left;
126 private int currentTaskbarPosition = 0;
127 private boolean showHideAutomagically = false;
128 private boolean positionIsVertical = false;
129 private boolean dashboardEnabled = false;
130 private boolean navbarButtonsEnabled = false;
132 private List<String> currentTaskbarIds = new ArrayList<>();
133 private int numOfPinnedApps = -1;
135 private View.OnClickListener ocl = view -> {
136 Intent intent = new Intent("com.farmerbb.taskbar.TOGGLE_START_MENU");
137 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(intent);
140 private BroadcastReceiver showReceiver = new BroadcastReceiver() {
142 public void onReceive(Context context, Intent intent) {
147 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
149 public void onReceive(Context context, Intent intent) {
154 private BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
156 public void onReceive(Context context, Intent intent) {
161 private BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
163 public void onReceive(Context context, Intent intent) {
164 tempHideTaskbar(false);
168 private BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
170 public void onReceive(Context context, Intent intent) {
171 if(startButton.getVisibility() == View.GONE
172 && (!LauncherHelper.getInstance().isOnHomeScreen() || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
173 layout.setVisibility(View.GONE);
177 private BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
179 public void onReceive(Context context, Intent intent) {
180 if(startButton.getVisibility() == View.GONE)
181 layout.setVisibility(View.VISIBLE);
186 public IBinder onBind(Intent intent) {
191 public int onStartCommand(Intent intent, int flags, int startId) {
195 @TargetApi(Build.VERSION_CODES.M)
197 public void onCreate() {
200 SharedPreferences pref = U.getSharedPreferences(this);
201 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
202 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this))
205 pref.edit().putBoolean("taskbar_active", false).apply();
212 @SuppressLint("RtlHardcoded")
213 private void drawTaskbar() {
214 IconCache.getInstance(this).clearCache();
216 // Initialize layout params
217 windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
218 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
220 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
221 WindowManager.LayoutParams.WRAP_CONTENT,
222 WindowManager.LayoutParams.WRAP_CONTENT,
224 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
225 PixelFormat.TRANSLUCENT);
227 // Determine where to show the taskbar on screen
228 switch(U.getTaskbarPosition(this)) {
230 layoutId = R.layout.taskbar_left;
231 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
232 positionIsVertical = false;
234 case "bottom_vertical_left":
235 layoutId = R.layout.taskbar_vertical;
236 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
237 positionIsVertical = true;
240 layoutId = R.layout.taskbar_right;
241 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
242 positionIsVertical = false;
244 case "bottom_vertical_right":
245 layoutId = R.layout.taskbar_vertical;
246 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
247 positionIsVertical = true;
250 layoutId = R.layout.taskbar_left;
251 params.gravity = Gravity.TOP | Gravity.LEFT;
252 positionIsVertical = false;
254 case "top_vertical_left":
255 layoutId = R.layout.taskbar_top_vertical;
256 params.gravity = Gravity.TOP | Gravity.LEFT;
257 positionIsVertical = true;
260 layoutId = R.layout.taskbar_right;
261 params.gravity = Gravity.TOP | Gravity.RIGHT;
262 positionIsVertical = false;
264 case "top_vertical_right":
265 layoutId = R.layout.taskbar_top_vertical;
266 params.gravity = Gravity.TOP | Gravity.RIGHT;
267 positionIsVertical = true;
274 SharedPreferences pref = U.getSharedPreferences(this);
275 switch(pref.getString("theme", "light")) {
277 theme = R.style.AppTheme;
280 theme = R.style.AppTheme_Dark;
284 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
286 ContextThemeWrapper wrapper = new ContextThemeWrapper(this, theme);
287 layout = (LinearLayout) LayoutInflater.from(wrapper).inflate(layoutId, null);
288 taskbar = layout.findViewById(R.id.taskbar);
289 scrollView = layout.findViewById(R.id.taskbar_scrollview);
291 if(altButtonConfig) {
292 space = layout.findViewById(R.id.space_alt);
293 layout.findViewById(R.id.space).setVisibility(View.GONE);
295 space = layout.findViewById(R.id.space);
296 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
299 space.setOnClickListener(v -> toggleTaskbar());
301 startButton = layout.findViewById(R.id.start_button);
304 if(pref.getBoolean("app_drawer_icon", false)) {
305 startButton.setImageDrawable(ContextCompat.getDrawable(this, R.mipmap.ic_launcher));
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 oneDayAgo = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
340 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
341 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
342 long startTime = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
344 searchInterval = startTime > oneDayAgo ? startTime : oneDayAgo;
348 Intent intent = new Intent("com.farmerbb.taskbar.HIDE_START_MENU");
349 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(intent);
351 if(altButtonConfig) {
352 button = layout.findViewById(R.id.hide_taskbar_button_alt);
353 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
355 button = layout.findViewById(R.id.hide_taskbar_button);
356 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
360 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
361 } catch (RuntimeException e) { /* Gracefully fail */ }
364 button.setOnClickListener(v -> toggleTaskbar());
366 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
367 ? R.id.hide_taskbar_button_layout_alt
368 : R.id.hide_taskbar_button_layout);
369 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar());
371 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
372 ? R.id.hide_taskbar_button_layout
373 : R.id.hide_taskbar_button_layout_alt);
374 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
376 int backgroundTint = U.getBackgroundTint(this);
377 int accentColor = U.getAccentColor(this);
379 dashboardButton = layout.findViewById(R.id.dashboard_button);
380 navbarButtons = layout.findViewById(R.id.navbar_buttons);
382 dashboardEnabled = pref.getBoolean("dashboard", false);
383 if(dashboardEnabled) {
384 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
385 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
386 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
387 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
388 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
389 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
391 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
393 dashboardButton.setVisibility(View.GONE);
395 if(pref.getBoolean("button_back", false)) {
396 navbarButtonsEnabled = true;
398 ImageView backButton = layout.findViewById(R.id.button_back);
399 backButton.setVisibility(View.VISIBLE);
400 backButton.setOnClickListener(v -> {
401 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_BACK);
402 if(U.shouldCollapse(this, false))
406 backButton.setOnLongClickListener(v -> {
407 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
408 imm.showInputMethodPicker();
410 if(U.shouldCollapse(this, false))
416 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
417 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
418 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
419 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
420 imm.showInputMethodPicker();
422 if(U.shouldCollapse(this, false))
429 if(pref.getBoolean("button_home", false)) {
430 navbarButtonsEnabled = true;
432 ImageView homeButton = layout.findViewById(R.id.button_home);
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 = layout.findViewById(R.id.button_recents);
475 recentsButton.setVisibility(View.VISIBLE);
476 recentsButton.setOnClickListener(v -> {
477 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_RECENTS);
478 if(U.shouldCollapse(this, false))
482 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
483 recentsButton.setOnLongClickListener(v -> {
484 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
485 if(U.shouldCollapse(this, false))
491 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
492 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
493 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
494 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
495 if(U.shouldCollapse(this, false))
503 if(!navbarButtonsEnabled)
504 navbarButtons.setVisibility(View.GONE);
506 layout.setBackgroundColor(backgroundTint);
507 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
508 button.setTextColor(accentColor);
510 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
512 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
515 if(pref.getBoolean("auto_hide_navbar", false))
516 U.showHideNavigationBar(this, false);
518 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
520 lbm.unregisterReceiver(showReceiver);
521 lbm.unregisterReceiver(hideReceiver);
522 lbm.unregisterReceiver(tempShowReceiver);
523 lbm.unregisterReceiver(tempHideReceiver);
524 lbm.unregisterReceiver(startMenuAppearReceiver);
525 lbm.unregisterReceiver(startMenuDisappearReceiver);
527 lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
528 lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
529 lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
530 lbm.registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
531 lbm.registerReceiver(startMenuAppearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_APPEARING"));
532 lbm.registerReceiver(startMenuDisappearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
534 startRefreshingRecents();
536 windowManager.addView(layout, params);
538 isFirstStart = false;
541 private void startRefreshingRecents() {
542 if(thread != null) thread.interrupt();
545 SharedPreferences pref = U.getSharedPreferences(this);
546 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
548 currentTaskbarIds.clear();
550 handler = new Handler();
551 thread = new Thread(() -> {
552 updateRecentApps(true);
554 if(!isRefreshingRecents) {
555 isRefreshingRecents = true;
557 while(shouldRefreshRecents) {
558 SystemClock.sleep(refreshInterval);
559 updateRecentApps(false);
561 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
564 int[] location = new int[2];
565 layout.getLocationOnScreen(location);
567 if(location[1] != 0) {
568 if(location[1] > currentTaskbarPosition) {
569 currentTaskbarPosition = location[1];
570 } else if(location[1] < currentTaskbarPosition) {
571 if(currentTaskbarPosition - location[1] == getNavBarSize())
572 currentTaskbarPosition = location[1];
573 else if(!startThread2) {
575 tempHideTaskbar(true);
583 isRefreshingRecents = false;
590 @SuppressWarnings("Convert2streamapi")
591 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
592 private void updateRecentApps(final boolean firstRefresh) {
593 SharedPreferences pref = U.getSharedPreferences(this);
594 final PackageManager pm = getPackageManager();
595 final List<AppEntry> entries = new ArrayList<>();
596 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
597 int maxNumOfEntries = U.getMaxNumOfEntries(this);
598 int realNumOfPinnedApps = 0;
599 boolean fullLength = pref.getBoolean("full_length", false);
601 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(this);
602 List<AppEntry> pinnedApps = pba.getPinnedApps();
603 List<AppEntry> blockedApps = pba.getBlockedApps();
604 List<String> applicationIdsToRemove = new ArrayList<>();
606 // Filter out anything on the pinned/blocked apps lists
607 if(pinnedApps.size() > 0) {
608 UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
609 LauncherApps launcherApps = (LauncherApps) getSystemService(LAUNCHER_APPS_SERVICE);
611 for(AppEntry entry : pinnedApps) {
612 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
613 userManager.getUserForSerialNumber(entry.getUserId(this)));
618 realNumOfPinnedApps--;
620 applicationIdsToRemove.add(entry.getPackageName());
623 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
626 if(blockedApps.size() > 0) {
627 for(AppEntry entry : blockedApps) {
628 applicationIdsToRemove.add(entry.getPackageName());
632 // Get list of all recently used apps
633 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
634 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
635 if(realNumOfPinnedApps < maxNumOfEntries) {
636 List<AppEntry> usageStatsList2 = new ArrayList<>();
637 List<AppEntry> usageStatsList3 = new ArrayList<>();
638 List<AppEntry> usageStatsList4 = new ArrayList<>();
639 List<AppEntry> usageStatsList5 = new ArrayList<>();
640 List<AppEntry> usageStatsList6;
642 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
643 homeIntent.addCategory(Intent.CATEGORY_HOME);
644 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
646 // Filter out apps without a launcher intent
647 // Also filter out the current launcher, and Taskbar itself
648 for(AppEntry packageInfo : usageStatsList) {
649 if(hasLauncherIntent(packageInfo.getPackageName())
650 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
651 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
652 usageStatsList2.add(packageInfo);
655 // Filter out apps that don't fall within our current search interval
656 for(AppEntry stats : usageStatsList2) {
657 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
658 usageStatsList3.add(stats);
661 // Sort apps by either most recently used, or most time used
662 if(!runningAppsOnly && sortOrder.contains("most_used")) {
663 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
665 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
668 // Filter out any duplicate entries
669 List<String> applicationIds = new ArrayList<>();
670 for(AppEntry stats : usageStatsList3) {
671 if(!applicationIds.contains(stats.getPackageName())) {
672 usageStatsList4.add(stats);
673 applicationIds.add(stats.getPackageName());
677 // Filter out the currently running foreground app, if requested by the user
678 if(pref.getBoolean("hide_foreground", false)) {
679 UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
680 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
681 UsageEvents.Event eventCache = new UsageEvents.Event();
682 String currentForegroundApp = null;
684 while(events.hasNextEvent()) {
685 events.getNextEvent(eventCache);
687 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
688 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
689 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
690 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
691 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
692 currentForegroundApp = eventCache.getPackageName();
696 if(!applicationIdsToRemove.contains(currentForegroundApp))
697 applicationIdsToRemove.add(currentForegroundApp);
700 for(AppEntry stats : usageStatsList4) {
701 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
702 usageStatsList5.add(stats);
706 // Truncate list to a maximum length
707 if(usageStatsList5.size() > maxNumOfEntries)
708 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
710 usageStatsList6 = usageStatsList5;
712 // Determine if we need to reverse the order
713 boolean needToReverseOrder;
714 switch(U.getTaskbarPosition(this)) {
717 needToReverseOrder = sortOrder.contains("false");
720 needToReverseOrder = sortOrder.contains("true");
724 if(needToReverseOrder) {
725 Collections.reverse(usageStatsList6);
728 // Generate the AppEntries for TaskbarAdapter
729 int number = usageStatsList6.size() == maxNumOfEntries
730 ? usageStatsList6.size() - realNumOfPinnedApps
731 : usageStatsList6.size();
733 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
734 LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
736 final List<UserHandle> userHandles = userManager.getUserProfiles();
738 for(int i = 0; i < number; i++) {
739 for(UserHandle handle : userHandles) {
740 String packageName = usageStatsList6.get(i).getPackageName();
741 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
742 if(!list.isEmpty()) {
743 // Google App workaround
744 if(!packageName.equals("com.google.android.googlequicksearchbox"))
745 launcherAppCache.add(list.get(0));
747 boolean added = false;
748 for(LauncherActivityInfo info : list) {
749 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
750 launcherAppCache.add(info);
755 if(!added) launcherAppCache.add(list.get(0));
758 AppEntry newEntry = new AppEntry(
766 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
767 entries.add(newEntry);
775 while(entries.size() > maxNumOfEntries) {
777 entries.remove(entries.size() - 1);
778 launcherAppCache.remove(launcherAppCache.size() - 1);
779 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
782 // Determine if we need to reverse the order again
783 if(U.getTaskbarPosition(this).contains("vertical")) {
784 Collections.reverse(entries);
785 Collections.reverse(launcherAppCache);
788 // Now that we've generated the list of apps,
789 // we need to determine if we need to redraw the Taskbar or not
790 boolean shouldRedrawTaskbar = firstRefresh;
792 List<String> finalApplicationIds = new ArrayList<>();
793 for(AppEntry entry : entries) {
794 finalApplicationIds.add(entry.getPackageName());
797 if(finalApplicationIds.size() != currentTaskbarIds.size()
798 || numOfPinnedApps != realNumOfPinnedApps)
799 shouldRedrawTaskbar = true;
801 for(int i = 0; i < finalApplicationIds.size(); i++) {
802 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
803 shouldRedrawTaskbar = true;
809 if(shouldRedrawTaskbar) {
810 currentTaskbarIds = finalApplicationIds;
811 numOfPinnedApps = realNumOfPinnedApps;
813 UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
815 int launcherAppCachePos = -1;
816 for(int i = 0; i < entries.size(); i++) {
817 if(entries.get(i).getComponentName() == null) {
818 launcherAppCachePos++;
819 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
820 String packageName = entries.get(i).getPackageName();
824 AppEntry newEntry = new AppEntry(
826 appInfo.getComponentName().flattenToString(),
827 appInfo.getLabel().toString(),
828 IconCache.getInstance(TaskbarService.this).getIcon(TaskbarService.this, pm, appInfo),
831 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
832 entries.add(i, newEntry);
836 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
839 if(numOfEntries > 0 || fullLength) {
840 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
841 DisplayMetrics metrics = U.getRealDisplayMetrics(this);
842 int recentsSize = getResources().getDimensionPixelSize(R.dimen.icon_size) * numOfEntries;
843 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
845 if(U.getTaskbarPosition(TaskbarService.this).contains("vertical")) {
846 int maxScreenSize = metrics.heightPixels
847 - U.getStatusBarHeight(TaskbarService.this)
848 - U.getBaseTaskbarSize(TaskbarService.this);
850 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
851 + getResources().getDimensionPixelSize(R.dimen.divider_size);
853 if(fullLength && U.getTaskbarPosition(this).contains("bottom")) {
855 Space whitespace = layout.findViewById(R.id.whitespace);
856 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
857 params2.height = maxScreenSize - recentsSize;
858 whitespace.setLayoutParams(params2);
859 } catch (NullPointerException e) { /* Gracefully fail */ }
862 int maxScreenSize = metrics.widthPixels
863 - U.getBaseTaskbarSize(TaskbarService.this);
865 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
866 + getResources().getDimensionPixelSize(R.dimen.divider_size);
868 if(fullLength && U.getTaskbarPosition(this).contains("right")) {
870 Space whitespace = layout.findViewById(R.id.whitespace);
871 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
872 params2.width = maxScreenSize - recentsSize;
873 whitespace.setLayoutParams(params2);
874 } catch (NullPointerException e) { /* Gracefully fail */ }
878 scrollView.setLayoutParams(params);
880 taskbar.removeAllViews();
881 for(int i = 0; i < entries.size(); i++) {
882 taskbar.addView(getView(entries, i));
885 isShowingRecents = true;
886 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
888 scrollView.setVisibility(View.INVISIBLE);
890 scrollView.setVisibility(View.VISIBLE);
893 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
894 new Handler().post(() -> {
895 switch(U.getTaskbarPosition(TaskbarService.this)) {
900 if(sortOrder.contains("false"))
901 scrollView.scrollTo(0, 0);
902 else if(sortOrder.contains("true"))
903 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
905 case "bottom_vertical_left":
906 case "bottom_vertical_right":
907 case "top_vertical_left":
908 case "top_vertical_right":
909 if(sortOrder.contains("false"))
910 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
911 else if(sortOrder.contains("true"))
912 scrollView.scrollTo(0, 0);
916 if(shouldRefreshRecents) {
917 scrollView.setVisibility(View.VISIBLE);
921 isShowingRecents = false;
922 scrollView.setVisibility(View.GONE);
926 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
927 currentTaskbarIds.clear();
929 isShowingRecents = false;
930 scrollView.setVisibility(View.GONE);
935 private void toggleTaskbar() {
936 if(startButton.getVisibility() == View.GONE)
942 private void showTaskbar(boolean clearVariables) {
944 taskbarShownTemporarily = false;
945 taskbarHiddenTemporarily = false;
948 if(startButton.getVisibility() == View.GONE) {
949 startButton.setVisibility(View.VISIBLE);
950 space.setVisibility(View.VISIBLE);
953 dashboardButton.setVisibility(View.VISIBLE);
955 if(navbarButtonsEnabled)
956 navbarButtons.setVisibility(View.VISIBLE);
958 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
959 scrollView.setVisibility(View.INVISIBLE);
961 shouldRefreshRecents = true;
962 startRefreshingRecents();
964 SharedPreferences pref = U.getSharedPreferences(this);
965 pref.edit().putBoolean("collapsed", true).apply();
969 new Handler().post(() -> LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_START_MENU_SPACE")));
973 private void hideTaskbar(boolean clearVariables) {
975 taskbarShownTemporarily = false;
976 taskbarHiddenTemporarily = false;
979 if(startButton.getVisibility() == View.VISIBLE) {
980 startButton.setVisibility(View.GONE);
981 space.setVisibility(View.GONE);
984 dashboardButton.setVisibility(View.GONE);
986 if(navbarButtonsEnabled)
987 navbarButtons.setVisibility(View.GONE);
989 if(isShowingRecents) {
990 scrollView.setVisibility(View.GONE);
993 shouldRefreshRecents = false;
994 if(thread != null) thread.interrupt();
996 SharedPreferences pref = U.getSharedPreferences(this);
997 pref.edit().putBoolean("collapsed", false).apply();
1001 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
1002 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1004 new Handler().post(() -> LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_SPACE")));
1008 private void tempShowTaskbar() {
1009 if(!taskbarHiddenTemporarily) {
1010 SharedPreferences pref = U.getSharedPreferences(TaskbarService.this);
1011 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1016 if(taskbarHiddenTemporarily)
1017 taskbarHiddenTemporarily = false;
1020 private void tempHideTaskbar(boolean monitorPositionChanges) {
1021 if(!taskbarShownTemporarily) {
1022 SharedPreferences pref = U.getSharedPreferences(TaskbarService.this);
1023 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1028 if(taskbarShownTemporarily)
1029 taskbarShownTemporarily = false;
1031 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1032 if(thread2 != null) thread2.interrupt();
1034 handler2 = new Handler();
1035 thread2 = new Thread(() -> {
1036 stopThread2 = false;
1038 while(!stopThread2) {
1039 SystemClock.sleep(refreshInterval);
1041 handler2.post(() -> stopThread2 = checkPositionChange());
1044 startThread2 = false;
1051 private boolean checkPositionChange() {
1052 if(layout != null) {
1053 int[] location = new int[2];
1054 layout.getLocationOnScreen(location);
1056 if(location[1] == 0) {
1059 if(location[1] > currentTaskbarPosition) {
1060 currentTaskbarPosition = location[1];
1061 if(taskbarHiddenTemporarily) {
1065 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1068 } else if(location[1] < currentTaskbarPosition
1069 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1070 currentTaskbarPosition = location[1];
1078 private int getNavBarSize() {
1079 Point size = new Point();
1080 Point realSize = new Point();
1082 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
1083 Display display = wm.getDefaultDisplay();
1084 display.getSize(size);
1085 display.getRealSize(realSize);
1087 return realSize.y - size.y;
1091 public void onDestroy() {
1092 shouldRefreshRecents = false;
1097 windowManager.removeView(layout);
1098 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1100 SharedPreferences pref = U.getSharedPreferences(this);
1101 if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1102 pref.edit().remove("skip_auto_hide_navbar").apply();
1103 } else if(pref.getBoolean("auto_hide_navbar", false))
1104 U.showHideNavigationBar(this, true);
1106 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
1108 lbm.unregisterReceiver(showReceiver);
1109 lbm.unregisterReceiver(hideReceiver);
1110 lbm.unregisterReceiver(tempShowReceiver);
1111 lbm.unregisterReceiver(tempHideReceiver);
1112 lbm.unregisterReceiver(startMenuAppearReceiver);
1113 lbm.unregisterReceiver(startMenuDisappearReceiver);
1115 isFirstStart = true;
1118 @SuppressWarnings("deprecation")
1119 private void openContextMenu() {
1120 SharedPreferences pref = U.getSharedPreferences(this);
1121 Intent intent = null;
1123 switch(pref.getString("theme", "light")) {
1125 intent = new Intent(this, ContextMenuActivity.class);
1128 intent = new Intent(this, ContextMenuActivityDark.class);
1132 if(intent != null) {
1133 intent.putExtra("dont_show_quit", LauncherHelper.getInstance().isOnHomeScreen() && !pref.getBoolean("taskbar_active", false));
1134 intent.putExtra("is_start_button", true);
1135 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1138 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1139 DisplayMetrics metrics = U.getRealDisplayMetrics(this);
1141 if(intent != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
1142 intent.putExtra("context_menu_fix", true);
1144 startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)).toBundle());
1146 startActivity(intent);
1149 private void updateButton(boolean isCollapsed) {
1150 SharedPreferences pref = U.getSharedPreferences(this);
1151 boolean hide = pref.getBoolean("invisible_button", false);
1153 if(button != null) button.setText(getString(isCollapsed ? R.string.right_arrow : R.string.left_arrow));
1154 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1157 @TargetApi(Build.VERSION_CODES.M)
1159 public void onConfigurationChanged(Configuration newConfig) {
1160 super.onConfigurationChanged(newConfig);
1162 if(layout != null) {
1164 windowManager.removeView(layout);
1165 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1167 currentTaskbarPosition = 0;
1169 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this))
1172 SharedPreferences pref = U.getSharedPreferences(this);
1173 pref.edit().putBoolean("taskbar_active", false).apply();
1180 private View getView(List<AppEntry> list, int position) {
1181 View convertView = View.inflate(this, R.layout.icon, null);
1183 final AppEntry entry = list.get(position);
1184 final SharedPreferences pref = U.getSharedPreferences(this);
1186 ImageView imageView = convertView.findViewById(R.id.icon);
1187 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1188 imageView.setImageDrawable(entry.getIcon(this));
1189 imageView2.setBackgroundColor(pref.getInt("accent_color", getResources().getInteger(R.integer.translucent_white)));
1191 String taskbarPosition = U.getTaskbarPosition(this);
1192 if(pref.getBoolean("shortcut_icon", true)) {
1193 boolean shouldShowShortcutIcon;
1194 if(taskbarPosition.contains("vertical"))
1195 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1197 shouldShowShortcutIcon = position < numOfPinnedApps;
1199 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1202 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1203 imageView.setRotationY(180);
1204 imageView2.setRotationY(180);
1207 FrameLayout layout = convertView.findViewById(R.id.entry);
1208 layout.setOnClickListener(view -> U.launchApp(TaskbarService.this, entry.getPackageName(), entry.getComponentName(), entry.getUserId(TaskbarService.this), null, true, false));
1210 layout.setOnLongClickListener(view -> {
1211 int[] location = new int[2];
1212 view.getLocationOnScreen(location);
1213 openContextMenu(entry, location);
1217 layout.setOnGenericMotionListener((view, motionEvent) -> {
1218 int action = motionEvent.getAction();
1220 if(action == MotionEvent.ACTION_BUTTON_PRESS
1221 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1222 int[] location = new int[2];
1223 view.getLocationOnScreen(location);
1224 openContextMenu(entry, location);
1227 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1228 view.setBackgroundColor(0);
1233 if(pref.getBoolean("visual_feedback", true)) {
1234 layout.setOnHoverListener((v, event) -> {
1235 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1236 int accentColor = U.getAccentColor(TaskbarService.this);
1237 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1238 v.setBackgroundColor(accentColor);
1241 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1242 v.setBackgroundColor(0);
1244 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1245 v.setPointerIcon(PointerIcon.getSystemIcon(TaskbarService.this, PointerIcon.TYPE_DEFAULT));
1250 layout.setOnTouchListener((v, event) -> {
1251 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1259 @SuppressWarnings("deprecation")
1260 private void openContextMenu(AppEntry entry, int[] location) {
1261 SharedPreferences pref = U.getSharedPreferences(this);
1262 Intent intent = null;
1264 switch(pref.getString("theme", "light")) {
1266 intent = new Intent(this, ContextMenuActivity.class);
1269 intent = new Intent(this, ContextMenuActivityDark.class);
1273 if(intent != null) {
1274 intent.putExtra("package_name", entry.getPackageName());
1275 intent.putExtra("app_name", entry.getLabel());
1276 intent.putExtra("component_name", entry.getComponentName());
1277 intent.putExtra("user_id", entry.getUserId(this));
1278 intent.putExtra("x", location[0]);
1279 intent.putExtra("y", location[1]);
1280 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1283 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1284 DisplayMetrics metrics = U.getRealDisplayMetrics(this);
1286 if(intent != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
1287 intent.putExtra("context_menu_fix", true);
1289 startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)).toBundle());
1291 startActivity(intent);
1294 private List<AppEntry> getAppEntries() {
1295 SharedPreferences pref = U.getSharedPreferences(this);
1297 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1299 return getAppEntriesUsingUsageStats();
1302 @SuppressWarnings("deprecation")
1303 @TargetApi(Build.VERSION_CODES.M)
1304 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1305 ActivityManager mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1306 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1307 List<AppEntry> entries = new ArrayList<>();
1309 for(int i = 0; i < usageStatsList.size(); i++) {
1310 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1311 if(recentTaskInfo.id != -1) {
1312 String packageName = recentTaskInfo.baseActivity.getPackageName();
1313 AppEntry newEntry = new AppEntry(
1322 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1323 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1324 } catch (Exception e) {
1325 newEntry.setLastTimeUsed(i);
1328 entries.add(newEntry);
1335 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1336 private List<AppEntry> getAppEntriesUsingUsageStats() {
1337 UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
1338 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, searchInterval, System.currentTimeMillis());
1339 List<AppEntry> entries = new ArrayList<>();
1341 for(UsageStats usageStats : usageStatsList) {
1342 AppEntry newEntry = new AppEntry(
1343 usageStats.getPackageName(),
1350 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1351 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1352 entries.add(newEntry);
1358 private boolean hasLauncherIntent(String packageName) {
1359 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1360 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1361 intentToResolve.setPackage(packageName);
1363 List<ResolveInfo> ris = getPackageManager().queryIntentActivities(intentToResolve, 0);
1364 return ris != null && ris.size() > 0;