• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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.launcher3;
18 
19 import android.Manifest.permission;
20 import android.animation.LayoutTransition;
21 import android.annotation.TargetApi;
22 import android.app.ActionBar;
23 import android.app.Activity;
24 import android.app.WallpaperManager;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.content.res.Resources;
30 import android.database.Cursor;
31 import android.database.DataSetObserver;
32 import android.graphics.Bitmap;
33 import android.graphics.BitmapFactory;
34 import android.graphics.Canvas;
35 import android.graphics.Matrix;
36 import android.graphics.Point;
37 import android.graphics.PorterDuff;
38 import android.graphics.RectF;
39 import android.graphics.drawable.BitmapDrawable;
40 import android.graphics.drawable.Drawable;
41 import android.Manifest;
42 import android.net.Uri;
43 import android.os.AsyncTask;
44 import android.os.Build;
45 import android.os.Bundle;
46 import android.os.Process;
47 import android.provider.MediaStore;
48 import android.util.Log;
49 import android.util.Pair;
50 import android.view.ActionMode;
51 import android.view.LayoutInflater;
52 import android.view.Menu;
53 import android.view.MenuInflater;
54 import android.view.MenuItem;
55 import android.view.MotionEvent;
56 import android.view.View;
57 import android.view.View.OnClickListener;
58 import android.view.View.OnLayoutChangeListener;
59 import android.view.ViewGroup;
60 import android.view.ViewPropertyAnimator;
61 import android.view.ViewTreeObserver;
62 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
63 import android.view.WindowManager;
64 import android.view.animation.AccelerateInterpolator;
65 import android.view.animation.DecelerateInterpolator;
66 import android.widget.ArrayAdapter;
67 import android.widget.BaseAdapter;
68 import android.widget.FrameLayout;
69 import android.widget.HorizontalScrollView;
70 import android.widget.ImageView;
71 import android.widget.LinearLayout;
72 import android.widget.Toast;
73 
74 import com.android.gallery3d.common.BitmapCropTask;
75 import com.android.gallery3d.common.BitmapUtils;
76 import com.android.gallery3d.common.Utils;
77 import com.android.launcher3.util.Thunk;
78 import com.android.launcher3.util.WallpaperUtils;
79 import com.android.photos.BitmapRegionTileSource;
80 import com.android.photos.BitmapRegionTileSource.BitmapSource;
81 import com.android.photos.views.TiledImageRenderer.TileSource;
82 
83 import java.io.File;
84 import java.io.FileOutputStream;
85 import java.io.IOException;
86 import java.util.ArrayList;
87 
88 public class WallpaperPickerActivity extends WallpaperCropActivity {
89     static final String TAG = "Launcher.WallpaperPickerActivity";
90 
91     public static final int IMAGE_PICK = 5;
92     public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
93     private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
94     private static final String SELECTED_INDEX = "SELECTED_INDEX";
95     private static final int FLAG_POST_DELAY_MILLIS = 200;
96 
97     @Thunk View mSelectedTile;
98     @Thunk boolean mIgnoreNextTap;
99     @Thunk OnClickListener mThumbnailOnClickListener;
100 
101     @Thunk LinearLayout mWallpapersView;
102     @Thunk HorizontalScrollView mWallpaperScrollContainer;
103     @Thunk View mWallpaperStrip;
104 
105     @Thunk ActionMode.Callback mActionModeCallback;
106     @Thunk ActionMode mActionMode;
107 
108     @Thunk View.OnLongClickListener mLongClickListener;
109 
110     ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
111     private SavedWallpaperImages mSavedImages;
112     @Thunk int mSelectedIndex = -1;
113 
114     public static abstract class WallpaperTileInfo {
115         protected View mView;
116         public Drawable mThumb;
117 
setView(View v)118         public void setView(View v) {
119             mView = v;
120         }
onClick(WallpaperPickerActivity a)121         public void onClick(WallpaperPickerActivity a) {}
onSave(WallpaperPickerActivity a)122         public void onSave(WallpaperPickerActivity a) {}
onDelete(WallpaperPickerActivity a)123         public void onDelete(WallpaperPickerActivity a) {}
isSelectable()124         public boolean isSelectable() { return false; }
isNamelessWallpaper()125         public boolean isNamelessWallpaper() { return false; }
onIndexUpdated(CharSequence label)126         public void onIndexUpdated(CharSequence label) {
127             if (isNamelessWallpaper()) {
128                 mView.setContentDescription(label);
129             }
130         }
131     }
132 
133     public static class PickImageInfo extends WallpaperTileInfo {
134         @Override
onClick(WallpaperPickerActivity a)135         public void onClick(WallpaperPickerActivity a) {
136             Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
137             intent.setType("image/*");
138             a.startActivityForResultSafely(intent, IMAGE_PICK);
139         }
140     }
141 
142     public static class UriWallpaperInfo extends WallpaperTileInfo {
143         private Uri mUri;
UriWallpaperInfo(Uri uri)144         public UriWallpaperInfo(Uri uri) {
145             mUri = uri;
146         }
147         @Override
onClick(final WallpaperPickerActivity a)148         public void onClick(final WallpaperPickerActivity a) {
149             a.setWallpaperButtonEnabled(false);
150             final BitmapRegionTileSource.UriBitmapSource bitmapSource =
151                     new BitmapRegionTileSource.UriBitmapSource(a.getContext(), mUri);
152             a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
153 
154                 @Override
155                 public void run() {
156                     if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
157                         a.selectTile(mView);
158                         a.setWallpaperButtonEnabled(true);
159                     } else {
160                         ViewGroup parent = (ViewGroup) mView.getParent();
161                         if (parent != null) {
162                             parent.removeView(mView);
163                             Toast.makeText(a.getContext(), R.string.image_load_fail,
164                                     Toast.LENGTH_SHORT).show();
165                         }
166                     }
167                 }
168             });
169         }
170         @Override
onSave(final WallpaperPickerActivity a)171         public void onSave(final WallpaperPickerActivity a) {
172             boolean finishActivityWhenDone = true;
173             BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
174                 public void onBitmapCropped(byte[] imageBytes) {
175                     Point thumbSize = getDefaultThumbnailSize(a.getResources());
176                     // rotation is set to 0 since imageBytes has already been correctly rotated
177                     Bitmap thumb = createThumbnail(
178                             thumbSize, null, null, imageBytes, null, 0, 0, true);
179                     a.getSavedImages().writeImage(thumb, imageBytes);
180                 }
181             };
182             a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone);
183         }
184         @Override
isSelectable()185         public boolean isSelectable() {
186             return true;
187         }
188         @Override
isNamelessWallpaper()189         public boolean isNamelessWallpaper() {
190             return true;
191         }
192     }
193 
194     public static class FileWallpaperInfo extends WallpaperTileInfo {
195         private File mFile;
196 
FileWallpaperInfo(File target, Drawable thumb)197         public FileWallpaperInfo(File target, Drawable thumb) {
198             mFile = target;
199             mThumb = thumb;
200         }
201         @Override
onClick(final WallpaperPickerActivity a)202         public void onClick(final WallpaperPickerActivity a) {
203             a.setWallpaperButtonEnabled(false);
204             final BitmapRegionTileSource.UriBitmapSource bitmapSource =
205                     new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
206             a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
207 
208                 @Override
209                 public void run() {
210                     if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
211                         a.setWallpaperButtonEnabled(true);
212                     }
213                 }
214             });
215         }
216         @Override
onSave(WallpaperPickerActivity a)217         public void onSave(WallpaperPickerActivity a) {
218             a.setWallpaper(Uri.fromFile(mFile), true);
219         }
220         @Override
isSelectable()221         public boolean isSelectable() {
222             return true;
223         }
224         @Override
isNamelessWallpaper()225         public boolean isNamelessWallpaper() {
226             return true;
227         }
228     }
229 
230     public static class ResourceWallpaperInfo extends WallpaperTileInfo {
231         private Resources mResources;
232         private int mResId;
233 
ResourceWallpaperInfo(Resources res, int resId, Drawable thumb)234         public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
235             mResources = res;
236             mResId = resId;
237             mThumb = thumb;
238         }
239         @Override
onClick(final WallpaperPickerActivity a)240         public void onClick(final WallpaperPickerActivity a) {
241             a.setWallpaperButtonEnabled(false);
242             final BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
243                     new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId);
244             a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleProvider() {
245 
246                 @Override
247                 public float getScale(TileSource src) {
248                     Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
249                             a.getResources(), a.getWindowManager());
250                     RectF crop = Utils.getMaxCropRect(
251                             src.getImageWidth(), src.getImageHeight(),
252                             wallpaperSize.x, wallpaperSize.y, false);
253                     return wallpaperSize.x / crop.width();
254                 }
255             }, new Runnable() {
256 
257                 @Override
258                 public void run() {
259                     if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
260                         a.setWallpaperButtonEnabled(true);
261                     }
262                 }
263             });
264         }
265         @Override
onSave(WallpaperPickerActivity a)266         public void onSave(WallpaperPickerActivity a) {
267             boolean finishActivityWhenDone = true;
268             a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone);
269         }
270         @Override
isSelectable()271         public boolean isSelectable() {
272             return true;
273         }
274         @Override
isNamelessWallpaper()275         public boolean isNamelessWallpaper() {
276             return true;
277         }
278     }
279 
280     @TargetApi(Build.VERSION_CODES.KITKAT)
281     public static class DefaultWallpaperInfo extends WallpaperTileInfo {
DefaultWallpaperInfo(Drawable thumb)282         public DefaultWallpaperInfo(Drawable thumb) {
283             mThumb = thumb;
284         }
285         @Override
onClick(WallpaperPickerActivity a)286         public void onClick(WallpaperPickerActivity a) {
287             CropView c = a.getCropView();
288             Drawable defaultWallpaper = WallpaperManager.getInstance(a.getContext())
289                     .getBuiltInDrawable(c.getWidth(), c.getHeight(), false, 0.5f, 0.5f);
290             if (defaultWallpaper == null) {
291                 Log.w(TAG, "Null default wallpaper encountered.");
292                 c.setTileSource(null, null);
293                 return;
294             }
295 
296             LoadRequest req = new LoadRequest();
297             req.moveToLeft = false;
298             req.touchEnabled = false;
299             req.scaleProvider = new CropViewScaleProvider() {
300 
301                 @Override
302                 public float getScale(TileSource src) {
303                     return 1f;
304                 }
305             };
306             req.result = new DrawableTileSource(a.getContext(),
307                     defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
308             a.onLoadRequestComplete(req, true);
309         }
310         @Override
onSave(WallpaperPickerActivity a)311         public void onSave(WallpaperPickerActivity a) {
312             try {
313                 WallpaperManager.getInstance(a.getContext()).clear();
314                 a.setResult(Activity.RESULT_OK);
315             } catch (IOException e) {
316                 Log.w("Setting wallpaper to default threw exception", e);
317             }
318             a.finish();
319         }
320         @Override
isSelectable()321         public boolean isSelectable() {
322             return true;
323         }
324         @Override
isNamelessWallpaper()325         public boolean isNamelessWallpaper() {
326             return true;
327         }
328     }
329 
330     /**
331      * shows the system wallpaper behind the window and hides the {@link
332      * #mCropView} if visible
333      * @param visible should the system wallpaper be shown
334      */
setSystemWallpaperVisiblity(final boolean visible)335     protected void setSystemWallpaperVisiblity(final boolean visible) {
336         // hide our own wallpaper preview if necessary
337         if(!visible) {
338             mCropView.setVisibility(View.VISIBLE);
339         } else {
340             changeWallpaperFlags(visible);
341         }
342         // the change of the flag must be delayed in order to avoid flickering,
343         // a simple post / double post does not suffice here
344         mCropView.postDelayed(new Runnable() {
345             @Override
346             public void run() {
347                 if(!visible) {
348                     changeWallpaperFlags(visible);
349                 } else {
350                     mCropView.setVisibility(View.INVISIBLE);
351                 }
352             }
353         }, FLAG_POST_DELAY_MILLIS);
354     }
355 
changeWallpaperFlags(boolean visible)356     @Thunk void changeWallpaperFlags(boolean visible) {
357         int desiredWallpaperFlag = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
358         int currentWallpaperFlag = getWindow().getAttributes().flags
359                 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
360         if (desiredWallpaperFlag != currentWallpaperFlag) {
361             getWindow().setFlags(desiredWallpaperFlag,
362                     WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
363         }
364     }
365 
366     @Override
onLoadRequestComplete(LoadRequest req, boolean success)367     protected void onLoadRequestComplete(LoadRequest req, boolean success) {
368         super.onLoadRequestComplete(req, success);
369         if (success) {
370             setSystemWallpaperVisiblity(false);
371         }
372     }
373 
374     // called by onCreate; this is subclassed to overwrite WallpaperCropActivity
init()375     protected void init() {
376         setContentView(R.layout.wallpaper_picker);
377 
378         mCropView = (CropView) findViewById(R.id.cropView);
379         mCropView.setVisibility(View.INVISIBLE);
380 
381         mProgressView = findViewById(R.id.loading);
382         mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
383         mWallpaperStrip = findViewById(R.id.wallpaper_strip);
384         mCropView.setTouchCallback(new CropView.TouchCallback() {
385             ViewPropertyAnimator mAnim;
386             @Override
387             public void onTouchDown() {
388                 if (mAnim != null) {
389                     mAnim.cancel();
390                 }
391                 if (mWallpaperStrip.getAlpha() == 1f) {
392                     mIgnoreNextTap = true;
393                 }
394                 mAnim = mWallpaperStrip.animate();
395                 mAnim.alpha(0f)
396                     .setDuration(150)
397                     .withEndAction(new Runnable() {
398                         public void run() {
399                             mWallpaperStrip.setVisibility(View.INVISIBLE);
400                         }
401                     });
402                 mAnim.setInterpolator(new AccelerateInterpolator(0.75f));
403                 mAnim.start();
404             }
405             @Override
406             public void onTouchUp() {
407                 mIgnoreNextTap = false;
408             }
409             @Override
410             public void onTap() {
411                 boolean ignoreTap = mIgnoreNextTap;
412                 mIgnoreNextTap = false;
413                 if (!ignoreTap) {
414                     if (mAnim != null) {
415                         mAnim.cancel();
416                     }
417                     mWallpaperStrip.setVisibility(View.VISIBLE);
418                     mAnim = mWallpaperStrip.animate();
419                     mAnim.alpha(1f)
420                          .setDuration(150)
421                          .setInterpolator(new DecelerateInterpolator(0.75f));
422                     mAnim.start();
423                 }
424             }
425         });
426 
427         mThumbnailOnClickListener = new OnClickListener() {
428             public void onClick(View v) {
429                 if (mActionMode != null) {
430                     // When CAB is up, clicking toggles the item instead
431                     if (v.isLongClickable()) {
432                         mLongClickListener.onLongClick(v);
433                     }
434                     return;
435                 }
436                 setWallpaperButtonEnabled(true);
437                 WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
438                 if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
439                     selectTile(v);
440                 }
441                 info.onClick(WallpaperPickerActivity.this);
442             }
443         };
444         mLongClickListener = new View.OnLongClickListener() {
445             // Called when the user long-clicks on someView
446             public boolean onLongClick(View view) {
447                 CheckableFrameLayout c = (CheckableFrameLayout) view;
448                 c.toggle();
449 
450                 if (mActionMode != null) {
451                     mActionMode.invalidate();
452                 } else {
453                     // Start the CAB using the ActionMode.Callback defined below
454                     mActionMode = startActionMode(mActionModeCallback);
455                     int childCount = mWallpapersView.getChildCount();
456                     for (int i = 0; i < childCount; i++) {
457                         mWallpapersView.getChildAt(i).setSelected(false);
458                     }
459                 }
460                 return true;
461             }
462         };
463 
464         // Populate the built-in wallpapers
465         ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
466         mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
467         SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(getContext(), wallpapers);
468         populateWallpapersFromAdapter(mWallpapersView, ia, false);
469 
470         // Populate the saved wallpapers
471         mSavedImages = new SavedWallpaperImages(getContext());
472         mSavedImages.loadThumbnailsAndImageIdList();
473         populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true);
474 
475         // Populate the live wallpapers
476         final LinearLayout liveWallpapersView =
477                 (LinearLayout) findViewById(R.id.live_wallpaper_list);
478         final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(getContext());
479         a.registerDataSetObserver(new DataSetObserver() {
480             public void onChanged() {
481                 liveWallpapersView.removeAllViews();
482                 populateWallpapersFromAdapter(liveWallpapersView, a, false);
483                 initializeScrollForRtl();
484                 updateTileIndices();
485             }
486         });
487 
488         // Populate the third-party wallpaper pickers
489         final LinearLayout thirdPartyWallpapersView =
490                 (LinearLayout) findViewById(R.id.third_party_wallpaper_list);
491         final ThirdPartyWallpaperPickerListAdapter ta =
492                 new ThirdPartyWallpaperPickerListAdapter(getContext());
493         populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false);
494 
495         // Add a tile for the Gallery
496         LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
497         FrameLayout pickImageTile = (FrameLayout) getLayoutInflater().
498                 inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false);
499         masterWallpaperList.addView(pickImageTile, 0);
500 
501         // Make its background the last photo taken on external storage
502         Bitmap lastPhoto = getThumbnailOfLastPhoto();
503         if (lastPhoto != null) {
504             ImageView galleryThumbnailBg =
505                     (ImageView) pickImageTile.findViewById(R.id.wallpaper_image);
506             galleryThumbnailBg.setImageBitmap(lastPhoto);
507             int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray);
508             galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP);
509         }
510 
511         PickImageInfo pickImageInfo = new PickImageInfo();
512         pickImageTile.setTag(pickImageInfo);
513         pickImageInfo.setView(pickImageTile);
514         pickImageTile.setOnClickListener(mThumbnailOnClickListener);
515 
516         // Select the first item; wait for a layout pass so that we initialize the dimensions of
517         // cropView or the defaultWallpaperView first
518         mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
519             @Override
520             public void onLayoutChange(View v, int left, int top, int right, int bottom,
521                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
522                 if ((right - left) > 0 && (bottom - top) > 0) {
523                     if (mSelectedIndex >= 0 && mSelectedIndex < mWallpapersView.getChildCount()) {
524                         mThumbnailOnClickListener.onClick(
525                                 mWallpapersView.getChildAt(mSelectedIndex));
526                         setSystemWallpaperVisiblity(false);
527                     }
528                     v.removeOnLayoutChangeListener(this);
529                 }
530             }
531         });
532 
533         updateTileIndices();
534 
535         // Update the scroll for RTL
536         initializeScrollForRtl();
537 
538         // Create smooth layout transitions for when items are deleted
539         final LayoutTransition transitioner = new LayoutTransition();
540         transitioner.setDuration(200);
541         transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
542         transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
543         mWallpapersView.setLayoutTransition(transitioner);
544 
545         // Action bar
546         // Show the custom action bar view
547         final ActionBar actionBar = getActionBar();
548         actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
549         actionBar.getCustomView().setOnClickListener(
550                 new View.OnClickListener() {
551                     @Override
552                     public void onClick(View v) {
553                         // Ensure that a tile is slelected and loaded.
554                         if (mSelectedTile != null && mCropView.getTileSource() != null) {
555                             // Prevent user from selecting any new tile.
556                             mWallpaperStrip.setVisibility(View.GONE);
557                             actionBar.hide();
558 
559                             WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
560                             info.onSave(WallpaperPickerActivity.this);
561                         } else {
562                             // no tile was selected, so we just finish the activity and go back
563                             setResult(Activity.RESULT_OK);
564                             finish();
565                         }
566                     }
567                 });
568         mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
569 
570         // CAB for deleting items
571         mActionModeCallback = new ActionMode.Callback() {
572             // Called when the action mode is created; startActionMode() was called
573             @Override
574             public boolean onCreateActionMode(ActionMode mode, Menu menu) {
575                 // Inflate a menu resource providing context menu items
576                 MenuInflater inflater = mode.getMenuInflater();
577                 inflater.inflate(R.menu.cab_delete_wallpapers, menu);
578                 return true;
579             }
580 
581             private int numCheckedItems() {
582                 int childCount = mWallpapersView.getChildCount();
583                 int numCheckedItems = 0;
584                 for (int i = 0; i < childCount; i++) {
585                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
586                     if (c.isChecked()) {
587                         numCheckedItems++;
588                     }
589                 }
590                 return numCheckedItems;
591             }
592 
593             // Called each time the action mode is shown. Always called after onCreateActionMode,
594             // but may be called multiple times if the mode is invalidated.
595             @Override
596             public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
597                 int numCheckedItems = numCheckedItems();
598                 if (numCheckedItems == 0) {
599                     mode.finish();
600                     return true;
601                 } else {
602                     mode.setTitle(getResources().getQuantityString(
603                             R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
604                     return true;
605                 }
606             }
607 
608             // Called when the user selects a contextual menu item
609             @Override
610             public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
611                 int itemId = item.getItemId();
612                 if (itemId == R.id.menu_delete) {
613                     int childCount = mWallpapersView.getChildCount();
614                     ArrayList<View> viewsToRemove = new ArrayList<View>();
615                     boolean selectedTileRemoved = false;
616                     for (int i = 0; i < childCount; i++) {
617                         CheckableFrameLayout c =
618                                 (CheckableFrameLayout) mWallpapersView.getChildAt(i);
619                         if (c.isChecked()) {
620                             WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
621                             info.onDelete(WallpaperPickerActivity.this);
622                             viewsToRemove.add(c);
623                             if (i == mSelectedIndex) {
624                                 selectedTileRemoved = true;
625                             }
626                         }
627                     }
628                     for (View v : viewsToRemove) {
629                         mWallpapersView.removeView(v);
630                     }
631                     if (selectedTileRemoved) {
632                         mSelectedIndex = -1;
633                         mSelectedTile = null;
634                         setSystemWallpaperVisiblity(true);
635                     }
636                     updateTileIndices();
637                     mode.finish(); // Action picked, so close the CAB
638                     return true;
639                 } else {
640                     return false;
641                 }
642             }
643 
644             // Called when the user exits the action mode
645             @Override
646             public void onDestroyActionMode(ActionMode mode) {
647                 int childCount = mWallpapersView.getChildCount();
648                 for (int i = 0; i < childCount; i++) {
649                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
650                     c.setChecked(false);
651                 }
652                 if (mSelectedTile != null) {
653                     mSelectedTile.setSelected(true);
654                 }
655                 mActionMode = null;
656             }
657         };
658     }
659 
setWallpaperButtonEnabled(boolean enabled)660     public void setWallpaperButtonEnabled(boolean enabled) {
661         mSetWallpaperButton.setEnabled(enabled);
662     }
663 
selectTile(View v)664     @Thunk void selectTile(View v) {
665         if (mSelectedTile != null) {
666             mSelectedTile.setSelected(false);
667             mSelectedTile = null;
668         }
669         mSelectedTile = v;
670         v.setSelected(true);
671         mSelectedIndex = mWallpapersView.indexOfChild(v);
672         // TODO: Remove this once the accessibility framework and
673         // services have better support for selection state.
674         v.announceForAccessibility(
675                 getContext().getString(R.string.announce_selection, v.getContentDescription()));
676     }
677 
initializeScrollForRtl()678     @Thunk void initializeScrollForRtl() {
679         if (Utilities.isRtl(getResources())) {
680             final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver();
681             observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
682                 public void onGlobalLayout() {
683                     LinearLayout masterWallpaperList =
684                             (LinearLayout) findViewById(R.id.master_wallpaper_list);
685                     mWallpaperScrollContainer.scrollTo(masterWallpaperList.getWidth(), 0);
686                     mWallpaperScrollContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
687                 }
688             });
689         }
690     }
691 
getThumbnailOfLastPhoto()692     protected Bitmap getThumbnailOfLastPhoto() {
693         boolean canReadExternalStorage = getActivity().checkPermission(
694                 Manifest.permission.READ_EXTERNAL_STORAGE, Process.myPid(), Process.myUid()) ==
695                 PackageManager.PERMISSION_GRANTED;
696 
697         if (!canReadExternalStorage) {
698             // MediaStore.Images.Media.EXTERNAL_CONTENT_URI requires
699             // the READ_EXTERNAL_STORAGE permission
700             return null;
701         }
702 
703         Cursor cursor = MediaStore.Images.Media.query(getContext().getContentResolver(),
704                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
705                 new String[] { MediaStore.Images.ImageColumns._ID,
706                     MediaStore.Images.ImageColumns.DATE_TAKEN},
707                 null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
708 
709         Bitmap thumb = null;
710         if (cursor != null) {
711             if (cursor.moveToNext()) {
712                 int id = cursor.getInt(0);
713                 thumb = MediaStore.Images.Thumbnails.getThumbnail(getContext().getContentResolver(),
714                         id, MediaStore.Images.Thumbnails.MINI_KIND, null);
715             }
716             cursor.close();
717         }
718         return thumb;
719     }
720 
onStop()721     public void onStop() {
722         super.onStop();
723         mWallpaperStrip = findViewById(R.id.wallpaper_strip);
724         if (mWallpaperStrip.getAlpha() < 1f) {
725             mWallpaperStrip.setAlpha(1f);
726             mWallpaperStrip.setVisibility(View.VISIBLE);
727         }
728     }
729 
onSaveInstanceState(Bundle outState)730     public void onSaveInstanceState(Bundle outState) {
731         outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles);
732         outState.putInt(SELECTED_INDEX, mSelectedIndex);
733     }
734 
onRestoreInstanceState(Bundle savedInstanceState)735     protected void onRestoreInstanceState(Bundle savedInstanceState) {
736         ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES);
737         for (Uri uri : uris) {
738             addTemporaryWallpaperTile(uri, true);
739         }
740         mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, -1);
741     }
742 
populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, boolean addLongPressHandler)743     @Thunk void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter,
744             boolean addLongPressHandler) {
745         for (int i = 0; i < adapter.getCount(); i++) {
746             FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent);
747             parent.addView(thumbnail, i);
748             WallpaperTileInfo info = (WallpaperTileInfo) adapter.getItem(i);
749             thumbnail.setTag(info);
750             info.setView(thumbnail);
751             if (addLongPressHandler) {
752                 addLongPressHandler(thumbnail);
753             }
754             thumbnail.setOnClickListener(mThumbnailOnClickListener);
755         }
756     }
757 
updateTileIndices()758     @Thunk void updateTileIndices() {
759         LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
760         final int childCount = masterWallpaperList.getChildCount();
761         final Resources res = getResources();
762 
763         // Do two passes; the first pass gets the total number of tiles
764         int numTiles = 0;
765         for (int passNum = 0; passNum < 2; passNum++) {
766             int tileIndex = 0;
767             for (int i = 0; i < childCount; i++) {
768                 View child = masterWallpaperList.getChildAt(i);
769                 LinearLayout subList;
770 
771                 int subListStart;
772                 int subListEnd;
773                 if (child.getTag() instanceof WallpaperTileInfo) {
774                     subList = masterWallpaperList;
775                     subListStart = i;
776                     subListEnd = i + 1;
777                 } else { // if (child instanceof LinearLayout) {
778                     subList = (LinearLayout) child;
779                     subListStart = 0;
780                     subListEnd = subList.getChildCount();
781                 }
782 
783                 for (int j = subListStart; j < subListEnd; j++) {
784                     WallpaperTileInfo info = (WallpaperTileInfo) subList.getChildAt(j).getTag();
785                     if (info.isNamelessWallpaper()) {
786                         if (passNum == 0) {
787                             numTiles++;
788                         } else {
789                             CharSequence label = res.getString(
790                                     R.string.wallpaper_accessibility_name, ++tileIndex, numTiles);
791                             info.onIndexUpdated(label);
792                         }
793                     }
794                 }
795             }
796         }
797     }
798 
getDefaultThumbnailSize(Resources res)799     @Thunk static Point getDefaultThumbnailSize(Resources res) {
800         return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth),
801                 res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight));
802 
803     }
804 
createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes, Resources res, int resId, int rotation, boolean leftAligned)805     @Thunk static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes,
806             Resources res, int resId, int rotation, boolean leftAligned) {
807         int width = size.x;
808         int height = size.y;
809 
810         BitmapCropTask cropTask;
811         if (uri != null) {
812             cropTask = new BitmapCropTask(
813                     context, uri, null, rotation, width, height, false, true, null);
814         } else if (imageBytes != null) {
815             cropTask = new BitmapCropTask(
816                     imageBytes, null, rotation, width, height, false, true, null);
817         }  else {
818             cropTask = new BitmapCropTask(
819                     context, res, resId, null, rotation, width, height, false, true, null);
820         }
821         Point bounds = cropTask.getImageBounds();
822         if (bounds == null || bounds.x == 0 || bounds.y == 0) {
823             return null;
824         }
825 
826         Matrix rotateMatrix = new Matrix();
827         rotateMatrix.setRotate(rotation);
828         float[] rotatedBounds = new float[] { bounds.x, bounds.y };
829         rotateMatrix.mapPoints(rotatedBounds);
830         rotatedBounds[0] = Math.abs(rotatedBounds[0]);
831         rotatedBounds[1] = Math.abs(rotatedBounds[1]);
832 
833         RectF cropRect = Utils.getMaxCropRect(
834                 (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
835         cropTask.setCropBounds(cropRect);
836 
837         if (cropTask.cropBitmap()) {
838             return cropTask.getCroppedBitmap();
839         } else {
840             return null;
841         }
842     }
843 
addTemporaryWallpaperTile(final Uri uri, boolean fromRestore)844     private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) {
845         mTempWallpaperTiles.add(uri);
846         // Add a tile for the image picked from Gallery
847         final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
848                 inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
849         pickedImageThumbnail.setVisibility(View.GONE);
850         mWallpapersView.addView(pickedImageThumbnail, 0);
851 
852         // Load the thumbnail
853         final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
854         final Point defaultSize = getDefaultThumbnailSize(this.getResources());
855         final Context context = getContext();
856         new AsyncTask<Void, Bitmap, Bitmap>() {
857             protected Bitmap doInBackground(Void...args) {
858                 try {
859                     int rotation = BitmapUtils.getRotationFromExif(context, uri);
860                     return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
861                 } catch (SecurityException securityException) {
862                     if (isActivityDestroyed()) {
863                         // Temporarily granted permissions are revoked when the activity
864                         // finishes, potentially resulting in a SecurityException here.
865                         // Even though {@link #isDestroyed} might also return true in different
866                         // situations where the configuration changes, we are fine with
867                         // catching these cases here as well.
868                         cancel(false);
869                     } else {
870                         // otherwise it had a different cause and we throw it further
871                         throw securityException;
872                     }
873                     return null;
874                 }
875             }
876             protected void onPostExecute(Bitmap thumb) {
877                 if (!isCancelled() && thumb != null) {
878                     image.setImageBitmap(thumb);
879                     Drawable thumbDrawable = image.getDrawable();
880                     thumbDrawable.setDither(true);
881                     pickedImageThumbnail.setVisibility(View.VISIBLE);
882                 } else {
883                     Log.e(TAG, "Error loading thumbnail for uri=" + uri);
884                 }
885             }
886         }.execute();
887 
888         UriWallpaperInfo info = new UriWallpaperInfo(uri);
889         pickedImageThumbnail.setTag(info);
890         info.setView(pickedImageThumbnail);
891         addLongPressHandler(pickedImageThumbnail);
892         updateTileIndices();
893         pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
894         if (!fromRestore) {
895             mThumbnailOnClickListener.onClick(pickedImageThumbnail);
896         }
897     }
898 
onActivityResult(int requestCode, int resultCode, Intent data)899     public void onActivityResult(int requestCode, int resultCode, Intent data) {
900         if (requestCode == IMAGE_PICK && resultCode == Activity.RESULT_OK) {
901             if (data != null && data.getData() != null) {
902                 Uri uri = data.getData();
903                 addTemporaryWallpaperTile(uri, false);
904             }
905         } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY
906                 && resultCode == Activity.RESULT_OK) {
907             // Something was set on the third-party activity.
908             setResult(Activity.RESULT_OK);
909             finish();
910         }
911     }
912 
addLongPressHandler(View v)913     private void addLongPressHandler(View v) {
914         v.setOnLongClickListener(mLongClickListener);
915 
916         // Enable stylus button to also trigger long click.
917         final StylusEventHelper stylusEventHelper = new StylusEventHelper(v);
918         v.setOnTouchListener(new View.OnTouchListener() {
919             @Override
920             public boolean onTouch(View view, MotionEvent event) {
921                 return stylusEventHelper.checkAndPerformStylusEvent(event);
922             }
923         });
924     }
925 
findBundledWallpapers()926     private ArrayList<WallpaperTileInfo> findBundledWallpapers() {
927         final PackageManager pm = getContext().getPackageManager();
928         final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24);
929 
930         Partner partner = Partner.get(pm);
931         if (partner != null) {
932             final Resources partnerRes = partner.getResources();
933             final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array",
934                     partner.getPackageName());
935             if (resId != 0) {
936                 addWallpapers(bundled, partnerRes, partner.getPackageName(), resId);
937             }
938 
939             // Add system wallpapers
940             File systemDir = partner.getWallpaperDirectory();
941             if (systemDir != null && systemDir.isDirectory()) {
942                 for (File file : systemDir.listFiles()) {
943                     if (!file.isFile()) {
944                         continue;
945                     }
946                     String name = file.getName();
947                     int dotPos = name.lastIndexOf('.');
948                     String extension = "";
949                     if (dotPos >= -1) {
950                         extension = name.substring(dotPos);
951                         name = name.substring(0, dotPos);
952                     }
953 
954                     if (name.endsWith("_small")) {
955                         // it is a thumbnail
956                         continue;
957                     }
958 
959                     File thumbnail = new File(systemDir, name + "_small" + extension);
960                     Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath());
961                     if (thumb != null) {
962                         bundled.add(new FileWallpaperInfo(file, new BitmapDrawable(thumb)));
963                     }
964                 }
965             }
966         }
967 
968         Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
969         if (r != null) {
970             try {
971                 Resources wallpaperRes = getContext().getPackageManager()
972                         .getResourcesForApplication(r.first);
973                 addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second);
974             } catch (PackageManager.NameNotFoundException e) {
975             }
976         }
977 
978         if (partner == null || !partner.hideDefaultWallpaper()) {
979             // Add an entry for the default wallpaper (stored in system resources)
980             WallpaperTileInfo defaultWallpaperInfo = Utilities.ATLEAST_KITKAT
981                     ? getDefaultWallpaper() : getPreKKDefaultWallpaperInfo();
982             if (defaultWallpaperInfo != null) {
983                 bundled.add(0, defaultWallpaperInfo);
984             }
985         }
986         return bundled;
987     }
988 
writeImageToFileAsJpeg(File f, Bitmap b)989     private boolean writeImageToFileAsJpeg(File f, Bitmap b) {
990         try {
991             f.createNewFile();
992             FileOutputStream thumbFileStream =
993                     getContext().openFileOutput(f.getName(), Context.MODE_PRIVATE);
994             b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
995             thumbFileStream.close();
996             return true;
997         } catch (IOException e) {
998             Log.e(TAG, "Error while writing bitmap to file " + e);
999             f.delete();
1000         }
1001         return false;
1002     }
1003 
getDefaultThumbFile()1004     private File getDefaultThumbFile() {
1005         return new File(getContext().getFilesDir(), Build.VERSION.SDK_INT
1006                 + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL);
1007     }
1008 
saveDefaultWallpaperThumb(Bitmap b)1009     private boolean saveDefaultWallpaperThumb(Bitmap b) {
1010         // Delete old thumbnails.
1011         new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL_OLD).delete();
1012         new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
1013 
1014         for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) {
1015             new File(getContext().getFilesDir(), i + "_"
1016                     + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
1017         }
1018         return writeImageToFileAsJpeg(getDefaultThumbFile(), b);
1019     }
1020 
getPreKKDefaultWallpaperInfo()1021     private ResourceWallpaperInfo getPreKKDefaultWallpaperInfo() {
1022         Resources sysRes = Resources.getSystem();
1023         int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
1024 
1025         File defaultThumbFile = getDefaultThumbFile();
1026         Bitmap thumb = null;
1027         boolean defaultWallpaperExists = false;
1028         if (defaultThumbFile.exists()) {
1029             thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
1030             defaultWallpaperExists = true;
1031         } else {
1032             Resources res = getResources();
1033             Point defaultThumbSize = getDefaultThumbnailSize(res);
1034             int rotation = BitmapUtils.getRotationFromExif(res, resId);
1035             thumb = createThumbnail(
1036                     defaultThumbSize, getContext(), null, null, sysRes, resId, rotation, false);
1037             if (thumb != null) {
1038                 defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
1039             }
1040         }
1041         if (defaultWallpaperExists) {
1042             return new ResourceWallpaperInfo(sysRes, resId, new BitmapDrawable(thumb));
1043         }
1044         return null;
1045     }
1046 
1047     @TargetApi(Build.VERSION_CODES.KITKAT)
getDefaultWallpaper()1048     private DefaultWallpaperInfo getDefaultWallpaper() {
1049         File defaultThumbFile = getDefaultThumbFile();
1050         Bitmap thumb = null;
1051         boolean defaultWallpaperExists = false;
1052         if (defaultThumbFile.exists()) {
1053             thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
1054             defaultWallpaperExists = true;
1055         } else {
1056             Resources res = getResources();
1057             Point defaultThumbSize = getDefaultThumbnailSize(res);
1058             Drawable wallpaperDrawable = WallpaperManager.getInstance(getContext()).getBuiltInDrawable(
1059                     defaultThumbSize.x, defaultThumbSize.y, true, 0.5f, 0.5f);
1060             if (wallpaperDrawable != null) {
1061                 thumb = Bitmap.createBitmap(
1062                         defaultThumbSize.x, defaultThumbSize.y, Bitmap.Config.ARGB_8888);
1063                 Canvas c = new Canvas(thumb);
1064                 wallpaperDrawable.setBounds(0, 0, defaultThumbSize.x, defaultThumbSize.y);
1065                 wallpaperDrawable.draw(c);
1066                 c.setBitmap(null);
1067             }
1068             if (thumb != null) {
1069                 defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
1070             }
1071         }
1072         if (defaultWallpaperExists) {
1073             return new DefaultWallpaperInfo(new BitmapDrawable(thumb));
1074         }
1075         return null;
1076     }
1077 
getWallpaperArrayResourceId()1078     public Pair<ApplicationInfo, Integer> getWallpaperArrayResourceId() {
1079         // Context.getPackageName() may return the "original" package name,
1080         // com.android.launcher3; Resources needs the real package name,
1081         // com.android.launcher3. So we ask Resources for what it thinks the
1082         // package name should be.
1083         final String packageName = getResources().getResourcePackageName(R.array.wallpapers);
1084         try {
1085             ApplicationInfo info = getContext().getPackageManager().getApplicationInfo(packageName, 0);
1086             return new Pair<ApplicationInfo, Integer>(info, R.array.wallpapers);
1087         } catch (PackageManager.NameNotFoundException e) {
1088             return null;
1089         }
1090     }
1091 
addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res, String packageName, int listResId)1092     private void addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res,
1093             String packageName, int listResId) {
1094         final String[] extras = res.getStringArray(listResId);
1095         for (String extra : extras) {
1096             int resId = res.getIdentifier(extra, "drawable", packageName);
1097             if (resId != 0) {
1098                 final int thumbRes = res.getIdentifier(extra + "_small", "drawable", packageName);
1099 
1100                 if (thumbRes != 0) {
1101                     ResourceWallpaperInfo wallpaperInfo =
1102                             new ResourceWallpaperInfo(res, resId, res.getDrawable(thumbRes));
1103                     known.add(wallpaperInfo);
1104                     // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
1105                 }
1106             } else {
1107                 Log.e(TAG, "Couldn't find wallpaper " + extra);
1108             }
1109         }
1110     }
1111 
getCropView()1112     public CropView getCropView() {
1113         return mCropView;
1114     }
1115 
getSavedImages()1116     public SavedWallpaperImages getSavedImages() {
1117         return mSavedImages;
1118     }
1119 
1120     private static class SimpleWallpapersAdapter extends ArrayAdapter<WallpaperTileInfo> {
1121         private final LayoutInflater mLayoutInflater;
1122 
SimpleWallpapersAdapter(Context context, ArrayList<WallpaperTileInfo> wallpapers)1123         SimpleWallpapersAdapter(Context context, ArrayList<WallpaperTileInfo> wallpapers) {
1124             super(context, R.layout.wallpaper_picker_item, wallpapers);
1125             mLayoutInflater = LayoutInflater.from(context);
1126         }
1127 
getView(int position, View convertView, ViewGroup parent)1128         public View getView(int position, View convertView, ViewGroup parent) {
1129             Drawable thumb = getItem(position).mThumb;
1130             if (thumb == null) {
1131                 Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
1132             }
1133             return createImageTileView(mLayoutInflater, convertView, parent, thumb);
1134         }
1135     }
1136 
createImageTileView(LayoutInflater layoutInflater, View convertView, ViewGroup parent, Drawable thumb)1137     public static View createImageTileView(LayoutInflater layoutInflater,
1138             View convertView, ViewGroup parent, Drawable thumb) {
1139         View view;
1140 
1141         if (convertView == null) {
1142             view = layoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false);
1143         } else {
1144             view = convertView;
1145         }
1146 
1147         ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
1148 
1149         if (thumb != null) {
1150             image.setImageDrawable(thumb);
1151             thumb.setDither(true);
1152         }
1153 
1154         return view;
1155     }
1156 
startActivityForResultSafely(Intent intent, int requestCode)1157     public void startActivityForResultSafely(Intent intent, int requestCode) {
1158         Utilities.startActivityForResultSafely(getActivity(), intent, requestCode);
1159     }
1160 
1161     @Override
enableRotation()1162     public boolean enableRotation() {
1163         // Check if rotation is enabled for this device.
1164         if (Utilities.isRotationAllowedForDevice(getContext()))
1165             return true;
1166 
1167         // Check if the user has specifically enabled rotation via preferences.
1168         return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), true);
1169     }
1170 }
1171