OSDN Git Service

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