OSDN Git Service

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