1 /*
2  * Copyright (C) 2019 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.wallpaper.picker;
17 
18 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
19 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
20 
21 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
22 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.CUSTOMIZE;
23 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.DELETE;
24 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
25 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
26 
27 import android.annotation.SuppressLint;
28 import android.app.Activity;
29 import android.app.AlertDialog;
30 import android.app.WallpaperColors;
31 import android.app.WallpaperInfo;
32 import android.app.WallpaperManager;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.pm.ActivityInfo;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageManager;
39 import android.content.pm.ServiceInfo;
40 import android.graphics.Point;
41 import android.net.Uri;
42 import android.os.Bundle;
43 import android.os.RemoteException;
44 import android.service.wallpaper.IWallpaperConnection;
45 import android.service.wallpaper.WallpaperService;
46 import android.service.wallpaper.WallpaperSettingsActivity;
47 import android.text.TextUtils;
48 import android.util.Log;
49 import android.view.LayoutInflater;
50 import android.view.MotionEvent;
51 import android.view.SurfaceView;
52 import android.view.View;
53 import android.view.ViewGroup;
54 import android.widget.ImageView;
55 
56 import androidx.annotation.NonNull;
57 import androidx.annotation.Nullable;
58 import androidx.cardview.widget.CardView;
59 import androidx.constraintlayout.widget.ConstraintLayout;
60 import androidx.constraintlayout.widget.ConstraintSet;
61 import androidx.lifecycle.LiveData;
62 import androidx.slice.Slice;
63 import androidx.slice.widget.SliceLiveData;
64 import androidx.slice.widget.SliceView;
65 
66 import com.android.wallpaper.R;
67 import com.android.wallpaper.compat.BuildCompat;
68 import com.android.wallpaper.model.SetWallpaperViewModel;
69 import com.android.wallpaper.util.FullScreenAnimation;
70 import com.android.wallpaper.util.ResourceUtils;
71 import com.android.wallpaper.util.ScreenSizeCalculator;
72 import com.android.wallpaper.util.SizeCalculator;
73 import com.android.wallpaper.util.WallpaperConnection;
74 import com.android.wallpaper.util.WallpaperSurfaceCallback;
75 import com.android.wallpaper.widget.BottomActionBar;
76 import com.android.wallpaper.widget.BottomActionBar.AccessibilityCallback;
77 import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent;
78 import com.android.wallpaper.widget.LockScreenPreviewer;
79 import com.android.wallpaper.widget.WallpaperColorsLoader;
80 
81 import java.util.Locale;
82 import java.util.concurrent.ExecutionException;
83 import java.util.concurrent.Future;
84 import java.util.concurrent.TimeUnit;
85 import java.util.concurrent.TimeoutException;
86 
87 /**
88  * Fragment which displays the UI for previewing an individual live wallpaper, its attribution
89  * information and settings slices if available.
90  */
91 public class LivePreviewFragment extends PreviewFragment implements
92         WallpaperConnection.WallpaperConnectionListener {
93 
94     public static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info";
95     public static final String KEY_ACTION_DELETE_LIVE_WALLPAPER = "action_delete_live_wallpaper";
96 
97     private static final String TAG = "LivePreviewFragment";
98 
99     /**
100      * Instance of {@link WallpaperConnection} used to bind to the live wallpaper service to show
101      * it in this preview fragment.
102      * @see IWallpaperConnection
103      */
104     protected WallpaperConnection mWallpaperConnection;
105     protected CardView mHomePreviewCard;
106     protected SurfaceView mWorkspaceSurface;
107     protected WallpaperSurfaceCallback mWallpaperSurfaceCallback;
108     protected WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback;
109     protected ViewGroup mLockPreviewContainer;
110     protected LockScreenPreviewer mLockScreenPreviewer;
111 
112     private Intent mDeleteIntent;
113     private Intent mSettingsIntent;
114     private SliceView mSettingsSliceView;
115     private LiveData<Slice> mSettingsLiveData;
116     private Point mScreenSize;
117     private ViewGroup mPreviewContainer;
118     private TouchForwardingLayout mTouchForwardingLayout;
119     private SurfaceView mWallpaperSurface;
120     private Future<Integer> mPlaceholderColorFuture;
121     private WallpaperColors mWallpaperColors;
122 
123     @Override
onCreate(Bundle savedInstanceState)124     public void onCreate(Bundle savedInstanceState) {
125         super.onCreate(savedInstanceState);
126         android.app.WallpaperInfo info = mWallpaper.getWallpaperComponent();
127         mPlaceholderColorFuture = mWallpaper.computePlaceholderColor(getContext());
128 
129         String deleteAction = getDeleteAction(info);
130         if (!TextUtils.isEmpty(deleteAction)) {
131             mDeleteIntent = new Intent(deleteAction);
132             mDeleteIntent.setPackage(info.getPackageName());
133             mDeleteIntent.putExtra(EXTRA_LIVE_WALLPAPER_INFO, info);
134         }
135         String settingsActivity = getSettingsActivity(info);
136         if (settingsActivity != null) {
137             mSettingsIntent = new Intent();
138             mSettingsIntent.setComponent(new ComponentName(info.getPackageName(),
139                     settingsActivity));
140             mSettingsIntent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true);
141             PackageManager pm = requireContext().getPackageManager();
142             ActivityInfo activityInfo = mSettingsIntent.resolveActivityInfo(pm, 0);
143             if (activityInfo == null) {
144                 Log.i(TAG, "Couldn't find wallpaper settings activity: " + settingsActivity);
145                 mSettingsIntent = null;
146             }
147         }
148     }
149 
150     @Nullable
getSettingsActivity(WallpaperInfo info)151     protected String getSettingsActivity(WallpaperInfo info) {
152         return info.getSettingsActivity();
153     }
154 
getWallpaperIntent(WallpaperInfo info)155     protected Intent getWallpaperIntent(WallpaperInfo info) {
156         return new Intent(WallpaperService.SERVICE_INTERFACE)
157                 .setClassName(info.getPackageName(), info.getServiceName());
158     }
159 
160     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)161     public View onCreateView(LayoutInflater inflater, ViewGroup container,
162             Bundle savedInstanceState) {
163         View view = super.onCreateView(inflater, container, savedInstanceState);
164 
165         Activity activity = requireActivity();
166         mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
167                 activity.getWindowManager().getDefaultDisplay());
168         mPreviewContainer = view.findViewById(R.id.live_wallpaper_preview);
169         mTouchForwardingLayout = view.findViewById(R.id.touch_forwarding_layout);
170 
171         // Update preview header color which covers toolbar and status bar area.
172         View previewHeader = view.findViewById(R.id.preview_header);
173         previewHeader.setBackgroundColor(activity.getColor(R.color.settingslib_colorSurfaceHeader));
174 
175         // Set aspect ratio on the preview card.
176         ConstraintSet set = new ConstraintSet();
177         set.clone((ConstraintLayout) mPreviewContainer);
178         String ratio = String.format(Locale.US, "%d:%d", mScreenSize.x, mScreenSize.y);
179         set.setDimensionRatio(mTouchForwardingLayout.getId(), ratio);
180         set.applyTo((ConstraintLayout) mPreviewContainer);
181 
182         mHomePreviewCard = mPreviewContainer.findViewById(R.id.wallpaper_full_preview_card);
183         mLockPreviewContainer = mPreviewContainer.findViewById(R.id.lock_screen_preview_container);
184         mLockScreenPreviewer = new LockScreenPreviewer(getLifecycle(), getContext(),
185                 mLockPreviewContainer);
186         mLockScreenPreviewer.setDateViewVisibility(!mFullScreenAnimation.isFullScreen());
187         mFullScreenAnimation.setFullScreenStatusListener(
188                 isFullScreen -> {
189                     mLockScreenPreviewer.setDateViewVisibility(!isFullScreen);
190                     if (!isFullScreen) {
191                         mBottomActionBar.focusAccessibilityAction(EDIT);
192                     }
193                 });
194         mWallpaperSurface = mHomePreviewCard.findViewById(R.id.wallpaper_surface);
195         mTouchForwardingLayout.setTargetView(mHomePreviewCard);
196         mTouchForwardingLayout.setForwardingEnabled(true);
197         mWorkspaceSurface = mHomePreviewCard.findViewById(R.id.workspace_surface);
198 
199         mWorkspaceSurfaceCallback = createWorkspaceSurfaceCallback(mWorkspaceSurface);
200         mWallpaperSurfaceCallback = new WallpaperSurfaceCallback(getContext(),
201                 mHomePreviewCard, mWallpaperSurface, mPlaceholderColorFuture,
202                 new WallpaperSurfaceCallback.SurfaceListener() {
203                     @Override
204                     public void onSurfaceCreated() {
205                         previewLiveWallpaper(null);
206                     }
207                 });
208 
209         setUpTabs(view.findViewById(R.id.separated_tabs));
210 
211         view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
212             @Override
213             public void onLayoutChange(View thisView, int left, int top, int right, int bottom,
214                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
215                 mHomePreviewCard.setRadius(SizeCalculator.getPreviewCornerRadius(activity,
216                         mHomePreviewCard.getMeasuredWidth()));
217                 view.removeOnLayoutChangeListener(this);
218             }
219         });
220 
221         return view;
222     }
223 
224     @Override
onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)225     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
226         super.onViewCreated(view, savedInstanceState);
227         updateWallpaperSurface();
228         setupCurrentWallpaperPreview();
229         renderWorkspaceSurface();
230     }
231 
updateWallpaperSurface()232     private void updateWallpaperSurface() {
233         mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
234         mWallpaperSurface.setZOrderMediaOverlay(true);
235     }
236 
237     @Override
updateScreenPreview(boolean isHomeSelected)238     protected void updateScreenPreview(boolean isHomeSelected) {
239         mWorkspaceSurface.setVisibility(isHomeSelected ? View.VISIBLE : View.INVISIBLE);
240 
241         mLockPreviewContainer.setVisibility(isHomeSelected ? View.INVISIBLE : View.VISIBLE);
242 
243         mFullScreenAnimation.setIsHomeSelected(isHomeSelected);
244     }
245 
setupCurrentWallpaperPreview()246     private void setupCurrentWallpaperPreview() {
247         mHomePreviewCard.setOnTouchListener((v, ev) -> {
248             if (mWallpaperConnection != null && mWallpaperConnection.getEngine() != null) {
249                 float scaleRatio =
250                         (float) mTouchForwardingLayout.getWidth() / (float) mScreenSize.x;
251                 int action = ev.getActionMasked();
252                 if (action == MotionEvent.ACTION_DOWN) {
253                     mBottomActionBar.collapseBottomSheetIfExpanded();
254                 }
255                 MotionEvent dup = MotionEvent.obtainNoHistory(ev);
256                 dup.setLocation(ev.getX() / scaleRatio, ev.getY() / scaleRatio);
257                 try {
258                     mWallpaperConnection.getEngine().dispatchPointer(dup);
259                     if (action == MotionEvent.ACTION_UP) {
260                         mWallpaperConnection.getEngine().dispatchWallpaperCommand(
261                                 WallpaperManager.COMMAND_TAP,
262                                 (int) ev.getX(), (int) ev.getY(), 0, null);
263                     } else if (action == MotionEvent.ACTION_POINTER_UP) {
264                         int pointerIndex = ev.getActionIndex();
265                         mWallpaperConnection.getEngine().dispatchWallpaperCommand(
266                                 WallpaperManager.COMMAND_SECONDARY_TAP,
267                                 (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0, null);
268                     }
269                 } catch (RemoteException e) {
270                     Log.e(TAG, "Remote exception of wallpaper connection");
271                 }
272             }
273             return false;
274         });
275     }
276 
277     @Override
onDestroyView()278     public void onDestroyView() {
279         super.onDestroyView();
280         if (mSettingsLiveData != null && mSettingsLiveData.hasObservers()
281                 && mSettingsSliceView != null) {
282             mSettingsLiveData.removeObserver(mSettingsSliceView);
283             mSettingsLiveData = null;
284         }
285         if (mWallpaperConnection != null) {
286             mWallpaperConnection.disconnect();
287             mWallpaperConnection = null;
288         }
289         if (mLockScreenPreviewer != null) {
290             mLockScreenPreviewer.release();
291         }
292         mWorkspaceSurfaceCallback.cleanUp();
293         mWorkspaceSurface.getHolder().removeCallback(mWorkspaceSurfaceCallback);
294         mWallpaperSurfaceCallback.cleanUp();
295         mWallpaperSurface.getHolder().removeCallback(mWallpaperSurfaceCallback);
296     }
297 
previewLiveWallpaper(ImageView thumbnailView)298     protected void previewLiveWallpaper(ImageView thumbnailView) {
299         mWallpaperSurface.post(() -> {
300             Activity activity = getActivity();
301             if (activity == null) {
302                 return;
303             }
304             if (mWallpaperSurfaceCallback.getHomeImageWallpaper() != null) {
305                 Integer placeholderColor = null;
306                 try {
307                     placeholderColor = mPlaceholderColorFuture.get(50, TimeUnit.MILLISECONDS);
308                 } catch (InterruptedException | ExecutionException | TimeoutException e) {
309                     Log.i(TAG, "Couldn't obtain placeholder color", e);
310                 }
311                 mWallpaper.getThumbAsset(activity.getApplicationContext())
312                         .loadLowResDrawable(activity,
313                                 mWallpaperSurfaceCallback.getHomeImageWallpaper(),
314                                 placeholderColor != null
315                                         ? placeholderColor
316                                         : ResourceUtils.getColorAttr(activity,
317                                                 android.R.attr.colorBackground),
318                                 mPreviewBitmapTransformation);
319             }
320             setUpLiveWallpaperPreview(mWallpaper);
321         });
322     }
323 
setUpLiveWallpaperPreview( com.android.wallpaper.model.WallpaperInfo homeWallpaper)324     protected void setUpLiveWallpaperPreview(
325             com.android.wallpaper.model.WallpaperInfo homeWallpaper) {
326         Activity activity = getActivity();
327         if (activity == null || activity.isFinishing()) {
328             return;
329         }
330         if (mWallpaperConnection != null) {
331             mWallpaperConnection.disconnect();
332         }
333 
334         if (WallpaperConnection.isPreviewAvailable()) {
335             mWallpaperConnection = new WallpaperConnection(
336                     getWallpaperIntent(homeWallpaper.getWallpaperComponent()),
337                     activity,
338                     /* listener= */ this,
339                     mWallpaperSurface);
340 
341             mWallpaperConnection.setVisibility(true);
342         } else {
343             WallpaperColorsLoader.getWallpaperColors(
344                     activity,
345                     homeWallpaper.getThumbAsset(activity),
346                     colors -> onWallpaperColorsChanged(colors, 0));
347         }
348         if (mWallpaperConnection != null && !mWallpaperConnection.connect()) {
349             mWallpaperConnection = null;
350         }
351     }
352 
renderWorkspaceSurface()353     private void renderWorkspaceSurface() {
354         mWorkspaceSurface.setZOrderMediaOverlay(true);
355         mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback);
356     }
357 
358     @Override
onBottomActionBarReady(BottomActionBar bottomActionBar)359     protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
360         super.onBottomActionBarReady(bottomActionBar);
361         mBottomActionBar.showActionsOnly(INFORMATION, DELETE, EDIT, CUSTOMIZE, APPLY);
362         mBottomActionBar.setActionClickListener(APPLY, unused -> onSetWallpaperClicked(null));
363         mBottomActionBar.bindBottomSheetContentWithAction(
364                 new WallpaperInfoContent(getContext()), INFORMATION);
365 
366         View separatedTabsContainer = getView().findViewById(R.id.separated_tabs_container);
367         // Update target view's accessibility param since it will be blocked by the bottom sheet
368         // when expanded.
369         mBottomActionBar.setAccessibilityCallback(new AccessibilityCallback() {
370             @Override
371             public void onBottomSheetCollapsed() {
372                 mPreviewContainer.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
373                 separatedTabsContainer.setImportantForAccessibility(
374                         IMPORTANT_FOR_ACCESSIBILITY_YES);
375             }
376 
377             @Override
378             public void onBottomSheetExpanded() {
379                 mPreviewContainer.setImportantForAccessibility(
380                         IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
381                 separatedTabsContainer.setImportantForAccessibility(
382                         IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
383             }
384         });
385         final Uri uriSettingsSlice = getSettingsSliceUri(mWallpaper.getWallpaperComponent());
386         if (uriSettingsSlice != null) {
387             mSettingsLiveData = SliceLiveData.fromUri(requireContext(), uriSettingsSlice);
388             mBottomActionBar.bindBottomSheetContentWithAction(
389                     new PreviewCustomizeSettingsContent(getContext()), CUSTOMIZE);
390         } else {
391             if (mSettingsIntent != null) {
392                 mBottomActionBar.setActionClickListener(CUSTOMIZE, listener ->
393                         startActivity(mSettingsIntent));
394             } else {
395                 mBottomActionBar.hideActions(CUSTOMIZE);
396             }
397         }
398 
399         if (TextUtils.isEmpty(getDeleteAction(mWallpaper.getWallpaperComponent()))) {
400             mBottomActionBar.hideActions(DELETE);
401         } else {
402             mBottomActionBar.setActionClickListener(DELETE, listener ->
403                     showDeleteConfirmDialog());
404         }
405         mBottomActionBar.show();
406         // Action buttons are disabled when live wallpaper is not loaded.
407         mBottomActionBar.disableActions();
408         // Enable buttons if loaded, or wait for it.
409         if (isLoaded()) {
410             mBottomActionBar.enableActions();
411         }
412     }
413 
414     @Override
onEngineShown()415     public void onEngineShown() {
416         Activity activity = getActivity();
417         if (activity == null) {
418             return;
419         }
420         mWallpaperSurfaceCallback.getHomeImageWallpaper().animate()
421                 .setStartDelay(250)
422                 .setDuration(250)
423                 .alpha(0f)
424                 .setInterpolator(ALPHA_OUT)
425                 .start();
426 
427         if (mBottomActionBar != null) {
428             mBottomActionBar.enableActions();
429         }
430     }
431 
432     @Override
onWallpaperColorsChanged(WallpaperColors colors, int displayId)433     public void onWallpaperColorsChanged(WallpaperColors colors, int displayId) {
434         mWallpaperColors = colors;
435         mLockScreenPreviewer.setColor(colors);
436 
437         mFullScreenAnimation.setFullScreenTextColor(
438                 colors == null || (colors.getColorHints()
439                         & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
440                             ? FullScreenAnimation.FullScreenTextColor.LIGHT
441                             : FullScreenAnimation.FullScreenTextColor.DARK);
442     }
443 
444     @Override
isLoaded()445     protected boolean isLoaded() {
446         return mWallpaperConnection != null && mWallpaperConnection.isEngineReady();
447     }
448 
449     @SuppressLint("NewApi") //Already checking with isAtLeastQ
getSettingsSliceUri(android.app.WallpaperInfo info)450     protected Uri getSettingsSliceUri(android.app.WallpaperInfo info) {
451         if (BuildCompat.isAtLeastQ()) {
452             return info.getSettingsSliceUri();
453         }
454         return null;
455     }
456 
457     @Override
getLayoutResId()458     protected int getLayoutResId() {
459         return R.layout.fragment_live_preview;
460     }
461 
462     @Override
setCurrentWallpaper(int destination)463     protected void setCurrentWallpaper(int destination) {
464         mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, null,
465                 destination, 0, null, mWallpaperColors,
466                 SetWallpaperViewModel.getCallback(mViewModelProvider));
467     }
468 
469     @Nullable
getDeleteAction(android.app.WallpaperInfo wallpaperInfo)470     protected String getDeleteAction(android.app.WallpaperInfo wallpaperInfo) {
471         android.app.WallpaperInfo currentInfo =
472                 WallpaperManager.getInstance(requireContext()).getWallpaperInfo();
473         ServiceInfo serviceInfo = wallpaperInfo.getServiceInfo();
474         if (!isPackagePreInstalled(serviceInfo.applicationInfo)) {
475             Log.d(TAG, "This wallpaper is not pre-installed: " + serviceInfo.name);
476             return null;
477         }
478 
479         ServiceInfo currentService = currentInfo == null ? null : currentInfo.getServiceInfo();
480         // A currently set Live wallpaper should not be deleted.
481         if (currentService != null && TextUtils.equals(serviceInfo.name, currentService.name)) {
482             return null;
483         }
484 
485         final Bundle metaData = serviceInfo.metaData;
486         if (metaData != null) {
487             return metaData.getString(KEY_ACTION_DELETE_LIVE_WALLPAPER);
488         }
489         return null;
490     }
491 
492     @Override
onResume()493     public void onResume() {
494         super.onResume();
495         if (mWallpaperConnection != null) {
496             mWallpaperConnection.setVisibility(true);
497         }
498     }
499 
500     @Override
onPause()501     public void onPause() {
502         super.onPause();
503         if (mWallpaperConnection != null) {
504             mWallpaperConnection.setVisibility(false);
505         }
506     }
507 
508     @Override
onStop()509     public void onStop() {
510         super.onStop();
511         if (mWallpaperConnection != null) {
512             mWallpaperConnection.disconnect();
513             mWallpaperConnection = null;
514         }
515     }
516 
showDeleteConfirmDialog()517     private void showDeleteConfirmDialog() {
518         final AlertDialog alertDialog = new AlertDialog.Builder(getContext())
519                 .setMessage(R.string.delete_wallpaper_confirmation)
520                 .setOnDismissListener(dialog -> mBottomActionBar.deselectAction(DELETE))
521                 .setPositiveButton(R.string.delete_live_wallpaper,
522                         (dialog, which) -> deleteLiveWallpaper())
523                 .setNegativeButton(android.R.string.cancel, null /* listener */)
524                 .create();
525         alertDialog.show();
526     }
527 
deleteLiveWallpaper()528     private void deleteLiveWallpaper() {
529         if (mDeleteIntent != null) {
530             requireContext().startService(mDeleteIntent);
531             finishActivity(/* success= */ false);
532         }
533     }
534 
isPackagePreInstalled(ApplicationInfo info)535     private boolean isPackagePreInstalled(ApplicationInfo info) {
536         return info != null && (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
537     }
538 
539     private final class PreviewCustomizeSettingsContent extends BottomSheetContent<View> {
540 
PreviewCustomizeSettingsContent(Context context)541         private PreviewCustomizeSettingsContent(Context context) {
542             super(context);
543         }
544 
545         @Override
getViewId()546         public int getViewId() {
547             return R.layout.preview_customize_settings;
548         }
549 
550         @Override
onViewCreated(View previewPage)551         public void onViewCreated(View previewPage) {
552             mSettingsSliceView = previewPage.findViewById(R.id.settings_slice);
553             mSettingsSliceView.setMode(SliceView.MODE_LARGE);
554             mSettingsSliceView.setScrollable(false);
555             if (mSettingsLiveData != null) {
556                 mSettingsLiveData.observeForever(mSettingsSliceView);
557             }
558         }
559 
560         @Override
onRecreateView(View oldPreviewPage)561         public void onRecreateView(View oldPreviewPage) {
562             super.onRecreateView(oldPreviewPage);
563             if (mSettingsLiveData != null && mSettingsLiveData.hasObservers()
564                     && mSettingsSliceView != null) {
565                 mSettingsLiveData.removeObserver(mSettingsSliceView);
566             }
567         }
568     }
569 }
570