1 /* Copyright 2016 Braden Farmer
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 package com.farmerbb.taskbar.ui;
18 import android.accessibilityservice.AccessibilityService;
19 import android.annotation.SuppressLint;
20 import android.annotation.TargetApi;
21 import android.app.ActivityManager;
22 import android.app.AlarmManager;
23 import android.app.usage.UsageEvents;
24 import android.app.usage.UsageStats;
25 import android.app.usage.UsageStatsManager;
26 import android.bluetooth.BluetoothAdapter;
27 import android.content.ActivityNotFoundException;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.SharedPreferences;
33 import android.content.pm.LauncherActivityInfo;
34 import android.content.pm.LauncherApps;
35 import android.content.pm.PackageManager;
36 import android.content.pm.ResolveInfo;
37 import android.graphics.Color;
38 import android.graphics.Point;
39 import android.graphics.Typeface;
40 import android.graphics.drawable.Drawable;
41 import android.net.ConnectivityManager;
42 import android.net.NetworkInfo;
43 import android.net.Uri;
44 import android.net.wifi.WifiInfo;
45 import android.net.wifi.WifiManager;
46 import android.os.BatteryManager;
47 import android.os.Build;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.os.PowerManager;
51 import android.os.SystemClock;
52 import android.os.UserHandle;
53 import android.os.UserManager;
54 import android.provider.Settings;
55 import android.speech.RecognizerIntent;
56 import android.support.v4.content.ContextCompat;
57 import android.support.v4.graphics.ColorUtils;
58 import android.telephony.PhoneStateListener;
59 import android.telephony.SignalStrength;
60 import android.telephony.TelephonyManager;
61 import android.text.format.DateFormat;
62 import android.view.Display;
63 import android.view.Gravity;
64 import android.view.LayoutInflater;
65 import android.view.MotionEvent;
66 import android.view.PointerIcon;
67 import android.view.View;
68 import android.view.ViewGroup;
69 import android.view.WindowManager;
70 import android.view.inputmethod.InputMethodManager;
71 import android.widget.Button;
72 import android.widget.FrameLayout;
73 import android.widget.ImageView;
76 import java.lang.reflect.Field;
77 import java.util.ArrayList;
78 import java.util.Collections;
79 import java.util.Date;
80 import java.util.List;
82 import android.support.v4.content.LocalBroadcastManager;
83 import android.widget.LinearLayout;
84 import android.widget.Space;
85 import android.widget.TextView;
87 import com.farmerbb.taskbar.BuildConfig;
88 import com.farmerbb.taskbar.activity.HomeActivityDelegate;
89 import com.farmerbb.taskbar.activity.MainActivity;
90 import com.farmerbb.taskbar.R;
91 import com.farmerbb.taskbar.activity.HomeActivity;
92 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
93 import com.farmerbb.taskbar.activity.SecondaryHomeActivity;
94 import com.farmerbb.taskbar.util.AppEntry;
95 import com.farmerbb.taskbar.util.DisplayInfo;
96 import com.farmerbb.taskbar.util.FreeformHackHelper;
97 import com.farmerbb.taskbar.util.IconCache;
98 import com.farmerbb.taskbar.util.LauncherHelper;
99 import com.farmerbb.taskbar.util.PinnedBlockedApps;
100 import com.farmerbb.taskbar.util.MenuHelper;
101 import com.farmerbb.taskbar.util.U;
103 public class TaskbarController implements UIController {
105 private Context context;
106 private LinearLayout layout;
107 private ImageView startButton;
108 private LinearLayout taskbar;
109 private FrameLayout scrollView;
110 private Button button;
112 private FrameLayout dashboardButton;
113 private LinearLayout navbarButtons;
114 private LinearLayout sysTrayLayout;
115 private FrameLayout sysTrayParentLayout;
116 private TextView time;
118 private Handler handler;
119 private Handler handler2;
120 private Thread thread;
121 private Thread thread2;
123 private boolean isShowingRecents = true;
124 private boolean shouldRefreshRecents = true;
125 private boolean taskbarShownTemporarily = false;
126 private boolean taskbarHiddenTemporarily = false;
127 private boolean isRefreshingRecents = false;
128 private boolean isFirstStart = true;
130 private boolean startThread2 = false;
131 private boolean stopThread2 = false;
133 private int refreshInterval = -1;
134 private long searchInterval = -1;
135 private String sortOrder = "false";
136 private boolean runningAppsOnly = false;
138 private int layoutId = R.layout.taskbar_left;
139 private int currentTaskbarPosition = 0;
140 private boolean showHideAutomagically = false;
141 private boolean positionIsVertical = false;
142 private boolean dashboardEnabled = false;
143 private boolean navbarButtonsEnabled = false;
144 private boolean sysTrayEnabled = false;
146 private List<String> currentTaskbarIds = new ArrayList<>();
147 private List<String> currentRunningAppIds = new ArrayList<>();
148 private List<String> prevRunningAppIds = new ArrayList<>();
149 private int numOfPinnedApps = -1;
151 private int cellStrength = -1;
153 private View.OnClickListener ocl = view -> {
154 Intent intent = new Intent("com.farmerbb.taskbar.TOGGLE_START_MENU");
155 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
158 private BroadcastReceiver showReceiver = new BroadcastReceiver() {
160 public void onReceive(Context context, Intent intent) {
165 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
167 public void onReceive(Context context, Intent intent) {
172 private BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
174 public void onReceive(Context context, Intent intent) {
179 private BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
181 public void onReceive(Context context, Intent intent) {
182 tempHideTaskbar(false);
186 private BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
188 public void onReceive(Context context, Intent intent) {
189 if(startButton.getVisibility() == View.GONE
190 && (!LauncherHelper.getInstance().isOnHomeScreen() || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
191 layout.setVisibility(View.GONE);
195 private BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
197 public void onReceive(Context context, Intent intent) {
198 if(startButton.getVisibility() == View.GONE)
199 layout.setVisibility(View.VISIBLE);
203 @TargetApi(Build.VERSION_CODES.M)
204 private PhoneStateListener listener = new PhoneStateListener() {
206 public void onSignalStrengthsChanged(SignalStrength signalStrength) {
207 cellStrength = signalStrength.getLevel();
211 public TaskbarController(Context context) {
212 this.context = context;
215 @TargetApi(Build.VERSION_CODES.M)
217 public void onCreateHost(UIHost host) {
218 SharedPreferences pref = U.getSharedPreferences(context);
219 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
220 if(U.canDrawOverlays(context))
223 pref.edit().putBoolean("taskbar_active", false).apply();
227 } else host.terminate();
230 @SuppressLint("RtlHardcoded")
231 private void drawTaskbar(UIHost host) {
232 IconCache.getInstance(context).clearCache();
234 // Initialize layout params
235 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
236 U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
238 final ViewParams params = new ViewParams(
239 WindowManager.LayoutParams.WRAP_CONTENT,
240 WindowManager.LayoutParams.WRAP_CONTENT,
242 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
245 // Determine where to show the taskbar on screen
246 switch(U.getTaskbarPosition(context)) {
248 layoutId = R.layout.taskbar_left;
249 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
250 positionIsVertical = false;
252 case "bottom_vertical_left":
253 layoutId = R.layout.taskbar_vertical;
254 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
255 positionIsVertical = true;
258 layoutId = R.layout.taskbar_right;
259 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
260 positionIsVertical = false;
262 case "bottom_vertical_right":
263 layoutId = R.layout.taskbar_vertical;
264 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
265 positionIsVertical = true;
268 layoutId = R.layout.taskbar_left;
269 params.gravity = Gravity.TOP | Gravity.LEFT;
270 positionIsVertical = false;
272 case "top_vertical_left":
273 layoutId = R.layout.taskbar_top_vertical;
274 params.gravity = Gravity.TOP | Gravity.LEFT;
275 positionIsVertical = true;
278 layoutId = R.layout.taskbar_right;
279 params.gravity = Gravity.TOP | Gravity.RIGHT;
280 positionIsVertical = false;
282 case "top_vertical_right":
283 layoutId = R.layout.taskbar_top_vertical;
284 params.gravity = Gravity.TOP | Gravity.RIGHT;
285 positionIsVertical = true;
290 SharedPreferences pref = U.getSharedPreferences(context);
291 boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
293 layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
294 taskbar = layout.findViewById(R.id.taskbar);
295 scrollView = layout.findViewById(R.id.taskbar_scrollview);
297 int backgroundTint = U.getBackgroundTint(context);
298 int accentColor = U.getAccentColor(context);
300 if(altButtonConfig) {
301 space = layout.findViewById(R.id.space_alt);
302 layout.findViewById(R.id.space).setVisibility(View.GONE);
304 space = layout.findViewById(R.id.space);
305 layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
308 space.setOnClickListener(v -> toggleTaskbar(true));
310 startButton = layout.findViewById(R.id.start_button);
313 switch(pref.getString("start_button_image", U.getDefaultStartButtonImage(context))) {
315 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.all_apps_button_icon));
316 padding = context.getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding);
321 if(U.isBlissOs(context)) {
322 drawable = ContextCompat.getDrawable(context, R.drawable.bliss);
323 drawable.setTint(accentColor);
325 drawable = ContextCompat.getDrawable(context, R.mipmap.ic_launcher);
327 startButton.setImageDrawable(drawable);
328 padding = context.getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding_alt);
331 File file = new File(context.getFilesDir() + "/images", "custom_image");
334 startButton.setImageURI(Uri.fromFile(file));
335 } catch (Exception e) {
336 U.showToastLong(context, R.string.error_reading_custom_start_image);
337 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.all_apps_button_icon));
340 startButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.all_apps_button_icon));
342 padding = context.getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding);
346 startButton.setPadding(padding, padding, padding, padding);
347 startButton.setOnClickListener(ocl);
348 startButton.setOnLongClickListener(view -> {
353 startButton.setOnGenericMotionListener((view, motionEvent) -> {
354 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
355 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
361 refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "1")) * 1000);
362 if(refreshInterval == 0)
363 refreshInterval = 100;
365 sortOrder = pref.getString("sort_order", "false");
366 runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
368 switch(pref.getString("recents_amount", "past_day")) {
370 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
373 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
374 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
376 searchInterval = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
383 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
384 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
385 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.UPDATE_HOME_SCREEN_MARGINS"));
387 if(altButtonConfig) {
388 button = layout.findViewById(R.id.hide_taskbar_button_alt);
389 layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
391 button = layout.findViewById(R.id.hide_taskbar_button);
392 layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
396 button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
397 } catch (RuntimeException e) { /* Gracefully fail */ }
400 button.setOnClickListener(v -> toggleTaskbar(true));
402 LinearLayout buttonLayout = layout.findViewById(altButtonConfig
403 ? R.id.hide_taskbar_button_layout_alt
404 : R.id.hide_taskbar_button_layout);
405 if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
407 LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
408 ? R.id.hide_taskbar_button_layout
409 : R.id.hide_taskbar_button_layout_alt);
410 if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
412 dashboardButton = layout.findViewById(R.id.dashboard_button);
413 navbarButtons = layout.findViewById(R.id.navbar_buttons);
415 dashboardEnabled = pref.getBoolean("dashboard", context.getResources().getBoolean(R.bool.def_dashboard));
416 if(dashboardEnabled) {
417 layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
418 layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
419 layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
420 layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
421 layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
422 layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
424 dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
426 dashboardButton.setVisibility(View.GONE);
428 if(pref.getBoolean("button_back", false)) {
429 navbarButtonsEnabled = true;
431 ImageView backButton = layout.findViewById(R.id.button_back);
432 backButton.setColorFilter(accentColor);
433 backButton.setVisibility(View.VISIBLE);
434 backButton.setOnClickListener(v -> {
435 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
436 if(U.shouldCollapse(context, false))
440 backButton.setOnLongClickListener(v -> {
441 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
442 imm.showInputMethodPicker();
444 if(U.shouldCollapse(context, false))
450 backButton.setOnGenericMotionListener((view13, motionEvent) -> {
451 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
452 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
453 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
454 imm.showInputMethodPicker();
456 if(U.shouldCollapse(context, false))
463 if(pref.getBoolean("button_home", false)) {
464 navbarButtonsEnabled = true;
466 ImageView homeButton = layout.findViewById(R.id.button_home);
467 homeButton.setColorFilter(accentColor);
468 homeButton.setVisibility(View.VISIBLE);
469 homeButton.setOnClickListener(v -> {
470 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
471 if(U.shouldCollapse(context, false))
475 homeButton.setOnLongClickListener(v -> {
476 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
477 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
480 context.startActivity(voiceSearchIntent);
481 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
483 if(U.shouldCollapse(context, false))
489 homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
490 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
491 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
492 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
493 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
496 context.startActivity(voiceSearchIntent);
497 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
499 if(U.shouldCollapse(context, false))
506 if(pref.getBoolean("button_recents", false)) {
507 navbarButtonsEnabled = true;
509 ImageView recentsButton = layout.findViewById(R.id.button_recents);
510 recentsButton.setColorFilter(accentColor);
511 recentsButton.setVisibility(View.VISIBLE);
512 recentsButton.setOnClickListener(v -> {
513 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
514 if(U.shouldCollapse(context, false))
518 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
519 recentsButton.setOnLongClickListener(v -> {
520 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
521 if(U.shouldCollapse(context, false))
527 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
528 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
529 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
530 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
531 if(U.shouldCollapse(context, false))
539 if(!navbarButtonsEnabled)
540 navbarButtons.setVisibility(View.GONE);
542 sysTrayEnabled = U.isSystemTrayEnabled(context);
545 sysTrayLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.system_tray, null);
547 FrameLayout.LayoutParams sysTrayParams = new FrameLayout.LayoutParams(
548 FrameLayout.LayoutParams.WRAP_CONTENT,
549 context.getResources().getDimensionPixelSize(R.dimen.icon_size)
552 if(layoutId == R.layout.taskbar_right) {
553 time = sysTrayLayout.findViewById(R.id.time_left);
554 sysTrayParams.gravity = Gravity.START;
556 time = sysTrayLayout.findViewById(R.id.time_right);
557 sysTrayParams.gravity = Gravity.END;
560 time.setVisibility(View.VISIBLE);
561 sysTrayLayout.setLayoutParams(sysTrayParams);
563 sysTrayLayout.setOnClickListener(v -> {
564 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
565 if(U.shouldCollapse(context, false))
569 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
570 sysTrayLayout.setOnLongClickListener(v -> {
571 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
572 if(U.shouldCollapse(context, false))
578 sysTrayLayout.setOnGenericMotionListener((view, motionEvent) -> {
579 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
580 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
581 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
582 if(U.shouldCollapse(context, false))
589 sysTrayParentLayout = layout.findViewById(R.id.add_systray_here);
590 sysTrayParentLayout.setVisibility(View.VISIBLE);
591 sysTrayParentLayout.addView(sysTrayLayout);
593 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
594 manager.listen(listener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
597 layout.setBackgroundColor(backgroundTint);
598 layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
599 button.setTextColor(accentColor);
601 if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
603 else if(!pref.getBoolean("collapsed", false) && pref.getBoolean("taskbar_active", false))
604 toggleTaskbar(false);
606 if(pref.getBoolean("auto_hide_navbar", false))
607 U.showHideNavigationBar(context, false);
609 if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
610 lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.FINISH_FREEFORM_ACTIVITY"));
612 new Handler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
615 lbm.unregisterReceiver(showReceiver);
616 lbm.unregisterReceiver(hideReceiver);
617 lbm.unregisterReceiver(tempShowReceiver);
618 lbm.unregisterReceiver(tempHideReceiver);
619 lbm.unregisterReceiver(startMenuAppearReceiver);
620 lbm.unregisterReceiver(startMenuDisappearReceiver);
622 lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
623 lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
624 lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
625 lbm.registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
626 lbm.registerReceiver(startMenuAppearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_APPEARING"));
627 lbm.registerReceiver(startMenuDisappearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
629 startRefreshingRecents();
631 host.addView(layout, params);
633 isFirstStart = false;
636 private void startRefreshingRecents() {
637 if(thread != null) thread.interrupt();
640 SharedPreferences pref = U.getSharedPreferences(context);
641 showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
643 currentTaskbarIds.clear();
645 handler = new Handler();
646 thread = new Thread(() -> {
648 updateRecentApps(true);
650 if(!isRefreshingRecents) {
651 isRefreshingRecents = true;
653 while(shouldRefreshRecents) {
654 SystemClock.sleep(refreshInterval);
656 updateRecentApps(false);
658 if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
661 int[] location = new int[2];
662 layout.getLocationOnScreen(location);
664 if(location[1] != 0) {
665 if(location[1] > currentTaskbarPosition) {
666 currentTaskbarPosition = location[1];
667 } else if(location[1] < currentTaskbarPosition) {
668 if(currentTaskbarPosition - location[1] == getNavBarSize())
669 currentTaskbarPosition = location[1];
670 else if(!startThread2) {
672 tempHideTaskbar(true);
680 isRefreshingRecents = false;
687 @SuppressWarnings("Convert2streamapi")
688 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
689 private void updateRecentApps(final boolean firstRefresh) {
690 if(isScreenOff()) return;
692 SharedPreferences pref = U.getSharedPreferences(context);
693 final PackageManager pm = context.getPackageManager();
694 final List<AppEntry> entries = new ArrayList<>();
695 List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
696 int maxNumOfEntries = U.getMaxNumOfEntries(context);
697 int realNumOfPinnedApps = 0;
698 boolean fullLength = pref.getBoolean("full_length", context.getResources().getBoolean(R.bool.def_full_length));
701 currentRunningAppIds.clear();
703 PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
704 List<AppEntry> pinnedApps = setTimeLastUsedFor(pba.getPinnedApps());
705 List<AppEntry> blockedApps = pba.getBlockedApps();
706 List<String> applicationIdsToRemove = new ArrayList<>();
708 // Filter out anything on the pinned/blocked apps lists
709 if(pinnedApps.size() > 0) {
710 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
711 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
713 for(AppEntry entry : pinnedApps) {
714 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
715 userManager.getUserForSerialNumber(entry.getUserId(context)));
720 realNumOfPinnedApps--;
722 applicationIdsToRemove.add(entry.getPackageName());
725 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
728 if(blockedApps.size() > 0) {
729 for(AppEntry entry : blockedApps) {
730 applicationIdsToRemove.add(entry.getPackageName());
734 // Get list of all recently used apps
735 List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
736 if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
737 if(realNumOfPinnedApps < maxNumOfEntries) {
738 List<AppEntry> usageStatsList2 = new ArrayList<>();
739 List<AppEntry> usageStatsList3 = new ArrayList<>();
740 List<AppEntry> usageStatsList4 = new ArrayList<>();
741 List<AppEntry> usageStatsList5 = new ArrayList<>();
742 List<AppEntry> usageStatsList6;
744 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
745 homeIntent.addCategory(Intent.CATEGORY_HOME);
746 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
748 // Filter out apps without a launcher intent
749 // Also filter out the current launcher, and Taskbar itself
750 for(AppEntry packageInfo : usageStatsList) {
751 if(hasLauncherIntent(packageInfo.getPackageName())
752 && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
753 && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
754 usageStatsList2.add(packageInfo);
757 // Filter out apps that don't fall within our current search interval
758 for(AppEntry stats : usageStatsList2) {
759 if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
760 usageStatsList3.add(stats);
763 // Sort apps by either most recently used, or most time used
764 if(!runningAppsOnly && sortOrder.contains("most_used")) {
765 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
767 Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
770 // Filter out any duplicate entries
771 List<String> applicationIds = new ArrayList<>();
772 for(AppEntry stats : usageStatsList3) {
773 if(!applicationIds.contains(stats.getPackageName())) {
774 usageStatsList4.add(stats);
775 applicationIds.add(stats.getPackageName());
779 // Filter out the currently running foreground app, if requested by the user
780 if(pref.getBoolean("hide_foreground", false)) {
781 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
782 UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
783 UsageEvents.Event eventCache = new UsageEvents.Event();
784 String currentForegroundApp = null;
786 while(events.hasNextEvent()) {
787 events.getNextEvent(eventCache);
789 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
790 if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
791 && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
792 && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
793 && !eventCache.getClassName().equals(HomeActivityDelegate.class.getCanonicalName())
794 && !eventCache.getClassName().equals(SecondaryHomeActivity.class.getCanonicalName())
795 && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
796 currentForegroundApp = eventCache.getPackageName();
800 if(!applicationIdsToRemove.contains(currentForegroundApp))
801 applicationIdsToRemove.add(currentForegroundApp);
804 for(AppEntry stats : usageStatsList4) {
805 if(!applicationIdsToRemove.contains(stats.getPackageName())) {
806 usageStatsList5.add(stats);
810 // Truncate list to a maximum length
811 if(usageStatsList5.size() > maxNumOfEntries)
812 usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
814 usageStatsList6 = usageStatsList5;
816 // Determine if we need to reverse the order
817 boolean needToReverseOrder;
818 switch(U.getTaskbarPosition(context)) {
821 needToReverseOrder = sortOrder.contains("false");
824 needToReverseOrder = sortOrder.contains("true");
828 if(needToReverseOrder) {
829 Collections.reverse(usageStatsList6);
832 // Generate the AppEntries for the recent apps list
833 int number = usageStatsList6.size() == maxNumOfEntries
834 ? usageStatsList6.size() - realNumOfPinnedApps
835 : usageStatsList6.size();
837 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
838 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
840 final List<UserHandle> userHandles = userManager.getUserProfiles();
842 for(int i = 0; i < number; i++) {
843 for(UserHandle handle : userHandles) {
844 String packageName = usageStatsList6.get(i).getPackageName();
845 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
846 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
847 if(!list.isEmpty()) {
848 // Google App workaround
849 if(!packageName.equals("com.google.android.googlequicksearchbox"))
850 launcherAppCache.add(list.get(0));
852 boolean added = false;
853 for(LauncherActivityInfo info : list) {
854 if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
855 launcherAppCache.add(info);
860 if(!added) launcherAppCache.add(list.get(0));
863 AppEntry newEntry = new AppEntry(
871 newEntry.setUserId(userManager.getSerialNumberForUser(handle));
872 newEntry.setLastTimeUsed(lastTimeUsed);
873 entries.add(newEntry);
881 while(entries.size() > maxNumOfEntries) {
883 entries.remove(entries.size() - 1);
884 launcherAppCache.remove(launcherAppCache.size() - 1);
885 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
888 // Determine if we need to reverse the order again
889 if(U.getTaskbarPosition(context).contains("vertical")) {
890 Collections.reverse(entries);
891 Collections.reverse(launcherAppCache);
894 // Now that we've generated the list of apps,
895 // we need to determine if we need to redraw the Taskbar or not
896 boolean shouldRedrawTaskbar = firstRefresh;
898 List<String> finalApplicationIds = new ArrayList<>();
899 for(AppEntry entry : entries) {
900 finalApplicationIds.add(entry.getPackageName());
903 if(finalApplicationIds.size() != currentTaskbarIds.size()
904 || (runningAppsOnly && currentRunningAppIds.size() != prevRunningAppIds.size())
905 || numOfPinnedApps != realNumOfPinnedApps)
906 shouldRedrawTaskbar = true;
908 for(int i = 0; i < finalApplicationIds.size(); i++) {
909 if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
910 shouldRedrawTaskbar = true;
915 if(!shouldRedrawTaskbar && runningAppsOnly) {
916 for(int i = 0; i < finalApplicationIds.size(); i++) {
917 if(!currentRunningAppIds.get(i).equals(prevRunningAppIds.get(i))) {
918 shouldRedrawTaskbar = true;
925 if(shouldRedrawTaskbar) {
926 currentTaskbarIds = finalApplicationIds;
927 numOfPinnedApps = realNumOfPinnedApps;
929 if(runningAppsOnly) {
930 prevRunningAppIds.clear();
931 prevRunningAppIds.addAll(currentRunningAppIds);
934 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
936 int launcherAppCachePos = -1;
937 for(int i = 0; i < entries.size(); i++) {
938 if(entries.get(i).getComponentName() == null) {
939 launcherAppCachePos++;
940 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
941 String packageName = entries.get(i).getPackageName();
942 long lastTimeUsed = entries.get(i).getLastTimeUsed();
946 AppEntry newEntry = new AppEntry(
948 appInfo.getComponentName().flattenToString(),
949 appInfo.getLabel().toString(),
950 IconCache.getInstance(context).getIcon(context, pm, appInfo),
953 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
954 newEntry.setLastTimeUsed(lastTimeUsed);
955 entries.add(i, newEntry);
959 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
962 if(numOfEntries > 0 || fullLength) {
963 ViewGroup.LayoutParams params = scrollView.getLayoutParams();
964 DisplayInfo display = U.getDisplayInfo(context, true);
965 int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size) * numOfEntries;
966 float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
968 if(U.getTaskbarPosition(context).contains("vertical")) {
969 int maxScreenSize = Math.max(0, display.height
970 - U.getStatusBarHeight(context)
971 - U.getBaseTaskbarSize(context));
973 params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
974 + context.getResources().getDimensionPixelSize(R.dimen.divider_size);
978 Space whitespaceTop = layout.findViewById(R.id.whitespace_top);
979 Space whitespaceBottom = layout.findViewById(R.id.whitespace_bottom);
980 int height = maxScreenSize - recentsSize;
982 if(pref.getBoolean("centered_icons", false)) {
983 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
984 topParams.height = height / 2;
985 whitespaceTop.setLayoutParams(topParams);
987 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
988 bottomParams.height = height / 2;
989 whitespaceBottom.setLayoutParams(bottomParams);
990 } else if(U.getTaskbarPosition(context).contains("bottom")) {
991 ViewGroup.LayoutParams topParams = whitespaceTop.getLayoutParams();
992 topParams.height = height;
993 whitespaceTop.setLayoutParams(topParams);
995 ViewGroup.LayoutParams bottomParams = whitespaceBottom.getLayoutParams();
996 bottomParams.height = height;
997 whitespaceBottom.setLayoutParams(bottomParams);
999 } catch (NullPointerException e) { /* Gracefully fail */ }
1002 int maxScreenSize = Math.max(0, display.width - U.getBaseTaskbarSize(context));
1004 params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
1005 + context.getResources().getDimensionPixelSize(R.dimen.divider_size);
1009 Space whitespaceLeft = layout.findViewById(R.id.whitespace_left);
1010 Space whitespaceRight = layout.findViewById(R.id.whitespace_right);
1011 int width = maxScreenSize - recentsSize;
1013 if(pref.getBoolean("centered_icons", false)) {
1014 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1015 leftParams.width = width / 2;
1016 whitespaceLeft.setLayoutParams(leftParams);
1018 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1019 rightParams.width = width / 2;
1020 whitespaceRight.setLayoutParams(rightParams);
1021 } else if(U.getTaskbarPosition(context).contains("right")) {
1022 ViewGroup.LayoutParams leftParams = whitespaceLeft.getLayoutParams();
1023 leftParams.width = width;
1024 whitespaceLeft.setLayoutParams(leftParams);
1026 ViewGroup.LayoutParams rightParams = whitespaceRight.getLayoutParams();
1027 rightParams.width = width;
1028 whitespaceRight.setLayoutParams(rightParams);
1030 } catch (NullPointerException e) { /* Gracefully fail */ }
1034 scrollView.setLayoutParams(params);
1036 taskbar.removeAllViews();
1037 for(int i = 0; i < entries.size(); i++) {
1038 taskbar.addView(getView(entries, i));
1041 isShowingRecents = true;
1042 if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
1044 scrollView.setVisibility(View.INVISIBLE);
1046 scrollView.setVisibility(View.VISIBLE);
1049 if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
1050 new Handler().post(() -> {
1051 switch(U.getTaskbarPosition(context)) {
1053 case "bottom_right":
1056 if(sortOrder.contains("false"))
1057 scrollView.scrollTo(0, 0);
1058 else if(sortOrder.contains("true"))
1059 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1061 case "bottom_vertical_left":
1062 case "bottom_vertical_right":
1063 case "top_vertical_left":
1064 case "top_vertical_right":
1065 if(sortOrder.contains("false"))
1066 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1067 else if(sortOrder.contains("true"))
1068 scrollView.scrollTo(0, 0);
1072 if(shouldRefreshRecents) {
1073 scrollView.setVisibility(View.VISIBLE);
1077 isShowingRecents = false;
1078 scrollView.setVisibility(View.GONE);
1082 } else if(firstRefresh || currentTaskbarIds.size() > 0) {
1083 currentTaskbarIds.clear();
1084 handler.post(() -> {
1085 isShowingRecents = false;
1086 scrollView.setVisibility(View.GONE);
1091 private void toggleTaskbar(boolean userInitiated) {
1092 if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
1093 SharedPreferences pref = U.getSharedPreferences(context);
1094 if(!pref.getBoolean("grip_rejection_toast_shown", false)) {
1095 U.showToastLong(context, R.string.essential_phone_grip_rejection);
1096 pref.edit().putBoolean("grip_rejection_toast_shown", true).apply();
1100 if(startButton.getVisibility() == View.GONE)
1106 private void showTaskbar(boolean clearVariables) {
1107 if(clearVariables) {
1108 taskbarShownTemporarily = false;
1109 taskbarHiddenTemporarily = false;
1112 if(startButton.getVisibility() == View.GONE) {
1113 startButton.setVisibility(View.VISIBLE);
1114 space.setVisibility(View.VISIBLE);
1116 if(dashboardEnabled)
1117 dashboardButton.setVisibility(View.VISIBLE);
1119 if(navbarButtonsEnabled)
1120 navbarButtons.setVisibility(View.VISIBLE);
1122 if(isShowingRecents && scrollView.getVisibility() == View.GONE)
1123 scrollView.setVisibility(View.INVISIBLE);
1126 sysTrayParentLayout.setVisibility(View.VISIBLE);
1128 shouldRefreshRecents = true;
1129 startRefreshingRecents();
1131 SharedPreferences pref = U.getSharedPreferences(context);
1132 pref.edit().putBoolean("collapsed", true).apply();
1134 updateButton(false);
1136 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_START_MENU_SPACE")));
1140 private void hideTaskbar(boolean clearVariables) {
1141 if(clearVariables) {
1142 taskbarShownTemporarily = false;
1143 taskbarHiddenTemporarily = false;
1146 if(startButton.getVisibility() == View.VISIBLE) {
1147 startButton.setVisibility(View.GONE);
1148 space.setVisibility(View.GONE);
1150 if(dashboardEnabled)
1151 dashboardButton.setVisibility(View.GONE);
1153 if(navbarButtonsEnabled)
1154 navbarButtons.setVisibility(View.GONE);
1156 if(isShowingRecents)
1157 scrollView.setVisibility(View.GONE);
1160 sysTrayParentLayout.setVisibility(View.GONE);
1162 shouldRefreshRecents = false;
1163 if(thread != null) thread.interrupt();
1165 SharedPreferences pref = U.getSharedPreferences(context);
1166 pref.edit().putBoolean("collapsed", false).apply();
1170 if(clearVariables) {
1171 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
1172 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1175 new Handler().post(() -> LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_SPACE")));
1179 private void tempShowTaskbar() {
1180 if(!taskbarHiddenTemporarily) {
1181 SharedPreferences pref = U.getSharedPreferences(context);
1182 if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1187 if(taskbarHiddenTemporarily)
1188 taskbarHiddenTemporarily = false;
1191 private void tempHideTaskbar(boolean monitorPositionChanges) {
1192 if(!taskbarShownTemporarily) {
1193 SharedPreferences pref = U.getSharedPreferences(context);
1194 if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1199 if(taskbarShownTemporarily)
1200 taskbarShownTemporarily = false;
1202 if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1203 if(thread2 != null) thread2.interrupt();
1205 handler2 = new Handler();
1206 thread2 = new Thread(() -> {
1207 stopThread2 = false;
1209 while(!stopThread2) {
1210 SystemClock.sleep(refreshInterval);
1212 handler2.post(() -> stopThread2 = checkPositionChange());
1215 startThread2 = false;
1222 private boolean checkPositionChange() {
1223 if(!isScreenOff() && layout != null) {
1224 int[] location = new int[2];
1225 layout.getLocationOnScreen(location);
1227 if(location[1] == 0) {
1230 if(location[1] > currentTaskbarPosition) {
1231 currentTaskbarPosition = location[1];
1232 if(taskbarHiddenTemporarily) {
1236 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1239 } else if(location[1] < currentTaskbarPosition
1240 && currentTaskbarPosition - location[1] == getNavBarSize()) {
1241 currentTaskbarPosition = location[1];
1249 private int getNavBarSize() {
1250 Point size = new Point();
1251 Point realSize = new Point();
1253 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1254 Display display = wm.getDefaultDisplay();
1255 display.getSize(size);
1256 display.getRealSize(realSize);
1258 return realSize.y - size.y;
1262 public void onDestroyHost(UIHost host) {
1263 shouldRefreshRecents = false;
1267 host.removeView(layout);
1268 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1270 SharedPreferences pref = U.getSharedPreferences(context);
1271 if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1272 pref.edit().remove("skip_auto_hide_navbar").apply();
1273 } else if(pref.getBoolean("auto_hide_navbar", false))
1274 U.showHideNavigationBar(context, true);
1276 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
1278 lbm.unregisterReceiver(showReceiver);
1279 lbm.unregisterReceiver(hideReceiver);
1280 lbm.unregisterReceiver(tempShowReceiver);
1281 lbm.unregisterReceiver(tempHideReceiver);
1282 lbm.unregisterReceiver(startMenuAppearReceiver);
1283 lbm.unregisterReceiver(startMenuDisappearReceiver);
1285 if(sysTrayEnabled) {
1286 TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1287 manager.listen(listener, PhoneStateListener.LISTEN_NONE);
1290 isFirstStart = true;
1293 private void openContextMenu() {
1294 SharedPreferences pref = U.getSharedPreferences(context);
1296 Bundle args = new Bundle();
1297 args.putBoolean("dont_show_quit",
1298 LauncherHelper.getInstance().isOnHomeScreen()
1299 && !pref.getBoolean("taskbar_active", false));
1300 args.putBoolean("is_start_button", true);
1302 U.startContextMenuActivity(context, args);
1305 private void updateButton(boolean isCollapsed) {
1306 SharedPreferences pref = U.getSharedPreferences(context);
1307 boolean hide = pref.getBoolean("invisible_button", false);
1309 if(button != null) button.setText(context.getString(isCollapsed ? R.string.right_arrow : R.string.left_arrow));
1310 if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1313 @TargetApi(Build.VERSION_CODES.M)
1315 public void onRecreateHost(UIHost host) {
1316 if(layout != null) {
1318 host.removeView(layout);
1319 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1321 currentTaskbarPosition = 0;
1323 if(U.canDrawOverlays(context))
1326 SharedPreferences pref = U.getSharedPreferences(context);
1327 pref.edit().putBoolean("taskbar_active", false).apply();
1334 private View getView(List<AppEntry> list, int position) {
1335 View convertView = View.inflate(context, R.layout.icon, null);
1337 final AppEntry entry = list.get(position);
1338 final SharedPreferences pref = U.getSharedPreferences(context);
1340 ImageView imageView = convertView.findViewById(R.id.icon);
1341 ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1342 imageView.setImageDrawable(entry.getIcon(context));
1343 imageView2.setBackgroundColor(U.getAccentColor(context));
1345 String taskbarPosition = U.getTaskbarPosition(context);
1346 if(pref.getBoolean("shortcut_icon", true)) {
1347 boolean shouldShowShortcutIcon;
1348 if(taskbarPosition.contains("vertical"))
1349 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1351 shouldShowShortcutIcon = position < numOfPinnedApps;
1353 if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1356 if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1357 imageView.setRotationY(180);
1358 imageView2.setRotationY(180);
1361 FrameLayout layout = convertView.findViewById(R.id.entry);
1362 layout.setOnClickListener(view -> U.launchApp(
1371 layout.setOnLongClickListener(view -> {
1372 int[] location = new int[2];
1373 view.getLocationOnScreen(location);
1374 openContextMenu(entry, location);
1378 layout.setOnGenericMotionListener((view, motionEvent) -> {
1379 int action = motionEvent.getAction();
1381 if(action == MotionEvent.ACTION_BUTTON_PRESS
1382 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1383 int[] location = new int[2];
1384 view.getLocationOnScreen(location);
1385 openContextMenu(entry, location);
1388 if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1389 view.setBackgroundColor(0);
1394 if(pref.getBoolean("visual_feedback", true)) {
1395 layout.setOnHoverListener((v, event) -> {
1396 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1397 int accentColor = U.getAccentColor(context);
1398 accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1399 v.setBackgroundColor(accentColor);
1402 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1403 v.setBackgroundColor(0);
1405 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1406 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1411 layout.setOnTouchListener((v, event) -> {
1412 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1417 if(runningAppsOnly) {
1418 ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1419 if(entry.getLastTimeUsed() > 0) {
1420 runningAppIndicator.setVisibility(View.VISIBLE);
1421 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1423 runningAppIndicator.setVisibility(View.GONE);
1429 private void openContextMenu(AppEntry entry, int[] location) {
1430 Bundle args = new Bundle();
1431 args.putSerializable("app_entry", entry);
1432 args.putInt("x", location[0]);
1433 args.putInt("y", location[1]);
1435 U.startContextMenuActivity(context, args);
1438 private List<AppEntry> getAppEntries() {
1439 SharedPreferences pref = U.getSharedPreferences(context);
1441 return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1443 return getAppEntriesUsingUsageStats();
1446 @SuppressWarnings("deprecation")
1447 @TargetApi(Build.VERSION_CODES.M)
1448 private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1449 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1450 List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1451 List<AppEntry> entries = new ArrayList<>();
1453 for(int i = 0; i < usageStatsList.size(); i++) {
1454 ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1455 if(recentTaskInfo.id != -1) {
1456 String packageName = recentTaskInfo.baseActivity.getPackageName();
1457 AppEntry newEntry = new AppEntry(
1466 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1467 newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1468 currentRunningAppIds.add(packageName);
1469 } catch (Exception e) {
1470 newEntry.setLastTimeUsed(i);
1473 entries.add(newEntry);
1480 @SuppressWarnings("deprecation")
1481 @TargetApi(Build.VERSION_CODES.M)
1482 private List<AppEntry> setTimeLastUsedFor(List<AppEntry> pinnedApps) {
1483 if(!runningAppsOnly || pinnedApps.size() == 0)
1486 ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1487 List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasks(Integer.MAX_VALUE, 0);
1489 for(AppEntry entry : pinnedApps) {
1490 for(ActivityManager.RecentTaskInfo task : recentTasks) {
1491 if(task.id != -1 && task.baseActivity.getPackageName().equals(entry.getPackageName())) {
1493 Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1494 entry.setLastTimeUsed(field.getLong(task));
1495 currentRunningAppIds.add(entry.getPackageName());
1497 } catch (Exception e) { /* Gracefully fail */ }
1505 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1506 private List<AppEntry> getAppEntriesUsingUsageStats() {
1507 UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1508 List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1509 List<AppEntry> entries = new ArrayList<>();
1511 for(UsageStats usageStats : usageStatsList) {
1512 AppEntry newEntry = new AppEntry(
1513 usageStats.getPackageName(),
1520 newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1521 newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1522 entries.add(newEntry);
1528 private boolean hasLauncherIntent(String packageName) {
1529 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1530 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1531 intentToResolve.setPackage(packageName);
1533 List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1534 return ris != null && ris.size() > 0;
1537 private boolean isScreenOff() {
1538 if(U.isChromeOs(context))
1541 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1542 return !pm.isInteractive();
1545 private void updateSystemTray() {
1546 if(!sysTrayEnabled || isScreenOff()) return;
1548 handler.post(() -> {
1549 ImageView battery = sysTrayLayout.findViewById(R.id.battery);
1550 battery.setImageDrawable(getBatteryDrawable());
1552 ImageView wifi = sysTrayLayout.findViewById(R.id.wifi);
1553 wifi.setImageDrawable(getWifiDrawable());
1555 ImageView bluetooth = sysTrayLayout.findViewById(R.id.bluetooth);
1556 bluetooth.setImageDrawable(getBluetoothDrawable());
1558 ImageView cellular = sysTrayLayout.findViewById(R.id.cellular);
1559 cellular.setImageDrawable(getCellularDrawable());
1561 time.setText(context.getString(R.string.systray_clock,
1562 DateFormat.getTimeFormat(context).format(new Date()),
1563 DateFormat.getDateFormat(context).format(new Date())));
1564 time.setTextColor(U.getAccentColor(context));
1568 @TargetApi(Build.VERSION_CODES.M)
1569 private Drawable getBatteryDrawable() {
1570 BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
1571 int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1573 if(batLevel == Integer.MIN_VALUE)
1576 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
1577 Intent batteryStatus = context.registerReceiver(null, ifilter);
1579 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
1580 boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
1581 status == BatteryManager.BATTERY_STATUS_FULL;
1584 if(batLevel < 10 && !isCharging)
1585 batDrawable = "alert";
1586 else if(batLevel < 25)
1588 else if(batLevel < 40)
1590 else if(batLevel < 55)
1592 else if(batLevel < 70)
1594 else if(batLevel < 85)
1596 else if(batLevel < 95)
1599 batDrawable = "full";
1603 charging = "charging_";
1607 String batRes = "ic_battery_" + charging + batDrawable + "_black_24dp";
1608 int id = getResourceIdFor(batRes);
1610 return applyTintTo(ContextCompat.getDrawable(context, id));
1613 @TargetApi(Build.VERSION_CODES.M)
1614 private Drawable getWifiDrawable() {
1615 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1617 NetworkInfo ethernet = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
1618 if(ethernet != null && ethernet.isConnected())
1619 return applyTintTo(ContextCompat.getDrawable(context, R.drawable.ic_settings_ethernet_black_24dp));
1621 NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
1622 if(wifi == null || !wifi.isConnected())
1625 WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
1626 int numberOfLevels = 5;
1628 WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1629 int level = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), numberOfLevels);
1631 String wifiRes = "ic_signal_wifi_" + level + "_bar_black_24dp";
1632 int id = getResourceIdFor(wifiRes);
1634 return applyTintTo(ContextCompat.getDrawable(context, id));
1637 private Drawable getBluetoothDrawable() {
1638 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1639 if(adapter != null && adapter.isEnabled())
1640 return applyTintTo(ContextCompat.getDrawable(context, R.drawable.ic_bluetooth_black_24dp));
1645 @TargetApi(Build.VERSION_CODES.M)
1646 private Drawable getCellularDrawable() {
1647 if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0)
1648 return applyTintTo(ContextCompat.getDrawable(context, R.drawable.ic_airplanemode_active_black_24dp));
1650 if(cellStrength == -1)
1653 String cellRes = "ic_signal_cellular_" + cellStrength + "_bar_black_24dp";
1654 int id = getResourceIdFor(cellRes);
1656 return applyTintTo(ContextCompat.getDrawable(context, id));
1659 private Drawable applyTintTo(Drawable drawable) {
1660 if(drawable == null) return null;
1662 drawable.setTint(U.getAccentColor(context));
1666 private int getResourceIdFor(String name) {
1667 String packageName = context.getResources().getResourcePackageName(R.mipmap.ic_launcher);
1668 return context.getResources().getIdentifier(name, "drawable", packageName);