OSDN Git Service

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