• 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.Rect;
34 import android.graphics.drawable.Drawable;
35 import android.net.Uri;
36 import android.os.Bundle;
37 import android.os.IBinder;
38 import android.os.ParcelFileDescriptor;
39 import android.os.RemoteException;
40 import android.service.wallpaper.IWallpaperConnection;
41 import android.service.wallpaper.IWallpaperEngine;
42 import android.service.wallpaper.IWallpaperService;
43 import android.service.wallpaper.WallpaperService;
44 import android.service.wallpaper.WallpaperSettingsActivity;
45 import android.support.design.widget.BottomSheetBehavior;
46 import android.text.TextUtils;
47 import android.util.Log;
48 import android.view.ContextThemeWrapper;
49 import android.view.Menu;
50 import android.view.MenuItem;
51 import android.view.MotionEvent;
52 import android.view.View;
53 import android.view.View.OnClickListener;
54 import android.view.ViewGroup;
55 import android.view.WindowManager.LayoutParams;
56 import android.view.animation.AnimationUtils;
57 import android.widget.ArrayAdapter;
58 import android.widget.Button;
59 import android.widget.ImageButton;
60 import android.widget.TextView;
61 import android.widget.Toolbar;
62 
63 import java.io.IOException;
64 
65 public class LiveWallpaperPreview extends Activity {
66     static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info";
67 
68     private static final String LOG_TAG = "LiveWallpaperPreview";
69 
70     private static final boolean SHOW_DUMMY_DATA = false;
71 
72     private WallpaperManager mWallpaperManager;
73     private WallpaperConnection mWallpaperConnection;
74 
75     private String mPackageName;
76     private Intent mWallpaperIntent;
77     private Intent mSettingsIntent;
78 
79     private TextView mAttributionTitle;
80     private TextView mAttributionSubtitle1;
81     private TextView mAttributionSubtitle2;
82     private Button mAttributionExploreButton;
83     private ImageButton mPreviewPaneArrow;
84     private View mBottomSheet;
85     private View mSpacer;
86     private View mLoading;
87 
88     @Override
onCreate(Bundle savedInstanceState)89     protected void onCreate(Bundle savedInstanceState) {
90         super.onCreate(savedInstanceState);
91         init();
92     }
93 
init()94     protected void init() {
95         Bundle extras = getIntent().getExtras();
96         WallpaperInfo info = extras.getParcelable(EXTRA_LIVE_WALLPAPER_INFO);
97         if (info == null) {
98             setResult(RESULT_CANCELED);
99             finish();
100         }
101         initUI(info);
102     }
103 
initUI(WallpaperInfo info)104     protected void initUI(WallpaperInfo info) {
105         getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
106                 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
107                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
108         setContentView(R.layout.live_wallpaper_preview);
109         mAttributionTitle = (TextView) findViewById(R.id.preview_attribution_pane_title);
110         mAttributionSubtitle1 = (TextView) findViewById(R.id.preview_attribution_pane_subtitle1);
111         mAttributionSubtitle2 = (TextView) findViewById(R.id.preview_attribution_pane_subtitle2);
112         mAttributionExploreButton = (Button) findViewById(
113                 R.id.preview_attribution_pane_explore_button);
114         mPreviewPaneArrow = (ImageButton) findViewById(R.id.preview_attribution_pane_arrow);
115         mBottomSheet = findViewById(R.id.bottom_sheet);
116         mSpacer = findViewById(R.id.spacer);
117         mLoading = findViewById(R.id.loading);
118 
119         mPackageName = info.getPackageName();
120         mWallpaperIntent = new Intent(WallpaperService.SERVICE_INTERFACE)
121                 .setClassName(info.getPackageName(), info.getServiceName());
122 
123         final String settingsActivity = info.getSettingsActivity();
124         if (settingsActivity != null) {
125             mSettingsIntent = new Intent();
126             mSettingsIntent.setComponent(new ComponentName(mPackageName, settingsActivity));
127             mSettingsIntent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true);
128             final PackageManager pm = getPackageManager();
129             final ActivityInfo activityInfo = mSettingsIntent.resolveActivityInfo(pm, 0);
130             if (activityInfo == null) {
131                 Log.e(LOG_TAG, "Couldn't find settings activity: " + settingsActivity);
132                 mSettingsIntent = null;
133             }
134         }
135 
136         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
137         setActionBar(toolbar);
138         getActionBar().setDisplayHomeAsUpEnabled(true);
139         getActionBar().setDisplayShowTitleEnabled(false);
140 
141         Drawable backArrow = getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp);
142         backArrow.setAutoMirrored(true);
143         toolbar.setNavigationIcon(backArrow);
144 
145         mWallpaperManager = WallpaperManager.getInstance(this);
146         mWallpaperConnection = new WallpaperConnection(mWallpaperIntent);
147 
148         populateAttributionPane(info);
149     }
150 
populateAttributionPane(WallpaperInfo info)151     private void populateAttributionPane(WallpaperInfo info) {
152         if (!info.getShowMetadataInPreview() && !SHOW_DUMMY_DATA) {
153             mBottomSheet.setVisibility(View.GONE);
154             return;
155         }
156         final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
157 
158         OnClickListener onClickListener = new OnClickListener() {
159             @Override
160             public void onClick(View view) {
161                 if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) {
162                     bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
163                 } else if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
164                     bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
165                 }
166             }
167         };
168         mAttributionTitle.setOnClickListener(onClickListener);
169         mPreviewPaneArrow.setOnClickListener(onClickListener);
170 
171         bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
172             @Override
173             public void onStateChanged(View bottomSheet, int newState) {
174                 if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
175                     mPreviewPaneArrow.setImageResource(R.drawable.ic_keyboard_arrow_up_white_24dp);
176                     mPreviewPaneArrow.setContentDescription(
177                             getResources().getString(R.string.expand_attribution_panel));
178                 } else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
179                     mPreviewPaneArrow.setImageResource(
180                             R.drawable.ic_keyboard_arrow_down_white_24dp);
181                     mPreviewPaneArrow.setContentDescription(
182                             getResources().getString(R.string.collapse_attribution_panel));
183                 }
184             }
185 
186             @Override
187             public void onSlide(View bottomSheet, float slideOffset) {
188                 float alpha;
189                 if (slideOffset >= 0) {
190                     alpha = slideOffset;
191                 } else {
192                     alpha = 1f - slideOffset;
193                 }
194                 mAttributionTitle.setAlpha(slideOffset);
195                 mAttributionSubtitle1.setAlpha(slideOffset);
196                 mAttributionSubtitle2.setAlpha(slideOffset);
197                 mAttributionExploreButton.setAlpha(slideOffset);
198             }
199         });
200 
201         bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
202         mPreviewPaneArrow.setImageResource(R.drawable.ic_keyboard_arrow_down_white_24dp);
203 
204         if (SHOW_DUMMY_DATA) {
205             mAttributionTitle.setText("Diorama, Yosemite");
206             mAttributionSubtitle1.setText("Live Earth Collection - Android Earth");
207             mAttributionSubtitle2.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit."
208                     + " Sed imperdiet et mauris molestie laoreet. Proin volutpat elit nec magna"
209                     + " tempus, ac aliquet lectus volutpat.");
210             mAttributionExploreButton.setText("Explore");
211         } else {
212             PackageManager pm = getPackageManager();
213 
214             CharSequence title = info.loadLabel(pm);
215             if (!TextUtils.isEmpty(title)) {
216                 mAttributionTitle.setText(title);
217             } else {
218                 mAttributionTitle.setVisibility(View.GONE);
219             }
220 
221             try {
222                 CharSequence author = info.loadAuthor(pm);
223                 if (TextUtils.isEmpty(author)) {
224                     throw new NotFoundException();
225                 }
226                 mAttributionSubtitle1.setText(author);
227             } catch (NotFoundException e) {
228                 mAttributionSubtitle1.setVisibility(View.GONE);
229             }
230 
231             try {
232                 CharSequence description = info.loadDescription(pm);
233                 if (TextUtils.isEmpty(description)) {
234                     throw new NotFoundException();
235                 }
236                 mAttributionSubtitle2.setText(description);
237             } catch (NotFoundException e) {
238                 mAttributionSubtitle2.setVisibility(View.GONE);
239             }
240 
241             try {
242                 Uri contextUri = info.loadContextUri(pm);
243                 CharSequence contextDescription = info.loadContextDescription(pm);
244                 if (contextUri == null) {
245                     throw new NotFoundException();
246                 }
247                 mAttributionExploreButton.setText(contextDescription);
248                 mAttributionExploreButton.setOnClickListener(v -> {
249                     Intent intent = new Intent(Intent.ACTION_VIEW, contextUri);
250                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
251                     try {
252                         startActivity(intent);
253                     } catch (ActivityNotFoundException e) {
254                         Log.e(LOG_TAG, "Couldn't find activity for context link.", e);
255                     }
256                 });
257             } catch (NotFoundException e) {
258                 mAttributionExploreButton.setVisibility(View.GONE);
259                 mSpacer.setVisibility(View.VISIBLE);
260             }
261         }
262 
263 
264     }
265 
266     @Override
onCreateOptionsMenu(Menu menu)267     public boolean onCreateOptionsMenu(Menu menu) {
268         getMenuInflater().inflate(R.menu.menu_preview, menu);
269         menu.findItem(R.id.configure).setVisible(mSettingsIntent != null);
270         menu.findItem(R.id.set_wallpaper).getActionView().setOnClickListener(
271                 this::setLiveWallpaper);
272         return super.onCreateOptionsMenu(menu);
273     }
274 
setLiveWallpaper(final View v)275     public void setLiveWallpaper(final View v) {
276         if (mWallpaperManager.getWallpaperInfo() != null
277             && mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0) {
278             // The lock screen does not have a distinct wallpaper and the current wallpaper is a
279             // live wallpaper, so since we cannot preserve any static imagery on the lock screen,
280             // set the live wallpaper directly without giving the user a destination option.
281             try {
282                 setLiveWallpaper(v.getRootView().getWindowToken());
283                 setResult(RESULT_OK);
284             } catch (RuntimeException e) {
285                 Log.w(LOG_TAG, "Failure setting wallpaper", e);
286             }
287             finish();
288         } else {
289             // Otherwise, prompt to either set on home or both home and lock screen.
290             Context themedContext = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault_Settings);
291             new AlertDialog.Builder(themedContext)
292                     .setTitle(R.string.set_live_wallpaper)
293                     .setAdapter(new WallpaperTargetAdapter(themedContext), new DialogInterface.OnClickListener() {
294                         @Override
295                         public void onClick(DialogInterface dialog, int which) {
296                             try {
297                                 setLiveWallpaper(v.getRootView().getWindowToken());
298                                 if (which == 1) {
299                                     // "Home screen and lock screen"; clear the lock screen so it
300                                     // shows through to the live wallpaper on home.
301                                     mWallpaperManager.clear(WallpaperManager.FLAG_LOCK);
302                                 }
303                                 setResult(RESULT_OK);
304                             } catch (RuntimeException|IOException e) {
305                                 Log.w(LOG_TAG, "Failure setting wallpaper", e);
306                             }
307                             finish();
308                         }
309                     })
310                     .show();
311         }
312     }
313 
setLiveWallpaper(IBinder windowToken)314     private void setLiveWallpaper(IBinder windowToken) {
315         mWallpaperManager.setWallpaperComponent(mWallpaperIntent.getComponent());
316         mWallpaperManager.setWallpaperOffsetSteps(0.5f, 0.0f);
317         mWallpaperManager.setWallpaperOffsets(windowToken, 0.5f, 0.0f);
318     }
319 
320     @Override
onOptionsItemSelected(MenuItem item)321     public boolean onOptionsItemSelected(MenuItem item) {
322         int id = item.getItemId();
323         if (id == R.id.configure) {
324             startActivity(mSettingsIntent);
325             return true;
326         } else if (id == R.id.set_wallpaper) {
327             setLiveWallpaper(getWindow().getDecorView());
328             return true;
329         } else if (id == android.R.id.home) {
330             onBackPressed();
331             return true;
332         }
333         return super.onOptionsItemSelected(item);
334     }
335 
336     @Override
onResume()337     public void onResume() {
338         super.onResume();
339         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
340             try {
341                 mWallpaperConnection.mEngine.setVisibility(true);
342             } catch (RemoteException e) {
343                 // Ignore
344             }
345         }
346     }
347 
348     @Override
onPause()349     public void onPause() {
350         super.onPause();
351         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
352             try {
353                 mWallpaperConnection.mEngine.setVisibility(false);
354             } catch (RemoteException e) {
355                 // Ignore
356             }
357         }
358     }
359 
360     @Override
onAttachedToWindow()361     public void onAttachedToWindow() {
362         super.onAttachedToWindow();
363 
364         getWindow().getDecorView().post(new Runnable() {
365             public void run() {
366                 if (!mWallpaperConnection.connect()) {
367                     mWallpaperConnection = null;
368                 }
369             }
370         });
371     }
372 
373     @Override
onDetachedFromWindow()374     public void onDetachedFromWindow() {
375         super.onDetachedFromWindow();
376 
377         if (mWallpaperConnection != null) {
378             mWallpaperConnection.disconnect();
379         }
380         mWallpaperConnection = null;
381     }
382 
383     @Override
dispatchTouchEvent(MotionEvent ev)384     public boolean dispatchTouchEvent(MotionEvent ev) {
385         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
386             MotionEvent dup = MotionEvent.obtainNoHistory(ev);
387             try {
388                 mWallpaperConnection.mEngine.dispatchPointer(dup);
389             } catch (RemoteException e) {
390             }
391         }
392 
393         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
394             onUserInteraction();
395         }
396         boolean handled = getWindow().superDispatchTouchEvent(ev);
397         if (!handled) {
398             handled = onTouchEvent(ev);
399         }
400 
401         if (!handled && mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
402             int action = ev.getActionMasked();
403             try {
404                 if (action == MotionEvent.ACTION_UP) {
405                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
406                             WallpaperManager.COMMAND_TAP,
407                             (int) ev.getX(), (int) ev.getY(), 0, null);
408                 } else if (action == MotionEvent.ACTION_POINTER_UP) {
409                     int pointerIndex = ev.getActionIndex();
410                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
411                             WallpaperManager.COMMAND_SECONDARY_TAP,
412                             (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0, null);
413                 }
414             } catch (RemoteException e) {
415             }
416         }
417         return handled;
418     }
419 
420     class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection {
421         final Intent mIntent;
422         IWallpaperService mService;
423         IWallpaperEngine mEngine;
424         boolean mConnected;
425 
WallpaperConnection(Intent intent)426         WallpaperConnection(Intent intent) {
427             mIntent = intent;
428         }
429 
connect()430         public boolean connect() {
431             synchronized (this) {
432                 if (!bindService(mIntent, this, Context.BIND_AUTO_CREATE)) {
433                     return false;
434                 }
435 
436                 mConnected = true;
437                 return true;
438             }
439         }
440 
disconnect()441         public void disconnect() {
442             synchronized (this) {
443                 mConnected = false;
444                 if (mEngine != null) {
445                     try {
446                         mEngine.destroy();
447                     } catch (RemoteException e) {
448                         // Ignore
449                     }
450                     mEngine = null;
451                 }
452                 try {
453                     unbindService(this);
454                 } catch (IllegalArgumentException e) {
455                     Log.w(LOG_TAG, "Can't unbind wallpaper service. "
456                             + "It might have crashed, just ignoring.", e);
457                 }
458                 mService = null;
459             }
460         }
461 
onServiceConnected(ComponentName name, IBinder service)462         public void onServiceConnected(ComponentName name, IBinder service) {
463             if (mWallpaperConnection == this) {
464                 mService = IWallpaperService.Stub.asInterface(service);
465                 try {
466                     final View root = getWindow().getDecorView();
467                     mService.attach(this, root.getWindowToken(),
468                             LayoutParams.TYPE_APPLICATION_MEDIA,
469                             true, root.getWidth(), root.getHeight(),
470                             new Rect(0, 0, 0, 0));
471                 } catch (RemoteException e) {
472                     Log.w(LOG_TAG, "Failed attaching wallpaper; clearing", e);
473                 }
474             }
475         }
476 
onServiceDisconnected(ComponentName name)477         public void onServiceDisconnected(ComponentName name) {
478             mService = null;
479             mEngine = null;
480             if (mWallpaperConnection == this) {
481                 Log.w(LOG_TAG, "Wallpaper service gone: " + name);
482             }
483         }
484 
attachEngine(IWallpaperEngine engine)485         public void attachEngine(IWallpaperEngine engine) {
486             synchronized (this) {
487                 if (mConnected) {
488                     mEngine = engine;
489                     try {
490                         engine.setVisibility(true);
491                     } catch (RemoteException e) {
492                         // Ignore
493                     }
494                 } else {
495                     try {
496                         engine.destroy();
497                     } catch (RemoteException e) {
498                         // Ignore
499                     }
500                 }
501             }
502         }
503 
setWallpaper(String name)504         public ParcelFileDescriptor setWallpaper(String name) {
505             return null;
506         }
507 
508         @Override
onWallpaperColorsChanged(WallpaperColors colors)509         public void onWallpaperColorsChanged(WallpaperColors colors) throws RemoteException {
510 
511         }
512 
513         @Override
engineShown(IWallpaperEngine engine)514         public void engineShown(IWallpaperEngine engine) throws RemoteException {
515             mLoading.post(() -> {
516                 mLoading.animate()
517                         .alpha(0f)
518                         .setDuration(220)
519                         .setInterpolator(AnimationUtils.loadInterpolator(LiveWallpaperPreview.this,
520                                 android.R.interpolator.fast_out_linear_in))
521                         .withEndAction(() -> mLoading.setVisibility(View.INVISIBLE));
522             });
523         }
524     }
525 
526     private static class WallpaperTargetAdapter extends ArrayAdapter<CharSequence> {
527 
WallpaperTargetAdapter(Context context)528         public WallpaperTargetAdapter(Context context) {
529             super(context, R.layout.wallpaper_target_dialog_item,
530                     context.getResources().getTextArray(R.array.which_wallpaper_options));
531         }
532 
533         @Override
hasStableIds()534         public boolean hasStableIds() {
535             return true;
536         }
537 
538         @Override
getItemId(int position)539         public long getItemId(int position) {
540             return position;
541         }
542 
543         @Override
getView(int position, View convertView, ViewGroup parent)544         public View getView(int position, View convertView, ViewGroup parent) {
545             TextView tv = (TextView) super.getView(position, convertView, parent);
546             tv.setCompoundDrawablesRelativeWithIntrinsicBounds(
547                     position == 0 ? R.drawable.ic_home : R.drawable.ic_device, 0, 0, 0);
548             return tv;
549         }
550     }
551 }
552