OSDN Git Service

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