• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.graphics;
17 
18 import static android.app.WallpaperManager.FLAG_SYSTEM;
19 import static android.view.View.MeasureSpec.EXACTLY;
20 import static android.view.View.MeasureSpec.makeMeasureSpec;
21 import static android.view.View.VISIBLE;
22 
23 import static com.android.launcher3.DeviceProfile.DEFAULT_SCALE;
24 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
25 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
26 import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
27 
28 import android.annotation.TargetApi;
29 import android.app.Fragment;
30 import android.app.WallpaperColors;
31 import android.app.WallpaperManager;
32 import android.appwidget.AppWidgetHost;
33 import android.appwidget.AppWidgetHostView;
34 import android.appwidget.AppWidgetProviderInfo;
35 import android.content.Context;
36 import android.content.ContextWrapper;
37 import android.content.Intent;
38 import android.content.res.TypedArray;
39 import android.graphics.Color;
40 import android.graphics.PointF;
41 import android.graphics.Rect;
42 import android.graphics.drawable.AdaptiveIconDrawable;
43 import android.graphics.drawable.ColorDrawable;
44 import android.os.Build;
45 import android.os.Handler;
46 import android.os.Looper;
47 import android.util.AttributeSet;
48 import android.util.Size;
49 import android.util.SparseArray;
50 import android.util.SparseIntArray;
51 import android.view.ContextThemeWrapper;
52 import android.view.LayoutInflater;
53 import android.view.MotionEvent;
54 import android.view.View;
55 import android.view.ViewGroup;
56 import android.view.WindowInsets;
57 import android.view.WindowManager;
58 import android.widget.TextClock;
59 
60 import androidx.annotation.NonNull;
61 import androidx.annotation.Nullable;
62 
63 import com.android.launcher3.BubbleTextView;
64 import com.android.launcher3.CellLayout;
65 import com.android.launcher3.DeviceProfile;
66 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
67 import com.android.launcher3.Hotseat;
68 import com.android.launcher3.InsettableFrameLayout;
69 import com.android.launcher3.InvariantDeviceProfile;
70 import com.android.launcher3.LauncherAppState;
71 import com.android.launcher3.LauncherPrefs;
72 import com.android.launcher3.LauncherSettings.Favorites;
73 import com.android.launcher3.R;
74 import com.android.launcher3.Utilities;
75 import com.android.launcher3.Workspace;
76 import com.android.launcher3.WorkspaceLayoutManager;
77 import com.android.launcher3.celllayout.CellLayoutLayoutParams;
78 import com.android.launcher3.celllayout.CellPosMapper;
79 import com.android.launcher3.config.FeatureFlags;
80 import com.android.launcher3.folder.FolderIcon;
81 import com.android.launcher3.icons.BaseIconFactory;
82 import com.android.launcher3.icons.BitmapInfo;
83 import com.android.launcher3.icons.LauncherIcons;
84 import com.android.launcher3.model.BgDataModel;
85 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
86 import com.android.launcher3.model.WidgetItem;
87 import com.android.launcher3.model.WidgetsModel;
88 import com.android.launcher3.model.data.FolderInfo;
89 import com.android.launcher3.model.data.ItemInfo;
90 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
91 import com.android.launcher3.model.data.WorkspaceItemInfo;
92 import com.android.launcher3.pm.InstallSessionHelper;
93 import com.android.launcher3.pm.UserCache;
94 import com.android.launcher3.uioverrides.PredictedAppIconInflater;
95 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
96 import com.android.launcher3.util.ComponentKey;
97 import com.android.launcher3.util.DisplayController;
98 import com.android.launcher3.util.IntArray;
99 import com.android.launcher3.util.IntSet;
100 import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
101 import com.android.launcher3.util.window.WindowManagerProxy;
102 import com.android.launcher3.views.ActivityContext;
103 import com.android.launcher3.views.BaseDragLayer;
104 import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
105 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
106 import com.android.launcher3.widget.LauncherWidgetHolder;
107 import com.android.launcher3.widget.LocalColorExtractor;
108 import com.android.launcher3.widget.NavigableAppWidgetHostView;
109 import com.android.launcher3.widget.custom.CustomWidgetManager;
110 import com.android.launcher3.widget.util.WidgetSizes;
111 
112 import java.util.ArrayList;
113 import java.util.Collections;
114 import java.util.HashMap;
115 import java.util.List;
116 import java.util.Map;
117 import java.util.concurrent.ConcurrentLinkedQueue;
118 
119 /**
120  * Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
121  * Steps:
122  *   1) Create a dummy icon info with just white icon
123  *   2) Inflate a strip down layout definition for Launcher
124  *   3) Place appropriate elements like icons and first-page qsb
125  *   4) Measure and draw the view on a canvas
126  */
127 @TargetApi(Build.VERSION_CODES.R)
128 public class LauncherPreviewRenderer extends ContextWrapper
129         implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
130 
131     /**
132      * Context used just for preview. It also provides a few objects (e.g. UserCache) just for
133      * preview purposes.
134      */
135     public static class PreviewContext extends SandboxContext {
136 
137         private final InvariantDeviceProfile mIdp;
138         private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
139                 new ConcurrentLinkedQueue<>();
140 
PreviewContext(Context base, InvariantDeviceProfile idp)141         public PreviewContext(Context base, InvariantDeviceProfile idp) {
142             super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
143                     LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
144                     CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE,
145                     WindowManagerProxy.INSTANCE, DisplayController.INSTANCE);
146             mIdp = idp;
147             mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
148             mObjectMap.put(LauncherAppState.INSTANCE,
149                     new LauncherAppState(this, null /* iconCacheFileName */));
150         }
151 
152         /**
153          * Creates a new LauncherIcons for the preview, skipping the global pool
154          */
newLauncherIcons(Context context)155         public LauncherIcons newLauncherIcons(Context context) {
156             LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
157             if (launcherIconsForPreview != null) {
158                 return launcherIconsForPreview;
159             }
160             return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize,
161                     -1 /* poolId */);
162         }
163 
164         private final class LauncherIconsForPreview extends LauncherIcons {
165 
LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize, int poolId)166             private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize,
167                     int poolId) {
168                 super(context, fillResIconDpi, iconBitmapSize, poolId);
169             }
170 
171             @Override
recycle()172             public void recycle() {
173                 // Clear any temporary state variables
174                 clear();
175                 mIconPool.offer(this);
176             }
177         }
178     }
179 
180     private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
181     private final Handler mUiHandler;
182     private final Context mContext;
183     private final InvariantDeviceProfile mIdp;
184     private final DeviceProfile mDp;
185     private final DeviceProfile mDpOrig;
186     private final Rect mInsets;
187     private final WorkspaceItemInfo mWorkspaceItemInfo;
188     private final LayoutInflater mHomeElementInflater;
189     private final InsettableFrameLayout mRootView;
190     private final Hotseat mHotseat;
191     private final Map<Integer, CellLayout> mWorkspaceScreens = new HashMap<>();
192     private final AppWidgetHost mAppWidgetHost;
193     private final SparseIntArray mWallpaperColorResources;
194     private final SparseArray<Size> mLauncherWidgetSpanInfo;
195 
LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp, WallpaperColors wallpaperColorsOverride, @Nullable final SparseArray<Size> launcherWidgetSpanInfo)196     public LauncherPreviewRenderer(Context context,
197             InvariantDeviceProfile idp,
198             WallpaperColors wallpaperColorsOverride,
199             @Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
200 
201         super(context);
202         mUiHandler = new Handler(Looper.getMainLooper());
203         mContext = context;
204         mIdp = idp;
205         mDp = idp.getDeviceProfile(context).toBuilder(context).setViewScaleProvider(
206                 this::getAppWidgetScale).build();
207         if (context instanceof PreviewContext) {
208             Context tempContext = ((PreviewContext) context).getBaseContext();
209             mDpOrig = new InvariantDeviceProfile(tempContext, InvariantDeviceProfile
210                     .getCurrentGridName(tempContext)).getDeviceProfile(tempContext)
211                     .copy(tempContext);
212         } else {
213             mDpOrig = mDp;
214         }
215 
216         WindowInsets currentWindowInsets = context.getSystemService(WindowManager.class)
217                 .getCurrentWindowMetrics().getWindowInsets();
218         mInsets = new Rect(
219                 currentWindowInsets.getSystemWindowInsetLeft(),
220                 currentWindowInsets.getSystemWindowInsetTop(),
221                 currentWindowInsets.getSystemWindowInsetRight(),
222                 mDp.isTaskbarPresent ? 0 : currentWindowInsets.getSystemWindowInsetBottom());
223         mDp.updateInsets(mInsets);
224 
225         BaseIconFactory iconFactory =
226                 new BaseIconFactory(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize) { };
227         BitmapInfo iconInfo = iconFactory.createBadgedIconBitmap(
228                 new AdaptiveIconDrawable(
229                         new ColorDrawable(Color.WHITE),
230                         new ColorDrawable(Color.WHITE)));
231 
232         mWorkspaceItemInfo = new WorkspaceItemInfo();
233         mWorkspaceItemInfo.bitmap = iconInfo;
234         mWorkspaceItemInfo.intent = new Intent();
235         mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
236                 context.getString(R.string.label_application);
237 
238         mHomeElementInflater = LayoutInflater.from(
239                 new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
240         mHomeElementInflater.setFactory2(this);
241 
242         int layoutRes = mDp.isTwoPanels ? R.layout.launcher_preview_two_panel_layout
243                 : R.layout.launcher_preview_layout;
244         mRootView = (InsettableFrameLayout) mHomeElementInflater.inflate(
245                 layoutRes, null, false);
246         mRootView.setInsets(mInsets);
247         measureView(mRootView, mDp.widthPx, mDp.heightPx);
248 
249         mHotseat = mRootView.findViewById(R.id.hotseat);
250         mHotseat.resetLayout(false);
251 
252         mLauncherWidgetSpanInfo = launcherWidgetSpanInfo == null ? new SparseArray<>() :
253                 launcherWidgetSpanInfo;
254 
255         CellLayout firstScreen = mRootView.findViewById(R.id.workspace);
256         firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingPx.left,
257                 mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top,
258                 (mDp.isTwoPanels ? mDp.cellLayoutBorderSpacePx.x / 2
259                         : mDp.workspacePadding.right) + mDp.cellLayoutPaddingPx.right,
260                 mDp.workspacePadding.bottom + mDp.cellLayoutPaddingPx.bottom
261         );
262         mWorkspaceScreens.put(FIRST_SCREEN_ID, firstScreen);
263 
264         if (mDp.isTwoPanels) {
265             CellLayout rightPanel = mRootView.findViewById(R.id.workspace_right);
266             rightPanel.setPadding(
267                     mDp.cellLayoutBorderSpacePx.x / 2  + mDp.cellLayoutPaddingPx.left,
268                     mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top,
269                     mDp.workspacePadding.right + mDp.cellLayoutPaddingPx.right,
270                     mDp.workspacePadding.bottom + mDp.cellLayoutPaddingPx.bottom
271             );
272             mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel);
273         }
274 
275         if (Utilities.ATLEAST_S) {
276             WallpaperColors wallpaperColors = wallpaperColorsOverride != null
277                     ? wallpaperColorsOverride
278                     : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
279             mWallpaperColorResources = wallpaperColors != null ? LocalColorExtractor.newInstance(
280                     context).generateColorsOverride(wallpaperColors) : null;
281         } else {
282             mWallpaperColorResources = null;
283         }
284         mAppWidgetHost = FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()
285                 ? new LauncherPreviewAppWidgetHost(context)
286                 : null;
287     }
288 
289     /** Populate preview and render it. */
getRenderedView(BgDataModel dataModel, Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap)290     public View getRenderedView(BgDataModel dataModel,
291             Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
292         populate(dataModel, widgetProviderInfoMap);
293         return mRootView;
294     }
295 
296     @Override
onCreateView(View parent, String name, Context context, AttributeSet attrs)297     public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
298         if ("TextClock".equals(name)) {
299             // Workaround for TextClock accessing handler for unregistering ticker.
300             return new TextClock(context, attrs) {
301 
302                 @Override
303                 public Handler getHandler() {
304                     return mUiHandler;
305                 }
306             };
307         } else if (!"fragment".equals(name)) {
308             return null;
309         }
310 
311         TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PreviewFragment);
312         FragmentWithPreview f = (FragmentWithPreview) Fragment.instantiate(
313                 context, ta.getString(R.styleable.PreviewFragment_android_name));
314         f.enterPreviewMode(context);
315         f.onInit(null);
316 
317         View view = f.onCreateView(LayoutInflater.from(context), (ViewGroup) parent, null);
318         view.setId(ta.getInt(R.styleable.PreviewFragment_android_id, View.NO_ID));
319         return view;
320     }
321 
322     @Override
323     public View onCreateView(String name, Context context, AttributeSet attrs) {
324         return onCreateView(null, name, context, attrs);
325     }
326 
327     @Override
328     public BaseDragLayer getDragLayer() {
329         throw new UnsupportedOperationException();
330     }
331 
332     @Override
333     public DeviceProfile getDeviceProfile() {
334         return mDp;
335     }
336 
337     @Override
338     public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
339         return mDpChangeListeners;
340     }
341 
342     @Override
343     public Hotseat getHotseat() {
344         return mHotseat;
345     }
346 
347     /**
348      * Hides the components in the bottom row.
349      *
350      * @param hide True to hide and false to show.
351      */
352     public void hideBottomRow(boolean hide) {
353         mUiHandler.post(() -> {
354             if (mDp.isTaskbarPresent) {
355                 // hotseat icons on bottom
356                 mHotseat.setIconsAlpha(hide ? 0 : 1);
357                 if (mDp.isQsbInline) {
358                     mHotseat.setQsbAlpha(hide ? 0 : 1);
359                 }
360             } else {
361                 mHotseat.setQsbAlpha(hide ? 0 : 1);
362             }
363         });
364     }
365 
366     @Override
367     public CellLayout getScreenWithId(int screenId) {
368         return mWorkspaceScreens.get(screenId);
369     }
370 
371     @Override
372     public CellPosMapper getCellPosMapper() {
373         return CellPosMapper.DEFAULT;
374     }
375 
376     private void inflateAndAddIcon(WorkspaceItemInfo info) {
377         CellLayout screen = mWorkspaceScreens.get(info.screenId);
378         BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
379                 R.layout.app_icon, screen, false);
380         icon.applyFromWorkspaceItem(info);
381         addInScreenFromBind(icon, info);
382     }
383 
384     private void inflateAndAddFolder(FolderInfo info) {
385         CellLayout screen = info.container == Favorites.CONTAINER_DESKTOP
386                 ? mWorkspaceScreens.get(info.screenId)
387                 : mHotseat;
388         FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, screen,
389                 info);
390         addInScreenFromBind(folderIcon, info);
391     }
392 
393     private void inflateAndAddWidgets(
394             LauncherAppWidgetInfo info,
395             Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
396         if (widgetProviderInfoMap == null) {
397             return;
398         }
399         AppWidgetProviderInfo providerInfo = widgetProviderInfoMap.get(
400                 new ComponentKey(info.providerName, info.user));
401         if (providerInfo == null) {
402             return;
403         }
404         inflateAndAddWidgets(info, LauncherAppWidgetProviderInfo.fromProviderInfo(
405                 getApplicationContext(), providerInfo));
406     }
407 
408     private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
409         WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
410                 info.providerName, info.user);
411         if (widgetItem == null) {
412             return;
413         }
414         inflateAndAddWidgets(info, widgetItem.widgetInfo);
415     }
416 
417     private void inflateAndAddWidgets(
418             LauncherAppWidgetInfo info, LauncherAppWidgetProviderInfo providerInfo) {
419         AppWidgetHostView view;
420         if (FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()) {
421             view = mAppWidgetHost.createView(mContext, info.appWidgetId, providerInfo);
422         } else {
423             view = new NavigableAppWidgetHostView(this) {
424                 @Override
425                 protected boolean shouldAllowDirectClick() {
426                     return false;
427                 }
428             };
429             view.setAppWidget(-1, providerInfo);
430             view.updateAppWidget(null);
431         }
432 
433         if (mWallpaperColorResources != null) {
434             view.setColorResources(mWallpaperColorResources);
435         }
436 
437         view.setTag(info);
438         addInScreenFromBind(view, info);
439     }
440 
441     @NonNull
442     private PointF getAppWidgetScale(@Nullable ItemInfo itemInfo) {
443         if (!(itemInfo instanceof LauncherAppWidgetInfo)) {
444             return DEFAULT_SCALE;
445         }
446         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) itemInfo;
447         final Size launcherWidgetSize = mLauncherWidgetSpanInfo.get(info.appWidgetId);
448         if (launcherWidgetSize == null) {
449             return DEFAULT_SCALE;
450         }
451         final Size origSize = WidgetSizes.getWidgetSizePx(mDpOrig,
452                 launcherWidgetSize.getWidth(), launcherWidgetSize.getHeight());
453         final Size newSize = WidgetSizes.getWidgetSizePx(mDp, info.spanX, info.spanY);
454         final Rect previewInset = new Rect();
455         final Rect origInset = new Rect();
456         // When the setup() is called for the LayoutParams, insets are added to the width
457         // and height of the view. This is not accounted for in WidgetSizes and is handled
458         // here.
459         if (mDp.shouldInsetWidgets()) {
460             previewInset.set(mDp.inv.defaultWidgetPadding);
461         } else {
462             previewInset.setEmpty();
463         }
464         if (mDpOrig.shouldInsetWidgets()) {
465             origInset.set(mDpOrig.inv.defaultWidgetPadding);
466         } else {
467             origInset.setEmpty();
468         }
469 
470         return new PointF((float) newSize.getWidth() / (origSize.getWidth()
471                 + origInset.left + origInset.right),
472                 (float) newSize.getHeight() / (origSize.getHeight()
473                 + origInset.top + origInset.bottom));
474     }
475 
476     private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
477         CellLayout screen = mWorkspaceScreens.get(info.screenId);
478         View view = PredictedAppIconInflater.inflate(mHomeElementInflater, screen, info);
479         if (view != null) {
480             addInScreenFromBind(view, info);
481         }
482     }
483 
484     private void dispatchVisibilityAggregated(View view, boolean isVisible) {
485         // Similar to View.dispatchVisibilityAggregated implementation.
486         final boolean thisVisible = view.getVisibility() == VISIBLE;
487         if (thisVisible || !isVisible) {
488             view.onVisibilityAggregated(isVisible);
489         }
490 
491         if (view instanceof ViewGroup) {
492             isVisible = thisVisible && isVisible;
493             ViewGroup vg = (ViewGroup) view;
494             int count = vg.getChildCount();
495 
496             for (int i = 0; i < count; i++) {
497                 dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
498             }
499         }
500     }
501 
502     private void populate(BgDataModel dataModel,
503             Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
504         // Separate the items that are on the current screen, and the other remaining items.
505         ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
506         ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
507         ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
508         ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
509 
510         IntSet currentScreenIds = IntSet.wrap(mWorkspaceScreens.keySet());
511         filterCurrentWorkspaceItems(currentScreenIds, dataModel.workspaceItems,
512                 currentWorkspaceItems, otherWorkspaceItems);
513         filterCurrentWorkspaceItems(currentScreenIds, dataModel.appWidgets, currentAppWidgets,
514                 otherAppWidgets);
515         for (ItemInfo itemInfo : currentWorkspaceItems) {
516             switch (itemInfo.itemType) {
517                 case Favorites.ITEM_TYPE_APPLICATION:
518                 case Favorites.ITEM_TYPE_SHORTCUT:
519                 case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
520                     inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
521                     break;
522                 case Favorites.ITEM_TYPE_FOLDER:
523                     inflateAndAddFolder((FolderInfo) itemInfo);
524                     break;
525                 default:
526                     break;
527             }
528         }
529         for (ItemInfo itemInfo : currentAppWidgets) {
530             switch (itemInfo.itemType) {
531                 case Favorites.ITEM_TYPE_APPWIDGET:
532                 case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
533                     if (widgetProviderInfoMap != null) {
534                         inflateAndAddWidgets(
535                                 (LauncherAppWidgetInfo) itemInfo, widgetProviderInfoMap);
536                     } else {
537                         inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
538                                 dataModel.widgetsModel);
539                     }
540                     break;
541                 default:
542                     break;
543             }
544         }
545         IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
546                 mDp.numShownHotseatIcons);
547         FixedContainerItems hotseatPredictions =
548                 dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
549         List<ItemInfo> predictions = hotseatPredictions == null
550                 ? Collections.emptyList() : hotseatPredictions.items;
551         int count = Math.min(ranks.size(), predictions.size());
552         for (int i = 0; i < count; i++) {
553             int rank = ranks.get(i);
554             WorkspaceItemInfo itemInfo =
555                     new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i));
556             itemInfo.container = CONTAINER_HOTSEAT_PREDICTION;
557             itemInfo.rank = rank;
558             itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
559             itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
560             itemInfo.screenId = rank;
561             inflateAndAddPredictedIcon(itemInfo);
562         }
563 
564         // Add first page QSB
565         if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
566             CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
567             View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, false);
568             CellLayoutLayoutParams lp = new CellLayoutLayoutParams(
569                     0, 0, firstScreen.getCountX(), 1);
570             lp.canReorder = false;
571             firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
572         }
573 
574         measureView(mRootView, mDp.widthPx, mDp.heightPx);
575         dispatchVisibilityAggregated(mRootView, true);
576         measureView(mRootView, mDp.widthPx, mDp.heightPx);
577         // Additional measure for views which use auto text size API
578         measureView(mRootView, mDp.widthPx, mDp.heightPx);
579     }
580 
581     private static void measureView(View view, int width, int height) {
582         view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
583         view.layout(0, 0, width, height);
584     }
585 
586     private class LauncherPreviewAppWidgetHost extends AppWidgetHost {
587 
588         private LauncherPreviewAppWidgetHost(Context context) {
589             super(context, LauncherWidgetHolder.APPWIDGET_HOST_ID);
590         }
591 
592         @Override
593         protected AppWidgetHostView onCreateView(
594                 Context context,
595                 int appWidgetId,
596                 AppWidgetProviderInfo appWidget) {
597             return new LauncherPreviewAppWidgetHostView(LauncherPreviewRenderer.this);
598         }
599     }
600 
601     private static class LauncherPreviewAppWidgetHostView extends BaseLauncherAppWidgetHostView {
602         private LauncherPreviewAppWidgetHostView(Context context) {
603             super(context);
604         }
605 
606         @Override
607         protected boolean shouldAllowDirectClick() {
608             return false;
609         }
610     }
611 
612     /** Root layout for launcher preview that intercepts all touch events. */
613     public static class LauncherPreviewLayout extends InsettableFrameLayout {
614         public LauncherPreviewLayout(Context context, AttributeSet attrs) {
615             super(context, attrs);
616         }
617 
618         @Override
619         public boolean onInterceptTouchEvent(MotionEvent ev) {
620             return true;
621         }
622     }
623 }
624