OSDN Git Service

Small fix for Chrome OS
[android-x86/packages-apps-Taskbar.git] / app / src / main / java / com / farmerbb / taskbar / ui / TaskbarController.java
1 /* Copyright 2016 Braden Farmer
2  *
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
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 package com.farmerbb.taskbar.ui;
17
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.content.res.Resources;
38 import android.graphics.Color;
39 import android.graphics.Point;
40 import android.graphics.Typeface;
41 import android.graphics.drawable.Drawable;
42 import android.net.ConnectivityManager;
43 import android.net.NetworkInfo;
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.Process;
52 import android.os.SystemClock;
53 import android.os.UserHandle;
54 import android.os.UserManager;
55 import android.provider.Settings;
56 import android.speech.RecognizerIntent;
57
58 import androidx.annotation.VisibleForTesting;
59 import androidx.core.content.ContextCompat;
60 import androidx.core.graphics.ColorUtils;
61 import android.telephony.PhoneStateListener;
62 import android.telephony.SignalStrength;
63 import android.telephony.TelephonyManager;
64 import android.text.format.DateFormat;
65 import android.view.Display;
66 import android.view.Gravity;
67 import android.view.LayoutInflater;
68 import android.view.MotionEvent;
69 import android.view.PointerIcon;
70 import android.view.View;
71 import android.view.ViewGroup;
72 import android.view.WindowManager;
73 import android.view.inputmethod.InputMethodManager;
74 import android.widget.Button;
75 import android.widget.FrameLayout;
76 import android.widget.ImageView;
77
78 import java.lang.reflect.Field;
79 import java.util.ArrayList;
80 import java.util.Collections;
81 import java.util.Date;
82 import java.util.HashMap;
83 import java.util.List;
84 import java.util.Map;
85
86 import android.widget.LinearLayout;
87 import android.widget.Space;
88 import android.widget.TextView;
89
90 import com.farmerbb.taskbar.BuildConfig;
91 import com.farmerbb.taskbar.activity.HomeActivityDelegate;
92 import com.farmerbb.taskbar.activity.MainActivity;
93 import com.farmerbb.taskbar.R;
94 import com.farmerbb.taskbar.activity.HomeActivity;
95 import com.farmerbb.taskbar.activity.InvisibleActivityFreeform;
96 import com.farmerbb.taskbar.activity.SecondaryHomeActivity;
97 import com.farmerbb.taskbar.util.TaskbarPosition;
98 import com.farmerbb.taskbar.util.AppEntry;
99 import com.farmerbb.taskbar.util.DisplayInfo;
100 import com.farmerbb.taskbar.helper.FreeformHackHelper;
101 import com.farmerbb.taskbar.util.IconCache;
102 import com.farmerbb.taskbar.helper.LauncherHelper;
103 import com.farmerbb.taskbar.util.PinnedBlockedApps;
104 import com.farmerbb.taskbar.helper.MenuHelper;
105 import com.farmerbb.taskbar.util.U;
106
107 import static com.farmerbb.taskbar.util.Constants.*;
108
109 public class TaskbarController extends UIController {
110
111     private LinearLayout layout;
112     private ImageView startButton;
113     private LinearLayout taskbar;
114     private FrameLayout scrollView;
115     private Button button;
116     private Space space;
117     private FrameLayout dashboardButton;
118     private LinearLayout navbarButtons;
119     private LinearLayout sysTrayLayout;
120     private FrameLayout sysTrayParentLayout;
121     private TextView time;
122     private ImageView notificationCountCircle;
123     private TextView notificationCountText;
124
125     private Handler handler;
126     private Handler handler2;
127     private Thread thread;
128     private Thread thread2;
129
130     private boolean isShowingRecents = true;
131     private boolean shouldRefreshRecents = true;
132     private boolean taskbarShownTemporarily = false;
133     private boolean taskbarHiddenTemporarily = false;
134     private boolean isRefreshingRecents = false;
135     private boolean isFirstStart = true;
136
137     private boolean startThread2 = false;
138     private boolean stopThread2 = false;
139
140     private int refreshInterval = -1;
141     private long searchInterval = -1;
142     private String sortOrder = "false";
143     private boolean runningAppsOnly = false;
144
145     private int currentTaskbarPosition = 0;
146     private boolean showHideAutomagically = false;
147     private boolean positionIsVertical = false;
148     private boolean dashboardEnabled = false;
149     private boolean navbarButtonsEnabled = false;
150     private boolean sysTrayEnabled = false;
151
152     private List<String> currentTaskbarIds = new ArrayList<>();
153     private int numOfPinnedApps = -1;
154
155     private int cellStrength = -1;
156     private int notificationCount = 0;
157     private int numOfSysTrayIcons = 0;
158
159     private boolean matchParent;
160     private Runnable updateParamsRunnable;
161
162     private final Map<Integer, Boolean> sysTrayIconStates = new HashMap<>();
163
164     private final View.OnClickListener ocl = view ->
165             U.sendBroadcast(context, ACTION_TOGGLE_START_MENU);
166
167     private final BroadcastReceiver showReceiver = new BroadcastReceiver() {
168         @Override
169         public void onReceive(Context context, Intent intent) {
170             showTaskbar(true);
171         }
172     };
173
174     private final BroadcastReceiver hideReceiver = new BroadcastReceiver() {
175         @Override
176         public void onReceive(Context context, Intent intent) {
177             hideTaskbar(true);
178         }
179     };
180
181     private final BroadcastReceiver tempShowReceiver = new BroadcastReceiver() {
182         @Override
183         public void onReceive(Context context, Intent intent) {
184             tempShowTaskbar();
185         }
186     };
187
188     private final BroadcastReceiver tempHideReceiver = new BroadcastReceiver() {
189         @Override
190         public void onReceive(Context context, Intent intent) {
191             tempHideTaskbar(false);
192         }
193     };
194
195     private final BroadcastReceiver startMenuAppearReceiver = new BroadcastReceiver() {
196         @Override
197         public void onReceive(Context context, Intent intent) {
198             if(startButton.getVisibility() == View.GONE
199                     && (!LauncherHelper.getInstance().isOnHomeScreen(context) || FreeformHackHelper.getInstance().isInFreeformWorkspace()))
200                 layout.setVisibility(View.GONE);
201         }
202     };
203
204     private final BroadcastReceiver startMenuDisappearReceiver = new BroadcastReceiver() {
205         @Override
206         public void onReceive(Context context, Intent intent) {
207             if(startButton.getVisibility() == View.GONE)
208                 layout.setVisibility(View.VISIBLE);
209         }
210     };
211
212     private final BroadcastReceiver notificationCountReceiver = new BroadcastReceiver() {
213         @Override
214         public void onReceive(Context context, Intent intent) {
215             notificationCount = intent.getIntExtra(EXTRA_COUNT, 0);
216         }
217     };
218
219     @TargetApi(Build.VERSION_CODES.M)
220     private final PhoneStateListener listener = new PhoneStateListener() {
221         @Override
222         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
223             try {
224                 cellStrength = signalStrength.getLevel();
225             } catch (SecurityException e) {
226                 cellStrength = -1;
227             }
228         }
229     };
230
231     public TaskbarController(Context context) {
232         super(context);
233     }
234
235     @Override
236     public void onCreateHost(UIHost host) {
237         init(context, host, () -> drawTaskbar(host));
238     }
239
240     private void drawTaskbar(UIHost host) {
241         IconCache.getInstance(context).clearCache();
242
243         // Initialize layout params
244         WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
245         TaskbarPosition.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
246
247         final ViewParams params = new ViewParams(
248                 WindowManager.LayoutParams.WRAP_CONTENT,
249                 WindowManager.LayoutParams.WRAP_CONTENT,
250                 -1,
251                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
252                 getBottomMargin(context, host)
253         );
254
255         // Determine where to show the taskbar on screen
256         String taskbarPosition = TaskbarPosition.getTaskbarPosition(context);
257         params.gravity = getTaskbarGravity(taskbarPosition);
258         int layoutId = getTaskbarLayoutId(taskbarPosition);
259         positionIsVertical = TaskbarPosition.isVertical(taskbarPosition);
260
261         // Initialize views
262         SharedPreferences pref = U.getSharedPreferences(context);
263         boolean altButtonConfig = pref.getBoolean(PREF_ALT_BUTTON_CONFIG, false);
264
265         layout = (LinearLayout) LayoutInflater.from(U.wrapContext(context)).inflate(layoutId, null);
266         taskbar = layout.findViewById(R.id.taskbar);
267         scrollView = layout.findViewById(R.id.taskbar_scrollview);
268
269         int backgroundTint = U.getBackgroundTint(context);
270         int accentColor = U.getAccentColor(context);
271
272         if(altButtonConfig) {
273             space = layout.findViewById(R.id.space_alt);
274             layout.findViewById(R.id.space).setVisibility(View.GONE);
275         } else {
276             space = layout.findViewById(R.id.space);
277             layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
278         }
279
280         space.setOnClickListener(v -> toggleTaskbar(true));
281
282         startButton = layout.findViewById(R.id.start_button);
283         drawStartButton(context, startButton, pref, accentColor);
284
285         refreshInterval = (int) (Float.parseFloat(pref.getString(PREF_REFRESH_FREQUENCY, "1")) * 1000);
286         if(refreshInterval == 0)
287             refreshInterval = 100;
288
289         sortOrder = pref.getString(PREF_SORT_ORDER, "false");
290         runningAppsOnly =
291                 PREF_RECENTS_AMOUNT_RUNNING_APPS_ONLY
292                         .equals(pref.getString(PREF_RECENTS_AMOUNT, PREF_RECENTS_AMOUNT_PAST_DAY));
293         searchInterval = getSearchInterval(pref);
294
295         U.sendBroadcast(context, ACTION_HIDE_START_MENU);
296         U.sendBroadcast(context, ACTION_UPDATE_HOME_SCREEN_MARGINS);
297
298         if(altButtonConfig) {
299             button = layout.findViewById(R.id.hide_taskbar_button_alt);
300             layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
301         } else {
302             button = layout.findViewById(R.id.hide_taskbar_button);
303             layout.findViewById(R.id.hide_taskbar_button_alt).setVisibility(View.GONE);
304         }
305
306         try {
307             button.setTypeface(Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf"));
308         } catch (RuntimeException ignored) {}
309
310         updateButton(false);
311         button.setOnClickListener(v -> toggleTaskbar(true));
312
313         LinearLayout buttonLayout = layout.findViewById(altButtonConfig
314                 ? R.id.hide_taskbar_button_layout_alt
315                 : R.id.hide_taskbar_button_layout);
316         if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar(true));
317
318         LinearLayout buttonLayoutToHide = layout.findViewById(altButtonConfig
319                 ? R.id.hide_taskbar_button_layout
320                 : R.id.hide_taskbar_button_layout_alt);
321         if(buttonLayoutToHide != null) buttonLayoutToHide.setVisibility(View.GONE);
322
323         dashboardButton = layout.findViewById(R.id.dashboard_button);
324         navbarButtons = layout.findViewById(R.id.navbar_buttons);
325         dashboardEnabled = drawDashboardButton(context, layout, dashboardButton, accentColor);
326         navbarButtonsEnabled = drawNavbarButtons(context, layout, pref, accentColor);
327         if(!navbarButtonsEnabled)
328             navbarButtons.setVisibility(View.GONE);
329
330         sysTrayEnabled = U.isSystemTrayEnabled(context);
331
332         if(sysTrayEnabled) {
333             drawSysTray(context, layoutId, layout);
334         }
335
336         layout.setBackgroundColor(backgroundTint);
337         layout.findViewById(R.id.divider).setBackgroundColor(accentColor);
338         button.setTextColor(accentColor);
339
340         applyMarginFix(host, layout, params);
341
342         if(isFirstStart && FreeformHackHelper.getInstance().isInFreeformWorkspace())
343             showTaskbar(false);
344         else if(!pref.getBoolean(PREF_COLLAPSED, false) && pref.getBoolean(PREF_TASKBAR_ACTIVE, false))
345             toggleTaskbar(false);
346
347         if(pref.getBoolean(PREF_AUTO_HIDE_NAVBAR, false))
348             U.showHideNavigationBar(context, false);
349
350         if(FreeformHackHelper.getInstance().isTouchAbsorberActive()) {
351             U.sendBroadcast(context, ACTION_FINISH_FREEFORM_ACTIVITY);
352
353             new Handler().postDelayed(() -> U.startTouchAbsorberActivity(context), 500);
354         }
355
356         U.registerReceiver(context, showReceiver, ACTION_SHOW_TASKBAR);
357         U.registerReceiver(context, hideReceiver, ACTION_HIDE_TASKBAR);
358         U.registerReceiver(context, tempShowReceiver, ACTION_TEMP_SHOW_TASKBAR);
359         U.registerReceiver(context, tempHideReceiver, ACTION_TEMP_HIDE_TASKBAR);
360         U.registerReceiver(context, startMenuAppearReceiver, ACTION_START_MENU_APPEARING);
361         U.registerReceiver(context, startMenuDisappearReceiver, ACTION_START_MENU_DISAPPEARING);
362
363         if(sysTrayEnabled) {
364             TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
365             manager.listen(listener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
366
367             U.registerReceiver(context, notificationCountReceiver, ACTION_NOTIFICATION_COUNT_CHANGED);
368             U.sendBroadcast(context, ACTION_REQUEST_NOTIFICATION_COUNT);
369         }
370
371         matchParent = false;
372         updateParamsRunnable = () -> {
373             ViewParams newParams;
374             if(TaskbarPosition.isVertical(context)) {
375                 newParams = matchParent
376                         ? params.updateHeight(WindowManager.LayoutParams.MATCH_PARENT)
377                         : params.updateHeight(WindowManager.LayoutParams.WRAP_CONTENT);
378             } else {
379                 newParams = matchParent
380                         ? params.updateWidth(WindowManager.LayoutParams.MATCH_PARENT)
381                         : params.updateWidth(WindowManager.LayoutParams.WRAP_CONTENT);
382             }
383
384             host.updateViewLayout(layout, newParams);
385         };
386
387         startRefreshingRecents();
388
389         host.addView(layout, params);
390
391         isFirstStart = false;
392     }
393
394     @SuppressLint("RtlHardcoded")
395     @VisibleForTesting
396     int getTaskbarGravity(String taskbarPosition) {
397         int gravity = Gravity.BOTTOM | Gravity.LEFT;
398         switch(taskbarPosition) {
399             case POSITION_BOTTOM_LEFT:
400             case POSITION_BOTTOM_VERTICAL_LEFT:
401                 gravity = Gravity.BOTTOM | Gravity.LEFT;
402                 break;
403             case POSITION_BOTTOM_RIGHT:
404             case POSITION_BOTTOM_VERTICAL_RIGHT:
405                 gravity = Gravity.BOTTOM | Gravity.RIGHT;
406                 break;
407             case POSITION_TOP_LEFT:
408             case POSITION_TOP_VERTICAL_LEFT:
409                 gravity = Gravity.TOP | Gravity.LEFT;
410                 break;
411             case POSITION_TOP_RIGHT:
412             case POSITION_TOP_VERTICAL_RIGHT:
413                 gravity = Gravity.TOP | Gravity.RIGHT;
414                 break;
415         }
416         return gravity;
417     }
418
419     @VisibleForTesting
420     int getTaskbarLayoutId(String taskbarPosition) {
421         int layoutId = R.layout.tb_taskbar_left;
422         switch(taskbarPosition) {
423             case POSITION_BOTTOM_LEFT:
424             case POSITION_TOP_LEFT:
425                 layoutId = R.layout.tb_taskbar_left;
426                 break;
427             case POSITION_BOTTOM_VERTICAL_LEFT:
428             case POSITION_BOTTOM_VERTICAL_RIGHT:
429                 layoutId = R.layout.tb_taskbar_vertical;
430                 break;
431             case POSITION_BOTTOM_RIGHT:
432             case POSITION_TOP_RIGHT:
433                 layoutId = R.layout.tb_taskbar_right;
434                 break;
435             case POSITION_TOP_VERTICAL_LEFT:
436             case POSITION_TOP_VERTICAL_RIGHT:
437                 layoutId = R.layout.tb_taskbar_top_vertical;
438                 break;
439         }
440         return layoutId;
441     }
442
443     @VisibleForTesting
444     void drawStartButton(Context context, ImageView startButton, SharedPreferences pref, int accentColor) {
445         Drawable allAppsIcon = ContextCompat.getDrawable(context, R.drawable.tb_all_apps_button_icon);
446         int padding = 0;
447
448         switch(pref.getString(PREF_START_BUTTON_IMAGE, U.getDefaultStartButtonImage(context))) {
449             case PREF_START_BUTTON_IMAGE_DEFAULT:
450                 startButton.setImageDrawable(allAppsIcon);
451                 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
452                 break;
453             case PREF_START_BUTTON_IMAGE_APP_LOGO:
454                 Drawable drawable;
455
456                 if(U.isBlissOs(context)) {
457                     drawable = ContextCompat.getDrawable(context, R.drawable.tb_bliss);
458                     drawable.setTint(accentColor);
459                 } else {
460                     LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
461                     LauncherActivityInfo info = launcherApps.getActivityList(context.getPackageName(), Process.myUserHandle()).get(0);
462                     drawable = IconCache.getInstance(context).getIcon(context, context.getPackageManager(), info);
463                 }
464
465                 startButton.setImageDrawable(drawable);
466                 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding_alt);
467                 break;
468             case PREF_START_BUTTON_IMAGE_CUSTOM:
469                 U.applyCustomImage(context, "custom_image", startButton, allAppsIcon);
470                 padding = context.getResources().getDimensionPixelSize(R.dimen.tb_app_drawer_icon_padding);
471                 break;
472         }
473
474         startButton.setPadding(padding, padding, padding, padding);
475         startButton.setOnClickListener(ocl);
476         startButton.setOnLongClickListener(view -> {
477             openContextMenu();
478             return true;
479         });
480
481         startButton.setOnGenericMotionListener((view, motionEvent) -> {
482             if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
483                     && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
484                 openContextMenu();
485
486             return false;
487         });
488     }
489
490     @VisibleForTesting
491     boolean drawDashboardButton(Context context,
492                                 LinearLayout layout,
493                                 FrameLayout dashboardButton,
494                                 int accentColor) {
495         boolean dashboardEnabled = U.getBooleanPrefWithDefault(context, PREF_DASHBOARD);
496         if(dashboardEnabled) {
497             layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
498             layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
499             layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
500             layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
501             layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
502             layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
503
504             dashboardButton.setOnClickListener(v -> U.sendBroadcast(context, ACTION_TOGGLE_DASHBOARD));
505             dashboardButton.setVisibility(View.VISIBLE);
506         } else
507             dashboardButton.setVisibility(View.GONE);
508
509         return dashboardEnabled;
510     }
511
512     @VisibleForTesting
513     boolean drawNavbarButtons(Context context,
514                               LinearLayout layout,
515                               SharedPreferences pref,
516                               int accentColor) {
517         boolean navbarButtonsEnabled = false;
518         if(pref.getBoolean(PREF_BUTTON_BACK, false)) {
519             navbarButtonsEnabled = true;
520
521             ImageView backButton = layout.findViewById(R.id.button_back);
522             backButton.setColorFilter(accentColor);
523             backButton.setVisibility(View.VISIBLE);
524             backButton.setOnClickListener(v -> {
525                 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_BACK);
526                 if(U.shouldCollapse(context, false))
527                     hideTaskbar(true);
528             });
529
530             backButton.setOnLongClickListener(v -> {
531                 InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
532                 imm.showInputMethodPicker();
533
534                 if(U.shouldCollapse(context, false))
535                     hideTaskbar(true);
536
537                 return true;
538             });
539
540             backButton.setOnGenericMotionListener((view13, motionEvent) -> {
541                 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
542                         && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
543                     InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
544                     imm.showInputMethodPicker();
545
546                     if(U.shouldCollapse(context, false))
547                         hideTaskbar(true);
548                 }
549                 return true;
550             });
551         }
552
553         if(pref.getBoolean(PREF_BUTTON_HOME, false)) {
554             navbarButtonsEnabled = true;
555
556             ImageView homeButton = layout.findViewById(R.id.button_home);
557             homeButton.setColorFilter(accentColor);
558             homeButton.setVisibility(View.VISIBLE);
559             homeButton.setOnClickListener(v -> {
560                 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_HOME);
561                 if(U.shouldCollapse(context, false))
562                     hideTaskbar(true);
563             });
564
565             homeButton.setOnLongClickListener(v -> {
566                 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
567                 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
568
569                 try {
570                     context.startActivity(voiceSearchIntent);
571                 } catch (ActivityNotFoundException ignored) {}
572
573                 if(U.shouldCollapse(context, false))
574                     hideTaskbar(true);
575
576                 return true;
577             });
578
579             homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
580                 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
581                         && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
582                     Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
583                     voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
584
585                     try {
586                         context.startActivity(voiceSearchIntent);
587                     } catch (ActivityNotFoundException ignored) {}
588
589                     if(U.shouldCollapse(context, false))
590                         hideTaskbar(true);
591                 }
592                 return true;
593             });
594         }
595
596         if(pref.getBoolean(PREF_BUTTON_RECENTS, false)) {
597             navbarButtonsEnabled = true;
598
599             ImageView recentsButton = layout.findViewById(R.id.button_recents);
600             recentsButton.setColorFilter(accentColor);
601             recentsButton.setVisibility(View.VISIBLE);
602             recentsButton.setOnClickListener(v -> {
603                 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_RECENTS);
604                 if(U.shouldCollapse(context, false))
605                     hideTaskbar(true);
606             });
607
608             if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
609                 recentsButton.setOnLongClickListener(v -> {
610                     U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
611                     if(U.shouldCollapse(context, false))
612                         hideTaskbar(true);
613
614                     return true;
615                 });
616
617                 recentsButton.setOnGenericMotionListener((view13, motionEvent) -> {
618                     if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
619                             && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
620                         U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
621                         if(U.shouldCollapse(context, false))
622                             hideTaskbar(true);
623                     }
624                     return true;
625                 });
626             }
627         }
628         return navbarButtonsEnabled;
629     }
630
631     @VisibleForTesting
632     long getSearchInterval(SharedPreferences pref) {
633         long searchInterval = -1;
634         switch(pref.getString(PREF_RECENTS_AMOUNT, PREF_RECENTS_AMOUNT_PAST_DAY)) {
635             case PREF_RECENTS_AMOUNT_PAST_DAY:
636                 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
637                 break;
638             case PREF_RECENTS_AMOUNT_APP_START:
639                 long appStartTime = pref.getLong(PREF_TIME_OF_SERVICE_START, System.currentTimeMillis());
640                 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
641                 searchInterval = Math.max(deviceStartTime, appStartTime);
642                 break;
643             case PREF_RECENTS_AMOUNT_SHOW_ALL:
644                 searchInterval = 0;
645                 break;
646         }
647         return searchInterval;
648     }
649
650     @VisibleForTesting
651     void drawSysTray(Context context, int layoutId, LinearLayout layout) {
652         sysTrayLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.tb_system_tray, null);
653
654         FrameLayout.LayoutParams sysTrayParams = new FrameLayout.LayoutParams(
655                 FrameLayout.LayoutParams.WRAP_CONTENT,
656                 context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size)
657         );
658
659         if(layoutId == R.layout.tb_taskbar_right) {
660             time = sysTrayLayout.findViewById(R.id.time_left);
661             sysTrayParams.gravity = Gravity.START;
662             sysTrayLayout.findViewById(R.id.space_right).setVisibility(View.VISIBLE);
663         } else {
664             time = sysTrayLayout.findViewById(R.id.time_right);
665             sysTrayParams.gravity = Gravity.END;
666             sysTrayLayout.findViewById(R.id.space_left).setVisibility(View.VISIBLE);
667         }
668
669         time.setVisibility(View.VISIBLE);
670         sysTrayLayout.setLayoutParams(sysTrayParams);
671
672         if(!U.isLibrary(context)) {
673             sysTrayLayout.setOnClickListener(v -> {
674                 U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, () -> {
675                     if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
676                         U.showToast(context, R.string.tb_opening_notification_tray);
677                         U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
678                     }
679                 });
680
681                 if(U.shouldCollapse(context, false))
682                     hideTaskbar(true);
683             });
684
685             if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
686                 sysTrayLayout.setOnLongClickListener(v -> {
687                     U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, () -> {
688                         if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
689                             U.showToast(context, R.string.tb_opening_quick_settings);
690                             U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
691                         }
692                     });
693
694                     if(U.shouldCollapse(context, false))
695                         hideTaskbar(true);
696
697                     return true;
698                 });
699
700                 sysTrayLayout.setOnGenericMotionListener((view, motionEvent) -> {
701                     if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
702                             && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
703                         U.sendAccessibilityAction(context, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, () -> {
704                             if(LauncherHelper.getInstance().isOnSecondaryHomeScreen(context)) {
705                                 U.showToast(context, R.string.tb_opening_quick_settings);
706                                 U.sendBroadcast(context, ACTION_UNDIM_SCREEN);
707                             }
708                         });
709
710                         if(U.shouldCollapse(context, false))
711                             hideTaskbar(true);
712                     }
713                     return true;
714                 });
715             }
716         }
717
718         notificationCountCircle = sysTrayLayout.findViewById(R.id.notification_count_circle);
719         notificationCountText = sysTrayLayout.findViewById(R.id.notification_count_text);
720
721         sysTrayParentLayout = layout.findViewById(R.id.add_systray_here);
722         sysTrayParentLayout.setVisibility(View.VISIBLE);
723         sysTrayParentLayout.addView(sysTrayLayout);
724
725         sysTrayIconStates.clear();
726         sysTrayIconStates.put(R.id.cellular, false);
727         sysTrayIconStates.put(R.id.bluetooth, false);
728         sysTrayIconStates.put(R.id.wifi, false);
729         sysTrayIconStates.put(R.id.battery, false);
730         sysTrayIconStates.put(R.id.notification_count, false);
731     }
732
733     private void startRefreshingRecents() {
734         if(thread != null) thread.interrupt();
735         stopThread2 = true;
736
737         SharedPreferences pref = U.getSharedPreferences(context);
738         showHideAutomagically = pref.getBoolean(PREF_HIDE_WHEN_KEYBOARD_SHOWN, false);
739
740         currentTaskbarIds.clear();
741
742         handler = new Handler();
743         thread = new Thread(() -> {
744             updateRecentApps(true);
745
746             if(!isRefreshingRecents) {
747                 isRefreshingRecents = true;
748
749                 while(shouldRefreshRecents) {
750                     updateRecentApps(false);
751
752                     if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen()) {
753                         handler.post(() -> {
754                             if(layout != null) {
755                                 int[] location = new int[2];
756                                 layout.getLocationOnScreen(location);
757
758                                 if(location[1] != 0) {
759                                     if(location[1] > currentTaskbarPosition) {
760                                         currentTaskbarPosition = location[1];
761                                     } else if(location[1] < currentTaskbarPosition) {
762                                         if(currentTaskbarPosition - location[1] == getNavBarSize())
763                                             currentTaskbarPosition = location[1];
764                                         else if(!startThread2) {
765                                             startThread2 = true;
766                                             tempHideTaskbar(true);
767                                         }
768                                     }
769                                 }
770                             }
771                         });
772                     }
773
774                     SystemClock.sleep(refreshInterval);
775                 }
776
777                 isRefreshingRecents = false;
778             }
779         });
780
781         thread.start();
782     }
783
784     @SuppressWarnings("Convert2streamapi")
785     @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
786     private void updateRecentApps(final boolean firstRefresh) {
787         if(isScreenOff()) return;
788
789         updateSystemTray();
790
791         SharedPreferences pref = U.getSharedPreferences(context);
792         final PackageManager pm = context.getPackageManager();
793         final List<AppEntry> entries = new ArrayList<>();
794         List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
795         int maxNumOfEntries = firstRefresh ? 0 : U.getMaxNumOfEntries(context);
796         boolean fullLength = pref.getBoolean(PREF_FULL_LENGTH, true);
797
798         PinnedBlockedApps pba = PinnedBlockedApps.getInstance(context);
799         List<AppEntry> pinnedApps = pba.getPinnedApps();
800         List<AppEntry> blockedApps = pba.getBlockedApps();
801         List<String> applicationIdsToRemove = new ArrayList<>();
802
803         // Filter out anything on the pinned/blocked apps lists
804         int realNumOfPinnedApps = filterRealPinnedApps(context, pinnedApps, entries, applicationIdsToRemove);
805
806         if(blockedApps.size() > 0) {
807             //noinspection SynchronizationOnLocalVariableOrMethodParameter
808             synchronized(blockedApps) {
809                 for(AppEntry entry : blockedApps) {
810                     applicationIdsToRemove.add(entry.getPackageName());
811                 }
812             }
813         }
814
815         // Get list of all recently used apps
816         List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
817         if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
818             if(realNumOfPinnedApps < maxNumOfEntries) {
819                 List<AppEntry> usageStatsList2 = new ArrayList<>();
820                 List<AppEntry> usageStatsList3 = new ArrayList<>();
821                 List<AppEntry> usageStatsList4 = new ArrayList<>();
822                 List<AppEntry> usageStatsList5 = new ArrayList<>();
823                 List<AppEntry> usageStatsList6;
824
825                 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
826                 homeIntent.addCategory(Intent.CATEGORY_HOME);
827                 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
828
829                 // Filter out apps without a launcher intent
830                 // Also filter out the current launcher, and Taskbar itself
831                 for(AppEntry packageInfo : usageStatsList) {
832                     if(hasLauncherIntent(packageInfo.getPackageName())
833                             && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
834                             && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName)
835                             && (!(U.launcherIsDefault(context) && pref.getBoolean(PREF_DESKTOP_MODE, false))
836                             || !packageInfo.getPackageName().equals(pref.getString(PREF_HSL_ID, "null"))))
837                         usageStatsList2.add(packageInfo);
838                 }
839
840                 // Filter out apps that don't fall within our current search interval
841                 for(AppEntry stats : usageStatsList2) {
842                     if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
843                         usageStatsList3.add(stats);
844                 }
845
846                 // Sort apps by either most recently used, or most time used
847                 if(!runningAppsOnly && sortOrder.contains("most_used")) {
848                     Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
849                 } else {
850                     Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
851                 }
852
853                 // Filter out any duplicate entries
854                 List<String> applicationIds = new ArrayList<>();
855                 for(AppEntry stats : usageStatsList3) {
856                     if(!applicationIds.contains(stats.getPackageName())) {
857                         usageStatsList4.add(stats);
858                         applicationIds.add(stats.getPackageName());
859                     }
860                 }
861
862                 // Filter out the currently running foreground app, if requested by the user
863                 filterForegroundApp(context, pref, searchInterval, applicationIdsToRemove);
864
865                 for(AppEntry stats : usageStatsList4) {
866                     if(!applicationIdsToRemove.contains(stats.getPackageName())) {
867                         usageStatsList5.add(stats);
868                     }
869                 }
870
871                 // Truncate list to a maximum length
872                 if(usageStatsList5.size() > maxNumOfEntries)
873                     usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
874                 else
875                     usageStatsList6 = usageStatsList5;
876
877                 // Determine if we need to reverse the order
878                 if(needToReverseOrder(context, sortOrder)) {
879                     Collections.reverse(usageStatsList6);
880                 }
881
882                 // Generate the AppEntries for the recent apps list
883                 int number = usageStatsList6.size() == maxNumOfEntries
884                         ? usageStatsList6.size() - realNumOfPinnedApps
885                         : usageStatsList6.size();
886
887                 generateAppEntries(context, number, usageStatsList6, entries, launcherAppCache);
888             }
889
890             while(entries.size() > maxNumOfEntries) {
891                 try {
892                     entries.remove(entries.size() - 1);
893                     launcherAppCache.remove(launcherAppCache.size() - 1);
894                 } catch (ArrayIndexOutOfBoundsException ignored) {}
895             }
896
897             // Determine if we need to reverse the order again
898             if(TaskbarPosition.isVertical(context)) {
899                 Collections.reverse(entries);
900                 Collections.reverse(launcherAppCache);
901             }
902
903             // Now that we've generated the list of apps,
904             // we need to determine if we need to redraw the Taskbar or not
905             boolean shouldRedrawTaskbar = firstRefresh;
906
907             List<String> finalApplicationIds = new ArrayList<>();
908             for(AppEntry entry : entries) {
909                 finalApplicationIds.add(entry.getPackageName());
910             }
911
912             int realNumOfSysTrayIcons = 0;
913             for(Integer key : sysTrayIconStates.keySet()) {
914                 if(sysTrayIconStates.get(key))
915                     realNumOfSysTrayIcons++;
916             }
917
918             if(finalApplicationIds.size() != currentTaskbarIds.size()
919                     || numOfPinnedApps != realNumOfPinnedApps
920                     || numOfSysTrayIcons != realNumOfSysTrayIcons)
921                 shouldRedrawTaskbar = true;
922             else {
923                 for(int i = 0; i < finalApplicationIds.size(); i++) {
924                     if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
925                         shouldRedrawTaskbar = true;
926                         break;
927                     }
928                 }
929             }
930
931             if(shouldRedrawTaskbar) {
932                 currentTaskbarIds = finalApplicationIds;
933                 numOfPinnedApps = realNumOfPinnedApps;
934                 numOfSysTrayIcons = realNumOfSysTrayIcons;
935
936                 populateAppEntries(context, pm, entries, launcherAppCache);
937
938                 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
939
940                 handler.post(() -> {
941                     if(numOfEntries > 0 || fullLength) {
942                         ViewGroup.LayoutParams params = scrollView.getLayoutParams();
943                         calculateScrollViewParams(context, pref, params, fullLength, numOfEntries);
944                         scrollView.setLayoutParams(params);
945
946                         for(Integer key : sysTrayIconStates.keySet()) {
947                             sysTrayLayout.findViewById(key).setVisibility(
948                                     sysTrayIconStates.get(key) ? View.VISIBLE : View.GONE
949                             );
950                         }
951
952                         taskbar.removeAllViews();
953                         for(int i = 0; i < entries.size(); i++) {
954                             taskbar.addView(getView(entries, i));
955                         }
956
957                         if(runningAppsOnly)
958                             updateRunningAppIndicators(pinnedApps, usageStatsList, entries);
959
960                         isShowingRecents = true;
961                         if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
962                             if(firstRefresh)
963                                 scrollView.setVisibility(View.INVISIBLE);
964                             else
965                                 scrollView.setVisibility(View.VISIBLE);
966                         }
967
968                         if(firstRefresh && scrollView.getVisibility() != View.VISIBLE) {
969                             new Handler().post(
970                                     () -> scrollTaskbar(
971                                             scrollView,
972                                             taskbar,
973                                             TaskbarPosition.getTaskbarPosition(context),
974                                             sortOrder,
975                                             shouldRefreshRecents
976                                     )
977                             );
978                         }
979                     } else {
980                         isShowingRecents = false;
981                         scrollView.setVisibility(View.GONE);
982                     }
983                 });
984             } else if(runningAppsOnly)
985                 handler.post(() -> updateRunningAppIndicators(pinnedApps, usageStatsList, entries));
986         } else if(firstRefresh || currentTaskbarIds.size() > 0) {
987             currentTaskbarIds.clear();
988             handler.post(() -> {
989                 isShowingRecents = false;
990                 scrollView.setVisibility(View.GONE);
991             });
992         }
993     }
994
995     @VisibleForTesting
996     void calculateScrollViewParams(Context context,
997                                    SharedPreferences pref,
998                                    ViewGroup.LayoutParams params,
999                                    boolean fullLength,
1000                                    int numOfEntries) {
1001         DisplayInfo display = U.getDisplayInfo(context, true);
1002         int recentsSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size) * numOfEntries;
1003         float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
1004         int maxScreenSize;
1005
1006         float baseStart = U.getBaseTaskbarSizeStart(context);
1007         float baseEnd = U.getBaseTaskbarSizeEnd(context, sysTrayIconStates);
1008         int baseTotal = Math.round(baseStart + baseEnd);
1009
1010         int diff = Math.round(Math.max(baseStart, baseEnd) - Math.min(baseStart, baseEnd));
1011         boolean startIsBigger = Math.max(baseStart, baseEnd) == baseStart;
1012
1013         if(TaskbarPosition.isVertical(context)) {
1014             maxScreenSize = Math.max(0, display.height
1015                     - U.getStatusBarHeight(context)
1016                     - baseTotal);
1017
1018             params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
1019                     + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1020
1021             if(fullLength) {
1022                 try {
1023                     Space whitespaceStart = layout.findViewById(R.id.whitespace_start);
1024                     Space whitespaceEnd = layout.findViewById(R.id.whitespace_end);
1025                     int height = maxScreenSize - recentsSize;
1026
1027                     if(pref.getBoolean(PREF_CENTERED_ICONS, false)) {
1028                         int startHeight = (height / 2) + (diff / (startIsBigger ? -2 : 2));
1029                         int endHeight = (height / 2) + (diff / (startIsBigger ? 2 : -2));
1030                         
1031                         if(startHeight < 0) {
1032                             startHeight = 0;
1033                             endHeight = height;
1034                         }
1035
1036                         if(endHeight < 0) {
1037                             startHeight = height;
1038                             endHeight = 0;
1039                         }
1040                         
1041                         ViewGroup.LayoutParams startParams = whitespaceStart.getLayoutParams();
1042                         startParams.height = startHeight;
1043                         whitespaceStart.setLayoutParams(startParams);
1044
1045                         ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1046                         endParams.height = endHeight;
1047                         whitespaceEnd.setLayoutParams(endParams);
1048                     } else {
1049                         ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1050                         endParams.height = height;
1051                         whitespaceEnd.setLayoutParams(endParams);
1052                     }
1053                 } catch (NullPointerException ignored) {}
1054             }
1055         } else {
1056             maxScreenSize = Math.max(0, display.width - baseTotal);
1057
1058             params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
1059                     + context.getResources().getDimensionPixelSize(R.dimen.tb_divider_size);
1060
1061             if(fullLength) {
1062                 try {
1063                     Space whitespaceStart = layout.findViewById(R.id.whitespace_start);
1064                     Space whitespaceEnd = layout.findViewById(R.id.whitespace_end);
1065                     int width = maxScreenSize - recentsSize;
1066
1067                     if(pref.getBoolean(PREF_CENTERED_ICONS, false)) {
1068                         int startWidth = (width / 2) + (diff / (startIsBigger ? -2 : 2));
1069                         int endWidth = (width / 2) + (diff / (startIsBigger ? 2 : -2));
1070
1071                         if(startWidth < 0) {
1072                             startWidth = 0;
1073                             endWidth = width;
1074                         }
1075                         
1076                         if(endWidth < 0) {
1077                             startWidth = width;
1078                             endWidth = 0;
1079                         }
1080                         
1081                         ViewGroup.LayoutParams startParams = whitespaceStart.getLayoutParams();
1082                         startParams.width = startWidth;
1083                         whitespaceStart.setLayoutParams(startParams);
1084
1085                         ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1086                         endParams.width = endWidth;
1087                         whitespaceEnd.setLayoutParams(endParams);
1088                     } else {
1089                         ViewGroup.LayoutParams endParams = whitespaceEnd.getLayoutParams();
1090                         endParams.width = width;
1091                         whitespaceEnd.setLayoutParams(endParams);
1092                     }
1093                 } catch (NullPointerException ignored) {}
1094             }
1095         }
1096
1097         boolean realMatchParent = maxRecentsSize >= maxScreenSize
1098                 && pref.getBoolean(PREF_COLLAPSED, false)
1099                 && !(TaskbarPosition.isVertical(context) && U.isChromeOs(context));
1100
1101         if(realMatchParent != matchParent) {
1102             matchParent = realMatchParent;
1103             new Handler().post(updateParamsRunnable);
1104         }
1105     }
1106
1107     @VisibleForTesting
1108     void scrollTaskbar(FrameLayout scrollView,
1109                        LinearLayout taskbar,
1110                        String taskbarPosition,
1111                        String sortOrder,
1112                        boolean shouldRefreshRecents) {
1113         if(TaskbarPosition.isVertical(taskbarPosition)) {
1114             if(sortOrder.contains("false")) {
1115                 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1116             } else if(sortOrder.contains("true")) {
1117                 scrollView.scrollTo(0, 0);
1118             }
1119         } else {
1120             if(sortOrder.contains("false")) {
1121                 scrollView.scrollTo(0, 0);
1122             } else if(sortOrder.contains("true")) {
1123                 scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
1124             }
1125         }
1126
1127         if(shouldRefreshRecents) {
1128             scrollView.setVisibility(View.VISIBLE);
1129         }
1130     }
1131
1132     @TargetApi(Build.VERSION_CODES.M)
1133     @VisibleForTesting
1134     void filterForegroundApp(Context context,
1135                              SharedPreferences pref,
1136                              long searchInterval,
1137                              List<String> applicationIdsToRemove) {
1138         if(pref.getBoolean(PREF_HIDE_FOREGROUND, false)) {
1139             UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1140             UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
1141             UsageEvents.Event eventCache = new UsageEvents.Event();
1142             String currentForegroundApp = null;
1143
1144             while (events.hasNextEvent()) {
1145                 events.getNextEvent(eventCache);
1146
1147                 if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
1148                     if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
1149                             && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
1150                             && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
1151                             && !eventCache.getClassName().equals(HomeActivityDelegate.class.getCanonicalName())
1152                             && !eventCache.getClassName().equals(SecondaryHomeActivity.class.getCanonicalName())
1153                             && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName()))) {
1154                         currentForegroundApp = eventCache.getPackageName();
1155                     }
1156                 }
1157             }
1158
1159             if(!applicationIdsToRemove.contains(currentForegroundApp)) {
1160                 applicationIdsToRemove.add(currentForegroundApp);
1161             }
1162         }
1163     }
1164
1165     @VisibleForTesting
1166     boolean needToReverseOrder(Context context, String sortOrder) {
1167         switch(TaskbarPosition.getTaskbarPosition(context)) {
1168             case POSITION_BOTTOM_RIGHT:
1169             case POSITION_TOP_RIGHT:
1170                 return sortOrder.contains("false");
1171             default:
1172                 return sortOrder.contains("true");
1173         }
1174     }
1175
1176     @VisibleForTesting
1177     int filterRealPinnedApps(Context context,
1178                              List<AppEntry> pinnedApps,
1179                              List<AppEntry> entries,
1180                              List<String> applicationIdsToRemove) {
1181         int realNumOfPinnedApps = 0;
1182         if(pinnedApps.size() > 0) {
1183             //noinspection SynchronizationOnLocalVariableOrMethodParameter
1184             synchronized(pinnedApps) {
1185                 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1186                 LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
1187
1188                 for(AppEntry entry : pinnedApps) {
1189                     boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
1190                             userManager.getUserForSerialNumber(entry.getUserId(context)));
1191
1192                     if(packageEnabled)
1193                         entries.add(entry);
1194                     else
1195                         realNumOfPinnedApps--;
1196
1197                     applicationIdsToRemove.add(entry.getPackageName());
1198                 }
1199
1200                 realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
1201             }
1202         }
1203
1204         return realNumOfPinnedApps;
1205     }
1206
1207     @VisibleForTesting
1208     void generateAppEntries(Context context,
1209                             int number,
1210                             List<AppEntry> usageStatsList6,
1211                             List<AppEntry> entries,
1212                             List<LauncherActivityInfo> launcherAppCache) {
1213         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1214         LauncherApps launcherApps =
1215                 (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
1216
1217         final List<UserHandle> userHandles = userManager.getUserProfiles();
1218
1219         final String googleSearchBoxPackage = "com.google.android.googlequicksearchbox";
1220         final String googleSearchBoxActivity =
1221                 "com.google.android.googlequicksearchbox.SearchActivity";
1222         for(int i = 0; i < number; i++) {
1223             for(UserHandle handle : userHandles) {
1224                 String packageName = usageStatsList6.get(i).getPackageName();
1225                 long lastTimeUsed = usageStatsList6.get(i).getLastTimeUsed();
1226                 List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
1227                 if(!list.isEmpty()) {
1228                     // Google App workaround
1229                     if(!packageName.equals(googleSearchBoxPackage)) {
1230                         launcherAppCache.add(list.get(0));
1231                     } else {
1232                         boolean added = false;
1233                         for(LauncherActivityInfo info : list) {
1234                             if(info.getName().equals(googleSearchBoxActivity)) {
1235                                 launcherAppCache.add(info);
1236                                 added = true;
1237                             }
1238                         }
1239
1240                         if(!added) {
1241                             launcherAppCache.add(list.get(0));
1242                         }
1243                     }
1244
1245                     AppEntry newEntry = new AppEntry(packageName, null, null, null, false);
1246
1247                     newEntry.setUserId(userManager.getSerialNumberForUser(handle));
1248                     newEntry.setLastTimeUsed(lastTimeUsed);
1249                     entries.add(newEntry);
1250
1251                     break;
1252                 }
1253             }
1254         }
1255     }
1256
1257     @SuppressWarnings("SuspiciousListRemoveInLoop")
1258     @VisibleForTesting
1259     void populateAppEntries(Context context,
1260                             PackageManager pm,
1261                             List<AppEntry> entries,
1262                             List<LauncherActivityInfo> launcherAppCache) {
1263         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1264
1265         int launcherAppCachePos = -1;
1266         for(int i = 0; i < entries.size(); i++) {
1267             if(entries.get(i).getComponentName() == null) {
1268                 launcherAppCachePos++;
1269                 LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
1270                 String packageName = entries.get(i).getPackageName();
1271                 long lastTimeUsed = entries.get(i).getLastTimeUsed();
1272
1273                 entries.remove(i);
1274
1275                 AppEntry newEntry = new AppEntry(
1276                         packageName,
1277                         appInfo.getComponentName().flattenToString(),
1278                         appInfo.getLabel().toString(),
1279                         IconCache.getInstance(context).getIcon(context, pm, appInfo),
1280                         false);
1281
1282                 newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
1283                 newEntry.setLastTimeUsed(lastTimeUsed);
1284                 entries.add(i, newEntry);
1285             }
1286         }
1287     }
1288
1289     private void updateRunningAppIndicators(List<AppEntry> pinnedApps, List<AppEntry> usageStatsList, List<AppEntry> entries) {
1290         if(taskbar.getChildCount() != entries.size())
1291             return;
1292
1293         List<String> pinnedPackageList = new ArrayList<>();
1294         List<String> runningPackageList = new ArrayList<>();
1295
1296         for(AppEntry entry : pinnedApps)
1297             pinnedPackageList.add(entry.getPackageName());
1298
1299         for(AppEntry entry : usageStatsList)
1300             runningPackageList.add(entry.getPackageName());
1301
1302         for(int i = 0; i < taskbar.getChildCount(); i++) {
1303             View convertView = taskbar.getChildAt(i);
1304             String packageName = entries.get(i).getPackageName();
1305
1306             ImageView runningAppIndicator = convertView.findViewById(R.id.running_app_indicator);
1307             if(pinnedPackageList.contains(packageName) && !runningPackageList.contains(packageName))
1308                 runningAppIndicator.setVisibility(View.GONE);
1309             else {
1310                 runningAppIndicator.setVisibility(View.VISIBLE);
1311                 runningAppIndicator.setColorFilter(U.getAccentColor(context));
1312             }
1313         }
1314     }
1315
1316     private void toggleTaskbar(boolean userInitiated) {
1317         if(userInitiated && Build.BRAND.equalsIgnoreCase("essential")) {
1318             SharedPreferences pref = U.getSharedPreferences(context);
1319             LauncherHelper helper = LauncherHelper.getInstance();
1320
1321             if(!pref.getBoolean(PREF_GRIP_REJECTION_TOAST_SHOWN, false)
1322                     && !helper.isOnSecondaryHomeScreen(context)) {
1323                 U.showToastLong(context, R.string.tb_essential_phone_grip_rejection);
1324                 pref.edit().putBoolean(PREF_GRIP_REJECTION_TOAST_SHOWN, true).apply();
1325             }
1326         }
1327
1328         if(startButton.getVisibility() == View.GONE)
1329             showTaskbar(true);
1330         else
1331             hideTaskbar(true);
1332     }
1333
1334     private void showTaskbar(boolean clearVariables) {
1335         if(clearVariables) {
1336             taskbarShownTemporarily = false;
1337             taskbarHiddenTemporarily = false;
1338         }
1339
1340         if(startButton.getVisibility() == View.GONE) {
1341             startButton.setVisibility(View.VISIBLE);
1342             space.setVisibility(View.VISIBLE);
1343
1344             if(dashboardEnabled)
1345                 dashboardButton.setVisibility(View.VISIBLE);
1346
1347             if(navbarButtonsEnabled)
1348                 navbarButtons.setVisibility(View.VISIBLE);
1349
1350             if(isShowingRecents && scrollView.getVisibility() == View.GONE)
1351                 scrollView.setVisibility(View.INVISIBLE);
1352
1353             if(sysTrayEnabled)
1354                 sysTrayParentLayout.setVisibility(View.VISIBLE);
1355
1356             shouldRefreshRecents = true;
1357             startRefreshingRecents();
1358
1359             SharedPreferences pref = U.getSharedPreferences(context);
1360             pref.edit().putBoolean(PREF_COLLAPSED, true).apply();
1361
1362             updateButton(false);
1363
1364             new Handler().post(() -> U.sendBroadcast(context, ACTION_SHOW_START_MENU_SPACE));
1365         }
1366     }
1367
1368     private void hideTaskbar(boolean clearVariables) {
1369         if(clearVariables) {
1370             taskbarShownTemporarily = false;
1371             taskbarHiddenTemporarily = false;
1372         }
1373
1374         if(startButton.getVisibility() == View.VISIBLE) {
1375             startButton.setVisibility(View.GONE);
1376             space.setVisibility(View.GONE);
1377
1378             if(dashboardEnabled)
1379                 dashboardButton.setVisibility(View.GONE);
1380
1381             if(navbarButtonsEnabled)
1382                 navbarButtons.setVisibility(View.GONE);
1383
1384             if(isShowingRecents)
1385                 scrollView.setVisibility(View.GONE);
1386
1387             if(sysTrayEnabled)
1388                 sysTrayParentLayout.setVisibility(View.GONE);
1389
1390             shouldRefreshRecents = false;
1391             if(thread != null) thread.interrupt();
1392
1393             SharedPreferences pref = U.getSharedPreferences(context);
1394             pref.edit().putBoolean(PREF_COLLAPSED, false).apply();
1395
1396             updateButton(true);
1397
1398             if(clearVariables) {
1399                 U.sendBroadcast(context, ACTION_HIDE_START_MENU);
1400                 U.sendBroadcast(context, ACTION_HIDE_DASHBOARD);
1401             }
1402
1403             if(matchParent) {
1404                 matchParent = false;
1405                 new Handler().post(updateParamsRunnable);
1406             }
1407
1408             new Handler().post(() -> U.sendBroadcast(context, ACTION_HIDE_START_MENU_SPACE));
1409         }
1410     }
1411
1412     private void tempShowTaskbar() {
1413         if(!taskbarHiddenTemporarily) {
1414             SharedPreferences pref = U.getSharedPreferences(context);
1415             if(!pref.getBoolean(PREF_COLLAPSED, false)) taskbarShownTemporarily = true;
1416         }
1417
1418         showTaskbar(false);
1419
1420         if(taskbarHiddenTemporarily)
1421             taskbarHiddenTemporarily = false;
1422     }
1423
1424     private void tempHideTaskbar(boolean monitorPositionChanges) {
1425         if(!taskbarShownTemporarily) {
1426             SharedPreferences pref = U.getSharedPreferences(context);
1427             if(pref.getBoolean(PREF_COLLAPSED, false)) taskbarHiddenTemporarily = true;
1428         }
1429
1430         hideTaskbar(false);
1431
1432         if(taskbarShownTemporarily)
1433             taskbarShownTemporarily = false;
1434
1435         if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1436             if(thread2 != null) thread2.interrupt();
1437
1438             handler2 = new Handler();
1439             thread2 = new Thread(() -> {
1440                 stopThread2 = false;
1441
1442                 while(!stopThread2) {
1443                     SystemClock.sleep(refreshInterval);
1444
1445                     handler2.post(() -> stopThread2 = checkPositionChange());
1446                 }
1447
1448                 startThread2 = false;
1449             });
1450
1451             thread2.start();
1452         }
1453     }
1454
1455     private boolean checkPositionChange() {
1456         if(!isScreenOff() && layout != null) {
1457             int[] location = new int[2];
1458             layout.getLocationOnScreen(location);
1459
1460             if(location[1] == 0) {
1461                 return true;
1462             } else {
1463                 if(location[1] > currentTaskbarPosition) {
1464                     currentTaskbarPosition = location[1];
1465                     if(taskbarHiddenTemporarily) {
1466                         tempShowTaskbar();
1467                         return true;
1468                     }
1469                 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1470                     tempShowTaskbar();
1471                     return true;
1472                 } else if(location[1] < currentTaskbarPosition
1473                         && currentTaskbarPosition - location[1] == getNavBarSize()) {
1474                     currentTaskbarPosition = location[1];
1475                 }
1476             }
1477         }
1478
1479         return false;
1480     }
1481
1482     private int getNavBarSize() {
1483         Point size = new Point();
1484         Point realSize = new Point();
1485
1486         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1487         Display display = wm.getDefaultDisplay();
1488         display.getSize(size);
1489         display.getRealSize(realSize);
1490
1491         return realSize.y - size.y;
1492     }
1493
1494     @Override
1495     public void onDestroyHost(UIHost host) {
1496         shouldRefreshRecents = false;
1497
1498         if(layout != null)
1499             try {
1500                 host.removeView(layout);
1501             } catch (IllegalArgumentException ignored) {}
1502
1503         SharedPreferences pref = U.getSharedPreferences(context);
1504         if(pref.getBoolean(PREF_SKIP_AUTO_HIDE_NAVBAR, false)) {
1505             pref.edit().remove(PREF_SKIP_AUTO_HIDE_NAVBAR).apply();
1506         } else if(pref.getBoolean(PREF_AUTO_HIDE_NAVBAR, false))
1507             U.showHideNavigationBar(context, true);
1508
1509         U.unregisterReceiver(context, showReceiver);
1510         U.unregisterReceiver(context, hideReceiver);
1511         U.unregisterReceiver(context, tempShowReceiver);
1512         U.unregisterReceiver(context, tempHideReceiver);
1513         U.unregisterReceiver(context, startMenuAppearReceiver);
1514         U.unregisterReceiver(context, startMenuDisappearReceiver);
1515
1516         if(sysTrayEnabled) {
1517             TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1518             manager.listen(listener, PhoneStateListener.LISTEN_NONE);
1519
1520             U.unregisterReceiver(context, notificationCountReceiver);
1521         }
1522
1523         isFirstStart = true;
1524     }
1525
1526     private void openContextMenu() {
1527         SharedPreferences pref = U.getSharedPreferences(context);
1528
1529         Bundle args = new Bundle();
1530         args.putBoolean("dont_show_quit",
1531                 LauncherHelper.getInstance().isOnHomeScreen(context)
1532                         && !pref.getBoolean(PREF_TASKBAR_ACTIVE, false));
1533         args.putBoolean("is_start_button", true);
1534
1535         U.startContextMenuActivity(context, args);
1536     }
1537
1538     private void updateButton(boolean isCollapsed) {
1539         SharedPreferences pref = U.getSharedPreferences(context);
1540         boolean hide = pref.getBoolean(PREF_INVISIBLE_BUTTON, false);
1541
1542         if(button != null) button.setText(context.getString(isCollapsed ? R.string.tb_right_arrow : R.string.tb_left_arrow));
1543         if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1544     }
1545
1546     @TargetApi(Build.VERSION_CODES.M)
1547     @Override
1548     public void onRecreateHost(UIHost host) {
1549         if(layout != null) {
1550             try {
1551                 host.removeView(layout);
1552             } catch (IllegalArgumentException ignored) {}
1553
1554             currentTaskbarPosition = 0;
1555
1556             if(U.canDrawOverlays(context))
1557                 drawTaskbar(host);
1558             else {
1559                 SharedPreferences pref = U.getSharedPreferences(context);
1560                 pref.edit().putBoolean(PREF_TASKBAR_ACTIVE, false).apply();
1561
1562                 host.terminate();
1563             }
1564         }
1565     }
1566
1567     private View getView(List<AppEntry> list, int position) {
1568         View convertView = View.inflate(context, R.layout.tb_icon, null);
1569
1570         final AppEntry entry = list.get(position);
1571         final SharedPreferences pref = U.getSharedPreferences(context);
1572
1573         ImageView imageView = convertView.findViewById(R.id.icon);
1574         ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1575         imageView.setImageDrawable(entry.getIcon(context));
1576         imageView2.setBackgroundColor(U.getAccentColor(context));
1577
1578         String taskbarPosition = TaskbarPosition.getTaskbarPosition(context);
1579         if(pref.getBoolean(PREF_SHORTCUT_ICON, true)) {
1580             boolean shouldShowShortcutIcon;
1581             if(taskbarPosition.contains("vertical"))
1582                 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1583             else
1584                 shouldShowShortcutIcon = position < numOfPinnedApps;
1585
1586             if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1587         }
1588
1589         if(POSITION_BOTTOM_RIGHT.equals(taskbarPosition) || POSITION_TOP_RIGHT.equals(taskbarPosition)) {
1590             imageView.setRotationY(180);
1591             imageView2.setRotationY(180);
1592         }
1593
1594         FrameLayout layout = convertView.findViewById(R.id.entry);
1595         layout.setOnClickListener(view -> U.launchApp(
1596                 context,
1597                 entry,
1598                 null,
1599                 true,
1600                 false,
1601                 view
1602         ));
1603
1604         layout.setOnLongClickListener(view -> {
1605             int[] location = new int[2];
1606             view.getLocationOnScreen(location);
1607             openContextMenu(entry, location);
1608             return true;
1609         });
1610
1611         layout.setOnGenericMotionListener((view, motionEvent) -> {
1612             int action = motionEvent.getAction();
1613
1614             if(action == MotionEvent.ACTION_BUTTON_PRESS
1615                     && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1616                 int[] location = new int[2];
1617                 view.getLocationOnScreen(location);
1618                 openContextMenu(entry, location);
1619             }
1620
1621             if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean(PREF_VISUAL_FEEDBACK, true))
1622                 view.setBackgroundColor(0);
1623
1624             return false;
1625         });
1626
1627         if(pref.getBoolean(PREF_VISUAL_FEEDBACK, true)) {
1628             layout.setOnHoverListener((v, event) -> {
1629                 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1630                     int accentColor = U.getAccentColor(context);
1631                     accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1632                     v.setBackgroundColor(accentColor);
1633                 }
1634
1635                 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1636                     v.setBackgroundColor(0);
1637
1638                 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1639                     v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
1640
1641                 return false;
1642             });
1643
1644             layout.setOnTouchListener((v, event) -> {
1645                 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1646                 return false;
1647             });
1648         }
1649
1650         return convertView;
1651     }
1652
1653     private void openContextMenu(AppEntry entry, int[] location) {
1654         Bundle args = new Bundle();
1655         args.putSerializable("app_entry", entry);
1656         args.putInt("x", location[0]);
1657         args.putInt("y", location[1]);
1658
1659         U.startContextMenuActivity(context, args);
1660     }
1661
1662     private List<AppEntry> getAppEntries() {
1663         SharedPreferences pref = U.getSharedPreferences(context);
1664         if(runningAppsOnly)
1665             return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString(PREF_MAX_NUM_OF_RECENTS, "10")));
1666         else
1667             return getAppEntriesUsingUsageStats();
1668     }
1669
1670     @SuppressWarnings({"deprecation", "JavaReflectionMemberAccess"})
1671     @TargetApi(Build.VERSION_CODES.M)
1672     private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1673         ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1674         List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1675         List<AppEntry> entries = new ArrayList<>();
1676
1677         for(int i = 0; i < usageStatsList.size(); i++) {
1678             ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1679             if(recentTaskInfo.id != -1) {
1680                 String packageName = recentTaskInfo.baseActivity.getPackageName();
1681                 AppEntry newEntry = new AppEntry(
1682                         packageName,
1683                         null,
1684                         null,
1685                         null,
1686                         false
1687                 );
1688
1689                 U.allowReflection();
1690                 try {
1691                     Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1692                     newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1693                 } catch (Exception e) {
1694                     newEntry.setLastTimeUsed(i);
1695                 }
1696
1697                 entries.add(newEntry);
1698             }
1699         }
1700
1701         return entries;
1702     }
1703
1704     @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1705     private List<AppEntry> getAppEntriesUsingUsageStats() {
1706         UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
1707         List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, searchInterval, System.currentTimeMillis());
1708         List<AppEntry> entries = new ArrayList<>();
1709
1710         for(UsageStats usageStats : usageStatsList) {
1711             AppEntry newEntry = new AppEntry(
1712                     usageStats.getPackageName(),
1713                     null,
1714                     null,
1715                     null,
1716                     false
1717             );
1718
1719             newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1720             newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1721             entries.add(newEntry);
1722         }
1723
1724         return entries;
1725     }
1726
1727     private boolean hasLauncherIntent(String packageName) {
1728         Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1729         intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1730         intentToResolve.setPackage(packageName);
1731
1732         List<ResolveInfo> ris = context.getPackageManager().queryIntentActivities(intentToResolve, 0);
1733         return ris != null && ris.size() > 0;
1734     }
1735
1736     private boolean isScreenOff() {
1737         if(U.isChromeOs(context))
1738             return false;
1739
1740         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1741         return !pm.isInteractive();
1742     }
1743
1744     @SuppressLint("SetTextI18n")
1745     private void updateSystemTray() {
1746         if(!sysTrayEnabled) return;
1747
1748         handler.post(() -> {
1749             Map<Integer, Drawable> drawables = new HashMap<>();
1750             drawables.put(R.id.battery, getBatteryDrawable());
1751             drawables.put(R.id.wifi, getWifiDrawable());
1752             drawables.put(R.id.bluetooth, getBluetoothDrawable());
1753             drawables.put(R.id.cellular, getCellularDrawable());
1754
1755             for(Integer key : drawables.keySet()) {
1756                 ImageView view = sysTrayLayout.findViewById(key);
1757                 Drawable drawable = drawables.get(key);
1758
1759                 if(drawable != null) view.setImageDrawable(drawable);
1760                 sysTrayIconStates.put(key, drawable != null);
1761             }
1762
1763             if(notificationCount > 0) {
1764                 int color = ColorUtils.setAlphaComponent(U.getBackgroundTint(context), 255);
1765                 notificationCountText.setTextColor(color);
1766
1767                 Drawable drawable = ContextCompat.getDrawable(context, R.drawable.tb_circle);
1768                 drawable.setTint(U.getAccentColor(context));
1769
1770                 notificationCountCircle.setImageDrawable(drawable);
1771                 notificationCountText.setText(Integer.toString(notificationCount));
1772                 sysTrayIconStates.put(R.id.notification_count, true);
1773             } else
1774                 sysTrayIconStates.put(R.id.notification_count, false);
1775
1776             time.setText(context.getString(R.string.tb_systray_clock,
1777                     DateFormat.getTimeFormat(context).format(new Date()),
1778                     DateFormat.getDateFormat(context).format(new Date())));
1779             time.setTextColor(U.getAccentColor(context));
1780         });
1781     }
1782
1783     @TargetApi(Build.VERSION_CODES.M)
1784     private Drawable getBatteryDrawable() {
1785         BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
1786         int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1787
1788         if(batLevel == Integer.MIN_VALUE)
1789             return null;
1790
1791         IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
1792         Intent batteryStatus = context.registerReceiver(null, ifilter);
1793
1794         int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
1795         boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
1796                 status == BatteryManager.BATTERY_STATUS_FULL;
1797
1798         String batDrawable;
1799         if(batLevel < 10 && !isCharging)
1800             batDrawable = "alert";
1801         else if(batLevel < 25)
1802             batDrawable = "20";
1803         else if(batLevel < 40)
1804             batDrawable = "30";
1805         else if(batLevel < 55)
1806             batDrawable = "50";
1807         else if(batLevel < 70)
1808             batDrawable = "60";
1809         else if(batLevel < 85)
1810             batDrawable = "80";
1811         else if(batLevel < 95)
1812             batDrawable = "90";
1813         else
1814             batDrawable = "full";
1815
1816         String charging;
1817         if(isCharging)
1818             charging = "charging_";
1819         else
1820             charging = "";
1821
1822         String batRes = "tb_battery_" + charging + batDrawable;
1823         int id = getResourceIdFor(batRes);
1824
1825         return getDrawableForSysTray(id);
1826     }
1827
1828     @TargetApi(Build.VERSION_CODES.M)
1829     private Drawable getWifiDrawable() {
1830         ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1831
1832         NetworkInfo ethernet = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
1833         if(ethernet != null && ethernet.isConnected())
1834             return getDrawableForSysTray(R.drawable.tb_settings_ethernet);
1835
1836         NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
1837         if(wifi == null || !wifi.isConnected())
1838             return null;
1839
1840         WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
1841         int numberOfLevels = 5;
1842
1843         WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1844         int level = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), numberOfLevels);
1845
1846         String wifiRes = "tb_signal_wifi_" + level + "_bar";
1847         int id = getResourceIdFor(wifiRes);
1848
1849         return getDrawableForSysTray(id);
1850     }
1851
1852     private Drawable getBluetoothDrawable() {
1853         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1854         if(adapter != null && adapter.isEnabled())
1855             return getDrawableForSysTray(R.drawable.tb_bluetooth);
1856
1857         return null;
1858     }
1859
1860     @TargetApi(Build.VERSION_CODES.M)
1861     private Drawable getCellularDrawable() {
1862         if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0)
1863             return getDrawableForSysTray(R.drawable.tb_airplanemode_active);
1864
1865         if(cellStrength == -1)
1866             return null;
1867
1868         String cellRes = "tb_signal_cellular_" + cellStrength + "_bar";
1869         int id = getResourceIdFor(cellRes);
1870
1871         return getDrawableForSysTray(id);
1872     }
1873
1874     private Drawable getDrawableForSysTray(int id) {
1875         Drawable drawable = null;
1876         try {
1877             drawable = ContextCompat.getDrawable(context, id);
1878         } catch (Resources.NotFoundException ignored) {}
1879
1880         if(drawable == null) return null;
1881
1882         drawable.setTint(U.getAccentColor(context));
1883         return drawable;
1884     }
1885
1886     private int getResourceIdFor(String name) {
1887         String packageName = context.getResources().getResourcePackageName(R.drawable.tb_dummy);
1888         return context.getResources().getIdentifier(name, "drawable", packageName);
1889     }
1890 }