tr.com.turkcellteknoloji.turkcellupdater.UpdateManager.java Source code

Java tutorial

Introduction

Here is the source code for tr.com.turkcellteknoloji.turkcellupdater.UpdateManager.java

Source

/*******************************************************************************
 *
 *  Copyright (C) 2013 Turkcell
 *  
 *  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 tr.com.turkcellteknoloji.turkcellupdater;

import java.io.File;
import java.io.FileOutputStream;
import java.net.URI;
import java.net.URL;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;

/**
 * Provides a auto update mechanism for Android applications
 * @author Ugur Ozmen
 */
public class UpdateManager {
    private final static int DOWNLOAD_PROGRESS_PERCENT = 95;

    /**
     * Provides callback methods for update check results.<br>
     * Conditionally one of 4 methods will be called at the end of a update check:
     * <ul>
     * <li>A new version is found, {@link UpdateCheckListener#onUpdateCheckCompleted(UpdateManager, Update)}</li>
     * <li>A message should be displayed to user, {@link UpdateCheckListener#onUpdateCheckCompleted(UpdateManager, Message)}</li>
     * <li>Completed successfully and no messages and no new versions found, {@link UpdateCheckListener#onUpdateCheckCompleted(UpdateManager)}</li>
     * <li>Update check is fail due to an error</li>
     * </ul>
     *
     * @see UpdateManager#checkUpdates(Context, URI, Properties, UpdateCheckListener)
     * @author Ugur Ozmen
     */
    public interface UpdateCheckListener {
        /**
         * This method is called when update check is completed successfully and a newer version is found. Implementations should display {@link Update#description} to users.
         * Implementations should not provide an option to cancel and continue to application if {@link Update#forceUpdate} is true.
         *
         * @param manager Update manager that has performed update check.
         * @param update Information about next version found.
         */
        void onUpdateCheckCompleted(UpdateManager manager, Update update);

        /**
         * This method is called when update check is completed successfully and a message should be displayed to user.
         * Implementations should not continue normal operation after this message is dismissed.
         *
         * @param manager Update manager that has performed update check.
         * @param message Information delivered from server that should be displayed to user.
         */
        void onUpdateCheckCompleted(UpdateManager manager, Message message);

        /**
         * This method is called when update check is completed successfully and current version is the latest version.
         *
         * @param manager Update manager that has performed update check.
         */
        void onUpdateCheckCompleted(UpdateManager manager);

        /**
         * This method is called when update check is failed.
         * @param manager Update manager that has performed update check.
         * @param exception Failure reason.
         */
        void onUpdateCheckFailed(UpdateManager manager, Exception exception);
    }

    /**
     * Provides callback methods for update process events.
     * @see UpdateManager#applyUpdate(Context, Update, UpdateListener)
     * @author Ugur Ozmen
     */
    public interface UpdateListener {

        /**
         * Returns a percent value indicating update progress
         * @param percent <code>null</code> if current state is interminable otherwise percent of completed progress
         */
        void onUpdateProgress(Integer percent);

        /**
         * Called when update is cancelled. Implementations should close application if {@link Update#forceUpdate} is true.
         */
        void onUpdateCancelled();

        /**
         * Called when update is successfully completed. Implementations should close application if {@link Update#forceUpdate} is true.
         */
        void onUpdateCompleted();

        /**
         * Indicates that update is failed due to a reason.
         * @param exception Failure reason.
         */
        void onUpdateFailed(Exception exception);
    }

    /**
     * Creates an instance of {@link UpdateManager}.
     */
    public UpdateManager() {
        Log.printProductInfo();
    }

    /**
     * Starts an asynchronous operation for checking if a newer version of current application is available.
     * @param context Current context.
     * @param versionServerUri Location of update definitions.
     * @param currentProperties properties of current application and device.
     * @param postProperties <code>true</code> if current properties should post to server for server side processing.
     * @param listener Callback listener.
     * @throws UpdaterException if operation couldn't be started.
     */
    public void checkUpdates(Context context, final URI versionServerUri, Properties currentProperties,
            boolean postProperties, UpdateCheckListener listener) throws UpdaterException {
        final DefaultHttpClient client = Utilities.createClient("TurkcellUpdater/1.0", false);
        final VersionMapRequest request = new VersionMapRequest(context, versionServerUri, currentProperties,
                postProperties, listener);
        try {
            request.executeAsync(client);
        } catch (Exception e) {
            throw new UpdaterException(e);
        }
    }

    /**
     * Starts an asynchronous operation for installing newer version of application.
     * @param context Current context.
     * @param update Information about next version.
     * @param listener Callback listener.
     */
    public void applyUpdate(Context context, Update update, final UpdateListener listener) {
        if (update.targetGooglePlay) {
            openGooglePlayPage(context, update.targetPackageName, listener);
        } else if (update.targetWebsiteUrl != null) {
            openWebPage(context, update.targetWebsiteUrl, listener);
        } else if (update.targetPackageUrl != null) {
            try {
                final DownloadRequest dr = new DownloadRequest();
                dr.setUri(update.targetPackageUrl.toURI());
                dr.setExpectedContentType("application/vnd.android.package-archive");
                dr.setDownloadHandler(new DownloadHandlerAdapter(context, listener));

                HttpClient client = Utilities.createClient("Turkcell Updater/1.0 ", false);
                dr.executeAsync(client);
            } catch (Exception e) {
                if (listener == null) {
                    Log.e("Update failed", e);
                } else {
                    listener.onUpdateFailed(e);
                }
            }
        }
    }

    private final class DownloadHandlerAdapter implements DownloadHandler {
        private final UpdateListener listener;
        private final Context context;

        private DownloadHandlerAdapter(Context context, UpdateListener listener) {
            this.listener = listener;
            this.context = context;
        }

        @Override
        public void onSuccess(byte[] result) {
            try {
                installPackage(context, result, listener);
            } catch (Exception e) {
                if (listener != null) {
                    listener.onUpdateFailed(e);
                }
            }
        }

        @Override
        public void onProgress(Integer percent) {
            if (listener != null) {

                if (percent != null) {
                    percent *= DOWNLOAD_PROGRESS_PERCENT;
                    percent /= 100;
                }

                listener.onUpdateProgress(percent);
            }
        }

        @Override
        public void onFail(Exception ex) {
            if (listener == null) {
                Log.e("Update failed", ex);
            } else {
                listener.onUpdateFailed(ex);
            }
        }

        @Override
        public void onCancelled() {
            if (listener != null) {
                listener.onUpdateCancelled();
            }
        }
    }

    private void installPackage(final Context context, final byte[] apkContents, final UpdateListener listener) {
        final AsyncTask<Void, Void, File> task = new AsyncTask<Void, Void, File>() {
            volatile Exception exception;

            @SuppressLint("WorldReadableFiles")
            @Override
            protected File doInBackground(Void... params) {
                try {
                    if (Utilities.isNullOrEmpty(apkContents)) {
                        throw new NullPointerException("apkContents");
                    }

                    String tempFileName = "nextversion.apk";
                    File tempFile = context.getFileStreamPath(tempFileName);
                    if (tempFile.exists()) {
                        tempFile.delete();
                    }

                    FileOutputStream fout = context.openFileOutput(tempFileName, Context.MODE_WORLD_READABLE);
                    try {
                        fout.write(apkContents);
                        fout.flush();
                    } finally {
                        try {
                            fout.close();
                        } catch (Exception e) {
                            // omit
                        }
                    }

                    return tempFile;

                } catch (Exception e) {
                    exception = e;
                    Log.e("Couldn't save apk", e);
                    return null;
                }
            }

            @Override
            protected void onPostExecute(File result) {
                if (exception == null) {
                    try {
                        Uri data = Uri.fromFile(result);
                        String type = "application/vnd.android.package-archive";
                        Intent promptInstall = new Intent(Intent.ACTION_VIEW);
                        promptInstall.setDataAndType(data, type);
                        context.startActivity(promptInstall);
                        if (listener != null) {
                            listener.onUpdateCompleted();
                        }
                        return;
                    } catch (Exception e) {
                        exception = e;
                    }
                }

                if (listener == null) {
                    Log.e("Update failed", exception);
                } else {
                    listener.onUpdateFailed(exception);
                }

            }

        };
        task.execute();
    }

    private void openGooglePlayPage(Context context, String packageName, final UpdateListener listener) {
        if (listener != null) {
            listener.onUpdateProgress(null);
        }

        try {
            try {
                context.startActivity(
                        new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageName)));
            } catch (android.content.ActivityNotFoundException anfe) {
                context.startActivity(new Intent(Intent.ACTION_VIEW,
                        Uri.parse("http://play.google.com/store/apps/details?id=" + packageName)));
            }
        } catch (Exception e) {
            if (listener == null) {
                Log.e("open google play page failed", e);
            } else {
                listener.onUpdateFailed(e);
            }
        }

        if (listener != null) {
            listener.onUpdateProgress(100);
            listener.onUpdateCompleted();
        }
    }

    private void openWebPage(Context context, URL url, final UpdateListener listener) {
        if (listener != null) {
            listener.onUpdateProgress(null);
        }

        try {
            context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url.toExternalForm())));
        } catch (Exception e) {
            if (listener == null) {
                Log.e("open web page failed", e);
            } else {
                listener.onUpdateFailed(e);
            }
        }

        if (listener != null) {
            listener.onUpdateProgress(100);
            listener.onUpdateCompleted();
        }
    }

    private class VersionMapRequest extends RestRequest {

        @Override
        protected String getExpectedResponseContentType() {
            return Configuration.EXPECTED_JSON_MIME_TYPE;
        }

        public VersionMapRequest(final Context context, final URI versionServerUri,
                final Properties currentProperties, boolean postProperties, final UpdateCheckListener listener) {
            if (postProperties && currentProperties != null) {
                setHttpMethod(HttpMethod.POST);
                setInput(new JSONObject(currentProperties.toMap()));
            } else {
                setHttpMethod(HttpMethod.GET);
                setInputNone();
            }
            setUri(versionServerUri);
            setResultHandler(new RestJsonObjectResultHandler() {
                @Override
                public void onFail(Exception e) {
                    Log.d("Couldn't read update configuration file", e);
                    listener.onUpdateCheckFailed(UpdateManager.this, e);
                }

                @Override
                public void onSuccess(JSONObject jsonObject) {
                    try {
                        final String packageName = currentProperties.getValue(Properties.KEY_APP_PACKAGE_NAME);
                        if (jsonObject != null && jsonObject.has("errorMessage")) {
                            Log.e("Remote error: " + jsonObject.optString("errorMessage"));
                        }

                        if (VersionsMap.isVersionMapOfPackageId(packageName, jsonObject)) {
                            VersionsMap map = new VersionsMap(jsonObject);
                            final Update update = map.getUpdate(currentProperties);
                            if (update != null) {
                                Log.i("Update found: " + update);
                                listener.onUpdateCheckCompleted(UpdateManager.this, update);
                            } else {
                                final MessageDisplayRecords records = new MessageDisplayRecords(context);
                                final Message message = map.getMessage(currentProperties, records, context);
                                if (message == null) {
                                    Log.i("No update or message found.");
                                    listener.onUpdateCheckCompleted(UpdateManager.this);
                                } else {
                                    Log.i("Message found: " + message);
                                    listener.onUpdateCheckCompleted(UpdateManager.this, message);
                                }
                            }
                        } else {
                            throw new UpdaterException("Configuration file packagename should be: " + packageName);
                        }
                    } catch (Exception e) {
                        Log.d("Couldn't process update configuration file", e);
                        listener.onUpdateCheckFailed(UpdateManager.this, e);
                    }

                }
            });
        }
    }

}