org.droidkit.app.UpdateService.java Source code

Java tutorial

Introduction

Here is the source code for org.droidkit.app.UpdateService.java

Source

/*
 * Copyright 2010 Mike Novak <michael.novakjr@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.droidkit.app;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.widget.RemoteViews;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.droidkit.util.Resources;
//import org.droidkit.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Provides an easy over the air update service for applications that are not distributed via
 * any marketplace. If your application is used in a market of some kind this is not a recommended
 * service.
 * 
 * @version 1
 * @since 1
 * @author mrn
 */
public class UpdateService extends Service {

    public static final int UPDATE_AVAILABLE = 1;
    public static final int UPDATE_SERVICE_ID = 0x011;
    public static final int UPDATE_DOWNLOADING_ID = 0x012;
    public static final int DOWNLOAD_DONE = 0x013;

    private NotificationManager mNotificationManager;
    private IBinder mBinder = new UpdateBinder();
    private String mUpdateServer;
    private SharedPreferences mPreferences;
    private boolean isDownloading = false;
    private Handler mHandler = new Handler();

    @Override
    public void onCreate() {
        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        mPreferences = getSharedPreferences("droidkit.prefs", MODE_PRIVATE);
        boolean checkForUpdates = mPreferences.getBoolean("check.updates", true);

        if (!checkForUpdates) {
            stopSelf();
            return;
        }

        mUpdateServer = getString(Resources.getId(this, "update_service_url", Resources.TYPE_STRING));

        Log.i("DroidKit", "Update Server: " + mUpdateServer);

        if (mUpdateServer == null) {
            /* abort the process, we don't have an update server to contact. */
            Log.e("DroidKit", "No update service provided, stopping service.");
            stopSelf();
            return;
        }

        Thread thr = new Thread(mRunnable);
        thr.start();
    }

    public void setHandler(Handler handler) {
        mHandler = handler;
    }

    public boolean isDownloading() {
        return isDownloading;
    }

    private void queryServer() {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(mUpdateServer + "?foobar=boo");
        StringBuffer obj = new StringBuffer();

        Log.i("DroidKit", "Checking updates: " + mUpdateServer);

        try {
            HttpResponse response = client.execute(get);
            InputStream in = response.getEntity().getContent();

            BufferedReader reader = new BufferedReader(new InputStreamReader(in),
                    (int) response.getEntity().getContentLength());

            String line = "";

            while ((line = reader.readLine()) != null) {
                obj.append(line);
                obj.append("\n");
            }
        } catch (ClientProtocolException e) {
            Log.e("DroidKit", "Error contacting update server: " + e.toString());
        } catch (IOException e) {
            Log.e("DroidKit", "Error contacting update server: " + e.toString());
        }

        client.getConnectionManager().shutdown();

        parseJSONResponse(obj.toString());
    }

    private void parseJSONResponse(String obj) {
        /* find out the current version of the application.  we use the version code for updates. */
        int currentVersion = 0;

        try {
            PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
            currentVersion = info.versionCode;
        } catch (NameNotFoundException e) {
            Log.e("DroidKit", "Wtf! How can we not find ourselves! " + e.toString());
        }

        try {
            JSONObject json = new JSONObject(obj);
            JSONArray arr = json.getJSONArray("updates");
            JSONObject update = arr.getJSONObject(0);

            int updateVersion = update.getInt("version.code");
            Log.i("DroidKit", "Latest verion: " + updateVersion);

            if (currentVersion < updateVersion) {
                /* determine if we should download the update automatically. */
                boolean download = mPreferences.getBoolean("download.updates", true);

                if (download) {
                    /* display the ongoing download notification. */
                    notifyDownloading(obj);
                    isDownloading = true;

                    boolean result = updateVersion(update.getString("apk.url"));

                    mNotificationManager.cancel("Downloading update...", UPDATE_DOWNLOADING_ID);
                    isDownloading = false;
                    Message.obtain(mHandler, DOWNLOAD_DONE, null).sendToTarget();

                    if (result) {
                        notifyUser(obj);
                    }
                } else {
                    notifyUser(obj);
                }
            }
        } catch (JSONException e) {
            Log.e("DroidKit", "Error parsing update server response: " + e.toString());
        }
    }

    private boolean updateVersion(String apk) {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(apk);
        boolean result = false;

        try {
            HttpResponse response = client.execute(get);
            InputStream in = response.getEntity().getContent();

            File localFile = new File(
                    Environment.getExternalStorageDirectory() + "/" + apk.substring(apk.lastIndexOf("/") + 1));
            FileOutputStream out = new FileOutputStream(localFile);
            int count;
            byte[] buffer = new byte[8196];

            while ((count = in.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }

            out.close();
            in.close();
            result = true;
        } catch (ClientProtocolException e) {
            Log.e("DroidKit", "Error retrieving updated apk: " + e.toString());
        } catch (IOException e) {
            Log.e("DroidKit", "Error retrieving updated apk: " + e.toString());
        }

        client.getConnectionManager().shutdown();
        return result;
    }

    private void notifyUser(String json) {
        Intent intent = new Intent(this, UpdateActivity.class);
        intent.putExtra("json", json);

        String desc = "Update available for " + getString(getApplicationInfo().labelRes);

        PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
        Notification n = new Notification(android.R.drawable.stat_sys_download_done, "Update available",
                System.currentTimeMillis());
        n.setLatestEventInfo(this, "Update Available", desc, pending);

        mNotificationManager.notify("Update Available", UPDATE_SERVICE_ID, n);
    }

    private void notifyDownloading(String json) {
        Intent intent = new Intent(this, UpdateActivity.class);
        intent.putExtra("downloading", true);
        intent.putExtra("json", json);

        String desc = "Downloading " + getString(getApplicationInfo().labelRes);
        PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
        Notification n = new Notification(android.R.drawable.stat_sys_download, "Downloading update...",
                System.currentTimeMillis());
        n.flags = Notification.FLAG_ONGOING_EVENT;
        n.contentIntent = pending;

        RemoteViews view = new RemoteViews(getPackageName(),
                Resources.getId(this, "update_notification", Resources.TYPE_LAYOUT));
        view.setTextViewText(Resources.getId(this, "update_title_text", Resources.TYPE_ID), desc);
        view.setProgressBar(Resources.getId(this, "update_progress_bar", Resources.TYPE_ID), 100, 0, true);
        view.setImageViewResource(Resources.getId(this, "update_notif_icon", Resources.TYPE_ID),
                android.R.drawable.stat_sys_download);

        n.contentView = view;

        mNotificationManager.notify("Downloading update...", UPDATE_DOWNLOADING_ID, n);
    }

    private Runnable mRunnable = new Runnable() {
        public void run() {
            queryServer();
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        /* just start the service, it actually makes no sense to bind to it. */
        startService(new Intent(this, UpdateService.class));
        return mBinder;
    }

    /**
     * 
     * @version 1
     * @since 1
     * @author mrn
     */
    public class UpdateBinder extends Binder {
        public UpdateService getService() {
            return UpdateService.this;
        }
    }
}