OSDN Git Service

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