1 /* Based on code by Leonardo Fischer
2 * See https://github.com/lgfischer/WidgetHostExample
4 * Copyright 2016 Braden Farmer
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package com.farmerbb.taskbar.ui;
21 import android.animation.Animator;
22 import android.animation.AnimatorListenerAdapter;
23 import android.annotation.SuppressLint;
24 import android.annotation.TargetApi;
25 import android.appwidget.AppWidgetHost;
26 import android.appwidget.AppWidgetHostView;
27 import android.appwidget.AppWidgetManager;
28 import android.appwidget.AppWidgetProviderInfo;
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.SharedPreferences;
35 import android.content.pm.PackageManager;
36 import android.content.res.Configuration;
37 import android.graphics.Color;
38 import android.graphics.ColorMatrix;
39 import android.graphics.ColorMatrixColorFilter;
40 import android.graphics.drawable.Drawable;
41 import android.os.Build;
42 import android.os.Bundle;
43 import android.os.Handler;
44 import android.os.Process;
45 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
46 import androidx.core.graphics.ColorUtils;
47 import android.util.SparseArray;
48 import android.view.MotionEvent;
49 import android.view.PointerIcon;
50 import android.view.View;
51 import android.view.ViewGroup;
52 import android.view.WindowManager;
53 import android.widget.FrameLayout;
54 import android.widget.ImageView;
55 import android.widget.LinearLayout;
56 import android.widget.TextView;
57 import android.widget.Toast;
59 import com.farmerbb.taskbar.R;
60 import com.farmerbb.taskbar.activity.DashboardActivity;
61 import com.farmerbb.taskbar.activity.dark.DashboardActivityDark;
62 import com.farmerbb.taskbar.content.TaskbarIntent;
63 import com.farmerbb.taskbar.util.DashboardHelper;
64 import com.farmerbb.taskbar.widget.DashboardCell;
65 import com.farmerbb.taskbar.util.FreeformHackHelper;
66 import com.farmerbb.taskbar.util.LauncherHelper;
67 import com.farmerbb.taskbar.util.U;
69 import java.util.List;
71 public class DashboardController implements UIController {
73 private AppWidgetManager mAppWidgetManager;
74 private AppWidgetHost mAppWidgetHost;
76 private Context context;
77 private LinearLayout layout;
79 private SparseArray<DashboardCell> cells = new SparseArray<>();
80 private SparseArray<AppWidgetHostView> widgets = new SparseArray<>();
82 private final int APPWIDGET_HOST_ID = 123;
87 private int previouslySelectedCell = -1;
89 private View.OnClickListener ocl = view -> toggleDashboard();
91 private View.OnClickListener cellOcl = view -> cellClick(view, true);
93 private View.OnHoverListener cellOhl = (v, event) -> {
96 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
97 v.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_DEFAULT));
102 private View.OnLongClickListener olcl = v -> {
107 private View.OnGenericMotionListener ogml = (view, motionEvent) -> {
108 if(motionEvent.getAction() == MotionEvent.ACTION_BUTTON_PRESS
109 && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY)
115 private DashboardCell.OnInterceptedLongPressListener listener = this::cellLongClick;
117 private BroadcastReceiver toggleReceiver = new BroadcastReceiver() {
119 public void onReceive(Context context, Intent intent) {
124 private BroadcastReceiver addWidgetReceiver = new BroadcastReceiver() {
126 public void onReceive(Context context, Intent intent) {
129 if(intent.hasExtra("appWidgetId") && intent.hasExtra("cellId")) {
130 int appWidgetId = intent.getExtras().getInt("appWidgetId", -1);
131 int cellId = intent.getExtras().getInt("cellId", -1);
133 addWidget(appWidgetId, cellId, true);
138 private BroadcastReceiver removeWidgetReceiver = new BroadcastReceiver() {
140 public void onReceive(Context context, Intent intent) {
143 if(intent.hasExtra("cellId")) {
144 int cellId = intent.getExtras().getInt("cellId", -1);
146 removeWidget(cellId, false);
151 private BroadcastReceiver hideReceiver = new BroadcastReceiver() {
153 public void onReceive(Context context, Intent intent) {
158 public DashboardController(Context context) {
159 this.context = context;
162 @TargetApi(Build.VERSION_CODES.M)
164 public void onCreateHost(UIHost host) {
165 SharedPreferences pref = U.getSharedPreferences(context);
166 if(pref.getBoolean("dashboard", context.getResources().getBoolean(R.bool.tb_def_dashboard))) {
167 if(pref.getBoolean("taskbar_active", false) || LauncherHelper.getInstance().isOnHomeScreen()) {
168 if(U.canDrawOverlays(context))
171 pref.edit().putBoolean("taskbar_active", false).apply();
175 } else host.terminate();
176 } else host.terminate();
179 @SuppressLint("RtlHardcoded")
180 private void drawDashboard(UIHost host) {
181 final ViewParams params = new ViewParams(
182 WindowManager.LayoutParams.MATCH_PARENT,
183 WindowManager.LayoutParams.MATCH_PARENT,
185 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
189 layout = new LinearLayout(context);
190 layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
191 layout.setVisibility(View.GONE);
194 SharedPreferences pref = U.getSharedPreferences(context);
195 int width = pref.getInt("dashboard_width", context.getApplicationContext().getResources().getInteger(R.integer.tb_dashboard_width));
196 int height = pref.getInt("dashboard_height", context.getApplicationContext().getResources().getInteger(R.integer.tb_dashboard_height));
198 boolean isPortrait = context.getApplicationContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
199 boolean isLandscape = context.getApplicationContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
211 maxSize = columns * rows;
213 int backgroundTint = U.getBackgroundTint(context);
214 int accentColor = U.getAccentColor(context);
215 int accentColorAlt = accentColor;
216 accentColorAlt = ColorUtils.setAlphaComponent(accentColorAlt, Color.alpha(accentColorAlt) / 3);
220 for(int i = 0; i < columns; i++) {
221 LinearLayout layout2 = new LinearLayout(context);
222 layout2.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1));
223 layout2.setOrientation(LinearLayout.VERTICAL);
225 for(int j = 0; j < rows; j++) {
226 DashboardCell cellLayout = (DashboardCell) View.inflate(context, R.layout.tb_dashboard, null);
227 cellLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1));
228 cellLayout.setBackgroundColor(backgroundTint);
229 cellLayout.setOnClickListener(cellOcl);
230 cellLayout.setOnHoverListener(cellOhl);
232 TextView empty = cellLayout.findViewById(R.id.empty);
233 empty.setBackgroundColor(accentColorAlt);
234 empty.setTextColor(accentColor);
236 Bundle bundle = new Bundle();
237 bundle.putInt("cellId", cellCount);
239 cellLayout.setTag(bundle);
240 cells.put(cellCount, cellLayout);
243 layout2.addView(cellLayout);
246 layout.addView(layout2);
249 mAppWidgetManager = AppWidgetManager.getInstance(context);
250 mAppWidgetHost = new AppWidgetHost(context, APPWIDGET_HOST_ID);
251 mAppWidgetHost.startListening();
253 for(int i = 0; i < maxSize; i++) {
254 int appWidgetId = pref.getInt("dashboard_widget_" + i, -1);
255 if(appWidgetId != -1)
256 addWidget(appWidgetId, i, false);
257 else if(pref.getBoolean("dashboard_widget_" + i + "_placeholder", false))
261 mAppWidgetHost.stopListening();
263 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
265 lbm.unregisterReceiver(toggleReceiver);
266 lbm.unregisterReceiver(addWidgetReceiver);
267 lbm.unregisterReceiver(removeWidgetReceiver);
268 lbm.unregisterReceiver(hideReceiver);
270 lbm.registerReceiver(
272 new IntentFilter(TaskbarIntent.ACTION_TOGGLE_DASHBOARD)
274 lbm.registerReceiver(
276 new IntentFilter(TaskbarIntent.ACTION_ADD_WIDGET_COMPLETED)
278 lbm.registerReceiver(
279 removeWidgetReceiver,
280 new IntentFilter(TaskbarIntent.ACTION_REMOVE_WIDGET_COMPLETED)
282 lbm.registerReceiver(
284 new IntentFilter(TaskbarIntent.ACTION_HIDE_DASHBOARD)
287 host.addView(layout, params);
289 new Handler().postDelayed(() -> {
290 int paddingSize = context.getResources().getDimensionPixelSize(R.dimen.tb_icon_size);
292 switch(U.getTaskbarPosition(context)) {
293 case "top_vertical_left":
294 case "bottom_vertical_left":
295 layout.setPadding(paddingSize, 0, 0, 0);
299 layout.setPadding(0, paddingSize, 0, 0);
301 case "top_vertical_right":
302 case "bottom_vertical_right":
303 layout.setPadding(0, 0, paddingSize, 0);
307 layout.setPadding(0, 0, 0, paddingSize);
313 private void toggleDashboard() {
314 if(layout.getVisibility() == View.GONE)
320 @TargetApi(Build.VERSION_CODES.N)
321 private void showDashboard() {
322 if(layout.getVisibility() == View.GONE) {
323 layout.setOnClickListener(ocl);
326 LocalBroadcastManager
327 .getInstance(context)
328 .sendBroadcast(new Intent(TaskbarIntent.ACTION_DASHBOARD_APPEARING));
329 LocalBroadcastManager
330 .getInstance(context)
331 .sendBroadcast(new Intent(TaskbarIntent.ACTION_HIDE_START_MENU));
333 boolean inFreeformMode = FreeformHackHelper.getInstance().isInFreeformWorkspace();
335 final SharedPreferences pref = U.getSharedPreferences(context);
336 Intent intent = null;
338 switch(pref.getString("theme", "light")) {
340 intent = new Intent(context, DashboardActivity.class);
343 intent = new Intent(context, DashboardActivityDark.class);
348 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
349 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
353 if(intent != null && U.hasBrokenSetLaunchBoundsApi())
354 intent.putExtra("context_menu_fix", true);
356 U.startActivityMaximized(context, intent);
358 context.startActivity(intent);
360 for(int i = 0; i < maxSize; i++) {
361 final DashboardCell cellLayout = cells.get(i);
362 final AppWidgetHostView hostView = widgets.get(i);
364 if(hostView != null) {
366 context.getPackageManager().getApplicationInfo(hostView.getAppWidgetInfo().provider.getPackageName(), 0);
367 hostView.post(() -> {
368 ViewGroup.LayoutParams params = hostView.getLayoutParams();
369 params.width = cellLayout.getWidth();
370 params.height = cellLayout.getHeight();
371 hostView.setLayoutParams(params);
372 hostView.updateAppWidgetSize(null, cellLayout.getWidth(), cellLayout.getHeight(), cellLayout.getWidth(), cellLayout.getHeight());
374 } catch (PackageManager.NameNotFoundException e) {
375 removeWidget(i, false);
376 } catch (NullPointerException e) {
377 removeWidget(i, true);
382 if(!pref.getBoolean("dashboard_tutorial_shown", false)) {
383 U.showToastLong(context, R.string.tb_dashboard_tutorial);
384 pref.edit().putBoolean("dashboard_tutorial_shown", true).apply();
389 private void hideDashboard() {
390 if(layout.getVisibility() == View.VISIBLE) {
391 layout.setOnClickListener(null);
394 for(int i = 0; i < maxSize; i++) {
395 FrameLayout frameLayout = cells.get(i);
396 frameLayout.findViewById(R.id.empty).setVisibility(View.GONE);
399 previouslySelectedCell = -1;
403 private void fadeIn() {
404 mAppWidgetHost.startListening();
406 DashboardHelper.getInstance().setDashboardOpen(true);
408 layout.setVisibility(View.VISIBLE);
411 .setDuration(context.getResources().getInteger(android.R.integer.config_shortAnimTime))
415 private void fadeOut(final boolean sendIntent) {
416 mAppWidgetHost.stopListening();
418 DashboardHelper.getInstance().setDashboardOpen(false);
422 .setDuration(context.getResources().getInteger(android.R.integer.config_shortAnimTime))
423 .setListener(new AnimatorListenerAdapter() {
425 public void onAnimationEnd(Animator animation) {
426 layout.setVisibility(View.GONE);
428 LocalBroadcastManager
429 .getInstance(context)
431 new Intent(TaskbarIntent.ACTION_DASHBOARD_DISAPPEARING)
438 @TargetApi(Build.VERSION_CODES.M)
440 public void onRecreateHost(UIHost host) {
443 host.removeView(layout);
444 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
446 SharedPreferences pref = U.getSharedPreferences(context);
447 if(U.canDrawOverlays(context))
450 pref.edit().putBoolean("taskbar_active", false).apply();
458 public void onDestroyHost(UIHost host) {
461 host.removeView(layout);
462 } catch (IllegalArgumentException e) { /* Gracefully fail */ }
464 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
466 lbm.unregisterReceiver(toggleReceiver);
467 lbm.unregisterReceiver(addWidgetReceiver);
468 lbm.unregisterReceiver(removeWidgetReceiver);
469 lbm.unregisterReceiver(hideReceiver);
471 lbm.sendBroadcast(new Intent(TaskbarIntent.ACTION_DASHBOARD_DISAPPEARING));
473 SharedPreferences pref = U.getSharedPreferences(context);
474 pref.edit().remove("dont_stop_dashboard").apply();
477 private void cellClick(View view, boolean isActualClick) {
478 Bundle bundle = (Bundle) view.getTag();
479 int cellId = bundle.getInt("cellId");
480 int appWidgetId = bundle.getInt("appWidgetId", -1);
482 int currentlySelectedCell = appWidgetId == -1 ? cellId : -1;
484 SharedPreferences pref = U.getSharedPreferences(context);
485 boolean shouldShowPlaceholder = pref.getBoolean("dashboard_widget_" + cellId + "_placeholder", false);
486 if(isActualClick && ((appWidgetId == -1 && currentlySelectedCell == previouslySelectedCell) || shouldShowPlaceholder)) {
489 FrameLayout frameLayout = cells.get(currentlySelectedCell);
490 frameLayout.findViewById(R.id.empty).setVisibility(View.GONE);
492 Intent intent = new Intent(TaskbarIntent.ACTION_ADD_WIDGET_REQUESTED);
493 intent.putExtra("appWidgetId", APPWIDGET_HOST_ID);
494 intent.putExtra("cellId", cellId);
495 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
497 if(shouldShowPlaceholder) {
498 String providerName = pref.getString("dashboard_widget_" + cellId + "_provider", "null");
499 if(!providerName.equals("null")) {
500 ComponentName componentName = ComponentName.unflattenFromString(providerName);
502 List<AppWidgetProviderInfo> providerInfoList = mAppWidgetManager.getInstalledProvidersForProfile(Process.myUserHandle());
503 for(AppWidgetProviderInfo info : providerInfoList) {
504 if(info.provider.equals(componentName)) {
505 U.showToast(context, context.getString(R.string.tb_widget_restore_toast, info.loadLabel(context.getPackageManager())), Toast.LENGTH_SHORT);
512 previouslySelectedCell = -1;
514 for(int i = 0; i < maxSize; i++) {
515 FrameLayout frameLayout = cells.get(i);
516 frameLayout.findViewById(R.id.empty).setVisibility(i == currentlySelectedCell && !shouldShowPlaceholder ? View.VISIBLE : View.GONE);
519 previouslySelectedCell = currentlySelectedCell;
523 private void cellLongClick(View view) {
526 Bundle bundle = (Bundle) view.getTag();
527 int cellId = bundle.getInt("cellId");
529 Intent intent = new Intent(TaskbarIntent.ACTION_REMOVE_WIDGET_REQUESTED);
530 intent.putExtra("cellId", cellId);
531 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
534 private void addWidget(int appWidgetId, int cellId, boolean shouldSave) {
535 AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
537 final DashboardCell cellLayout = cells.get(cellId);
538 final AppWidgetHostView hostView = mAppWidgetHost.createView(context, appWidgetId, appWidgetInfo);
539 hostView.setAppWidget(appWidgetId, appWidgetInfo);
541 Bundle bundle = new Bundle();
542 bundle.putInt("cellId", cellId);
543 hostView.setTag(bundle);
545 cellLayout.findViewById(R.id.empty).setVisibility(View.GONE);
546 cellLayout.findViewById(R.id.placeholder).setVisibility(View.GONE);
547 cellLayout.setOnLongClickListener(olcl);
548 cellLayout.setOnGenericMotionListener(ogml);
549 cellLayout.setOnInterceptedLongPressListener(listener);
551 LinearLayout linearLayout = cellLayout.findViewById(R.id.dashboard);
552 linearLayout.addView(hostView);
554 Bundle bundle2 = (Bundle) cellLayout.getTag();
555 bundle2.putInt("appWidgetId", appWidgetId);
556 cellLayout.setTag(bundle2);
558 widgets.put(cellId, hostView);
561 SharedPreferences pref = U.getSharedPreferences(context);
562 SharedPreferences.Editor editor = pref.edit();
563 editor.putInt("dashboard_widget_" + cellId, appWidgetId);
564 editor.putString("dashboard_widget_" + cellId + "_provider", appWidgetInfo.provider.flattenToString());
565 editor.remove("dashboard_widget_" + cellId + "_placeholder");
569 new Handler().post(() -> {
570 ViewGroup.LayoutParams params = hostView.getLayoutParams();
571 params.width = cellLayout.getWidth();
572 params.height = cellLayout.getHeight();
573 hostView.setLayoutParams(params);
574 hostView.updateAppWidgetSize(null, cellLayout.getWidth(), cellLayout.getHeight(), cellLayout.getWidth(), cellLayout.getHeight());
578 private void removeWidget(int cellId, boolean tempRemove) {
579 widgets.remove(cellId);
581 DashboardCell cellLayout = cells.get(cellId);
582 Bundle bundle = (Bundle) cellLayout.getTag();
584 mAppWidgetHost.deleteAppWidgetId(bundle.getInt("appWidgetId"));
585 bundle.remove("appWidgetId");
587 LinearLayout linearLayout = cellLayout.findViewById(R.id.dashboard);
588 linearLayout.removeAllViews();
590 cellLayout.setTag(bundle);
591 cellLayout.setOnClickListener(cellOcl);
592 cellLayout.setOnHoverListener(cellOhl);
593 cellLayout.setOnLongClickListener(null);
594 cellLayout.setOnGenericMotionListener(null);
595 cellLayout.setOnInterceptedLongPressListener(null);
597 SharedPreferences pref = U.getSharedPreferences(context);
598 SharedPreferences.Editor editor = pref.edit();
599 editor.remove("dashboard_widget_" + cellId);
602 editor.putBoolean("dashboard_widget_" + cellId + "_placeholder", true);
603 addPlaceholder(cellId);
605 editor.remove("dashboard_widget_" + cellId + "_provider");
610 private void addPlaceholder(int cellId) {
611 FrameLayout placeholder = cells.get(cellId).findViewById(R.id.placeholder);
612 SharedPreferences pref = U.getSharedPreferences(context);
613 String providerName = pref.getString("dashboard_widget_" + cellId + "_provider", "null");
615 if(!providerName.equals("null")) {
616 ImageView imageView = placeholder.findViewById(R.id.placeholder_image);
617 ComponentName componentName = ComponentName.unflattenFromString(providerName);
619 List<AppWidgetProviderInfo> providerInfoList = mAppWidgetManager.getInstalledProvidersForProfile(Process.myUserHandle());
620 for(AppWidgetProviderInfo info : providerInfoList) {
621 if(info.provider.equals(componentName)) {
622 Drawable drawable = info.loadPreviewImage(context, -1);
623 if(drawable == null) drawable = info.loadIcon(context, -1);
625 ColorMatrix matrix = new ColorMatrix();
626 matrix.setSaturation(0);
628 imageView.setImageDrawable(drawable);
629 imageView.setColorFilter(new ColorMatrixColorFilter(matrix));
635 placeholder.setVisibility(View.VISIBLE);