13527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck/*
23527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * Copyright (C) 2010 The Android Open Source Project
33527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck *
43527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * Licensed under the Apache License, Version 2.0 (the "License");
53527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * you may not use this file except in compliance with the License.
63527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * You may obtain a copy of the License at
73527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck *
83527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck *      http://www.apache.org/licenses/LICENSE-2.0
93527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck *
103527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * Unless required by applicable law or agreed to in writing, software
113527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * distributed under the License is distributed on an "AS IS" BASIS,
123527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * See the License for the specific language governing permissions and
143527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * limitations under the License.
153527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck */
163527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
173527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckpackage com.android.browser;
183527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
193527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.app.ProgressDialog;
203527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.app.WallpaperManager;
213527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.content.Context;
223527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.content.DialogInterface;
233527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.graphics.Bitmap;
24b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdochimport android.graphics.BitmapFactory;
253527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.graphics.Canvas;
263527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.graphics.drawable.Drawable;
273527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.util.Log;
283527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.view.MenuItem;
293527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport android.view.MenuItem.OnMenuItemClickListener;
30b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdochimport java.io.BufferedInputStream;
31387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mountimport java.io.ByteArrayInputStream;
323527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport java.io.IOException;
333527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport java.io.InputStream;
343527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport java.net.MalformedURLException;
353527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckimport java.net.URL;
363527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
373527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck/**
383527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck * Handle setWallpaper requests
393527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck *
403527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck */
413527dd1ff849ec4798eab6289593aa404dcae40bJohn Reckpublic class WallpaperHandler extends Thread
423527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        implements OnMenuItemClickListener, DialogInterface.OnCancelListener {
433527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
443527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    private static final String LOGTAG = "WallpaperHandler";
45b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch    // This should be large enough for BitmapFactory to decode the header so
46b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch    // that we can mark and reset the input stream to avoid duplicate network i/o
47b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch    private static final int BUFFER_SIZE = 128 * 1024;
483527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
493527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    private Context mContext;
50387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount    private String  mUrl;
513527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    private ProgressDialog mWallpaperProgress;
523527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    private boolean mCanceled = false;
533527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
543527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    public WallpaperHandler(Context context, String url) {
553527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        mContext = context;
56387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount        mUrl = url;
573527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    }
583527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
593527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    @Override
603527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    public void onCancel(DialogInterface dialog) {
613527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        mCanceled = true;
623527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    }
633527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
643527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    @Override
653527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    public boolean onMenuItemClick(MenuItem item) {
668549365bc3e7fe5af07f687286404dbcc3553815John Reck        if (mUrl != null && getState() == State.NEW) {
673527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // The user may have tried to set a image with a large file size as
683527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // their background so it may take a few moments to perform the
693527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // operation.
703527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // Display a progress spinner while it is working.
713527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mWallpaperProgress = new ProgressDialog(mContext);
723527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mWallpaperProgress.setIndeterminate(true);
733527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mWallpaperProgress.setMessage(mContext.getResources()
743527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck                    .getText(R.string.progress_dialog_setting_wallpaper));
753527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mWallpaperProgress.setCancelable(true);
763527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mWallpaperProgress.setOnCancelListener(this);
773527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mWallpaperProgress.show();
783527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            start();
793527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        }
803527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        return true;
813527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    }
823527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
833527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    @Override
843527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    public void run() {
85b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch        WallpaperManager wm = WallpaperManager.getInstance(mContext);
86b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch        Drawable oldWallpaper = wm.getDrawable();
87b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch        InputStream inputstream = null;
883527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        try {
893527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // TODO: This will cause the resource to be downloaded again, when
903527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // we should in most cases be able to grab it from the cache. To fix
913527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // this we should query WebCore to see if we can access a cached
923527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // version and instead open an input stream on that. This pattern
933527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // could also be used in the download manager where the same problem
943527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // exists.
95387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount            inputstream = openStream();
963527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            if (inputstream != null) {
97b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                if (!inputstream.markSupported()) {
98b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    inputstream = new BufferedInputStream(inputstream, BUFFER_SIZE);
99b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                }
100b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                inputstream.mark(BUFFER_SIZE);
101b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                BitmapFactory.Options options = new BitmapFactory.Options();
102b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                options.inJustDecodeBounds = true;
103b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                // We give decodeStream a wrapped input stream so it doesn't
104b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                // mess with our mark (currently it sets a mark of 1024)
105b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                BitmapFactory.decodeStream(
106b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                        new BufferedInputStream(inputstream), null, options);
107b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                int maxWidth = wm.getDesiredMinimumWidth();
108b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                int maxHeight = wm.getDesiredMinimumHeight();
109b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                // Give maxWidth and maxHeight some leeway
110b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                maxWidth *= 1.25;
111b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                maxHeight *= 1.25;
112b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                int bmWidth = options.outWidth;
113b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                int bmHeight = options.outHeight;
114b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch
115b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                int scale = 1;
116387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount                while (bmWidth > maxWidth || bmHeight > maxHeight) {
117b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    scale <<= 1;
118b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    bmWidth >>= 1;
119b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    bmHeight >>= 1;
120b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                }
121b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                options.inJustDecodeBounds = false;
122b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                options.inSampleSize = scale;
123b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                try {
124b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    inputstream.reset();
125b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                } catch (IOException e) {
126b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    // BitmapFactory read more than we could buffer
127b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    // Re-open the stream
128b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    inputstream.close();
129387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount                    inputstream = openStream();
130b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                }
131b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                Bitmap scaledWallpaper = BitmapFactory.decodeStream(inputstream,
132b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                        null, options);
1334db7e933f4c26e28f82dc912863f7d02d8fb94abNils Holmström                if (scaledWallpaper != null) {
1344db7e933f4c26e28f82dc912863f7d02d8fb94abNils Holmström                    wm.setBitmap(scaledWallpaper);
1354db7e933f4c26e28f82dc912863f7d02d8fb94abNils Holmström                } else {
1364db7e933f4c26e28f82dc912863f7d02d8fb94abNils Holmström                    Log.e(LOGTAG, "Unable to set new wallpaper, " +
1374db7e933f4c26e28f82dc912863f7d02d8fb94abNils Holmström                            "decodeStream returned null.");
1384db7e933f4c26e28f82dc912863f7d02d8fb94abNils Holmström                }
1393527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            }
1403527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        } catch (IOException e) {
1413527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            Log.e(LOGTAG, "Unable to set new wallpaper");
1423527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // Act as though the user canceled the operation so we try to
1433527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // restore the old wallpaper.
1443527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mCanceled = true;
145b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch        } finally {
146b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch            if (inputstream != null) {
147b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                try {
148b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    inputstream.close();
149b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                } catch (IOException e) {
150b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                    // Ignore
151b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                }
152b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch            }
1533527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        }
1543527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
1553527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        if (mCanceled) {
1563527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // Restore the old wallpaper if the user cancelled whilst we were
1573527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // setting
1583527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            // the new wallpaper.
1593527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            int width = oldWallpaper.getIntrinsicWidth();
1603527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            int height = oldWallpaper.getIntrinsicHeight();
1613527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
1623527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            Canvas canvas = new Canvas(bm);
1633527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            oldWallpaper.setBounds(0, 0, width, height);
1643527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            oldWallpaper.draw(canvas);
16543cfe8a4521b5c153055fbfa181940bc56bb3b06Dianne Hackborn            canvas.setBitmap(null);
1663527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            try {
167b860a62a7fc645fa3b4f5b2733766f60570d65f9Ben Murdoch                wm.setBitmap(bm);
1683527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            } catch (IOException e) {
1693527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck                Log.e(LOGTAG, "Unable to restore old wallpaper.");
1703527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            }
1713527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mCanceled = false;
1723527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        }
1733527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck
1743527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        if (mWallpaperProgress.isShowing()) {
1753527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck            mWallpaperProgress.dismiss();
1763527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck        }
1773527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck    }
178387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount
179387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount    /**
180387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount     * Opens the input stream for the URL that the class should
181387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount     * use to set the wallpaper. Abstracts the difference between
182387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount     * standard URLs and data URLs.
183387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount     * @return An open InputStream for the data at the URL
184387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount     * @throws IOException if there is an error opening the URL stream
185387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount     * @throws MalformedURLException if the URL is malformed
186387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount     */
187387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount    private InputStream openStream() throws IOException, MalformedURLException {
188387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount        InputStream inputStream = null;
189387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount        if (DataUri.isDataUri(mUrl)) {
190387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount            DataUri dataUri = new DataUri(mUrl);
191387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount            inputStream = new ByteArrayInputStream(dataUri.getData());
192387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount        } else {
193387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount            URL url = new URL(mUrl);
194387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount            inputStream = url.openStream();
195387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount        }
196387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount        return inputStream;
197387d45d2284c7fd7f12cbadc96161f946ae29cadGeorge Mount    }
1983527dd1ff849ec4798eab6289593aa404dcae40bJohn Reck}
199