com.pk.wallpapermanager.PkWallpaperManager.java Source code

Java tutorial

Introduction

Here is the source code for com.pk.wallpapermanager.PkWallpaperManager.java

Source

/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2014 Pkmmte Xeleon
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.pk.wallpapermanager;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.NotificationManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.v4.app.NotificationCompat.Builder;
import android.util.Log;

public class PkWallpaperManager extends Static {
    //   TODO
    //    - Cache wallpaper list in SharedPreferences in the onStop of MainActivity and WallpaperActivity
    //   - Search and restore wallpaper list stored locally
    //   - Add timer interval to fetch wallpapers only when needed
    //   - Create parameter to override timer interval

    // General Public Constants
    public static final String DEFAULT_METADATA_FILE_NAME = "wallpapers.json";
    public static final String RESOURCE_URI_BASE = "android.resource://";
    public static final int MAX_PROGRESS = 100;

    // JSON Constants
    private final String FULL_SRC = "full_src";
    private final String THUMB_SRC = "thumb_src";
    private final String TITLE = "title";
    private final String BYLINE = "byline";
    private final String FILE_SIZE = "file_size";

    // Keep a single instance throughout the app for simplicity
    private static PkWallpaperManager mInstance = null;

    // Lists of wallpapers
    private List<Wallpaper> mLocalWallpapers;
    private List<Wallpaper> mCloudWallpapers;

    // For issue tracking purposes
    private boolean debugEnabled;
    private static final String LOG_TAG = "PkWallpaperManager";

    // Custom request configuration data
    private WallpaperSettings mSettings;

    // Context is always useful for some reason.
    private Context mContext;

    // Background threads
    private AsyncTask<Void, Void, Void> localWallpapersTask;
    private AsyncTask<Void, Void, Void> cloudWallpapersTask;

    // Listeners for various loading events
    private List<LocalWallpaperListener> mLocalWallpaperListeners;
    private List<CloudWallpaperListener> mCloudWallpaperListeners;
    private List<WallpaperSetListener> mWallpaperSetListeners;
    private List<WallpaperDownloadListener> mWallpaperDownloadListeners;

    // Our handy client for getting API JSON data
    private HttpClient httpClient;

    /**
     * Creates a global WallpaperManager instance.
     * 
     * @param context
     */
    public static void createInstance(Context context) {
        if (mInstance == null)
            mInstance = new PkWallpaperManager(context.getApplicationContext());
    }

    /**
     * Returns the global instance of this WallpaperManager.
     * If you don't remember whether or not you already created 
     * a previous instance, add the context as a parameter. 
     * 
     * @return
     */
    public static PkWallpaperManager getInstance() {
        return mInstance;
    }

    /**
     * Returns the global instance of this WallpaperManager.
     * 
     * @param context
     * @return
     */
    public static PkWallpaperManager getInstance(Context context) {
        if (mInstance == null)
            mInstance = new PkWallpaperManager(context.getApplicationContext());

        return mInstance;
    }

    /**
     * Standard WallpaperManager constructor.
     * 
     * 
     * @param context
     */
    public PkWallpaperManager(Context context) {
        this.debugEnabled = false;
        this.mSettings = new WallpaperSettings();
        this.mContext = context;
        this.mLocalWallpapers = new ArrayList<Wallpaper>();
        this.mCloudWallpapers = new ArrayList<Wallpaper>();
        this.mLocalWallpaperListeners = new ArrayList<LocalWallpaperListener>();
        this.mCloudWallpaperListeners = new ArrayList<CloudWallpaperListener>();
        this.mWallpaperSetListeners = new ArrayList<WallpaperSetListener>();
        this.mWallpaperDownloadListeners = new ArrayList<WallpaperDownloadListener>();
        this.initHttpClient();
        this.initLocalWallpapersTask();
        this.initCloudWallpapersTask();
    }

    /**
     * Loads wallpapers stored locally.<br>
     * Make sure to have set your String Array of Local Wallpapers in 
     * your settings before calling this method.
     */
    public void fetchLocalWallpapers() {
        final String[] localWallpapers = mSettings.getLocalWallpapers();
        final String packageName = mSettings.getPackageName();
        final String thumbSuffix = mSettings.getThumbSuffix();

        if (debugEnabled)
            Log.d(LOG_TAG, "Loading local wallpapers...");

        // Basic resources
        Resources resources = mContext.getResources();
        mLocalWallpapers.clear();
        Wallpaper mWall = null;

        // Loop through all listeners notifying them
        for (LocalWallpaperListener mListener : mLocalWallpaperListeners) {
            mListener.onLocalWallpapersLoading();
        }

        // Loop through extras looking for local wallpapers.
        for (String localWallpaper : localWallpapers) {
            int res = resources.getIdentifier(localWallpaper, "drawable", packageName);

            if (res != 0) {
                final int thumbRes = resources.getIdentifier(localWallpaper + thumbSuffix, "drawable", packageName);
                mWall = new Wallpaper();

                mWall.setFullResource(res);
                mWall.setFullUri(Uri.parse(RESOURCE_URI_BASE + packageName + "/drawable/" + localWallpaper));
                mWall.setThumbResource(thumbRes);
                mWall.setThumbUri(
                        Uri.parse(RESOURCE_URI_BASE + packageName + "/drawable/" + localWallpaper + thumbSuffix));
                mWall.setLocal(true);

                mLocalWallpapers.add(mWall);
                mWall = null;
            }
        }

        if (debugEnabled)
            Log.d(LOG_TAG, "Finished loading " + mLocalWallpapers.size() + " local wallpapers!");

        // Loop through all listeners notifying them
        for (LocalWallpaperListener mListener : mLocalWallpaperListeners) {
            mListener.onLocalWallpapersLoaded();
        }
    }

    /**
     * Loads wallpapers stored locally asynchronously in parallel.<br>
     * Make sure to have set your String Array of Local Wallpapers in 
     * your settings before calling this method.
     * <p>
     * This will not throw any exceptions but it does not guarantee success either.
     * Use this as a lazy way of loading stuff in the background.
     * 
     */
    public void fetchLocalWallpapersAsync() {
        fetchLocalWallpapersAsync(true);
    }

    /**
     * Loads wallpapers stored locally asynchronously.<br>
     * Make sure to have set your String Array of Local Wallpapers in 
     * your settings before calling this method.
     * <p>
     * This will not throw any exceptions but it does not guarantee success either.
     * Use this as a lazy way of loading stuff in the background.
     * 
     * @param parallel   Boolean indicating whether to run serially or in parallel. 
     *                True for parallel, False for serial.
     */
    public void fetchLocalWallpapersAsync(boolean parallel) {
        if (localWallpapersTask.getStatus() == AsyncTask.Status.PENDING) {
            // Execute task if it's ready to go!
            localWallpapersTask
                    .executeOnExecutor(parallel ? AsyncTask.THREAD_POOL_EXECUTOR : AsyncTask.SERIAL_EXECUTOR);
        } else if (localWallpapersTask.getStatus() == AsyncTask.Status.RUNNING && debugEnabled) {
            // Don't execute if already running
            Log.d(LOG_TAG, "Task is already running...");
        } else if (localWallpapersTask.getStatus() == AsyncTask.Status.FINISHED) {
            // Okay, this is not supposed to happen. Reset and recall.
            if (debugEnabled)
                Log.d(LOG_TAG, "Uh oh, it appears the task has finished without being reset. Resetting task...");

            initLocalWallpapersTask();
            fetchLocalWallpapersAsync(parallel);
        }
    }

    /**
     * Loads wallpapers stored on your cloud repository.
     * May throw an exception so watch out and handle it carefully.
     * 
     * Note: Do NOT call this from the main UI thread of it will force close!
     *        Call this from a separate thread instead.
     * 
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public void fetchCloudWallpapers() throws ClientProtocolException, IOException, JSONException {
        // Cancel if not enabled
        if (!mSettings.getWebEnabled()) {
            if (debugEnabled)
                Log.d(LOG_TAG, "Cloud wallpapers aren't enabled in settings! Canceling task...");

            return;
        }

        // Retrieve Metadata URL or build default (if null)
        String metadataURL = mSettings.getMetadataURL();
        if (metadataURL == null) {
            metadataURL = mSettings.getStorageURL() + "/" + mSettings.getWallpaperPath() + "/"
                    + DEFAULT_METADATA_FILE_NAME;
        }

        // Make a request to the Metadata URL and wait for the JSON response
        HttpGet get = new HttpGet(metadataURL);

        if (debugEnabled)
            Log.d(LOG_TAG, "Sending wallpaper info data request to " + metadataURL + "...");

        String response = "";
        try {
            response = httpClient.execute(get, new BasicResponseHandler());

            if (debugEnabled)
                Log.d(LOG_TAG, "Response: " + response);
        } catch (Exception e) {
            if (debugEnabled) {
                Log.d(LOG_TAG,
                        "Unable to make a request to metadata URL! Are you sure you specified your metadata URL in settings correctly?");
            }
        }

        // Loop through all listeners notifying them
        for (CloudWallpaperListener mListener : mCloudWallpaperListeners) {
            mListener.onCloudWallpapersLoading();
        }

        // Convert response into JSONArray and loop through it
        JSONArray jsonResponse = new JSONArray(response);
        int responseLength = jsonResponse.length();
        mCloudWallpapers.clear();
        Wallpaper mWall = null;

        for (int index = 0; index < responseLength; index++) {
            JSONObject jsonWallpaper = jsonResponse.getJSONObject(index);

            mWall = new Wallpaper();
            mWall.setPathURL(mSettings.getStorageURL() + "/" + mSettings.getWallpaperPath() + "/");
            mWall.setRelativeFullURL(jsonWallpaper.getString(FULL_SRC));
            mWall.setRelativeThumbURL(jsonWallpaper.getString(THUMB_SRC));
            mWall.setFullUri(Uri.parse(mWall.getFullURL()));
            mWall.setThumbUri(Uri.parse(mWall.getThumbURL()));
            mWall.setTitle(jsonWallpaper.getString(TITLE));
            mWall.setByLine(jsonWallpaper.getString(BYLINE));
            mWall.setFileSize(jsonWallpaper.getLong(FILE_SIZE));
            mWall.setLocal(false);
            mCloudWallpapers.add(mWall);

            if (debugEnabled)
                Log.d(LOG_TAG, mWall.toString());

            mWall = null;
        }

        if (debugEnabled)
            Log.d(LOG_TAG, "Finished loading " + mCloudWallpapers.size() + " cloud wallpapers!");

        // Loop through all listeners notifying them
        for (CloudWallpaperListener mListener : mCloudWallpaperListeners) {
            mListener.onCloudWallpapersLoaded();
        }
    }

    /**
     * Loads wallpapers stored on your cloud repository asynchronously in parallel. 
     * It's safe to call this on the main UI thread.
     * <p>
     * This will not throw any exceptions but it does not guarantee success either.
     * Use this as a lazy way of loading stuff in the background.
     */
    public void fetchCloudWallpapersAsync() {
        fetchCloudWallpapersAsync(true);
    }

    /**
     * Loads wallpapers stored on your cloud repository asynchronously. It's safe to 
     * call this on the main UI thread.
     * <p>
     * This will not throw any exceptions but it does not guarantee success either.
     * Use this as a lazy way of loading stuff in the background.
     * 
     * @param parallel   Boolean indicating whether to run serially or in parallel. 
     *                True for parallel, False for serial.
     */
    public void fetchCloudWallpapersAsync(boolean parallel) {
        if (cloudWallpapersTask.getStatus() == AsyncTask.Status.PENDING) {
            // Execute task if it's ready to go!
            cloudWallpapersTask
                    .executeOnExecutor(parallel ? AsyncTask.THREAD_POOL_EXECUTOR : AsyncTask.SERIAL_EXECUTOR);
        } else if (cloudWallpapersTask.getStatus() == AsyncTask.Status.RUNNING && debugEnabled) {
            // Don't execute if already running
            Log.d(LOG_TAG, "Task is already running...");
        } else if (cloudWallpapersTask.getStatus() == AsyncTask.Status.FINISHED) {
            // Okay, this is not supposed to happen. Reset and recall.
            if (debugEnabled)
                Log.d(LOG_TAG, "Uh oh, it appears the task has finished without being reset. Resetting task...");

            initCloudWallpapersTask();
            fetchCloudWallpapersAsync(parallel);
        }
    }

    /**
     * Loads all wallpapers. If you have cloud wallpapers enabled, 
     * this will load those too but might throw some exceptions.
     * 
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public void fetchWallpapers() throws ClientProtocolException, IOException, JSONException {
        fetchLocalWallpapers();
        fetchCloudWallpapers();
    }

    /**
     * Loads all wallpapers asynchronously in parallel. 
     * If you have cloud wallpapers enabled, this will load those too.
     * <p>
     * No exceptions will be thrown but nothing is guaranteed.
     */
    public void fetchWallpapersAsync() {
        fetchWallpapersAsync(true);
    }

    /**
     * Loads all wallpapers asynchronously in parallel. 
     * If you have cloud wallpapers enabled, this will load those too.
     * <p>
     * No exceptions will be thrown but nothing is guaranteed.
     * 
     * @param parallel   Boolean indicating whether to run serially or in parallel. 
     *                True for parallel, False for serial.
     */
    public void fetchWallpapersAsync(boolean parallel) {
        fetchLocalWallpapersAsync(parallel);
        fetchCloudWallpapersAsync(parallel);
    }

    /**
     * Returns an ArrayList of Wallpaper objects loaded locally
     * and from the cloud combined. This list will never be null.
     * 
     * @return
     */
    public List<Wallpaper> getWallpapers() {
        List<Wallpaper> mWallpapers = new ArrayList<Wallpaper>();
        mWallpapers.addAll(mLocalWallpapers);
        mWallpapers.addAll(mCloudWallpapers);

        return mWallpapers;
    }

    /**
     * Returns an ArrayList of Wallpaper objects loaded locally.
     * 
     * @return
     */
    public List<Wallpaper> getLocalWallpapers() {
        return this.mCloudWallpapers;
    }

    /**
     * Returns an ArrayList of Wallpaper objects loaded from the cloud.
     * 
     * @return
     */
    public List<Wallpaper> getCloudWallpapers() {
        return this.mCloudWallpapers;
    }

    /**
     * Returns the amount of wallpapers currently loaded
     * on the manager. This applies to both, local and cloud.
     * 
     * If the either wallpaper list is null for some odd 
     * reason, this returns -1.
     * 
     * @return
     */
    public int getNumWallpapers() {
        if (mLocalWallpapers == null || mCloudWallpapers == null)
            return -1;
        else
            return (mLocalWallpapers.size() + mCloudWallpapers.size());
    }

    /**
     * Adds an LocalWallpaperListener to this global instance.
     * 
     * @param listener
     */
    public void addLocalWallpaperListener(LocalWallpaperListener listener) {
        mLocalWallpaperListeners.add(listener);
    }

    /**
     * Removes an LocalWallpaperListener from this global instance.
     * 
     * @param listener
     */
    public void removeLocalWallpaperListener(LocalWallpaperListener listener) {
        mLocalWallpaperListeners.remove(listener);
    }

    /**
     * Removes all LocalWallpaperListeners from this global instance.
     */
    public void removeAllLocalWallpaperListeners() {
        mLocalWallpaperListeners.clear();
    }

    /**
     * Adds an CloudWallpaperListener to this global instance.
     * 
     * @param listener
     */
    public void addCloudWallpaperListener(CloudWallpaperListener listener) {
        mCloudWallpaperListeners.add(listener);
    }

    /**
     * Removes an CloudWallpaperListener from this global instance.
     * 
     * @param listener
     */
    public void removeCloudWallpaperListener(CloudWallpaperListener listener) {
        mCloudWallpaperListeners.remove(listener);
    }

    /**
     * Removes all CloudWallpaperListeners from this global instance.
     */
    public void removeAllCloudWallpaperListeners() {
        mCloudWallpaperListeners.clear();
    }

    /**
     * Adds an WallpaperSetListener to this global instance.
     * 
     * @param listener
     */
    public void addWallpaperSetListener(WallpaperSetListener listener) {
        mWallpaperSetListeners.add(listener);
    }

    /**
     * Removes an WallpaperSetListener from this global instance.
     * 
     * @param listener
     */
    public void removeWallpaperSetListener(WallpaperSetListener listener) {
        mWallpaperSetListeners.remove(listener);
    }

    /**
     * Removes all WallpaperSetListeners from this global instance.
     */
    public void removeAllWallpaperSetListeners() {
        mWallpaperSetListeners.clear();
    }

    /**
     * Adds an WallpaperDownloadListener to this global instance.
     * 
     * @param listener
     */
    public void addWallpaperDownloadListener(WallpaperDownloadListener listener) {
        mWallpaperDownloadListeners.add(listener);
    }

    /**
     * Removes an WallpaperDownloadListener from this global instance.
     * 
     * @param listener
     */
    public void removeWallpaperDownloadListener(WallpaperDownloadListener listener) {
        mWallpaperDownloadListeners.remove(listener);
    }

    /**
     * Removes all WallpaperDownloadListeners from this global instance.
     */
    public void removeAllWallpaperDownloadListeners() {
        mWallpaperDownloadListeners.clear();
    }

    /**
     * Removes all listeners from this global instance.
     */
    public void removeAllListeners() {
        mLocalWallpaperListeners.clear();
        mCloudWallpaperListeners.clear();
        mWallpaperSetListeners.clear();
        mWallpaperDownloadListeners.clear();
    }

    /**
     * Downloads the wallpaper. May throw an exception.
     * <p>
     * You can see download progress for the wallpaper passed 
     * through the interface.
     * <p>
     * Note: Do NOT call this from the main UI thread! Call 
     * downloadWallpaperAsync instead.
     * 
     * @param mWall
     * @throws IOException
     */
    public void downloadWallpaper(Wallpaper mWall, NotificationManager notification, Builder builder)
            throws IOException {
        // Returns and does nothing if the wallpaper is null or local
        if (mWall == null || mWall.isLocal()) {
            if (debugEnabled)
                Log.d(LOG_TAG, "Unable to download image. " + mWall == null ? "Wallpaper object is null."
                        : "Wallpaper object is local.");

            return;
        }

        // Loop through all listeners notifying them
        for (WallpaperDownloadListener mListener : mWallpaperDownloadListeners) {
            mListener.onWallpaperDownloading(mWall, 0);
        }

        // Connect to the server
        URL url = new URL(
                mSettings.getStorageURL() + "/" + mSettings.getWallpaperPath() + "/" + mWall.getRelativeFullURL());
        URLConnection connection = url.openConnection();
        connection.connect();
        InputStream input = new BufferedInputStream(url.openStream(), 8192);

        // Output stream
        String fileName = mWall.getTitle().length() > 0
                ? mWall.getTitle()
                        + mWall.getRelativeFullURL().substring(mWall.getRelativeFullURL().lastIndexOf("."))
                : mWall.getRelativeFullURL();
        File file = new File(mSettings.getSaveLocation() + "/" + fileName);
        file.getParentFile().mkdirs();
        OutputStream output = new FileOutputStream(file);

        byte data[] = new byte[mSettings.getByteBuffer()];
        int count, progress, total = 0;
        while ((count = input.read(data)) != -1) {
            total += count;
            progress = (int) ((total * 100) / mWall.getFileSize());
            Log.d(LOG_TAG, "Download Progress: " + progress);

            // Loop through all listeners notifying them
            for (WallpaperDownloadListener mListener : mWallpaperDownloadListeners) {
                mListener.onWallpaperDownloading(mWall, progress);
            }

            // Writing data to file
            output.write(data, 0, count);
        }

        // Flushing output
        output.flush();

        // Closing streams
        output.close();
        input.close();

        // Scan media for newly downloaded image
        new SingleMediaScanner(mContext, file).scanMedia();

        // Loop through all listeners notifying them
        for (WallpaperDownloadListener mListener : mWallpaperDownloadListeners) {
            mListener.onWallpaperDownloaded(mWall);
        }

        if (debugEnabled)
            Log.d(LOG_TAG, "Finished downloading wallpaper!");
    }

    /**
     * Downloads the wallpaper. Will not thrown an exception. 
     * Check the interface for failed status.
     * <p>
     * You can see download progress for the wallpaper passed 
     * through the interface.
     * 
     * @param mWall
     */
    public void downloadWallpaperAsync(final Wallpaper mWall, final NotificationManager notification,
            final Builder builder) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    downloadWallpaper(mWall, notification, builder);
                } catch (Exception e) {
                    // Loop through all listeners notifying them
                    for (WallpaperDownloadListener mListener : mWallpaperDownloadListeners) {
                        mListener.onWallpaperDownloadFailed(mWall);
                    }

                    e.printStackTrace();
                }

                return null;
            }
        }.execute();
    }

    /**
     * Sets the system wallpaper to the Wallpaper object passed.
     * May throw an exception if the data is invalid.
     * <p>
     * Note: Do NOT call this from the main UI thread if you're 
     * setting it to a cloud wallpaper! Call setWallpaperAsync instead.
     * 
     * @param mWall
     * @throws NumberFormatException
     * @throws IOException
     */
    public void setWallpaper(Wallpaper mWall) throws NumberFormatException, IOException {
        if (debugEnabled)
            Log.d(LOG_TAG, "Setting wallpaper...");

        // Load the original system WallpaperManager
        WallpaperManager wallManager = WallpaperManager.getInstance(mContext);
        Bitmap bitmap = null;

        // Set resource if local, otherwise download it and set it
        if (mWall.isLocal())
            wallManager.setResource(mWall.getFullResource());
        else {
            bitmap = getBitmapFromURL(mWall.getFullURL());
            wallManager.setBitmap(bitmap);
        }

        // Loop through all listeners notifying them
        for (WallpaperSetListener mListener : mWallpaperSetListeners) {
            mListener.onWallpaperSet(bitmap);
        }

        if (debugEnabled)
            Log.d(LOG_TAG, "Successfully set wallpaper!");
    }

    /**
     * Sets the system wallpaper to the Wallpaper object passed. 
     * <p>
     * This does not guarantee success. For further detail, set a 
     * listener
     * 
     * @param mWall
     */
    public void setWallpaperAsync(final Wallpaper mWall) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    setWallpaper(mWall);
                } catch (Exception e) {
                    // Loop through all listeners notifying them
                    for (WallpaperSetListener mListener : mWallpaperSetListeners) {
                        mListener.onWallpaperSetFailed();
                    }

                    e.printStackTrace();
                }

                return null;
            }
        }.execute();
    }

    /**
     * Returns a WallpaperSettings object with all values set.
     * 
     * @return
     */
    public WallpaperSettings getSettings() {
        return this.mSettings;
    }

    /**
     * Applies new settings using your own custom WallpaperSettings object.
     * 
     * @param settings
     */
    public void setSettings(WallpaperSettings settings) {
        this.mSettings = settings;
    }

    /**
     * Set the debug status for this manager.
     * If true, it will periodically print logs of current 
     * progress so you can see what's going on.
     * <p>
     * I suggest you disable this during production as it
     * will consume unnecessary processing power. Besides,
     * you don't want to spam your users' logs.
     * 
     * @param debug
     */
    public void setDebugging(boolean debug) {
        this.debugEnabled = debug;
    }

    /**
     * Initializes our handy little client for cloud calls.
     * Don't modify this unless you know what you're doing.
     */
    private void initHttpClient() {
        // Basic HTTP parameters & manager set up
        HttpParams parameters = new BasicHttpParams();
        HttpProtocolParams.setVersion(parameters, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(parameters, HTTP.DEFAULT_CONTENT_CHARSET);
        HttpProtocolParams.setUseExpectContinue(parameters, false);
        HttpConnectionParams.setTcpNoDelay(parameters, true);
        HttpConnectionParams.setSocketBufferSize(parameters, 8192);

        SchemeRegistry schReg = new SchemeRegistry();
        schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        ClientConnectionManager tsccm = new ThreadSafeClientConnManager(parameters, schReg);

        // Finally, initialize our client with these parameters and manager
        this.httpClient = new DefaultHttpClient(tsccm, parameters);
    }

    /**
     * Initializes our local wallpapers thread.
     */
    private void initLocalWallpapersTask() {
        this.localWallpapersTask = new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    fetchLocalWallpapers();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void p) {
                initLocalWallpapersTask();
            }
        };
    }

    /**
     * Initializes our cloud wallpapers thread.
     */
    private void initCloudWallpapersTask() {
        this.cloudWallpapersTask = new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    fetchCloudWallpapers();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void p) {
                initCloudWallpapersTask();
            }
        };
    }
}