OSDN Git Service

Taskbar 3.8.2 (release 4)
[android-x86/packages-apps-Taskbar.git] / app / src / main / java / com / farmerbb / taskbar / service / StartMenuService.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.annotation.SuppressLint;
19 import android.annotation.TargetApi;
20 import android.app.SearchManager;
21 import android.app.Service;
22 import android.content.ActivityNotFoundException;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.SharedPreferences;
29 import android.content.pm.LauncherActivityInfo;
30 import android.content.pm.LauncherApps;
31 import android.content.pm.PackageManager;
32 import android.content.res.Configuration;
33 import android.graphics.PixelFormat;
34 import android.graphics.Rect;
35 import android.graphics.drawable.Drawable;
36 import android.net.Uri;
37 import android.os.Build;
38 import android.os.Handler;
39 import android.os.IBinder;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.provider.Settings;
43 import android.support.v4.content.LocalBroadcastManager;
44 import android.support.v7.widget.SearchView;
45 import android.util.DisplayMetrics;
46 import android.util.Patterns;
47 import android.view.ContextThemeWrapper;
48 import android.view.Gravity;
49 import android.view.LayoutInflater;
50 import android.view.MotionEvent;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.view.WindowManager;
54 import android.view.inputmethod.EditorInfo;
55 import android.view.inputmethod.InputMethodManager;
56 import android.webkit.URLUtil;
57 import android.widget.EditText;
58 import android.widget.FrameLayout;
59 import android.widget.GridView;
60 import android.widget.LinearLayout;
61 import android.widget.ListAdapter;
62 import android.widget.TextView;
63
64 import com.farmerbb.taskbar.R;
65 import com.farmerbb.taskbar.activity.ContextMenuActivity;
66 import com.farmerbb.taskbar.activity.dark.ContextMenuActivityDark;
67 import com.farmerbb.taskbar.activity.InvisibleActivity;
68 import com.farmerbb.taskbar.activity.InvisibleActivityAlt;
69 import com.farmerbb.taskbar.adapter.StartMenuAdapter;
70 import com.farmerbb.taskbar.util.AppEntry;
71 import com.farmerbb.taskbar.util.ApplicationType;
72 import com.farmerbb.taskbar.util.Blacklist;
73 import com.farmerbb.taskbar.util.FreeformHackHelper;
74 import com.farmerbb.taskbar.util.IconCache;
75 import com.farmerbb.taskbar.util.LauncherHelper;
76 import com.farmerbb.taskbar.util.MenuHelper;
77 import com.farmerbb.taskbar.util.CompatUtils;
78 import com.farmerbb.taskbar.util.TopApps;
79 import com.farmerbb.taskbar.util.U;
80 import com.farmerbb.taskbar.widget.StartMenuLayout;
81
82 import java.text.Collator;
83 import java.util.ArrayList;
84 import java.util.Collections;
85 import java.util.Comparator;
86 import java.util.List;
87
88 public class StartMenuService extends Service {
89
90     private WindowManager windowManager;
91     private StartMenuLayout layout;
92     private GridView startMenu;
93     private SearchView searchView;
94     private TextView textView;
95     private PackageManager pm;
96
97     private Handler handler;
98     private Thread thread;
99
100     private boolean shouldShowSearchBox = false;
101     private boolean hasSubmittedQuery = false;
102
103     private int layoutId = R.layout.start_menu_left;
104
105     private List<String> currentStartMenuIds = new ArrayList<>();
106
107     private View.OnClickListener ocl = view -> toggleStartMenu();
108     
109     private BroadcastReceiver toggleReceiver = new BroadcastReceiver() {
110         @Override
111         public void onReceive(Context context, Intent intent) {
112             toggleStartMenu();
113         }
114     };
115
116     private BroadcastReceiver showSpaceReceiver = new BroadcastReceiver() {
117         @Override
118         public void onReceive(Context context, Intent intent) {
119             layout.findViewById(R.id.start_menu_space).setVisibility(View.VISIBLE);
120         }
121     };
122
123     private BroadcastReceiver hideSpaceReceiver = new BroadcastReceiver() {
124         @Override
125         public void onReceive(Context context, Intent intent) {
126             layout.findViewById(R.id.start_menu_space).setVisibility(View.GONE);
127         }
128     };
129
130     private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
131         @Override
132         public void onReceive(Context context, Intent intent) {
133             hideStartMenu(true);
134         }
135     };
136
137     private BroadcastReceiver hideReceiverNoReset = new BroadcastReceiver() {
138         @Override
139         public void onReceive(Context context, Intent intent) {
140             hideStartMenu(false);
141         }
142     };
143
144     private BroadcastReceiver resetReceiver = new BroadcastReceiver() {
145         @Override
146         public void onReceive(Context context, Intent intent) {
147             startMenu.setSelection(0);
148         }
149     };
150
151     private Comparator<LauncherActivityInfo> comparator = (ai1, ai2) -> {
152         String label1;
153         String label2;
154
155         try {
156             label1 = ai1.getLabel().toString();
157             label2 = ai2.getLabel().toString();
158         } catch (OutOfMemoryError e) {
159             System.gc();
160
161             label1 = ai1.getApplicationInfo().packageName;
162             label2 = ai2.getApplicationInfo().packageName;
163         }
164
165         return Collator.getInstance().compare(label1, label2);
166     };
167     
168     @Override
169     public IBinder onBind(Intent intent) {
170         return null;
171     }
172
173     @Override
174     public int onStartCommand(Intent intent, int flags, int startId) {
175         return START_STICKY;
176     }
177
178     @TargetApi(Build.VERSION_CODES.M)
179     @Override
180     public void onCreate() {
181         super.onCreate();
182
183         SharedPreferences pref = U.getSharedPreferences(this);
184         if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
185             if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this))
186                 drawStartMenu();
187             else {
188                 pref.edit().putBoolean("taskbar_active", false).apply();
189
190                 stopSelf();
191             }
192         } else stopSelf();
193     }
194
195     @SuppressLint("RtlHardcoded")
196     private void drawStartMenu() {
197         IconCache.getInstance(this).clearCache();
198
199         final SharedPreferences pref = U.getSharedPreferences(this);
200         final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
201
202         switch(pref.getString("show_search_bar", "keyboard")) {
203             case "always":
204                 shouldShowSearchBox = true;
205                 break;
206             case "keyboard":
207                 shouldShowSearchBox = hasHardwareKeyboard;
208                 break;
209             case "never":
210                 shouldShowSearchBox = false;
211                 break;
212         }
213
214         // Initialize layout params
215         windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
216         U.setCachedRotation(windowManager.getDefaultDisplay().getRotation());
217
218         final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
219                 WindowManager.LayoutParams.WRAP_CONTENT,
220                 WindowManager.LayoutParams.WRAP_CONTENT,
221                 CompatUtils.getOverlayType(),
222                 shouldShowSearchBox ? 0 : WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
223                 PixelFormat.TRANSLUCENT);
224
225         // Determine where to show the start menu on screen
226         switch(U.getTaskbarPosition(this)) {
227             case "bottom_left":
228                 layoutId = R.layout.start_menu_left;
229                 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
230                 break;
231             case "bottom_vertical_left":
232                 layoutId = R.layout.start_menu_vertical_left;
233                 params.gravity = Gravity.BOTTOM | Gravity.LEFT;
234                 break;
235             case "bottom_right":
236                 layoutId = R.layout.start_menu_right;
237                 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
238                 break;
239             case "bottom_vertical_right":
240                 layoutId = R.layout.start_menu_vertical_right;
241                 params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
242                 break;
243             case "top_left":
244                 layoutId = R.layout.start_menu_top_left;
245                 params.gravity = Gravity.TOP | Gravity.LEFT;
246                 break;
247             case "top_vertical_left":
248                 layoutId = R.layout.start_menu_vertical_left;
249                 params.gravity = Gravity.TOP | Gravity.LEFT;
250                 break;
251             case "top_right":
252                 layoutId = R.layout.start_menu_top_right;
253                 params.gravity = Gravity.TOP | Gravity.RIGHT;
254                 break;
255             case "top_vertical_right":
256                 layoutId = R.layout.start_menu_vertical_right;
257                 params.gravity = Gravity.TOP | Gravity.RIGHT;
258                 break;
259         }
260
261         // Initialize views
262         int theme = 0;
263
264         switch(pref.getString("theme", "light")) {
265             case "light":
266                 theme = R.style.AppTheme;
267                 break;
268             case "dark":
269                 theme = R.style.AppTheme_Dark;
270                 break;
271         }
272
273         ContextThemeWrapper wrapper = new ContextThemeWrapper(this, theme);
274         layout = (StartMenuLayout) LayoutInflater.from(wrapper).inflate(layoutId, null);
275         startMenu = U.findViewById(layout, R.id.start_menu);
276
277         if((shouldShowSearchBox && !hasHardwareKeyboard) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1)
278             layout.viewHandlesBackButton();
279
280         boolean scrollbar = pref.getBoolean("scrollbar", false);
281         startMenu.setFastScrollEnabled(scrollbar);
282         startMenu.setFastScrollAlwaysVisible(scrollbar);
283         startMenu.setScrollBarStyle(scrollbar ? View.SCROLLBARS_OUTSIDE_INSET : View.SCROLLBARS_INSIDE_OVERLAY);
284
285         if(pref.getBoolean("transparent_start_menu", false))
286             startMenu.setBackgroundColor(0);
287
288         searchView = U.findViewById(layout, R.id.search);
289
290         int backgroundTint = U.getBackgroundTint(this);
291
292         FrameLayout startMenuFrame = U.findViewById(layout, R.id.start_menu_frame);
293         FrameLayout searchViewLayout = U.findViewById(layout, R.id.search_view_layout);
294         startMenuFrame.setBackgroundColor(backgroundTint);
295         searchViewLayout.setBackgroundColor(backgroundTint);
296
297         if(shouldShowSearchBox) {
298             if(!hasHardwareKeyboard) searchView.setIconifiedByDefault(true);
299
300             searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
301                 @Override
302                 public boolean onQueryTextSubmit(String query) {
303                     if(!hasSubmittedQuery) {
304                         ListAdapter adapter = startMenu.getAdapter();
305                         if(adapter != null) {
306                             hasSubmittedQuery = true;
307
308                             if(adapter.getCount() > 0) {
309                                 View view = adapter.getView(0, null, startMenu);
310                                 LinearLayout layout = U.findViewById(view, R.id.entry);
311                                 layout.performClick();
312                             } else {
313                                 if(U.shouldCollapse(StartMenuService.this, true))
314                                     LocalBroadcastManager.getInstance(StartMenuService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_TASKBAR"));
315                                 else
316                                     LocalBroadcastManager.getInstance(StartMenuService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
317
318                                 Intent intent;
319
320                                 if(Patterns.WEB_URL.matcher(query).matches()) {
321                                     intent = new Intent(Intent.ACTION_VIEW);
322                                     intent.setData(Uri.parse(URLUtil.guessUrl(query)));
323                                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
324                                 } else {
325                                     intent = new Intent(Intent.ACTION_WEB_SEARCH);
326                                     intent.putExtra(SearchManager.QUERY, query);
327                                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
328                                 }
329
330                                 if(intent.resolveActivity(getPackageManager()) != null)
331                                     startActivity(intent);
332                                 else {
333                                     Uri uri = new Uri.Builder()
334                                             .scheme("https")
335                                             .authority("www.google.com")
336                                             .path("search")
337                                             .appendQueryParameter("q", query)
338                                             .build();
339
340                                     intent = new Intent(Intent.ACTION_VIEW);
341                                     intent.setData(uri);
342                                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
343
344                                     try {
345                                         startActivity(intent);
346                                     } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
347                                 }
348                             }
349                         }
350                     }
351                     return true;
352                 }
353
354                 @Override
355                 public boolean onQueryTextChange(String newText) {
356                     searchView.setIconified(false);
357                     
358                     View closeButton = searchView.findViewById(R.id.search_close_btn);
359                     if(closeButton != null) closeButton.setVisibility(View.GONE);
360
361                     refreshApps(newText, false);
362
363                     if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
364                         new Handler().postDelayed(() -> {
365                             EditText editText = U.findViewById(searchView, R.id.search_src_text);
366                             if(editText != null) {
367                                 editText.requestFocus();
368                                 editText.setSelection(editText.getText().length());
369                             }
370                         }, 50);
371                     }
372
373                     return true;
374                 }
375             });
376
377             searchView.setOnQueryTextFocusChangeListener((view, b) -> {
378                 if(!hasHardwareKeyboard) {
379                     ViewGroup.LayoutParams params1 = startMenu.getLayoutParams();
380                     params1.height = getResources().getDimensionPixelSize(
381                             b && !isSecondScreenDisablingKeyboard()
382                                     ? R.dimen.start_menu_height_half
383                                     : R.dimen.start_menu_height);
384                     startMenu.setLayoutParams(params1);
385                 }
386
387                 if(!b) {
388                     if(hasHardwareKeyboard && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1)
389                         LocalBroadcastManager.getInstance(StartMenuService.this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
390                     else {
391                         InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
392                         imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
393                     }
394                 }
395             });
396
397             searchView.setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
398
399             LinearLayout powerButton = U.findViewById(layout, R.id.power_button);
400             powerButton.setOnClickListener(view -> {
401                 int[] location = new int[2];
402                 view.getLocationOnScreen(location);
403                 openContextMenu(location);
404             });
405
406             powerButton.setOnGenericMotionListener((view, motionEvent) -> {
407                 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
408                         && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
409                     int[] location = new int[2];
410                     view.getLocationOnScreen(location);
411                     openContextMenu(location);
412                 }
413                 return false;
414             });
415             
416             searchViewLayout.setOnClickListener(view -> searchView.setIconified(false));
417
418             startMenu.setOnItemClickListener((parent, view, position, id) -> {
419                 hideStartMenu(true);
420
421                 AppEntry entry = (AppEntry) parent.getAdapter().getItem(position);
422                 U.launchApp(StartMenuService.this, entry.getPackageName(), entry.getComponentName(), entry.getUserId(StartMenuService.this), null, false, false);
423             });
424
425             if(pref.getBoolean("transparent_start_menu", false))
426                 layout.findViewById(R.id.search_view_child_layout).setBackgroundColor(0);
427         } else
428             searchViewLayout.setVisibility(View.GONE);
429         
430         textView = U.findViewById(layout, R.id.no_apps_found);
431
432         LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
433         
434         lbm.unregisterReceiver(toggleReceiver);
435         lbm.unregisterReceiver(hideReceiver);
436         lbm.unregisterReceiver(hideReceiverNoReset);
437         lbm.unregisterReceiver(showSpaceReceiver);
438         lbm.unregisterReceiver(hideSpaceReceiver);
439         lbm.unregisterReceiver(resetReceiver);
440
441         lbm.registerReceiver(toggleReceiver, new IntentFilter("com.farmerbb.taskbar.TOGGLE_START_MENU"));
442         lbm.registerReceiver(hideReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_START_MENU"));
443         lbm.registerReceiver(hideReceiverNoReset, new IntentFilter("com.farmerbb.taskbar.HIDE_START_MENU_NO_RESET"));
444         lbm.registerReceiver(showSpaceReceiver, new IntentFilter("com.farmerbb.taskbar.SHOW_START_MENU_SPACE"));
445         lbm.registerReceiver(hideSpaceReceiver, new IntentFilter("com.farmerbb.taskbar.HIDE_START_MENU_SPACE"));
446         lbm.registerReceiver(resetReceiver, new IntentFilter("com.farmerbb.taskbar.RESET_START_MENU"));
447
448         handler = new Handler();
449         refreshApps(true);
450
451         windowManager.addView(layout, params);
452     }
453     
454     private void refreshApps(boolean firstDraw) {
455         refreshApps(null, firstDraw);
456     }
457
458     private void refreshApps(final String query, final boolean firstDraw) {
459         if(thread != null) thread.interrupt();
460
461         handler = new Handler();
462         thread = new Thread(() -> {
463             if(pm == null) pm = getPackageManager();
464
465             UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
466             LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
467
468             final List<UserHandle> userHandles = userManager.getUserProfiles();
469             final List<LauncherActivityInfo> unfilteredList = new ArrayList<>();
470
471             for(UserHandle handle : userHandles) {
472                 unfilteredList.addAll(launcherApps.getActivityList(null, handle));
473             }
474
475             final List<LauncherActivityInfo> topAppsList = new ArrayList<>();
476             final List<LauncherActivityInfo> allAppsList = new ArrayList<>();
477             final List<LauncherActivityInfo> list = new ArrayList<>();
478
479             TopApps topApps = TopApps.getInstance(StartMenuService.this);
480             for(LauncherActivityInfo appInfo : unfilteredList) {
481                 if(topApps.isTopApp(appInfo.getComponentName().flattenToString())
482                         || topApps.isTopApp(appInfo.getName()))
483                     topAppsList.add(appInfo);
484             }
485
486             Blacklist blacklist = Blacklist.getInstance(StartMenuService.this);
487             for(LauncherActivityInfo appInfo : unfilteredList) {
488                 if(!(blacklist.isBlocked(appInfo.getComponentName().flattenToString())
489                         || blacklist.isBlocked(appInfo.getName()))
490                         && !(topApps.isTopApp(appInfo.getComponentName().flattenToString())
491                         || topApps.isTopApp(appInfo.getName())))
492                     allAppsList.add(appInfo);
493             }
494
495             Collections.sort(topAppsList, comparator);
496             Collections.sort(allAppsList, comparator);
497
498             list.addAll(topAppsList);
499             list.addAll(allAppsList);
500
501             topAppsList.clear();
502             allAppsList.clear();
503
504             List<LauncherActivityInfo> queryList;
505             if(query == null)
506                 queryList = list;
507             else {
508                 queryList = new ArrayList<>();
509                 for(LauncherActivityInfo appInfo : list) {
510                     if(appInfo.getLabel().toString().toLowerCase().contains(query.toLowerCase()))
511                         queryList.add(appInfo);
512                 }
513             }
514
515             // Now that we've generated the list of apps,
516             // we need to determine if we need to redraw the start menu or not
517             boolean shouldRedrawStartMenu = false;
518             List<String> finalApplicationIds = new ArrayList<>();
519
520             if(query == null && !firstDraw) {
521                 for(LauncherActivityInfo appInfo : queryList) {
522                     finalApplicationIds.add(appInfo.getApplicationInfo().packageName);
523                 }
524
525                 if(finalApplicationIds.size() != currentStartMenuIds.size())
526                     shouldRedrawStartMenu = true;
527                 else {
528                     for(int i = 0; i < finalApplicationIds.size(); i++) {
529                         if(!finalApplicationIds.get(i).equals(currentStartMenuIds.get(i))) {
530                             shouldRedrawStartMenu = true;
531                             break;
532                         }
533                     }
534                 }
535             } else shouldRedrawStartMenu = true;
536
537             if(shouldRedrawStartMenu) {
538                 if(query == null) currentStartMenuIds = finalApplicationIds;
539
540                 Drawable defaultIcon = pm.getDefaultActivityIcon();
541
542                 final List<AppEntry> entries = new ArrayList<>();
543                 for(LauncherActivityInfo appInfo : queryList) {
544
545                     // Attempt to work around frequently reported OutOfMemoryErrors
546                     String label;
547                     Drawable icon;
548
549                     try {
550                         label = appInfo.getLabel().toString();
551                         icon = IconCache.getInstance(StartMenuService.this).getIcon(StartMenuService.this, pm, appInfo);
552                     } catch (OutOfMemoryError e) {
553                         System.gc();
554
555                         label = appInfo.getApplicationInfo().packageName;
556                         icon = defaultIcon;
557                     }
558
559                     AppEntry newEntry = new AppEntry(
560                             appInfo.getApplicationInfo().packageName,
561                             new ComponentName(
562                                     appInfo.getApplicationInfo().packageName,
563                                     appInfo.getName()).flattenToString(),
564                             label,
565                             icon,
566                             false);
567
568                     newEntry.setUserId(userManager.getSerialNumberForUser(appInfo.getUser()));
569                     entries.add(newEntry);
570                 }
571
572                 handler.post(() -> {
573                     String queryText = searchView.getQuery().toString();
574                     if(query == null && queryText.length() == 0
575                             || query != null && query.equals(queryText)) {
576                         StartMenuAdapter adapter;
577                         SharedPreferences pref = U.getSharedPreferences(StartMenuService.this);
578                         if(pref.getString("start_menu_layout", "list").equals("grid")) {
579                             startMenu.setNumColumns(3);
580                             adapter = new StartMenuAdapter(StartMenuService.this, R.layout.row_alt, entries);
581                         } else
582                             adapter = new StartMenuAdapter(StartMenuService.this, R.layout.row, entries);
583
584                         int position = startMenu.getFirstVisiblePosition();
585                         startMenu.setAdapter(adapter);
586                         startMenu.setSelection(position);
587
588                         if(adapter.getCount() > 0)
589                             textView.setText(null);
590                         else if(query != null)
591                             textView.setText(getString(Patterns.WEB_URL.matcher(query).matches() ? R.string.press_enter_alt : R.string.press_enter));
592                         else
593                             textView.setText(getString(R.string.nothing_to_see_here));
594                     }
595                 });
596             }
597         });
598
599         thread.start();
600     }
601     
602     private void toggleStartMenu() {
603         if(layout.getVisibility() == View.GONE)
604             showStartMenu();
605         else
606             hideStartMenu(true);
607     }
608
609     @SuppressWarnings("deprecation")
610     @TargetApi(Build.VERSION_CODES.N)
611     private void showStartMenu() {
612         if(layout.getVisibility() == View.GONE) {
613             layout.setOnClickListener(ocl);
614             layout.setVisibility(View.VISIBLE);
615
616             if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1)
617                 layout.setAlpha(1);
618
619             MenuHelper.getInstance().setStartMenuOpen(true);
620
621             LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.START_MENU_APPEARING"));
622
623             boolean onHomeScreen = LauncherHelper.getInstance().isOnHomeScreen();
624             boolean inFreeformMode = FreeformHackHelper.getInstance().isInFreeformWorkspace();
625
626             if(!U.isChromeOs(this) && (!onHomeScreen || inFreeformMode)) {
627                 Class clazz = inFreeformMode && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
628                         ? InvisibleActivityAlt.class
629                         : InvisibleActivity.class;
630
631                 Intent intent = new Intent(this, clazz);
632                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
633                 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
634
635                 if(inFreeformMode) {
636                     if(clazz.equals(InvisibleActivity.class))
637                         U.launchAppLowerRight(this, intent);
638                     else if(clazz.equals(InvisibleActivityAlt.class))
639                         U.launchAppMaximized(this, intent);
640                 } else
641                     startActivity(intent);
642             }
643
644             if(searchView.getVisibility() == View.VISIBLE) searchView.requestFocus();
645
646             refreshApps(false);
647
648             new Handler().postDelayed(() -> {
649                 if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1)
650                     layout.setAlpha(1);
651
652                 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
653                 imm.hideSoftInputFromWindow(layout.getWindowToken(), 0);
654             }, 100);
655         }
656     }
657
658     private void hideStartMenu(boolean shouldReset) {
659         if(layout.getVisibility() == View.VISIBLE) {
660             layout.setOnClickListener(null);
661             layout.setAlpha(0);
662
663             MenuHelper.getInstance().setStartMenuOpen(false);
664
665             LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
666
667             layout.postDelayed(() -> {
668                 layout.setVisibility(View.GONE);
669                 searchView.setQuery(null, false);
670                 searchView.setIconified(true);
671                 hasSubmittedQuery = false;
672
673                 if(shouldReset) {
674                     startMenu.smoothScrollBy(0, 0);
675                     startMenu.setSelection(0);
676                 }
677
678                 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
679                 imm.hideSoftInputFromWindow(layout.getWindowToken(), 0);
680             }, 100);
681         }
682     }
683     
684     @Override
685     public void onDestroy() {
686         super.onDestroy();
687         if(layout != null)
688             try {
689                 windowManager.removeView(layout);
690             } catch (IllegalArgumentException e) { /* Gracefully fail */ }
691
692         LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
693
694         lbm.unregisterReceiver(toggleReceiver);
695         lbm.unregisterReceiver(hideReceiver);
696         lbm.unregisterReceiver(hideReceiverNoReset);
697         lbm.unregisterReceiver(showSpaceReceiver);
698         lbm.unregisterReceiver(hideSpaceReceiver);
699         lbm.unregisterReceiver(resetReceiver);
700
701         lbm.sendBroadcast(new Intent("com.farmerbb.taskbar.START_MENU_DISAPPEARING"));
702     }
703
704     @TargetApi(Build.VERSION_CODES.M)
705     @Override
706     public void onConfigurationChanged(Configuration newConfig) {
707         super.onConfigurationChanged(newConfig);
708
709         if(layout != null) {
710             try {
711                 windowManager.removeView(layout);
712             } catch (IllegalArgumentException e) { /* Gracefully fail */ }
713
714             if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this))
715                 drawStartMenu();
716             else {
717                 SharedPreferences pref = U.getSharedPreferences(this);
718                 pref.edit().putBoolean("taskbar_active", false).apply();
719
720                 stopSelf();
721             }
722         }
723     }
724
725     @SuppressWarnings("deprecation")
726     private void openContextMenu(final int[] location) {
727         LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_NO_RESET"));
728
729         new Handler().postDelayed(() -> {
730             SharedPreferences pref = U.getSharedPreferences(StartMenuService.this);
731             Intent intent = null;
732
733             switch(pref.getString("theme", "light")) {
734                 case "light":
735                     intent = new Intent(StartMenuService.this, ContextMenuActivity.class);
736                     break;
737                 case "dark":
738                     intent = new Intent(StartMenuService.this, ContextMenuActivityDark.class);
739                     break;
740             }
741
742             if(intent != null) {
743                 intent.putExtra("launched_from_start_menu", true);
744                 intent.putExtra("is_overflow_menu", true);
745                 intent.putExtra("x", location[0]);
746                 intent.putExtra("y", location[1]);
747                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
748             }
749
750             if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && FreeformHackHelper.getInstance().isInFreeformWorkspace()) {
751                 DisplayMetrics metrics = U.getRealDisplayMetrics(this);
752
753                 if(intent != null && Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1)
754                     intent.putExtra("context_menu_fix", true);
755
756                 startActivity(intent, U.getActivityOptions(ApplicationType.CONTEXT_MENU).setLaunchBounds(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)).toBundle());
757             } else
758                 startActivity(intent);
759         }, shouldDelay() ? 100 : 0);
760     }
761
762     private boolean shouldDelay() {
763         SharedPreferences pref = U.getSharedPreferences(this);
764         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
765                 && pref.getBoolean("freeform_hack", false)
766                 && !FreeformHackHelper.getInstance().isFreeformHackActive();
767     }
768
769     private boolean isSecondScreenDisablingKeyboard() {
770         return Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD)
771                 .startsWith("com.farmerbb.secondscreen");
772     }
773 }