OSDN Git Service

Merge branch 'temp-branch'
[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.os.Build;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.os.SystemClock;
47 import android.os.UserHandle;
48 import android.os.UserManager;
49 import android.provider.Settings;
50 import android.speech.RecognizerIntent;
51 import android.support.v4.content.ContextCompat;
52 import android.support.v4.graphics.ColorUtils;
53 import android.util.DisplayMetrics;
54 import android.view.ContextThemeWrapper;
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.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(TaskbarService.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(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.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         int theme = 0;
273
274         SharedPreferences pref = U.getSharedPreferences(this);
275         switch(pref.getString("theme", "light")) {
276             case "light":
277                 theme = R.style.AppTheme;
278                 break;
279             case "dark":
280                 theme = R.style.AppTheme_Dark;
281                 break;
282         }
283
284         boolean altButtonConfig = pref.getBoolean("alt_button_config", false);
285
286         ContextThemeWrapper wrapper = new ContextThemeWrapper(this, theme);
287         layout = (LinearLayout) LayoutInflater.from(wrapper).inflate(layoutId, null);
288         taskbar = layout.findViewById(R.id.taskbar);
289         scrollView = layout.findViewById(R.id.taskbar_scrollview);
290
291         if(altButtonConfig) {
292             space = layout.findViewById(R.id.space_alt);
293             layout.findViewById(R.id.space).setVisibility(View.GONE);
294         } else {
295             space = layout.findViewById(R.id.space);
296             layout.findViewById(R.id.space_alt).setVisibility(View.GONE);
297         }
298
299         space.setOnClickListener(v -> toggleTaskbar());
300
301         startButton = layout.findViewById(R.id.start_button);
302         int padding;
303
304         if(pref.getBoolean("app_drawer_icon", false)) {
305             startButton.setImageDrawable(ContextCompat.getDrawable(this, R.mipmap.ic_launcher));
306             padding = getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding_alt);
307         } else {
308             startButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.all_apps_button_icon));
309             padding = getResources().getDimensionPixelSize(R.dimen.app_drawer_icon_padding);
310         }
311
312         startButton.setPadding(padding, padding, padding, padding);
313         startButton.setOnClickListener(ocl);
314         startButton.setOnLongClickListener(view -> {
315             openContextMenu();
316             return true;
317         });
318
319         startButton.setOnGenericMotionListener((view, motionEvent) -> {
320             if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
321                     && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
322                 openContextMenu();
323
324             return false;
325         });
326
327         refreshInterval = (int) (Float.parseFloat(pref.getString("refresh_frequency", "2")) * 1000);
328         if(refreshInterval == 0)
329             refreshInterval = 100;
330
331         sortOrder = pref.getString("sort_order", "false");
332         runningAppsOnly = pref.getString("recents_amount", "past_day").equals("running_apps_only");
333
334         switch(pref.getString("recents_amount", "past_day")) {
335             case "past_day":
336                 searchInterval = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
337                 break;
338             case "app_start":
339                 long oneDayAgo = System.currentTimeMillis() - AlarmManager.INTERVAL_DAY;
340                 long appStartTime = pref.getLong("time_of_service_start", System.currentTimeMillis());
341                 long deviceStartTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
342                 long startTime = deviceStartTime > appStartTime ? deviceStartTime : appStartTime;
343
344                 searchInterval = startTime > oneDayAgo ? startTime : oneDayAgo;
345                 break;
346         }
347
348         Intent intent = new Intent("com.farmerbb.taskbar.HIDE_START_MENU");
349         LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(intent);
350
351         if(altButtonConfig) {
352             button = layout.findViewById(R.id.hide_taskbar_button_alt);
353             layout.findViewById(R.id.hide_taskbar_button).setVisibility(View.GONE);
354         } else {
355             button = layout.findViewById(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());
365
366         LinearLayout buttonLayout = layout.findViewById(altButtonConfig
367                 ? R.id.hide_taskbar_button_layout_alt
368                 : R.id.hide_taskbar_button_layout);
369         if(buttonLayout != null) buttonLayout.setOnClickListener(v -> toggleTaskbar());
370
371         LinearLayout buttonLayoutToHide = layout.findViewById(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         int backgroundTint = U.getBackgroundTint(this);
377         int accentColor = U.getAccentColor(this);
378
379         dashboardButton = layout.findViewById(R.id.dashboard_button);
380         navbarButtons = layout.findViewById(R.id.navbar_buttons);
381
382         dashboardEnabled = pref.getBoolean("dashboard", false);
383         if(dashboardEnabled) {
384             layout.findViewById(R.id.square1).setBackgroundColor(accentColor);
385             layout.findViewById(R.id.square2).setBackgroundColor(accentColor);
386             layout.findViewById(R.id.square3).setBackgroundColor(accentColor);
387             layout.findViewById(R.id.square4).setBackgroundColor(accentColor);
388             layout.findViewById(R.id.square5).setBackgroundColor(accentColor);
389             layout.findViewById(R.id.square6).setBackgroundColor(accentColor);
390
391             dashboardButton.setOnClickListener(v -> LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.TOGGLE_DASHBOARD")));
392         } else
393             dashboardButton.setVisibility(View.GONE);
394
395         if(pref.getBoolean("button_back", false)) {
396             navbarButtonsEnabled = true;
397
398             ImageView backButton = layout.findViewById(R.id.button_back);
399             backButton.setVisibility(View.VISIBLE);
400             backButton.setOnClickListener(v -> {
401                 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_BACK);
402                 if(U.shouldCollapse(this, false))
403                     hideTaskbar(true);
404             });
405
406             backButton.setOnLongClickListener(v -> {
407                 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
408                 imm.showInputMethodPicker();
409
410                 if(U.shouldCollapse(this, false))
411                     hideTaskbar(true);
412
413                 return true;
414             });
415
416             backButton.setOnGenericMotionListener((view13, motionEvent) -> {
417                 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
418                         && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
419                     InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
420                     imm.showInputMethodPicker();
421
422                     if(U.shouldCollapse(this, false))
423                         hideTaskbar(true);
424                 }
425                 return true;
426             });
427         }
428
429         if(pref.getBoolean("button_home", false)) {
430             navbarButtonsEnabled = true;
431
432             ImageView homeButton = layout.findViewById(R.id.button_home);
433             homeButton.setVisibility(View.VISIBLE);
434             homeButton.setOnClickListener(v -> {
435                 U.sendAccessibilityAction(this, AccessibilityService.GLOBAL_ACTION_HOME);
436                 if(U.shouldCollapse(this, false))
437                     hideTaskbar(true);
438             });
439
440             homeButton.setOnLongClickListener(v -> {
441                 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
442                 voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
443
444                 try {
445                     startActivity(voiceSearchIntent);
446                 } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
447
448                 if(U.shouldCollapse(this, false))
449                     hideTaskbar(true);
450
451                 return true;
452             });
453
454             homeButton.setOnGenericMotionListener((view13, motionEvent) -> {
455                 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
456                         && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
457                     Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
458                     voiceSearchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
459
460                     try {
461                         startActivity(voiceSearchIntent);
462                     } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
463
464                     if(U.shouldCollapse(this, false))
465                         hideTaskbar(true);
466                 }
467                 return true;
468             });
469         }
470
471         if(pref.getBoolean("button_recents", false)) {
472             navbarButtonsEnabled = true;
473
474             ImageView recentsButton = layout.findViewById(R.id.button_recents);
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();
514
515         if(pref.getBoolean("auto_hide_navbar", false))
516             U.showHideNavigationBar(this, false);
517
518         LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
519         
520         lbm.unregisterReceiver(showReceiver);
521         lbm.unregisterReceiver(hideReceiver);
522         lbm.unregisterReceiver(tempShowReceiver);
523         lbm.unregisterReceiver(tempHideReceiver);
524         lbm.unregisterReceiver(startMenuAppearReceiver);
525         lbm.unregisterReceiver(startMenuDisappearReceiver);
526
527         lbm.registerReceiver(showReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_TASKBAR"));
528         lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_TASKBAR"));
529         lbm.registerReceiver(tempShowReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_SHOW_TASKBAR"));
530         lbm.registerReceiver(tempHideReceiver, new IntentFilter("com.farmerbb.taskbar.TEMP_HIDE_TASKBAR"));
531         lbm.registerReceiver(startMenuAppearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_APPEARING"));
532         lbm.registerReceiver(startMenuDisappearReceiver, new IntentFilter("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
533         
534         startRefreshingRecents();
535
536         windowManager.addView(layout, params);
537
538         isFirstStart = false;
539     }
540
541     private void startRefreshingRecents() {
542         if(thread != null) thread.interrupt();
543         stopThread2 = true;
544
545         SharedPreferences pref = U.getSharedPreferences(this);
546         showHideAutomagically = pref.getBoolean("hide_when_keyboard_shown", false);
547
548         currentTaskbarIds.clear();
549
550         handler = new Handler();
551         thread = new Thread(() -> {
552             updateRecentApps(true);
553
554             if(!isRefreshingRecents) {
555                 isRefreshingRecents = true;
556
557                 while(shouldRefreshRecents) {
558                     SystemClock.sleep(refreshInterval);
559                     updateRecentApps(false);
560
561                     if(showHideAutomagically && !positionIsVertical && !MenuHelper.getInstance().isStartMenuOpen())
562                         handler.post(() -> {
563                             if(layout != null) {
564                                 int[] location = new int[2];
565                                 layout.getLocationOnScreen(location);
566
567                                 if(location[1] != 0) {
568                                     if(location[1] > currentTaskbarPosition) {
569                                         currentTaskbarPosition = location[1];
570                                     } else if(location[1] < currentTaskbarPosition) {
571                                         if(currentTaskbarPosition - location[1] == getNavBarSize())
572                                             currentTaskbarPosition = location[1];
573                                         else if(!startThread2) {
574                                             startThread2 = true;
575                                             tempHideTaskbar(true);
576                                         }
577                                     }
578                                 }
579                             }
580                         });
581                 }
582
583                 isRefreshingRecents = false;
584             }
585         });
586
587         thread.start();
588     }
589
590     @SuppressWarnings("Convert2streamapi")
591     @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
592     private void updateRecentApps(final boolean firstRefresh) {
593         SharedPreferences pref = U.getSharedPreferences(this);
594         final PackageManager pm = getPackageManager();
595         final List<AppEntry> entries = new ArrayList<>();
596         List<LauncherActivityInfo> launcherAppCache = new ArrayList<>();
597         int maxNumOfEntries = U.getMaxNumOfEntries(this);
598         int realNumOfPinnedApps = 0;
599         boolean fullLength = pref.getBoolean("full_length", false);
600
601         PinnedBlockedApps pba = PinnedBlockedApps.getInstance(this);
602         List<AppEntry> pinnedApps = pba.getPinnedApps();
603         List<AppEntry> blockedApps = pba.getBlockedApps();
604         List<String> applicationIdsToRemove = new ArrayList<>();
605
606         // Filter out anything on the pinned/blocked apps lists
607         if(pinnedApps.size() > 0) {
608             UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
609             LauncherApps launcherApps = (LauncherApps) getSystemService(LAUNCHER_APPS_SERVICE);
610
611             for(AppEntry entry : pinnedApps) {
612                 boolean packageEnabled = launcherApps.isPackageEnabled(entry.getPackageName(),
613                         userManager.getUserForSerialNumber(entry.getUserId(this)));
614
615                 if(packageEnabled)
616                     entries.add(entry);
617                 else
618                     realNumOfPinnedApps--;
619
620                 applicationIdsToRemove.add(entry.getPackageName());
621             }
622             
623             realNumOfPinnedApps = realNumOfPinnedApps + pinnedApps.size();
624         }
625
626         if(blockedApps.size() > 0) {
627             for(AppEntry entry : blockedApps) {
628                 applicationIdsToRemove.add(entry.getPackageName());
629             }
630         }
631
632         // Get list of all recently used apps
633         List<AppEntry> usageStatsList = realNumOfPinnedApps < maxNumOfEntries ? getAppEntries() : new ArrayList<>();
634         if(usageStatsList.size() > 0 || realNumOfPinnedApps > 0 || fullLength) {
635             if(realNumOfPinnedApps < maxNumOfEntries) {
636                 List<AppEntry> usageStatsList2 = new ArrayList<>();
637                 List<AppEntry> usageStatsList3 = new ArrayList<>();
638                 List<AppEntry> usageStatsList4 = new ArrayList<>();
639                 List<AppEntry> usageStatsList5 = new ArrayList<>();
640                 List<AppEntry> usageStatsList6;
641
642                 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
643                 homeIntent.addCategory(Intent.CATEGORY_HOME);
644                 ResolveInfo defaultLauncher = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
645
646                 // Filter out apps without a launcher intent
647                 // Also filter out the current launcher, and Taskbar itself
648                 for(AppEntry packageInfo : usageStatsList) {
649                     if(hasLauncherIntent(packageInfo.getPackageName())
650                             && !packageInfo.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
651                             && !packageInfo.getPackageName().equals(defaultLauncher.activityInfo.packageName))
652                         usageStatsList2.add(packageInfo);
653                 }
654
655                 // Filter out apps that don't fall within our current search interval
656                 for(AppEntry stats : usageStatsList2) {
657                     if(stats.getLastTimeUsed() > searchInterval || runningAppsOnly)
658                         usageStatsList3.add(stats);
659                 }
660
661                 // Sort apps by either most recently used, or most time used
662                 if(!runningAppsOnly && sortOrder.contains("most_used")) {
663                     Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getTotalTimeInForeground(), us1.getTotalTimeInForeground()));
664                 } else {
665                     Collections.sort(usageStatsList3, (us1, us2) -> Long.compare(us2.getLastTimeUsed(), us1.getLastTimeUsed()));
666                 }
667
668                 // Filter out any duplicate entries
669                 List<String> applicationIds = new ArrayList<>();
670                 for(AppEntry stats : usageStatsList3) {
671                     if(!applicationIds.contains(stats.getPackageName())) {
672                         usageStatsList4.add(stats);
673                         applicationIds.add(stats.getPackageName());
674                     }
675                 }
676
677                 // Filter out the currently running foreground app, if requested by the user
678                 if(pref.getBoolean("hide_foreground", false)) {
679                     UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
680                     UsageEvents events = mUsageStatsManager.queryEvents(searchInterval, System.currentTimeMillis());
681                     UsageEvents.Event eventCache = new UsageEvents.Event();
682                     String currentForegroundApp = null;
683
684                     while(events.hasNextEvent()) {
685                         events.getNextEvent(eventCache);
686
687                         if(eventCache.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
688                             if(!(eventCache.getPackageName().contains(BuildConfig.BASE_APPLICATION_ID)
689                                     && !eventCache.getClassName().equals(MainActivity.class.getCanonicalName())
690                                     && !eventCache.getClassName().equals(HomeActivity.class.getCanonicalName())
691                                     && !eventCache.getClassName().equals(InvisibleActivityFreeform.class.getCanonicalName())))
692                                 currentForegroundApp = eventCache.getPackageName();
693                         }
694                     }
695
696                     if(!applicationIdsToRemove.contains(currentForegroundApp))
697                         applicationIdsToRemove.add(currentForegroundApp);
698                 }
699
700                 for(AppEntry stats : usageStatsList4) {
701                     if(!applicationIdsToRemove.contains(stats.getPackageName())) {
702                         usageStatsList5.add(stats);
703                     }
704                 }
705
706                 // Truncate list to a maximum length
707                 if(usageStatsList5.size() > maxNumOfEntries)
708                     usageStatsList6 = usageStatsList5.subList(0, maxNumOfEntries);
709                 else
710                     usageStatsList6 = usageStatsList5;
711
712                 // Determine if we need to reverse the order
713                 boolean needToReverseOrder;
714                 switch(U.getTaskbarPosition(this)) {
715                     case "bottom_right":
716                     case "top_right":
717                         needToReverseOrder = sortOrder.contains("false");
718                         break;
719                     default:
720                         needToReverseOrder = sortOrder.contains("true");
721                         break;
722                 }
723
724                 if(needToReverseOrder) {
725                     Collections.reverse(usageStatsList6);
726                 }
727
728                 // Generate the AppEntries for TaskbarAdapter
729                 int number = usageStatsList6.size() == maxNumOfEntries
730                         ? usageStatsList6.size() - realNumOfPinnedApps
731                         : usageStatsList6.size();
732
733                 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
734                 LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
735
736                 final List<UserHandle> userHandles = userManager.getUserProfiles();
737
738                 for(int i = 0; i < number; i++) {
739                     for(UserHandle handle : userHandles) {
740                         String packageName = usageStatsList6.get(i).getPackageName();
741                         List<LauncherActivityInfo> list = launcherApps.getActivityList(packageName, handle);
742                         if(!list.isEmpty()) {
743                             // Google App workaround
744                             if(!packageName.equals("com.google.android.googlequicksearchbox"))
745                                 launcherAppCache.add(list.get(0));
746                             else {
747                                 boolean added = false;
748                                 for(LauncherActivityInfo info : list) {
749                                     if(info.getName().equals("com.google.android.googlequicksearchbox.SearchActivity")) {
750                                         launcherAppCache.add(info);
751                                         added = true;
752                                     }
753                                 }
754
755                                 if(!added) launcherAppCache.add(list.get(0));
756                             }
757
758                             AppEntry newEntry = new AppEntry(
759                                     packageName,
760                                     null,
761                                     null,
762                                     null,
763                                     false
764                             );
765
766                             newEntry.setUserId(userManager.getSerialNumberForUser(handle));
767                             entries.add(newEntry);
768
769                             break;
770                         }
771                     }
772                 }
773             }
774
775             while(entries.size() > maxNumOfEntries) {
776                 try {
777                     entries.remove(entries.size() - 1);
778                     launcherAppCache.remove(launcherAppCache.size() - 1);
779                 } catch (ArrayIndexOutOfBoundsException e) { /* Gracefully fail */ }
780             }
781
782             // Determine if we need to reverse the order again
783             if(U.getTaskbarPosition(this).contains("vertical")) {
784                 Collections.reverse(entries);
785                 Collections.reverse(launcherAppCache);
786             }
787
788             // Now that we've generated the list of apps,
789             // we need to determine if we need to redraw the Taskbar or not
790             boolean shouldRedrawTaskbar = firstRefresh;
791
792             List<String> finalApplicationIds = new ArrayList<>();
793             for(AppEntry entry : entries) {
794                 finalApplicationIds.add(entry.getPackageName());
795             }
796
797             if(finalApplicationIds.size() != currentTaskbarIds.size()
798                     || numOfPinnedApps != realNumOfPinnedApps)
799                 shouldRedrawTaskbar = true;
800             else {
801                 for(int i = 0; i < finalApplicationIds.size(); i++) {
802                     if(!finalApplicationIds.get(i).equals(currentTaskbarIds.get(i))) {
803                         shouldRedrawTaskbar = true;
804                         break;
805                     }
806                 }
807             }
808
809             if(shouldRedrawTaskbar) {
810                 currentTaskbarIds = finalApplicationIds;
811                 numOfPinnedApps = realNumOfPinnedApps;
812
813                 UserManager userManager = (UserManager) getSystemService(USER_SERVICE);
814
815                 int launcherAppCachePos = -1;
816                 for(int i = 0; i < entries.size(); i++) {
817                     if(entries.get(i).getComponentName() == null) {
818                         launcherAppCachePos++;
819                         LauncherActivityInfo appInfo = launcherAppCache.get(launcherAppCachePos);
820                         String packageName = entries.get(i).getPackageName();
821
822                         entries.remove(i);
823
824                         AppEntry newEntry = new AppEntry(
825                                 packageName,
826                                 appInfo.getComponentName().flattenToString(),
827                                 appInfo.getLabel().toString(),
828                                 IconCache.getInstance(TaskbarService.this).getIcon(TaskbarService.this, pm, appInfo),
829                                 false);
830
831                         newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
832                         entries.add(i, newEntry);
833                     }
834                 }
835
836                 final int numOfEntries = Math.min(entries.size(), maxNumOfEntries);
837
838                 handler.post(() -> {
839                     if(numOfEntries > 0 || fullLength) {
840                         ViewGroup.LayoutParams params = scrollView.getLayoutParams();
841                         DisplayMetrics metrics = U.getRealDisplayMetrics(this);
842                         int recentsSize = getResources().getDimensionPixelSize(R.dimen.icon_size) * numOfEntries;
843                         float maxRecentsSize = fullLength ? Float.MAX_VALUE : recentsSize;
844
845                         if(U.getTaskbarPosition(TaskbarService.this).contains("vertical")) {
846                             int maxScreenSize = metrics.heightPixels
847                                     - U.getStatusBarHeight(TaskbarService.this)
848                                     - U.getBaseTaskbarSize(TaskbarService.this);
849
850                             params.height = (int) Math.min(maxRecentsSize, maxScreenSize)
851                                     + getResources().getDimensionPixelSize(R.dimen.divider_size);
852
853                             if(fullLength && U.getTaskbarPosition(this).contains("bottom")) {
854                                 try {
855                                     Space whitespace = layout.findViewById(R.id.whitespace);
856                                     ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
857                                     params2.height = maxScreenSize - recentsSize;
858                                     whitespace.setLayoutParams(params2);
859                                 } catch (NullPointerException e) { /* Gracefully fail */ }
860                             }
861                         } else {
862                             int maxScreenSize = metrics.widthPixels
863                                     - U.getBaseTaskbarSize(TaskbarService.this);
864
865                             params.width = (int) Math.min(maxRecentsSize, maxScreenSize)
866                                     + getResources().getDimensionPixelSize(R.dimen.divider_size);
867
868                             if(fullLength && U.getTaskbarPosition(this).contains("right")) {
869                                 try {
870                                     Space whitespace = layout.findViewById(R.id.whitespace);
871                                     ViewGroup.LayoutParams params2 = whitespace.getLayoutParams();
872                                     params2.width = maxScreenSize - recentsSize;
873                                     whitespace.setLayoutParams(params2);
874                                 } catch (NullPointerException e) { /* Gracefully fail */ }
875                             }
876                         }
877
878                         scrollView.setLayoutParams(params);
879
880                         taskbar.removeAllViews();
881                         for(int i = 0; i < entries.size(); i++) {
882                             taskbar.addView(getView(entries, i));
883                         }
884
885                         isShowingRecents = true;
886                         if(shouldRefreshRecents && scrollView.getVisibility() != View.VISIBLE) {
887                             if(firstRefresh)
888                                 scrollView.setVisibility(View.INVISIBLE);
889                             else
890                                 scrollView.setVisibility(View.VISIBLE);
891                         }
892
893                         if(firstRefresh && scrollView.getVisibility() != View.VISIBLE)
894                             new Handler().post(() -> {
895                                 switch(U.getTaskbarPosition(TaskbarService.this)) {
896                                     case "bottom_left":
897                                     case "bottom_right":
898                                     case "top_left":
899                                     case "top_right":
900                                         if(sortOrder.contains("false"))
901                                             scrollView.scrollTo(0, 0);
902                                         else if(sortOrder.contains("true"))
903                                             scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
904                                         break;
905                                     case "bottom_vertical_left":
906                                     case "bottom_vertical_right":
907                                     case "top_vertical_left":
908                                     case "top_vertical_right":
909                                         if(sortOrder.contains("false"))
910                                             scrollView.scrollTo(taskbar.getWidth(), taskbar.getHeight());
911                                         else if(sortOrder.contains("true"))
912                                             scrollView.scrollTo(0, 0);
913                                         break;
914                                 }
915
916                                 if(shouldRefreshRecents) {
917                                     scrollView.setVisibility(View.VISIBLE);
918                                 }
919                             });
920                     } else {
921                         isShowingRecents = false;
922                         scrollView.setVisibility(View.GONE);
923                     }
924                 });
925             }
926         } else if(firstRefresh || currentTaskbarIds.size() > 0) {
927             currentTaskbarIds.clear();
928             handler.post(() -> {
929                 isShowingRecents = false;
930                 scrollView.setVisibility(View.GONE);
931             });
932         }
933     }
934
935     private void toggleTaskbar() {
936         if(startButton.getVisibility() == View.GONE)
937             showTaskbar(true);
938         else
939             hideTaskbar(true);
940     }
941
942     private void showTaskbar(boolean clearVariables) {
943         if(clearVariables) {
944             taskbarShownTemporarily = false;
945             taskbarHiddenTemporarily = false;
946         }
947
948         if(startButton.getVisibility() == View.GONE) {
949             startButton.setVisibility(View.VISIBLE);
950             space.setVisibility(View.VISIBLE);
951
952             if(dashboardEnabled)
953                 dashboardButton.setVisibility(View.VISIBLE);
954
955             if(navbarButtonsEnabled)
956                 navbarButtons.setVisibility(View.VISIBLE);
957
958             if(isShowingRecents && scrollView.getVisibility() == View.GONE)
959                 scrollView.setVisibility(View.INVISIBLE);
960
961             shouldRefreshRecents = true;
962             startRefreshingRecents();
963
964             SharedPreferences pref = U.getSharedPreferences(this);
965             pref.edit().putBoolean("collapsed", true).apply();
966
967             updateButton(false);
968
969             new Handler().post(() -> LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.SHOW_START_MENU_SPACE")));
970         }
971     }
972
973     private void hideTaskbar(boolean clearVariables) {
974         if(clearVariables) {
975             taskbarShownTemporarily = false;
976             taskbarHiddenTemporarily = false;
977         }
978
979         if(startButton.getVisibility() == View.VISIBLE) {
980             startButton.setVisibility(View.GONE);
981             space.setVisibility(View.GONE);
982
983             if(dashboardEnabled)
984                 dashboardButton.setVisibility(View.GONE);
985
986             if(navbarButtonsEnabled)
987                 navbarButtons.setVisibility(View.GONE);
988
989             if(isShowingRecents) {
990                 scrollView.setVisibility(View.GONE);
991             }
992
993             shouldRefreshRecents = false;
994             if(thread != null) thread.interrupt();
995
996             SharedPreferences pref = U.getSharedPreferences(this);
997             pref.edit().putBoolean("collapsed", false).apply();
998
999             updateButton(true);
1000
1001             LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
1002             LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_DASHBOARD"));
1003
1004             new Handler().post(() -> LocalBroadcastManager.getInstance(TaskbarService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_SPACE")));
1005         }
1006     }
1007
1008     private void tempShowTaskbar() {
1009         if(!taskbarHiddenTemporarily) {
1010             SharedPreferences pref = U.getSharedPreferences(TaskbarService.this);
1011             if(!pref.getBoolean("collapsed", false)) taskbarShownTemporarily = true;
1012         }
1013
1014         showTaskbar(false);
1015
1016         if(taskbarHiddenTemporarily)
1017             taskbarHiddenTemporarily = false;
1018     }
1019
1020     private void tempHideTaskbar(boolean monitorPositionChanges) {
1021         if(!taskbarShownTemporarily) {
1022             SharedPreferences pref = U.getSharedPreferences(TaskbarService.this);
1023             if(pref.getBoolean("collapsed", false)) taskbarHiddenTemporarily = true;
1024         }
1025
1026         hideTaskbar(false);
1027
1028         if(taskbarShownTemporarily)
1029             taskbarShownTemporarily = false;
1030
1031         if(monitorPositionChanges && showHideAutomagically && !positionIsVertical) {
1032             if(thread2 != null) thread2.interrupt();
1033
1034             handler2 = new Handler();
1035             thread2 = new Thread(() -> {
1036                 stopThread2 = false;
1037
1038                 while(!stopThread2) {
1039                     SystemClock.sleep(refreshInterval);
1040
1041                     handler2.post(() -> stopThread2 = checkPositionChange());
1042                 }
1043
1044                 startThread2 = false;
1045             });
1046
1047             thread2.start();
1048         }
1049     }
1050
1051     private boolean checkPositionChange() {
1052         if(layout != null) {
1053             int[] location = new int[2];
1054             layout.getLocationOnScreen(location);
1055
1056             if(location[1] == 0) {
1057                 return true;
1058             } else {
1059                 if(location[1] > currentTaskbarPosition) {
1060                     currentTaskbarPosition = location[1];
1061                     if(taskbarHiddenTemporarily) {
1062                         tempShowTaskbar();
1063                         return true;
1064                     }
1065                 } else if(location[1] == currentTaskbarPosition && taskbarHiddenTemporarily) {
1066                     tempShowTaskbar();
1067                     return true;
1068                 } else if(location[1] < currentTaskbarPosition
1069                         && currentTaskbarPosition - location[1] == getNavBarSize()) {
1070                     currentTaskbarPosition = location[1];
1071                 }
1072             }
1073         }
1074
1075         return false;
1076     }
1077
1078     private int getNavBarSize() {
1079         Point size = new Point();
1080         Point realSize = new Point();
1081
1082         WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
1083         Display display = wm.getDefaultDisplay();
1084         display.getSize(size);
1085         display.getRealSize(realSize);
1086
1087         return realSize.y - size.y;
1088     }
1089
1090     @Override
1091     public void onDestroy() {
1092         shouldRefreshRecents = false;
1093
1094         super.onDestroy();
1095         if(layout != null)
1096             try {
1097                 windowManager.removeView(layout);
1098             } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1099
1100         SharedPreferences pref = U.getSharedPreferences(this);
1101         if(pref.getBoolean("skip_auto_hide_navbar", false)) {
1102             pref.edit().remove("skip_auto_hide_navbar").apply();
1103         } else if(pref.getBoolean("auto_hide_navbar", false))
1104             U.showHideNavigationBar(this, true);
1105
1106         LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
1107
1108         lbm.unregisterReceiver(showReceiver);
1109         lbm.unregisterReceiver(hideReceiver);
1110         lbm.unregisterReceiver(tempShowReceiver);
1111         lbm.unregisterReceiver(tempHideReceiver);
1112         lbm.unregisterReceiver(startMenuAppearReceiver);
1113         lbm.unregisterReceiver(startMenuDisappearReceiver);
1114
1115         isFirstStart = true;
1116     }
1117
1118     @SuppressWarnings("deprecation")
1119     private void openContextMenu() {
1120         SharedPreferences pref = U.getSharedPreferences(this);
1121         Intent intent = null;
1122
1123         switch(pref.getString("theme", "light")) {
1124             case "light":
1125                 intent = new Intent(this, ContextMenuActivity.class);
1126                 break;
1127             case "dark":
1128                 intent = new Intent(this, ContextMenuActivityDark.class);
1129                 break;
1130         }
1131
1132         if(intent != null) {
1133             intent.putExtra("dont_show_quit", LauncherHelper.getInstance().isOnHomeScreen() && !pref.getBoolean("taskbar_active", false));
1134             intent.putExtra("is_start_button", true);
1135             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1136         }
1137
1138         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1139             DisplayMetrics metrics = U.getRealDisplayMetrics(this);
1140
1141             if(intent != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
1142                 intent.putExtra("context_menu_fix", true);
1143
1144             startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)).toBundle());
1145         } else
1146             startActivity(intent);
1147     }
1148
1149     private void updateButton(boolean isCollapsed) {
1150         SharedPreferences pref = U.getSharedPreferences(this);
1151         boolean hide = pref.getBoolean("invisible_button", false);
1152
1153         if(button != null) button.setText(getString(isCollapsed ? R.string.right_arrow : R.string.left_arrow));
1154         if(layout != null) layout.setAlpha(isCollapsed && hide ? 0 : 1);
1155     }
1156
1157     @TargetApi(Build.VERSION_CODES.M)
1158     @Override
1159     public void onConfigurationChanged(Configuration newConfig) {
1160         super.onConfigurationChanged(newConfig);
1161
1162         if(layout != null) {
1163             try {
1164                 windowManager.removeView(layout);
1165             } catch (IllegalArgumentException e) { /* Gracefully fail */ }
1166
1167             currentTaskbarPosition = 0;
1168
1169             if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this))
1170                 drawTaskbar();
1171             else {
1172                 SharedPreferences pref = U.getSharedPreferences(this);
1173                 pref.edit().putBoolean("taskbar_active", false).apply();
1174
1175                 stopSelf();
1176             }
1177         }
1178     }
1179
1180     private View getView(List<AppEntry> list, int position) {
1181         View convertView = View.inflate(this, R.layout.icon, null);
1182
1183         final AppEntry entry = list.get(position);
1184         final SharedPreferences pref = U.getSharedPreferences(this);
1185
1186         ImageView imageView = convertView.findViewById(R.id.icon);
1187         ImageView imageView2 = convertView.findViewById(R.id.shortcut_icon);
1188         imageView.setImageDrawable(entry.getIcon(this));
1189         imageView2.setBackgroundColor(pref.getInt("accent_color", getResources().getInteger(R.integer.translucent_white)));
1190
1191         String taskbarPosition = U.getTaskbarPosition(this);
1192         if(pref.getBoolean("shortcut_icon", true)) {
1193             boolean shouldShowShortcutIcon;
1194             if(taskbarPosition.contains("vertical"))
1195                 shouldShowShortcutIcon = position >= list.size() - numOfPinnedApps;
1196             else
1197                 shouldShowShortcutIcon = position < numOfPinnedApps;
1198
1199             if(shouldShowShortcutIcon) imageView2.setVisibility(View.VISIBLE);
1200         }
1201
1202         if(taskbarPosition.equals("bottom_right") || taskbarPosition.equals("top_right")) {
1203             imageView.setRotationY(180);
1204             imageView2.setRotationY(180);
1205         }
1206
1207         FrameLayout layout = convertView.findViewById(R.id.entry);
1208         layout.setOnClickListener(view -> U.launchApp(TaskbarService.this, entry.getPackageName(), entry.getComponentName(), entry.getUserId(TaskbarService.this), null, true, false));
1209
1210         layout.setOnLongClickListener(view -> {
1211             int[] location = new int[2];
1212             view.getLocationOnScreen(location);
1213             openContextMenu(entry, location);
1214             return true;
1215         });
1216
1217         layout.setOnGenericMotionListener((view, motionEvent) -> {
1218             int action = motionEvent.getAction();
1219
1220             if(action == MotionEvent.ACTION_BUTTON_PRESS
1221                     && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1222                 int[] location = new int[2];
1223                 view.getLocationOnScreen(location);
1224                 openContextMenu(entry, location);
1225             }
1226
1227             if(action == MotionEvent.ACTION_SCROLL && pref.getBoolean("visual_feedback", true))
1228                 view.setBackgroundColor(0);
1229
1230             return false;
1231         });
1232
1233         if(pref.getBoolean("visual_feedback", true)) {
1234             layout.setOnHoverListener((v, event) -> {
1235                 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
1236                     int accentColor = U.getAccentColor(TaskbarService.this);
1237                     accentColor = ColorUtils.setAlphaComponent(accentColor, Color.alpha(accentColor) / 2);
1238                     v.setBackgroundColor(accentColor);
1239                 }
1240
1241                 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
1242                     v.setBackgroundColor(0);
1243
1244                 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1245                     v.setPointerIcon(PointerIcon.getSystemIcon(TaskbarService.this, PointerIcon.TYPE_DEFAULT));
1246
1247                 return false;
1248             });
1249
1250             layout.setOnTouchListener((v, event) -> {
1251                 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
1252                 return false;
1253             });
1254         }
1255
1256         return convertView;
1257     }
1258
1259     @SuppressWarnings("deprecation")
1260     private void openContextMenu(AppEntry entry, int[] location) {
1261         SharedPreferences pref = U.getSharedPreferences(this);
1262         Intent intent = null;
1263
1264         switch(pref.getString("theme", "light")) {
1265             case "light":
1266                 intent = new Intent(this, ContextMenuActivity.class);
1267                 break;
1268             case "dark":
1269                 intent = new Intent(this, ContextMenuActivityDark.class);
1270                 break;
1271         }
1272
1273         if(intent != null) {
1274             intent.putExtra("package_name", entry.getPackageName());
1275             intent.putExtra("app_name", entry.getLabel());
1276             intent.putExtra("component_name", entry.getComponentName());
1277             intent.putExtra("user_id", entry.getUserId(this));
1278             intent.putExtra("x", location[0]);
1279             intent.putExtra("y", location[1]);
1280             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1281         }
1282
1283         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
1284             DisplayMetrics metrics = U.getRealDisplayMetrics(this);
1285
1286             if(intent != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
1287                 intent.putExtra("context_menu_fix", true);
1288
1289             startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)).toBundle());
1290         } else
1291             startActivity(intent);
1292     }
1293
1294     private List<AppEntry> getAppEntries() {
1295         SharedPreferences pref = U.getSharedPreferences(this);
1296         if(runningAppsOnly)
1297             return getAppEntriesUsingActivityManager(Integer.parseInt(pref.getString("max_num_of_recents", "10")));
1298         else
1299             return getAppEntriesUsingUsageStats();
1300     }
1301
1302     @SuppressWarnings("deprecation")
1303     @TargetApi(Build.VERSION_CODES.M)
1304     private List<AppEntry> getAppEntriesUsingActivityManager(int maxNum) {
1305         ActivityManager mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1306         List<ActivityManager.RecentTaskInfo> usageStatsList = mActivityManager.getRecentTasks(maxNum, 0);
1307         List<AppEntry> entries = new ArrayList<>();
1308
1309         for(int i = 0; i < usageStatsList.size(); i++) {
1310             ActivityManager.RecentTaskInfo recentTaskInfo = usageStatsList.get(i);
1311             if(recentTaskInfo.id != -1) {
1312                 String packageName = recentTaskInfo.baseActivity.getPackageName();
1313                 AppEntry newEntry = new AppEntry(
1314                         packageName,
1315                         null,
1316                         null,
1317                         null,
1318                         false
1319                 );
1320
1321                 try {
1322                     Field field = ActivityManager.RecentTaskInfo.class.getField("firstActiveTime");
1323                     newEntry.setLastTimeUsed(field.getLong(recentTaskInfo));
1324                 } catch (Exception e) {
1325                     newEntry.setLastTimeUsed(i);
1326                 }
1327
1328                 entries.add(newEntry);
1329             }
1330         }
1331
1332         return entries;
1333     }
1334
1335     @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
1336     private List<AppEntry> getAppEntriesUsingUsageStats() {
1337         UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
1338         List<UsageStats> usageStatsList = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, searchInterval, System.currentTimeMillis());
1339         List<AppEntry> entries = new ArrayList<>();
1340
1341         for(UsageStats usageStats : usageStatsList) {
1342             AppEntry newEntry = new AppEntry(
1343                     usageStats.getPackageName(),
1344                     null,
1345                     null,
1346                     null,
1347                     false
1348             );
1349
1350             newEntry.setTotalTimeInForeground(usageStats.getTotalTimeInForeground());
1351             newEntry.setLastTimeUsed(usageStats.getLastTimeUsed());
1352             entries.add(newEntry);
1353         }
1354
1355         return entries;
1356     }
1357
1358     private boolean hasLauncherIntent(String packageName) {
1359         Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
1360         intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
1361         intentToResolve.setPackage(packageName);
1362
1363         List<ResolveInfo> ris = getPackageManager().queryIntentActivities(intentToResolve, 0);
1364         return ris != null && ris.size() > 0;
1365     }
1366 }