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