OSDN Git Service

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