OSDN Git Service

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