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