OSDN Git Service

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