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