WallpaperChooserDialogFragment.java revision 2a898a0ac54beab0ae56c97eeccc017b02d61453
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 com.android.launcher.R;
19
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.app.Dialog;
23import android.app.DialogFragment;
24import android.app.WallpaperManager;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.res.Resources;
28import android.graphics.Bitmap;
29import android.graphics.BitmapFactory;
30import android.graphics.drawable.Drawable;
31import android.os.AsyncTask;
32import android.os.Bundle;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.View.OnClickListener;
37import android.view.ViewGroup;
38import android.widget.AdapterView;
39import android.widget.BaseAdapter;
40import android.widget.Gallery;
41import android.widget.GridView;
42import android.widget.ImageView;
43import android.widget.ListAdapter;
44import android.widget.SpinnerAdapter;
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 ImageView mImageView = null;
58    private Bitmap mBitmap = null;
59
60    private ArrayList<Integer> mThumbs;
61    private ArrayList<Integer> mImages;
62    private WallpaperLoader mLoader;
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    @Override
86    public void onDestroy() {
87        super.onDestroy();
88
89        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
90            mLoader.cancel(true);
91            mLoader = null;
92        }
93    }
94
95    @Override
96    public void onDismiss(DialogInterface dialog) {
97        super.onDismiss(dialog);
98        /* On orientation changes, the dialog is effectively "dismissed" so this is called
99         * when the activity is no longer associated with this dying dialog fragment. We
100         * should just safely ignore this case by checking if getActivity() returns null
101         */
102        Activity activity = getActivity();
103        if (activity != null) {
104            activity.finish();
105        }
106    }
107
108    /* This will only be called when in XLarge mode, since this Fragment is invoked like
109     * a dialog in that mode
110     */
111    @Override
112    public Dialog onCreateDialog(Bundle savedInstanceState) {
113        final View v = getActivity().getLayoutInflater().inflate(
114                R.layout.wallpaper_chooser, null, false);
115
116        GridView gridView = (GridView) v.findViewById(R.id.gallery);
117        gridView.setOnItemClickListener(this);
118        gridView.setAdapter(new ImageAdapter(getActivity()));
119
120        final int viewInset =
121                getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_inset);
122
123        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
124        builder.setNegativeButton(R.string.wallpaper_cancel, null);
125        builder.setTitle(R.string.wallpaper_dialog_title);
126        builder.setView(gridView, viewInset, viewInset, viewInset, viewInset);
127        return builder.create();
128    }
129
130    @Override
131    public View onCreateView(LayoutInflater inflater, ViewGroup container,
132            Bundle savedInstanceState) {
133        findWallpapers();
134
135        /* If this fragment is embedded in the layout of this activity, then we should
136         * generate a view to display. Otherwise, a dialog will be created in
137         * onCreateDialog()
138         */
139        if (mEmbedded) {
140            View view = inflater.inflate(R.layout.wallpaper_chooser, container, false);
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            mImageView = (ImageView) view.findViewById(R.id.wallpaper);
155            return view;
156        }
157        return null;
158    }
159
160    private void selectWallpaper(int position) {
161        try {
162            WallpaperManager wpm = (WallpaperManager) getActivity().getSystemService(
163                    Context.WALLPAPER_SERVICE);
164            wpm.setResource(mImages.get(position));
165            Activity activity = getActivity();
166            activity.setResult(Activity.RESULT_OK);
167            activity.finish();
168        } catch (IOException e) {
169            Log.e(TAG, "Failed to set wallpaper: " + e);
170        }
171    }
172
173    // Click handler for the Dialog's GridView
174    @Override
175    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
176        selectWallpaper(position);
177    }
178
179    // Selection handler for the embedded Gallery view
180    @Override
181    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
182        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
183            mLoader.cancel();
184        }
185        mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
186    }
187
188    @Override
189    public void onNothingSelected(AdapterView<?> parent) {
190    }
191
192    private void findWallpapers() {
193        mThumbs = new ArrayList<Integer>(24);
194        mImages = new ArrayList<Integer>(24);
195
196        final Resources resources = getResources();
197        // Context.getPackageName() may return the "original" package name,
198        // com.android.launcher2; Resources needs the real package name,
199        // com.android.launcher. So we ask Resources for what it thinks the
200        // package name should be.
201        final String packageName = resources.getResourcePackageName(R.array.wallpapers);
202
203        addWallpapers(resources, packageName, R.array.wallpapers);
204        addWallpapers(resources, packageName, R.array.extra_wallpapers);
205    }
206
207    private void addWallpapers(Resources resources, String packageName, int list) {
208        final String[] extras = resources.getStringArray(list);
209        for (String extra : extras) {
210            int res = resources.getIdentifier(extra, "drawable", packageName);
211            if (res != 0) {
212                final int thumbRes = resources.getIdentifier(extra + "_small",
213                        "drawable", packageName);
214
215                if (thumbRes != 0) {
216                    mThumbs.add(thumbRes);
217                    mImages.add(res);
218                    // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
219                }
220            }
221        }
222    }
223
224    private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
225        private LayoutInflater mLayoutInflater;
226
227        ImageAdapter(Activity activity) {
228            mLayoutInflater = activity.getLayoutInflater();
229        }
230
231        public int getCount() {
232            return mThumbs.size();
233        }
234
235        public Object getItem(int position) {
236            return position;
237        }
238
239        public long getItemId(int position) {
240            return position;
241        }
242
243        public View getView(int position, View convertView, ViewGroup parent) {
244            ImageView image;
245
246            if (convertView == null) {
247                image = (ImageView) mLayoutInflater.inflate(R.layout.wallpaper_item, parent, false);
248            } else {
249                image = (ImageView) convertView;
250            }
251
252            int thumbRes = mThumbs.get(position);
253            image.setImageResource(thumbRes);
254            Drawable thumbDrawable = image.getDrawable();
255            if (thumbDrawable != null) {
256                thumbDrawable.setDither(true);
257            } else {
258                Log.e(TAG, "Error decoding thumbnail resId=" + thumbRes + " for wallpaper #"
259                        + position);
260            }
261
262            return image;
263        }
264    }
265
266    class WallpaperLoader extends AsyncTask<Integer, Void, Bitmap> {
267        BitmapFactory.Options mOptions;
268
269        WallpaperLoader() {
270            mOptions = new BitmapFactory.Options();
271            mOptions.inDither = false;
272            mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
273        }
274
275        @Override
276        protected Bitmap doInBackground(Integer... params) {
277            if (isCancelled()) return null;
278            try {
279                return BitmapFactory.decodeResource(getResources(),
280                        mImages.get(params[0]), mOptions);
281            } catch (OutOfMemoryError e) {
282                return null;
283            }
284        }
285
286        @Override
287        protected void onPostExecute(Bitmap b) {
288            if (b == null) return;
289
290            if (!isCancelled() && !mOptions.mCancel) {
291                // Help the GC
292                if (mBitmap != null) {
293                    mBitmap.recycle();
294                }
295
296                // This should always be the case, but check anyways
297                final ImageView view = mImageView;
298                if (view != null) {
299                    view.setImageBitmap(b);
300
301                    mBitmap = b;
302
303                    final Drawable drawable = view.getDrawable();
304                    drawable.setFilterBitmap(true);
305                    drawable.setDither(true);
306
307                    view.postInvalidate();
308                }
309
310                mLoader = null;
311            } else {
312               b.recycle();
313            }
314        }
315
316        void cancel() {
317            mOptions.requestCancelDecode();
318            super.cancel(true);
319        }
320    }
321}