WallpaperChooserDialogFragment.java revision 3a9fcedbcd235372cd2ab64f825a0b5b3937f59e
1/*
2 * Copyright (C) 2010 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 */
16package com.android.launcher2;
17
18import android.app.Activity;
19import android.app.Dialog;
20import android.app.DialogFragment;
21import android.app.WallpaperManager;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.res.Resources;
25import android.graphics.Bitmap;
26import android.graphics.BitmapFactory;
27import android.graphics.Canvas;
28import android.graphics.ColorFilter;
29import android.graphics.drawable.Drawable;
30import android.os.AsyncTask;
31import android.os.Bundle;
32import android.util.Log;
33import android.view.LayoutInflater;
34import android.view.View;
35import android.view.View.OnClickListener;
36import android.view.ViewGroup;
37import android.widget.AdapterView;
38import android.widget.BaseAdapter;
39import android.widget.Gallery;
40import android.widget.ImageView;
41import android.widget.ListAdapter;
42import android.widget.SpinnerAdapter;
43
44import com.android.launcher.R;
45
46import java.io.IOException;
47import java.util.ArrayList;
48
49public class WallpaperChooserDialogFragment extends DialogFragment implements
50        AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener {
51
52    private static final String TAG = "Launcher.WallpaperChooserDialogFragment";
53    private static final String EMBEDDED_KEY = "com.android.launcher2."
54            + "WallpaperChooserDialogFragment.EMBEDDED_KEY";
55
56    private boolean mEmbedded;
57    private Bitmap mBitmap = null;
58
59    private ArrayList<Integer> mThumbs;
60    private ArrayList<Integer> mImages;
61    private WallpaperLoader mLoader;
62    private WallpaperDrawable mWallpaperDrawable = new WallpaperDrawable();
63
64    public static WallpaperChooserDialogFragment newInstance() {
65        WallpaperChooserDialogFragment fragment = new WallpaperChooserDialogFragment();
66        fragment.setCancelable(true);
67        return fragment;
68    }
69
70    @Override
71    public void onCreate(Bundle savedInstanceState) {
72        super.onCreate(savedInstanceState);
73        if (savedInstanceState != null && savedInstanceState.containsKey(EMBEDDED_KEY)) {
74            mEmbedded = savedInstanceState.getBoolean(EMBEDDED_KEY);
75        } else {
76            mEmbedded = isInLayout();
77        }
78    }
79
80    @Override
81    public void onSaveInstanceState(Bundle outState) {
82        outState.putBoolean(EMBEDDED_KEY, mEmbedded);
83    }
84
85    private void cancelLoader() {
86        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
87            mLoader.cancel(true);
88            mLoader = null;
89        }
90    }
91
92    @Override
93    public void onDetach() {
94        super.onDetach();
95
96        cancelLoader();
97    }
98
99    @Override
100    public void onDestroy() {
101        super.onDestroy();
102
103        cancelLoader();
104    }
105
106    @Override
107    public void onDismiss(DialogInterface dialog) {
108        super.onDismiss(dialog);
109        /* On orientation changes, the dialog is effectively "dismissed" so this is called
110         * when the activity is no longer associated with this dying dialog fragment. We
111         * should just safely ignore this case by checking if getActivity() returns null
112         */
113        Activity activity = getActivity();
114        if (activity != null) {
115            activity.finish();
116        }
117    }
118
119    /* This will only be called when in XLarge mode, since this Fragment is invoked like
120     * a dialog in that mode
121     */
122    @Override
123    public Dialog onCreateDialog(Bundle savedInstanceState) {
124        findWallpapers();
125
126        return null;
127    }
128
129    @Override
130    public View onCreateView(LayoutInflater inflater, ViewGroup container,
131            Bundle savedInstanceState) {
132        findWallpapers();
133
134        /* If this fragment is embedded in the layout of this activity, then we should
135         * generate a view to display. Otherwise, a dialog will be created in
136         * onCreateDialog()
137         */
138        if (mEmbedded) {
139            View view = inflater.inflate(R.layout.wallpaper_chooser, container, false);
140            view.setBackground(mWallpaperDrawable);
141
142            final Gallery gallery = (Gallery) view.findViewById(R.id.gallery);
143            gallery.setCallbackDuringFling(false);
144            gallery.setOnItemSelectedListener(this);
145            gallery.setAdapter(new ImageAdapter(getActivity()));
146
147            View setButton = view.findViewById(R.id.set);
148            setButton.setOnClickListener(new OnClickListener() {
149                @Override
150                public void onClick(View v) {
151                    selectWallpaper(gallery.getSelectedItemPosition());
152                }
153            });
154            return view;
155        }
156        return null;
157    }
158
159    private void selectWallpaper(int position) {
160        try {
161            WallpaperManager wpm = (WallpaperManager) getActivity().getSystemService(
162                    Context.WALLPAPER_SERVICE);
163            wpm.setResource(mImages.get(position));
164            Activity activity = getActivity();
165            activity.setResult(Activity.RESULT_OK);
166            activity.finish();
167        } catch (IOException e) {
168            Log.e(TAG, "Failed to set wallpaper: " + e);
169        }
170    }
171
172    // Click handler for the Dialog's GridView
173    @Override
174    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
175        selectWallpaper(position);
176    }
177
178    // Selection handler for the embedded Gallery view
179    @Override
180    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
181        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
182            mLoader.cancel();
183        }
184        mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
185    }
186
187    @Override
188    public void onNothingSelected(AdapterView<?> parent) {
189    }
190
191    private void findWallpapers() {
192        mThumbs = new ArrayList<Integer>(24);
193        mImages = new ArrayList<Integer>(24);
194
195        final Resources resources = getResources();
196        // Context.getPackageName() may return the "original" package name,
197        // com.android.launcher2; Resources needs the real package name,
198        // com.android.launcher. So we ask Resources for what it thinks the
199        // package name should be.
200        final String packageName = resources.getResourcePackageName(R.array.wallpapers);
201
202        addWallpapers(resources, packageName, R.array.wallpapers);
203        addWallpapers(resources, packageName, R.array.extra_wallpapers);
204    }
205
206    private void addWallpapers(Resources resources, String packageName, int list) {
207        final String[] extras = resources.getStringArray(list);
208        for (String extra : extras) {
209            int res = resources.getIdentifier(extra, "drawable", packageName);
210            if (res != 0) {
211                final int thumbRes = resources.getIdentifier(extra + "_small",
212                        "drawable", packageName);
213
214                if (thumbRes != 0) {
215                    mThumbs.add(thumbRes);
216                    mImages.add(res);
217                    // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
218                }
219            }
220        }
221    }
222
223    private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
224        private LayoutInflater mLayoutInflater;
225
226        ImageAdapter(Activity activity) {
227            mLayoutInflater = activity.getLayoutInflater();
228        }
229
230        public int getCount() {
231            return mThumbs.size();
232        }
233
234        public Object getItem(int position) {
235            return position;
236        }
237
238        public long getItemId(int position) {
239            return position;
240        }
241
242        public View getView(int position, View convertView, ViewGroup parent) {
243            View view;
244
245            if (convertView == null) {
246                view = mLayoutInflater.inflate(R.layout.wallpaper_item, parent, false);
247            } else {
248                view = convertView;
249            }
250
251            ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
252
253            int thumbRes = mThumbs.get(position);
254            image.setImageResource(thumbRes);
255            Drawable thumbDrawable = image.getDrawable();
256            if (thumbDrawable != null) {
257                thumbDrawable.setDither(true);
258            } else {
259                Log.e(TAG, "Error decoding thumbnail resId=" + thumbRes + " for wallpaper #"
260                        + position);
261            }
262
263            return view;
264        }
265    }
266
267    class WallpaperLoader extends AsyncTask<Integer, Void, Bitmap> {
268        BitmapFactory.Options mOptions;
269
270        WallpaperLoader() {
271            mOptions = new BitmapFactory.Options();
272            mOptions.inDither = false;
273            mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
274        }
275
276        @Override
277        protected Bitmap doInBackground(Integer... params) {
278            if (isCancelled()) return null;
279            try {
280                return BitmapFactory.decodeResource(getResources(),
281                        mImages.get(params[0]), mOptions);
282            } catch (OutOfMemoryError e) {
283                return null;
284            }
285        }
286
287        @Override
288        protected void onPostExecute(Bitmap b) {
289            if (b == null) return;
290
291            if (!isCancelled() && !mOptions.mCancel) {
292                // Help the GC
293                if (mBitmap != null) {
294                    mBitmap.recycle();
295                }
296
297                View v = getView();
298                if (v != null) {
299                    mBitmap = b;
300                    mWallpaperDrawable.setBitmap(b);
301                    v.postInvalidate();
302                } else {
303                    mBitmap = null;
304                    mWallpaperDrawable.setBitmap(null);
305                }
306                mLoader = null;
307            } else {
308               b.recycle();
309            }
310        }
311
312        void cancel() {
313            mOptions.requestCancelDecode();
314            super.cancel(true);
315        }
316    }
317
318    /**
319     * Custom drawable that centers the bitmap fed to it.
320     */
321    static class WallpaperDrawable extends Drawable {
322
323        Bitmap mBitmap;
324        int mIntrinsicWidth;
325        int mIntrinsicHeight;
326
327        /* package */void setBitmap(Bitmap bitmap) {
328            mBitmap = bitmap;
329            if (mBitmap == null)
330                return;
331            mIntrinsicWidth = mBitmap.getWidth();
332            mIntrinsicHeight = mBitmap.getHeight();
333        }
334
335        @Override
336        public void draw(Canvas canvas) {
337            if (mBitmap == null) return;
338            int width = canvas.getWidth();
339            int height = canvas.getHeight();
340            int x = (width - mIntrinsicWidth) / 2;
341            int y = (height - mIntrinsicHeight) / 2;
342            canvas.drawBitmap(mBitmap, x, y, null);
343        }
344
345        @Override
346        public int getOpacity() {
347            return android.graphics.PixelFormat.OPAQUE;
348        }
349
350        @Override
351        public void setAlpha(int alpha) {
352            // Ignore
353        }
354
355        @Override
356        public void setColorFilter(ColorFilter cf) {
357            // Ignore
358        }
359    }
360}
361