OSDN Git Service

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