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.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.content.res.Configuration;
37 import android.graphics.Color;
38 import android.graphics.PixelFormat;
39 import android.graphics.Point;
40 import android.graphics.Rect;
41 import android.graphics.Typeface;
42 import android.hardware.display.DisplayManager;
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.support.v4.content.ContextCompat;
51 import android.support.v4.graphics.ColorUtils;
52 import android.util.DisplayMetrics;
53 import android.view.ContextThemeWrapper;
54 import android.view.Display;
55 import android.view.Gravity;
56 import android.view.LayoutInflater;
57 import android.view.MotionEvent;
58 import android.view.PointerIcon;
59 import android.view.View;
60 import android.view.ViewGroup;
61 import android.view.WindowManager;
62 import android.widget.Button;
63 import android.widget.FrameLayout;
64 import android.widget.ImageView;
66 import java.util.ArrayList;
67 import java.util.Collections;
68 import java.util.List;
70 import android.support.v4.content.LocalBroadcastManager;
71 import android.widget.LinearLayout;
72 import android.widget.Space;
74 import com.farmerbb.taskbar.BuildConfig;
75 import com.farmerbb.taskbar.MainActivity;
76 import com.farmerbb.taskbar.R;
77 import com.farmerbb.taskbar.activity.ContextMenuActivity;
78 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
79 import com.farmerbb.taskbar.activity.HomeActivity;
80 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
81 import com.farmerbb.taskbar.util.AppEntry;
82 import com.farmerbb.taskbar.util.ApplicationType;
83 import com.farmerbb.taskbar.util.FreeformHackHelper;
84 import com.farmerbb.taskbar.util.IconCache;
85 import com.farmerbb.taskbar.util.LauncherHelper;
86 import com.farmerbb.taskbar.util.PinnedBlockedApps;
87 import com.farmerbb.taskbar.util.StartMenuHelper;
88 import com.farmerbb.taskbar.util.U;
90 public class TaskbarService extends Service {
92 private WindowManager windowManager;
93 private LinearLayout layout;
94 private ImageView startButton;
95 private LinearLayout taskbar;
96 private FrameLayout scrollView;
97 private Button button;
99 private FrameLayout dashboardButton;
100 private LinearLayout navbarButtons;
102 private Handler handler;
103 private Handler handler2;
104 private Thread thread;
105 private Thread thread2;
107 private boolean isShowingRecents = true;
108 private boolean shouldRefreshRecents = true;
109 private boolean taskbarShownTemporarily = false;
110 private boolean taskbarHiddenTemporarily = false;
111 private boolean isRefreshingRecents = false;
112 private boolean isFirstStart = true;
114 private boolean startThread2 = false;
115 private boolean stopThread2 = false;
117 private int refreshInterval = -1;
118 private long searchInterval = -1;
119 private String sortOrder = "false";
120 private boolean runningAppsOnly = false;
122 private int layoutId = R.layout.taskbar_left;
123 private int currentTaskbarPosition = 0;
124 private boolean showHideAutomagically = false;
125 private boolean positionIsVertical = false;
126 private boolean dashboardEnabled = false;
127 private boolean navbarButtonsEnabled = false;
129 private List<String> currentTaskbarIds = new ArrayList<>();
130 private int numOfPinnedApps = -1;
132 private View.OnClickListener ocl = view -> {
133 Intent intent = new Intent("com.farmerbb.taskbar.TOGGLE_START_MENU");
134 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(intent);
137 private BroadcastReceiver showReceiver = new BroadcastReceiver() {
139 public void onReceive(Context context, Intent intent) {
144 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
146 public void onReceive(Context context, Intent intent) {
151 private BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
153 public void onReceive(Context context, Intent intent) {
158 private BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
160 public void onReceive(Context context, Intent intent) {
161 tempHideTaskbar(false);
166 public IBinder onBind(Intent intent) {
171 public int onStartCommand(Intent intent, int flags, int startId) {
175 @TargetApi(Build.VERSION_CODES.M)
177 public void onCreate() {
180 SharedPreferences pref = U.getSharedPreferences(this);
181 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
182 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this))
185 pref.edit().putBoolean("taskbar_active", false).apply();
192 @SuppressLint("RtlHardcoded")
193 private void drawTaskbar() {
194 IconCache.getInstance(this).clearCache();
196 // Initialize layout params
197 windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
198 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
200 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
201 WindowManager.LayoutParams.WRAP_CONTENT,
202 WindowManager.LayoutParams.WRAP_CONTENT,
203 WindowManager.LayoutParams.TYPE_PHONE,
204 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
205 PixelFormat.TRANSLUCENT);
207 // Determine where to show the taskbar on screen
208 switch(U.getTaskbarPosition(this)) {
210 layoutId = R.layout.taskbar_left;
211 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
212 positionIsVertical = false;
214 case "bottom_vertical_left":
215 layoutId = R.layout.taskbar_vertical;
216 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
217 positionIsVertical = true;
220 layoutId = R.layout.taskbar_right;
221 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
222 positionIsVertical = false;
224 case "bottom_vertical_right":
225 layoutId = R.layout.taskbar_vertical;
226 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
227 positionIsVertical = true;
230 layoutId = R.layout.taskbar_left;
231 params.gravity = Gravity.TOP | Gravity.LEFT;
232 positionIsVertical = false;
234 case "top_vertical_left":
235 layoutId = R.layout.taskbar_top_vertical;
236 params.gravity = Gravity.TOP | Gravity.LEFT;
237 positionIsVertical = true;
240 layoutId = R.layout.taskbar_right;
241 params.gravity = Gravity.TOP | Gravity.RIGHT;
242 positionIsVertical = false;
244 case "top_vertical_right":
245 layoutId = R.layout.taskbar_top_vertical;
246 params.gravity = Gravity.TOP | Gravity.RIGHT;
247 positionIsVertical = true;
254 SharedPreferences pref = U.getSharedPreferences(this);
255 switch(pref.getString("theme", "light")) {
257 theme = R.style.AppTheme;
260 theme = R.style.AppTheme_Dark;
264 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
266 ContextThemeWrapper wrapper = new ContextThemeWrapper(this, theme);
267 layout = (LinearLayout) LayoutInflater.from(wrapper).inflate(layoutId, null);
268 taskbar = (LinearLayout) layout.findViewById(R.id.taskbar);
269 scrollView = (FrameLayout) layout.findViewById(R.id.taskbar_scrollview);
271 if(altButtonConfig) {
272 space = (Space) layout.findViewById(R.id.space_alt);
273 layout.findViewById(R.id.space).setVisibility(View.GONE);
275 space = (Space) layout.findViewById(R.id.space);
276 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
279 space.setOnClickListener(v -> toggleTaskbar());
281 startButton = (ImageView) layout.findViewById(R.id.start_button);
284 if(pref.getBoolean("app_drawer_icon", false)) {
285 startButton.setImageDrawable(ContextCompat.getDrawable(this, R.mipmap.ic_launcher));
286 padding = getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding_alt);
288 startButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.all_apps_button_icon));
289 padding = getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding);
292 startButton.setPadding(padding, padding, padding, padding);
293 startButton.setOnClickListener(ocl);
294 startButton.setOnLongClickListener(view -> {
299 startButton.setOnGenericMotionListener((view, motionEvent) -> {
300 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
301 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
307 refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "2")) * 1000);
308 if(refreshInterval == 0)
309 refreshInterval = 100;
311 sortOrder = pref.getString("sort_order", "false");
312 runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
314 switch(pref.getString("recents_amount", "past_day")) {
316 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
319 long oneDayAgo = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
320 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
321 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
322 long startTime = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
324 searchInterval = startTime > oneDayAgo ? startTime : oneDayAgo;
328 Intent intent = new Intent("com.farmerbb.taskbar.HIDE_START_MENU");
329 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(intent);
331 if(altButtonConfig) {
332 button = (Button) layout.findViewById(R.id.hide_taskbar_button_alt);
333 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
335 button = (Button) layout.findViewById(R.id.hide_taskbar_button);
336 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
340 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
341 } catch (RuntimeException e) { /* Gracefully fail */ }
344 button.setOnClickListener(v -> toggleTaskbar());
346 LinearLayout buttonLayout = (LinearLayout) layout.findViewById(altButtonConfig
347 ? R.id.hide_taskbar_button_layout_alt
348 : R.id.hide_taskbar_button_layout);
349 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar());
351 LinearLayout buttonLayoutToHide = (LinearLayout) layout.findViewById(altButtonConfig
352 ? R.id.hide_taskbar_button_layout
353 : R.id.hide_taskbar_button_layout_alt);
354 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
356 int backgroundTint = U.getBackgroundTint(this);
357 int accentColor = U.getAccentColor(this);
359 dashboardButton = (FrameLayout) layout.findViewById(R.id.dashboard_button);
360 navbarButtons = (LinearLayout) layout.findViewById(R.id.navbar_buttons);
362 dashboardEnabled = pref.getBoolean("dashboard", false);
363 if(dashboardEnabled) {
364 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
365 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
366 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
367 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
368 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
369 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
371 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
373 dashboardButton.setVisibility(View.GONE);
375 if(pref.getBoolean("button_back", false)) {
376 navbarButtonsEnabled = true;
378 ImageView backButton = (ImageView) layout.findViewById(R.id.button_back);
379 backButton.setVisibility(View.VISIBLE);
380 backButton.setOnClickListener(v -> {
381 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_BACK);
382 if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
387 if(pref.getBoolean("button_home", false)) {
388 navbarButtonsEnabled = true;
390 ImageView homeButton = (ImageView) layout.findViewById(R.id.button_home);
391 homeButton.setVisibility(View.VISIBLE);
392 homeButton.setOnClickListener(v -> {
393 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_HOME);
394 if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
399 if(pref.getBoolean("button_recents", false)) {
400 navbarButtonsEnabled = true;
402 ImageView recentsButton = (ImageView) layout.findViewById(R.id.button_recents);
403 recentsButton.setVisibility(View.VISIBLE);
404 recentsButton.setOnClickListener(v -> {
405 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_RECENTS);
406 if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
410 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
411 recentsButton.setOnLongClickListener(v -> {
412 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
413 if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
419 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
420 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
421 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
422 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
423 if(pref.getBoolean("hide_taskbar", true) && !FreeformHackHelper.getInstance().isInFreeformWorkspace())
431 if(!navbarButtonsEnabled)
432 navbarButtons.setVisibility(View.GONE);
434 layout.setBackgroundColor(backgroundTint);
435 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
436 button.setTextColor(accentColor);
438 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
440 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
443 LocalBroadcastManager.getInstance(this).unregisterReceiver(showReceiver);
444 LocalBroadcastManager.getInstance(this).unregisterReceiver(hideReceiver);
445 LocalBroadcastManager.getInstance(this).unregisterReceiver(tempShowReceiver);
446 LocalBroadcastManager.getInstance(this).unregisterReceiver(tempHideReceiver);
448 LocalBroadcastManager.getInstance(this).registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
449 LocalBroadcastManager.getInstance(this).registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
450 LocalBroadcastManager.getInstance(this).registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
451 LocalBroadcastManager.getInstance(this).registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
453 startRefreshingRecents();
455 windowManager.addView(layout, params);
457 isFirstStart = false;
460 private void startRefreshingRecents() {
461 if(thread != null) thread.interrupt();
464 SharedPreferences pref = U.getSharedPreferences(this);
465 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
467 currentTaskbarIds.clear();
469 handler = new Handler();
470 thread = new Thread() {
473 updateRecentApps(true);
475 if(!isRefreshingRecents) {
476 isRefreshingRecents = true;
478 while(shouldRefreshRecents) {
479 SystemClock.sleep(refreshInterval);
480 updateRecentApps(false);
482 if(showHideAutomagically && !positionIsVertical && !StartMenuHelper.getInstance().isStartMenuOpen())
485 int[] location = new int[2];
486 layout.getLocationOnScreen(location);
488 if(location[1] != 0) {
489 if(location[1] > currentTaskbarPosition) {
490 currentTaskbarPosition = location[1];
491 } else if(location[1] < currentTaskbarPosition) {
492 if(currentTaskbarPosition - location[1] == getNavBarSize())
493 currentTaskbarPosition = location[1];
494 else if(!startThread2) {
496 tempHideTaskbar(true);
504 isRefreshingRecents = false;
512 @SuppressWarnings("Convert2streamapi")
513 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
514 private void updateRecentApps(final boolean firstRefresh) {
515 SharedPreferences pref = U.getSharedPreferences(this);
516 final PackageManager pm = getPackageManager();
517 final List<AppEntry> entries = new ArrayList<>();
518 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
519 int maxNumOfEntries = U.getMaxNumOfEntries(this);
520 int realNumOfPinnedApps = 0;
521 boolean fullLength = pref.getBoolean("full_length", false);
523 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(this);
524 List<AppEntry> pinnedApps = pba.getPinnedApps();
525 List<AppEntry> blockedApps = pba.getBlockedApps();
526 List<String> applicationIdsToRemove = new ArrayList<>();
528 // Filter out anything on the pinned/blocked apps lists
529 if(pinnedApps.size() > 0) {
530 UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
531 LauncherApps launcherApps = (LauncherApps) getSystemService(LAUNCHER_APPS_SERVICE);
533 for(AppEntry entry : pinnedApps) {
534 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
535 userManager.getUserForSerialNumber(entry.getUserId(this)));
540 realNumOfPinnedApps--;
542 applicationIdsToRemove.add(entry.getPackageName());
545 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
548 if(blockedApps.size() > 0) {
549 for(AppEntry entry : blockedApps) {
550 applicationIdsToRemove.add(entry.getPackageName());
554 // Get list of all recently used apps
555 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
556 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
557 if(realNumOfPinnedApps < maxNumOfEntries) {
558 List<AppEntry> usageStatsList2 = new ArrayList<>();
559 List<AppEntry> usageStatsList3 = new ArrayList<>();
560 List<AppEntry> usageStatsList4 = new ArrayList<>();
561 List<AppEntry> usageStatsList5 = new ArrayList<>();
562 List<AppEntry> usageStatsList6;
564 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
565 homeIntent.addCategory(Intent.CATEGORY_HOME);
566 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
568 // Filter out apps without a launcher intent
569 // Also filter out the current launcher, and Taskbar itself
570 for(AppEntry packageInfo : usageStatsList) {
571 if(pm.getLaunchIntentForPackage(packageInfo.getPackageName()) != null
572 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
573 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
574 usageStatsList2.add(packageInfo);
577 // Filter out apps that don't fall within our current search interval
578 for(AppEntry stats : usageStatsList2) {
579 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
580 usageStatsList3.add(stats);
583 // Sort apps by either most recently used, or most time used
584 if(!runningAppsOnly) {
585 if(sortOrder.contains("most_used")) {
586 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
588 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
592 // Filter out any duplicate entries
593 List<String> applicationIds = new ArrayList<>();
594 for(AppEntry stats : usageStatsList3) {
595 if(!applicationIds.contains(stats.getPackageName())) {
596 usageStatsList4.add(stats);
597 applicationIds.add(stats.getPackageName());
601 // Filter out the currently running foreground app, if requested by the user
602 if(pref.getBoolean("hide_foreground", false)) {
603 UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
604 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
605 UsageEvents.Event eventCache = new UsageEvents.Event();
606 String currentForegroundApp = null;
608 while(events.hasNextEvent()) {
609 events.getNextEvent(eventCache);
611 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
612 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
613 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
614 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
615 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
616 currentForegroundApp = eventCache.getPackageName();
620 if(!applicationIdsToRemove.contains(currentForegroundApp))
621 applicationIdsToRemove.add(currentForegroundApp);
624 for(AppEntry stats : usageStatsList4) {
625 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
626 usageStatsList5.add(stats);
630 // Truncate list to a maximum length
631 if(usageStatsList5.size() > maxNumOfEntries)
632 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
634 usageStatsList6 = usageStatsList5;
636 // Determine if we need to reverse the order
637 boolean needToReverseOrder;
638 switch(U.getTaskbarPosition(this)) {
641 needToReverseOrder = sortOrder.contains("false");
644 needToReverseOrder = sortOrder.contains("true");
648 if(needToReverseOrder) {
649 Collections.reverse(usageStatsList6);
652 // Generate the AppEntries for TaskbarAdapter
653 int number = usageStatsList6.size() == maxNumOfEntries
654 ? usageStatsList6.size() - realNumOfPinnedApps
655 : usageStatsList6.size();
657 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
658 LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
660 final List<UserHandle> userHandles = userManager.getUserProfiles();
662 for(int i = 0; i < number; i++) {
663 for(UserHandle handle : userHandles) {
664 String packageName = usageStatsList6.get(i).getPackageName();
665 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
666 if(!list.isEmpty()) {
667 // Google App workaround
668 if(!packageName.equals("com.google.android.googlequicksearchbox"))
669 launcherAppCache.add(list.get(0));
671 boolean added = false;
672 for(LauncherActivityInfo info : list) {
673 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
674 launcherAppCache.add(info);
679 if(!added) launcherAppCache.add(list.get(0));
682 AppEntry newEntry = new AppEntry(
690 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
691 entries.add(newEntry);
699 while(entries.size() > maxNumOfEntries) {
701 entries.remove(entries.size() - 1);
702 launcherAppCache.remove(launcherAppCache.size() - 1);
703 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
706 // Determine if we need to reverse the order again
707 if(U.getTaskbarPosition(this).contains("vertical")) {
708 Collections.reverse(entries);
709 Collections.reverse(launcherAppCache);
712 // Now that we've generated the list of apps,
713 // we need to determine if we need to redraw the Taskbar or not
714 boolean shouldRedrawTaskbar = firstRefresh;
716 List<String> finalApplicationIds = new ArrayList<>();
717 for(AppEntry entry : entries) {
718 finalApplicationIds.add(entry.getPackageName());
721 if(finalApplicationIds.size() != currentTaskbarIds.size()
722 || numOfPinnedApps != realNumOfPinnedApps)
723 shouldRedrawTaskbar = true;
725 for(int i = 0; i < finalApplicationIds.size(); i++) {
726 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
727 shouldRedrawTaskbar = true;
733 if(shouldRedrawTaskbar) {
734 currentTaskbarIds = finalApplicationIds;
735 numOfPinnedApps = realNumOfPinnedApps;
737 UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
739 int launcherAppCachePos = -1;
740 for(int i = 0; i < entries.size(); i++) {
741 if(entries.get(i).getComponentName() == null) {
742 launcherAppCachePos++;
743 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
744 String packageName = entries.get(i).getPackageName();
748 AppEntry newEntry = new AppEntry(
750 appInfo.getComponentName().flattenToString(),
751 appInfo.getLabel().toString(),
752 IconCache.getInstance(TaskbarService.this).getIcon(TaskbarService.this, pm, appInfo),
755 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
756 entries.add(i, newEntry);
760 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
763 if(numOfEntries > 0 || fullLength) {
764 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
765 DisplayMetrics metrics = getResources().getDisplayMetrics();
766 int recentsSize = getResources().getDimensionPixelSize(R.dimen.icon_size) * numOfEntries;
767 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
769 if(U.getTaskbarPosition(TaskbarService.this).contains("vertical")) {
770 int maxScreenSize = metrics.heightPixels
771 - U.getStatusBarHeight(TaskbarService.this)
772 - U.getBaseTaskbarSize(TaskbarService.this);
774 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
775 + getResources().getDimensionPixelSize(R.dimen.divider_size);
777 if(fullLength && U.getTaskbarPosition(this).contains("bottom")) {
778 Space whitespace = (Space) layout.findViewById(R.id.whitespace);
779 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
780 if(params2 != null) {
781 params2.height = maxScreenSize - recentsSize;
782 whitespace.setLayoutParams(params2);
786 int maxScreenSize = metrics.widthPixels
787 - U.getBaseTaskbarSize(TaskbarService.this);
789 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
790 + getResources().getDimensionPixelSize(R.dimen.divider_size);
792 if(fullLength && U.getTaskbarPosition(this).contains("right")) {
793 Space whitespace = (Space) layout.findViewById(R.id.whitespace);
794 ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
795 if(params2 != null) {
796 params2.width = maxScreenSize - recentsSize;
797 whitespace.setLayoutParams(params2);
802 scrollView.setLayoutParams(params);
804 taskbar.removeAllViews();
805 for(int i = 0; i < entries.size(); i++) {
806 taskbar.addView(getView(entries, i));
809 isShowingRecents = true;
810 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
812 scrollView.setVisibility(View.INVISIBLE);
814 scrollView.setVisibility(View.VISIBLE);
817 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
818 new Handler().post(() -> {
819 switch(U.getTaskbarPosition(TaskbarService.this)) {
824 if(sortOrder.contains("false"))
825 scrollView.scrollTo(0, 0);
826 else if(sortOrder.contains("true"))
827 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
829 case "bottom_vertical_left":
830 case "bottom_vertical_right":
831 case "top_vertical_left":
832 case "top_vertical_right":
833 if(sortOrder.contains("false"))
834 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
835 else if(sortOrder.contains("true"))
836 scrollView.scrollTo(0, 0);
840 if(shouldRefreshRecents) {
841 scrollView.setVisibility(View.VISIBLE);
845 isShowingRecents = false;
846 scrollView.setVisibility(View.GONE);
850 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
851 currentTaskbarIds.clear();
853 isShowingRecents = false;
854 scrollView.setVisibility(View.GONE);
859 private void toggleTaskbar() {
860 if(startButton.getVisibility() == View.GONE)
866 private void showTaskbar(boolean clearVariables) {
868 taskbarShownTemporarily = false;
869 taskbarHiddenTemporarily = false;
872 if(startButton.getVisibility() == View.GONE) {
873 startButton.setVisibility(View.VISIBLE);
874 space.setVisibility(View.VISIBLE);
877 dashboardButton.setVisibility(View.VISIBLE);
879 if(navbarButtonsEnabled)
880 navbarButtons.setVisibility(View.VISIBLE);
882 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
883 scrollView.setVisibility(View.INVISIBLE);
885 shouldRefreshRecents = true;
886 startRefreshingRecents();
888 SharedPreferences pref = U.getSharedPreferences(this);
889 pref.edit().putBoolean("collapsed", true).apply();
895 private void hideTaskbar(boolean clearVariables) {
897 taskbarShownTemporarily = false;
898 taskbarHiddenTemporarily = false;
901 if(startButton.getVisibility() == View.VISIBLE) {
902 startButton.setVisibility(View.GONE);
903 space.setVisibility(View.GONE);
906 dashboardButton.setVisibility(View.GONE);
908 if(navbarButtonsEnabled)
909 navbarButtons.setVisibility(View.GONE);
911 if(isShowingRecents) {
912 scrollView.setVisibility(View.GONE);
915 shouldRefreshRecents = false;
916 if(thread != null) thread.interrupt();
918 SharedPreferences pref = U.getSharedPreferences(this);
919 pref.edit().putBoolean("collapsed", false).apply();
923 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
924 LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
928 private void tempShowTaskbar() {
929 if(!taskbarHiddenTemporarily) {
930 SharedPreferences pref = U.getSharedPreferences(TaskbarService.this);
931 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
936 if(taskbarHiddenTemporarily)
937 taskbarHiddenTemporarily = false;
940 private void tempHideTaskbar(boolean monitorPositionChanges) {
941 if(!taskbarShownTemporarily) {
942 SharedPreferences pref = U.getSharedPreferences(TaskbarService.this);
943 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
948 if(taskbarShownTemporarily)
949 taskbarShownTemporarily = false;
951 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
952 if(thread2 != null) thread2.interrupt();
954 handler2 = new Handler();
955 thread2 = new Thread() {
960 while(!stopThread2) {
961 SystemClock.sleep(refreshInterval);
963 handler2.post(() -> stopThread2 = checkPositionChange());
966 startThread2 = false;
974 private boolean checkPositionChange() {
976 int[] location = new int[2];
977 layout.getLocationOnScreen(location);
979 if(location[1] == 0) {
982 if(location[1] > currentTaskbarPosition) {
983 currentTaskbarPosition = location[1];
984 if(taskbarHiddenTemporarily) {
988 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
991 } else if(location[1] < currentTaskbarPosition
992 && currentTaskbarPosition - location[1] == getNavBarSize()) {
993 currentTaskbarPosition = location[1];
1001 private int getNavBarSize() {
1002 Point size = new Point();
1003 Point realSize = new Point();
1005 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
1006 Display display = wm.getDefaultDisplay();
1007 display.getSize(size);
1008 display.getRealSize(realSize);
1010 return realSize.y - size.y;
1014 public void onDestroy() {
1015 shouldRefreshRecents = false;
1020 windowManager.removeView(layout);
1021 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1023 LocalBroadcastManager.getInstance(this).unregisterReceiver(showReceiver);
1024 LocalBroadcastManager.getInstance(this).unregisterReceiver(hideReceiver);
1025 LocalBroadcastManager.getInstance(this).unregisterReceiver(tempShowReceiver);
1026 LocalBroadcastManager.getInstance(this).unregisterReceiver(tempHideReceiver);
1028 isFirstStart = true;
1031 @SuppressWarnings("deprecation")
1032 private void openContextMenu() {
1033 SharedPreferences pref = U.getSharedPreferences(this);
1034 Intent intent = null;
1036 switch(pref.getString("theme", "light")) {
1038 intent = new Intent(this, ContextMenuActivity.class);
1041 intent = new Intent(this, ContextMenuActivityDark.class);
1045 if(intent != null) {
1046 intent.putExtra("dont_show_quit", LauncherHelper.getInstance().isOnHomeScreen() && !pref.getBoolean("taskbar_active", false));
1047 intent.putExtra("is_start_button", true);
1048 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1051 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1052 DisplayManager dm = (DisplayManager) getSystemService(DISPLAY_SERVICE);
1053 Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
1055 if(intent != null && U.isOPreview())
1056 intent.putExtra("context_menu_fix", true);
1058 startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, display.getWidth(), display.getHeight())).toBundle());
1060 startActivity(intent);
1063 private void updateButton(boolean isCollapsed) {
1064 SharedPreferences pref = U.getSharedPreferences(this);
1065 boolean hide = pref.getBoolean("invisible_button", false);
1067 if(button != null) button.setText(getString(isCollapsed ? R.string.right_arrow : R.string.left_arrow));
1068 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1071 @TargetApi(Build.VERSION_CODES.M)
1073 public void onConfigurationChanged(Configuration newConfig) {
1074 super.onConfigurationChanged(newConfig);
1076 if(layout != null) {
1078 windowManager.removeView(layout);
1079 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1081 currentTaskbarPosition = 0;
1083 if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this))
1086 SharedPreferences pref = U.getSharedPreferences(this);
1087 pref.edit().putBoolean("taskbar_active", false).apply();
1094 private View getView(List<AppEntry> list, int position) {
1095 View convertView = View.inflate(this, R.layout.icon, null);
1097 final AppEntry entry = list.get(position);
1098 final SharedPreferences pref = U.getSharedPreferences(this);
1100 ImageView imageView = (ImageView) convertView.findViewById(R.id.icon);
1101 ImageView imageView2 = (ImageView) convertView.findViewById(R.id.shortcut_icon);
1102 imageView.setImageDrawable(entry.getIcon(this));
1103 imageView2.setBackgroundColor(pref.getInt("accent_color", getResources().getInteger(R.integer.translucent_white)));
1105 String taskbarPosition = U.getTaskbarPosition(this);
1106 if(pref.getBoolean("shortcut_icon", true)) {
1107 boolean shouldShowShortcutIcon;
1108 if(taskbarPosition.contains("vertical"))
1109 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1111 shouldShowShortcutIcon = position < numOfPinnedApps;
1113 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1116 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1117 imageView.setRotationY(180);
1118 imageView2.setRotationY(180);
1121 FrameLayout layout = (FrameLayout) convertView.findViewById(R.id.entry);
1122 layout.setOnClickListener(view -> U.launchApp(TaskbarService.this, entry.getPackageName(), entry.getComponentName(), entry.getUserId(TaskbarService.this), null, true, false));
1124 layout.setOnLongClickListener(view -> {
1125 int[] location = new int[2];
1126 view.getLocationOnScreen(location);
1127 openContextMenu(entry, location);
1131 layout.setOnGenericMotionListener((view, motionEvent) -> {
1132 int action = motionEvent.getAction();
1134 if(action == MotionEvent.ACTION_BUTTON_PRESS
1135 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1136 int[] location = new int[2];
1137 view.getLocationOnScreen(location);
1138 openContextMenu(entry, location);
1141 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1142 view.setBackgroundColor(0);
1147 if(pref.getBoolean("visual_feedback", true)) {
1148 layout.setOnHoverListener((v, event) -> {
1149 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1150 int accentColor = U.getAccentColor(TaskbarService.this);
1151 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1152 v.setBackgroundColor(accentColor);
1155 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1156 v.setBackgroundColor(0);
1158 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1159 v.setPointerIcon(PointerIcon.getSystemIcon(TaskbarService.this, PointerIcon.TYPE_DEFAULT));
1164 layout.setOnTouchListener((v, event) -> {
1165 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1173 @SuppressWarnings("deprecation")
1174 private void openContextMenu(AppEntry entry, int[] location) {
1175 SharedPreferences pref = U.getSharedPreferences(this);
1176 Intent intent = null;
1178 switch(pref.getString("theme", "light")) {
1180 intent = new Intent(this, ContextMenuActivity.class);
1183 intent = new Intent(this, ContextMenuActivityDark.class);
1187 if(intent != null) {
1188 intent.putExtra("package_name", entry.getPackageName());
1189 intent.putExtra("app_name", entry.getLabel());
1190 intent.putExtra("component_name", entry.getComponentName());
1191 intent.putExtra("user_id", entry.getUserId(this));
1192 intent.putExtra("x", location[0]);
1193 intent.putExtra("y", location[1]);
1194 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1197 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1198 DisplayManager dm = (DisplayManager) getSystemService(DISPLAY_SERVICE);
1199 Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
1201 if(intent != null && U.isOPreview())
1202 intent.putExtra("context_menu_fix", true);
1204 startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, display.getWidth(), display.getHeight())).toBundle());
1206 startActivity(intent);
1209 private List<AppEntry> getAppEntries() {
1210 SharedPreferences pref = U.getSharedPreferences(this);
1212 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1214 return getAppEntriesUsingUsageStats();
1217 @SuppressWarnings("deprecation")
1218 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1219 ActivityManager mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1220 List<ActivityManager.RunningTaskInfo> usageStatsList = mActivityManager.getRunningTasks(maxNum);
1221 List<AppEntry> entries = new ArrayList<>();
1223 for(ActivityManager.RunningTaskInfo usageStats : usageStatsList) {
1224 AppEntry newEntry = new AppEntry(
1225 usageStats.baseActivity.getPackageName(),
1232 entries.add(newEntry);
1238 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1239 private List<AppEntry> getAppEntriesUsingUsageStats() {
1240 UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
1241 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, searchInterval, System.currentTimeMillis());
1242 List<AppEntry> entries = new ArrayList<>();
1244 for(UsageStats usageStats : usageStatsList) {
1245 AppEntry newEntry = new AppEntry(
1246 usageStats.getPackageName(),
1253 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1254 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1255 entries.add(newEntry);