• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 
17 package com.android.wallpaper.livepicker;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.WallpaperColors;
22 import android.app.WallpaperInfo;
23 import android.app.WallpaperManager;
24 import android.content.ActivityNotFoundException;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.ServiceConnection;
30 import android.content.pm.ActivityInfo;
31 import android.content.pm.PackageManager;
32 import android.content.res.Resources.NotFoundException;
33 import android.graphics.Outline;
34 import android.graphics.Rect;
35 import android.graphics.drawable.Drawable;
36 import android.net.Uri;
37 import android.os.Bundle;
38 import android.os.IBinder;
39 import android.os.ParcelFileDescriptor;
40 import android.os.RemoteException;
41 import android.service.wallpaper.IWallpaperConnection;
42 import android.service.wallpaper.IWallpaperEngine;
43 import android.service.wallpaper.IWallpaperService;
44 import android.service.wallpaper.WallpaperService;
45 import android.service.wallpaper.WallpaperSettingsActivity;
46 import android.text.TextUtils;
47 import android.util.Log;
48 import android.util.Pair;
49 import android.view.ContextThemeWrapper;
50 import android.view.Menu;
51 import android.view.MenuItem;
52 import android.view.MotionEvent;
53 import android.view.View;
54 import android.view.ViewGroup;
55 import android.view.ViewOutlineProvider;
56 import android.view.WindowManager.LayoutParams;
57 import android.view.animation.AnimationUtils;
58 import android.widget.ArrayAdapter;
59 import android.widget.Button;
60 import android.widget.CheckBox;
61 import android.widget.TextView;
62 import android.widget.Toolbar;
63 
64 import androidx.annotation.NonNull;
65 import androidx.annotation.Nullable;
66 import androidx.annotation.VisibleForTesting;
67 import androidx.lifecycle.LiveData;
68 import androidx.slice.Slice;
69 import androidx.slice.widget.SliceLiveData;
70 import androidx.slice.widget.SliceView;
71 import androidx.viewpager.widget.PagerAdapter;
72 import androidx.viewpager.widget.ViewPager;
73 
74 import com.google.android.material.bottomsheet.BottomSheetBehavior;
75 import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback;
76 import com.google.android.material.tabs.TabLayout;
77 
78 import java.io.IOException;
79 import java.util.ArrayList;
80 import java.util.List;
81 
82 public class LiveWallpaperPreview extends Activity {
83     static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info";
84 
85     private static final String LOG_TAG = "LiveWallpaperPreview";
86 
87     private static final boolean SHOW_DUMMY_DATA = false;
88 
89     private WallpaperManager mWallpaperManager;
90     private WallpaperConnection mWallpaperConnection;
91 
92     private Intent mWallpaperIntent;
93     private Intent mSettingsIntent;
94     private Intent mDeleteIntent;
95 
96     private View mLoading;
97     private View mViewBottomPane;
98     private BottomSheetBehavior mBottomSheetBehavior;
99     private ViewPager mViewPager;
100     private TabLayout mTabLayout;
101     private CheckBox mPreview;
102 
103     protected final List<Pair<String, View>> mPages = new ArrayList<>();
104     private SliceView mSliceViewSettings;
105     private LiveData<Slice> mLiveDataSettings;
106 
107     @Override
onCreate(Bundle savedInstanceState)108     protected void onCreate(Bundle savedInstanceState) {
109         super.onCreate(savedInstanceState);
110         init();
111     }
112 
init()113     protected void init() {
114         WallpaperInfo info = getIntent().getParcelableExtra(EXTRA_LIVE_WALLPAPER_INFO);
115         if (info == null) {
116             finish();
117             return;
118         }
119         initUI(info, null /* deleteAction */);
120     }
121 
initUI(WallpaperInfo info, @Nullable String deleteAction)122     protected void initUI(WallpaperInfo info, @Nullable String deleteAction) {
123         getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
124                 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
125                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
126         setContentView(R.layout.live_wallpaper_preview);
127         mLoading = findViewById(R.id.loading);
128 
129         final String packageName = info.getPackageName();
130         mWallpaperIntent = new Intent(WallpaperService.SERVICE_INTERFACE)
131                 .setClassName(info.getPackageName(), info.getServiceName());
132 
133         final String settingsActivity = info.getSettingsActivity();
134         if (settingsActivity != null) {
135             mSettingsIntent = new Intent();
136             mSettingsIntent.setComponent(new ComponentName(packageName, settingsActivity));
137             mSettingsIntent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true);
138             final PackageManager pm = getPackageManager();
139             final ActivityInfo activityInfo = mSettingsIntent.resolveActivityInfo(pm, 0);
140             if (activityInfo == null) {
141                 Log.e(LOG_TAG, "Couldn't find settings activity: " + settingsActivity);
142                 mSettingsIntent = null;
143             }
144         }
145 
146         final Toolbar toolbar = findViewById(R.id.toolbar);
147         setActionBar(toolbar);
148         getActionBar().setDisplayHomeAsUpEnabled(true);
149         getActionBar().setDisplayShowTitleEnabled(false);
150 
151         final Drawable backArrow = getDrawable(R.drawable.ic_arrow_back_white_24dp);
152         backArrow.setAutoMirrored(true);
153         toolbar.setNavigationIcon(backArrow);
154 
155         mWallpaperManager = WallpaperManager.getInstance(this);
156         mWallpaperConnection = new WallpaperConnection(mWallpaperIntent);
157 
158         if (!TextUtils.isEmpty(deleteAction)) {
159             mDeleteIntent = new Intent(deleteAction);
160             mDeleteIntent.setPackage(info.getPackageName());
161             mDeleteIntent.putExtra(EXTRA_LIVE_WALLPAPER_INFO, info);
162         }
163 
164         initInfoPage(info);
165         initSettingsPage(info);
166         populateBottomPane();
167     }
168 
initInfoPage(WallpaperInfo info)169     private void initInfoPage(WallpaperInfo info) {
170         final View pageInfo = getLayoutInflater().inflate(R.layout.page_info, null /* root */);
171         final TextView attributionTitle = pageInfo.findViewById(
172                 R.id.preview_attribution_pane_title);
173         final TextView attributionAuthor = pageInfo.findViewById(
174                 R.id.preview_attribution_pane_author);
175         final TextView attributionDescription = pageInfo.findViewById(
176                 R.id.preview_attribution_pane_description);
177         final Button attributionExploreButton = pageInfo.findViewById(
178                 R.id.preview_attribution_pane_explore_button);
179         final View spacer = pageInfo.findViewById(R.id.spacer);
180         final Button setWallpaperButton = pageInfo.findViewById(
181                 R.id.preview_attribution_pane_set_wallpaper_button);
182 
183         setWallpaperButton.setOnClickListener(this::setLiveWallpaper);
184         mPages.add(Pair.create(getString(R.string.tab_info), pageInfo));
185 
186         if (SHOW_DUMMY_DATA) {
187             attributionTitle.setText("Diorama, Yosemite");
188             attributionTitle.setVisibility(View.VISIBLE);
189             attributionAuthor.setText("Live Earth Collection - Android Earth");
190             attributionAuthor.setVisibility(View.VISIBLE);
191             attributionDescription.setText("Lorem ipsum dolor sit amet, consectetur adipiscing"
192                     + " elit. Sed imperdiet et mauris molestie laoreet. Proin volutpat elit nec"
193                     + " magna tempus, ac aliquet lectus volutpat.");
194             attributionDescription.setVisibility(View.VISIBLE);
195             attributionExploreButton.setText("Explore");
196             attributionExploreButton.setVisibility(View.VISIBLE);
197             spacer.setVisibility(View.VISIBLE);
198             return;
199         }
200 
201         final PackageManager pm = getPackageManager();
202 
203         // Set attribution title
204         final CharSequence title = info.loadLabel(pm);
205         if (!TextUtils.isEmpty(title)) {
206             attributionTitle.setText(title);
207             attributionTitle.setVisibility(View.VISIBLE);
208         }
209 
210         // Don't show other meta data if attribute showMetadataInPreview is set to False
211         if (!info.getShowMetadataInPreview()) {
212             return;
213         }
214 
215         // Set attribution author
216         try {
217             final CharSequence author = info.loadAuthor(pm);
218             if (!TextUtils.isEmpty(author)) {
219                 attributionAuthor.setText(author);
220                 attributionAuthor.setVisibility(View.VISIBLE);
221             }
222         } catch (NotFoundException e) {
223             // It's expected if the live wallpaper doesn't provide this information
224         }
225 
226         // Set attribution description
227         try {
228             final CharSequence description = info.loadDescription(pm);
229             if (!TextUtils.isEmpty(description)) {
230                 attributionDescription.setText(description);
231                 attributionDescription.setVisibility(View.VISIBLE);
232             }
233         } catch (NotFoundException e) {
234             // It's expected if the live wallpaper doesn't provide this information
235         }
236 
237         // Set context information
238         try {
239             final Uri contextUri = info.loadContextUri(pm);
240             if (contextUri != null) {
241                 final Intent intent = new Intent(Intent.ACTION_VIEW, contextUri);
242                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
243                 attributionExploreButton.setOnClickListener(v -> {
244                     try {
245                         startActivity(intent);
246                     } catch (ActivityNotFoundException e) {
247                         Log.e(LOG_TAG, "Couldn't find activity for context link.", e);
248                     }
249                 });
250                 attributionExploreButton.setVisibility(View.VISIBLE);
251                 spacer.setVisibility(View.VISIBLE);
252 
253                 // Update context description string if it's provided
254                 final CharSequence contextDescription = info.loadContextDescription(pm);
255                 if (!TextUtils.isEmpty(contextDescription)) {
256                     attributionExploreButton.setText(contextDescription);
257                 }
258             }
259         } catch (NotFoundException e) {
260             // It's expected if the wallpaper doesn't provide this information
261         }
262     }
263 
264     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
getSettingsSliceUri(@onNull WallpaperInfo info)265     protected Uri getSettingsSliceUri(@NonNull WallpaperInfo info) {
266         return info.getSettingsSliceUri();
267     }
268 
initSettingsPage(WallpaperInfo info)269     private void initSettingsPage(WallpaperInfo info) {
270         final Uri uriSettingsSlice = getSettingsSliceUri(info);
271         if (uriSettingsSlice == null) {
272             return;
273         }
274 
275         final View pageSettings = getLayoutInflater().inflate(R.layout.page_settings,
276                 null /* root */);
277         final Button setWallpaperButton = pageSettings.findViewById(
278                 R.id.preview_attribution_pane_set_wallpaper_button);
279 
280         mSliceViewSettings = pageSettings.findViewById(R.id.settings_slice);
281         mSliceViewSettings.setMode(SliceView.MODE_LARGE);
282         mSliceViewSettings.setScrollable(false);
283 
284         // Set LiveData for SliceView
285         mLiveDataSettings = SliceLiveData.fromUri(this /* context */, uriSettingsSlice);
286         mLiveDataSettings.observeForever(mSliceViewSettings);
287 
288         setWallpaperButton.setOnClickListener(this::setLiveWallpaper);
289 
290         mPages.add(Pair.create(getResources().getString(R.string.tab_customize), pageSettings));
291     }
292 
populateBottomPane()293     private void populateBottomPane() {
294         mViewBottomPane = findViewById(R.id.bottom_pane);
295         mViewPager = findViewById(R.id.viewpager);
296         mTabLayout = findViewById(R.id.tablayout);
297 
298         mBottomSheetBehavior = BottomSheetBehavior.from(mViewBottomPane);
299         mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
300 
301         // Create PagerAdapter
302         final PagerAdapter pagerAdapter = new PagerAdapter() {
303             @NonNull
304             @Override
305             public Object instantiateItem(ViewGroup container, int position) {
306                 final View page = mPages.get(position).second;
307                 container.addView(page);
308                 return page;
309             }
310 
311             @Override
312             public void destroyItem(@NonNull ViewGroup container, int position,
313                     @NonNull Object object) {
314                 if (object instanceof View) {
315                     container.removeView((View) object);
316                 }
317             }
318 
319             @Override
320             public int getCount() {
321                 return mPages.size();
322             }
323 
324             @Override
325             public CharSequence getPageTitle(int position) {
326                 try {
327                     return mPages.get(position).first;
328                 } catch (IndexOutOfBoundsException e) {
329                     return null;
330                 }
331             }
332 
333             @Override
334             public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
335                 return (view == object);
336             }
337         };
338 
339         // Add OnPageChangeListener to re-measure ViewPager's height
340         mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
341             @Override
342             public void onPageSelected(int position) {
343                 mViewPager.requestLayout();
344             }
345         });
346 
347         // Set PagerAdapter
348         mViewPager.setAdapter(pagerAdapter);
349 
350         // Make TabLayout visible if there are more than one page
351         if (mPages.size() > 1) {
352             mTabLayout.setVisibility(View.VISIBLE);
353             mTabLayout.setupWithViewPager(mViewPager);
354         }
355 
356         // Initializes a rounded rectangle outline and clips the upper corners to be rounded.
357         mViewBottomPane.setOutlineProvider(new ViewOutlineProvider() {
358             private final int radius = getResources().getDimensionPixelSize(
359                     R.dimen.preview_viewpager_round_radius);
360 
361             @Override
362             public void getOutline(View view, Outline outline) {
363                 outline.setRoundRect(0 /* left */, 0 /* top */, view.getWidth(),
364                         view.getHeight() + radius, radius);
365             }
366         });
367         mViewBottomPane.setClipToOutline(true);
368     }
369 
370     @Override
onCreateOptionsMenu(Menu menu)371     public boolean onCreateOptionsMenu(Menu menu) {
372         getMenuInflater().inflate(R.menu.menu_preview, menu);
373         setupPreviewMenu(menu);
374         menu.findItem(R.id.configure).setVisible(mSettingsIntent != null);
375         menu.findItem(R.id.delete_wallpaper).setVisible(mDeleteIntent != null);
376         return super.onCreateOptionsMenu(menu);
377     }
378 
setupPreviewMenu(Menu menu)379     private void setupPreviewMenu(Menu menu) {
380         mPreview = (CheckBox) menu.findItem(R.id.preview).getActionView();
381         mPreview.setOnClickListener(this::setPreviewBehavior);
382 
383         BottomSheetCallback callback = new BottomSheetCallback() {
384             @Override
385             public void onStateChanged(View bottomSheet, int newState) {
386                 switch (newState) {
387                     case BottomSheetBehavior.STATE_COLLAPSED:
388                         setPreviewChecked(true /* checked */);
389                         break;
390                     case BottomSheetBehavior.STATE_EXPANDED:
391                         setPreviewChecked(false /* checked */);
392                         break;
393                 }
394             }
395 
396             @Override
397             public void onSlide(View bottomSheet, float slideOffset) {
398                 mTabLayout.setAlpha(slideOffset);
399                 mViewPager.setAlpha(slideOffset);
400             }
401         };
402         mBottomSheetBehavior.setBottomSheetCallback(callback);
403 
404         int state = mBottomSheetBehavior.getState();
405         callback.onStateChanged(mViewBottomPane, state);
406         switch (state) {
407             case BottomSheetBehavior.STATE_COLLAPSED:
408                 callback.onSlide(mViewBottomPane, 0f);
409                 break;
410             case BottomSheetBehavior.STATE_EXPANDED:
411                 callback.onSlide(mViewBottomPane, 1f);
412                 break;
413         }
414     }
415 
setPreviewChecked(boolean checked)416     private void setPreviewChecked(boolean checked) {
417         if (mPreview != null) {
418             mPreview.setChecked(checked);
419             int resId = checked ? R.string.expand_attribution_panel
420                     : R.string.collapse_attribution_panel;
421             mPreview.setContentDescription(getResources().getString(resId));
422         }
423     }
424 
setPreviewBehavior(final View v)425     private void setPreviewBehavior(final View v) {
426         CheckBox checkbox = (CheckBox) v;
427         if (checkbox.isChecked()) {
428             mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
429         } else {
430             mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
431         }
432     }
433 
setLiveWallpaper(final View v)434     public void setLiveWallpaper(final View v) {
435         if (mWallpaperManager.getWallpaperInfo() != null
436                 && mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0) {
437             // The lock screen does not have a distinct wallpaper and the current wallpaper is a
438             // live wallpaper, so since we cannot preserve any static imagery on the lock screen,
439             // set the live wallpaper directly without giving the user a destination option.
440             try {
441                 setLiveWallpaper(v.getRootView().getWindowToken());
442                 setResult(RESULT_OK);
443             } catch (RuntimeException e) {
444                 Log.w(LOG_TAG, "Failure setting wallpaper", e);
445             }
446             finish();
447         } else {
448             // Otherwise, prompt to either set on home or both home and lock screen.
449             final Context themedContext = new ContextThemeWrapper(this /* base */,
450                     android.R.style.Theme_DeviceDefault_Settings);
451             new AlertDialog.Builder(themedContext)
452                     .setTitle(R.string.set_live_wallpaper)
453                     .setAdapter(new WallpaperTargetAdapter(themedContext),
454                             new DialogInterface.OnClickListener() {
455                                 @Override
456                                 public void onClick(DialogInterface dialog, int which) {
457                                     try {
458                                         setLiveWallpaper(v.getRootView().getWindowToken());
459                                         if (which == 1) {
460                                             // "Home screen and lock screen"; clear the lock
461                                             // screen so it
462                                             // shows through to the live wallpaper on home.
463                                             mWallpaperManager.clear(WallpaperManager.FLAG_LOCK);
464                                         }
465                                         setResult(RESULT_OK);
466                                     } catch (RuntimeException | IOException e) {
467                                         Log.w(LOG_TAG, "Failure setting wallpaper", e);
468                                     }
469                                     finish();
470                                 }
471                             })
472                     .show();
473         }
474     }
475 
setLiveWallpaper(IBinder windowToken)476     private void setLiveWallpaper(IBinder windowToken) {
477         mWallpaperManager.setWallpaperComponent(mWallpaperIntent.getComponent());
478         mWallpaperManager.setWallpaperOffsetSteps(0.5f /* xStep */, 0.0f /* yStep */);
479         mWallpaperManager.setWallpaperOffsets(windowToken, 0.5f /* xOffset */, 0.0f /* yOffset */);
480     }
481 
482     @VisibleForTesting
deleteLiveWallpaper()483     void deleteLiveWallpaper() {
484         if (mDeleteIntent != null) {
485             startService(mDeleteIntent);
486             finish();
487         }
488     }
489 
showDeleteConfirmDialog()490     private void showDeleteConfirmDialog() {
491         final AlertDialog alertDialog = new AlertDialog.Builder(this /* context */,
492                 R.style.AlertDialogStyle)
493                 .setMessage(R.string.delete_wallpaper_confirmation)
494                 .setPositiveButton(R.string.delete_live_wallpaper,
495                         (dialog, which) -> deleteLiveWallpaper())
496                 .setNegativeButton(android.R.string.cancel, null /* listener */)
497                 .create();
498         alertDialog.show();
499     }
500 
501     @Override
onOptionsItemSelected(MenuItem item)502     public boolean onOptionsItemSelected(MenuItem item) {
503         int id = item.getItemId();
504         if (id == R.id.configure) {
505             startActivity(mSettingsIntent);
506             return true;
507         } else if (id == R.id.delete_wallpaper) {
508             showDeleteConfirmDialog();
509             return true;
510         } else if (id == android.R.id.home) {
511             onBackPressed();
512             return true;
513         }
514         return super.onOptionsItemSelected(item);
515     }
516 
517     @Override
onResume()518     public void onResume() {
519         super.onResume();
520         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
521             try {
522                 mWallpaperConnection.mEngine.setVisibility(true);
523             } catch (RemoteException e) {
524                 // Ignore
525             }
526         }
527     }
528 
529     @Override
onPause()530     public void onPause() {
531         super.onPause();
532         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
533             try {
534                 mWallpaperConnection.mEngine.setVisibility(false);
535             } catch (RemoteException e) {
536                 // Ignore
537             }
538         }
539     }
540 
541     @Override
onAttachedToWindow()542     public void onAttachedToWindow() {
543         super.onAttachedToWindow();
544 
545         getWindow().getDecorView().post(new Runnable() {
546             public void run() {
547                 if (!mWallpaperConnection.connect()) {
548                     mWallpaperConnection = null;
549                 }
550             }
551         });
552     }
553 
554     @Override
onDetachedFromWindow()555     public void onDetachedFromWindow() {
556         super.onDetachedFromWindow();
557 
558         if (mWallpaperConnection != null) {
559             mWallpaperConnection.disconnect();
560         }
561         mWallpaperConnection = null;
562     }
563 
564     @Override
onDestroy()565     protected void onDestroy () {
566         if (mLiveDataSettings != null && mLiveDataSettings.hasObservers()) {
567             mLiveDataSettings.removeObserver(mSliceViewSettings);
568             mLiveDataSettings = null;
569         }
570         super.onDestroy();
571     }
572 
573     @Override
dispatchTouchEvent(MotionEvent ev)574     public boolean dispatchTouchEvent(MotionEvent ev) {
575         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
576             onUserInteraction();
577         }
578         boolean handled = getWindow().superDispatchTouchEvent(ev);
579         if (!handled) {
580             handled = onTouchEvent(ev);
581         }
582 
583         if (!handled && mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
584             MotionEvent dup = MotionEvent.obtainNoHistory(ev);
585             try {
586                 mWallpaperConnection.mEngine.dispatchPointer(dup);
587             } catch (RemoteException e) {
588             }
589 
590             int action = ev.getActionMasked();
591             try {
592                 if (action == MotionEvent.ACTION_UP) {
593                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
594                             WallpaperManager.COMMAND_TAP,
595                             (int) ev.getX(), (int) ev.getY(), 0, null);
596                 } else if (action == MotionEvent.ACTION_POINTER_UP) {
597                     int pointerIndex = ev.getActionIndex();
598                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
599                             WallpaperManager.COMMAND_SECONDARY_TAP,
600                             (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0, null);
601                 }
602             } catch (RemoteException e) {
603             }
604         }
605         return handled;
606     }
607 
608     class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection {
609         final Intent mIntent;
610         IWallpaperService mService;
611         IWallpaperEngine mEngine;
612         boolean mConnected;
613 
WallpaperConnection(Intent intent)614         WallpaperConnection(Intent intent) {
615             mIntent = intent;
616         }
617 
connect()618         public boolean connect() {
619             synchronized (this) {
620                 if (!bindService(mIntent, this, Context.BIND_AUTO_CREATE)) {
621                     return false;
622                 }
623 
624                 mConnected = true;
625                 return true;
626             }
627         }
628 
disconnect()629         public void disconnect() {
630             synchronized (this) {
631                 mConnected = false;
632                 if (mEngine != null) {
633                     try {
634                         mEngine.destroy();
635                     } catch (RemoteException e) {
636                         // Ignore
637                     }
638                     mEngine = null;
639                 }
640                 try {
641                     unbindService(this);
642                 } catch (IllegalArgumentException e) {
643                     Log.w(LOG_TAG, "Can't unbind wallpaper service. "
644                             + "It might have crashed, just ignoring.", e);
645                 }
646                 mService = null;
647             }
648         }
649 
onServiceConnected(ComponentName name, IBinder service)650         public void onServiceConnected(ComponentName name, IBinder service) {
651             if (mWallpaperConnection == this) {
652                 mService = IWallpaperService.Stub.asInterface(service);
653                 try {
654                     final int displayId = getWindow().getDecorView().getDisplay().getDisplayId();
655                     final View root = getWindow().getDecorView();
656                     mService.attach(this, root.getWindowToken(),
657                             LayoutParams.TYPE_APPLICATION_MEDIA,
658                             true, root.getWidth(), root.getHeight(),
659                             new Rect(0, 0, 0, 0), displayId);
660                 } catch (RemoteException e) {
661                     Log.w(LOG_TAG, "Failed attaching wallpaper; clearing", e);
662                 }
663             }
664         }
665 
onServiceDisconnected(ComponentName name)666         public void onServiceDisconnected(ComponentName name) {
667             mService = null;
668             mEngine = null;
669             if (mWallpaperConnection == this) {
670                 Log.w(LOG_TAG, "Wallpaper service gone: " + name);
671             }
672         }
673 
attachEngine(IWallpaperEngine engine, int displayId)674         public void attachEngine(IWallpaperEngine engine, int displayId) {
675             synchronized (this) {
676                 if (mConnected) {
677                     mEngine = engine;
678                     try {
679                         engine.setVisibility(true);
680                     } catch (RemoteException e) {
681                         // Ignore
682                     }
683                 } else {
684                     try {
685                         engine.destroy();
686                     } catch (RemoteException e) {
687                         // Ignore
688                     }
689                 }
690             }
691         }
692 
setWallpaper(String name)693         public ParcelFileDescriptor setWallpaper(String name) {
694             return null;
695         }
696 
697         @Override
onWallpaperColorsChanged(WallpaperColors colors, int displayId)698         public void onWallpaperColorsChanged(WallpaperColors colors, int displayId)
699                 throws RemoteException {
700 
701         }
702 
703         @Override
engineShown(IWallpaperEngine engine)704         public void engineShown(IWallpaperEngine engine) throws RemoteException {
705             mLoading.post(() -> {
706                 mLoading.animate()
707                         .alpha(0f)
708                         .setDuration(220)
709                         .setStartDelay(300)
710                         .setInterpolator(AnimationUtils.loadInterpolator(LiveWallpaperPreview.this,
711                                 android.R.interpolator.fast_out_linear_in))
712                         .withEndAction(() -> mLoading.setVisibility(View.INVISIBLE));
713             });
714         }
715     }
716 
717     private static class WallpaperTargetAdapter extends ArrayAdapter<CharSequence> {
718 
WallpaperTargetAdapter(Context context)719         public WallpaperTargetAdapter(Context context) {
720             super(context, R.layout.wallpaper_target_dialog_item,
721                     context.getResources().getTextArray(R.array.which_wallpaper_options));
722         }
723 
724         @Override
hasStableIds()725         public boolean hasStableIds() {
726             return true;
727         }
728 
729         @Override
getItemId(int position)730         public long getItemId(int position) {
731             return position;
732         }
733 
734         @Override
getView(int position, View convertView, ViewGroup parent)735         public View getView(int position, View convertView, ViewGroup parent) {
736             TextView tv = (TextView) super.getView(position, convertView, parent);
737             tv.setCompoundDrawablesRelativeWithIntrinsicBounds(
738                     position == 0 ? R.drawable.ic_home : R.drawable.ic_device, 0, 0, 0);
739             return tv;
740         }
741     }
742 }
743