it.telecomitalia.my.base_struct_apps.VersionUpdate.java Source code

Java tutorial

Introduction

Here is the source code for it.telecomitalia.my.base_struct_apps.VersionUpdate.java

Source

/*******************************************************************************
 * This software is distributed under the following BSD license:
 *
 * Copyright (c) 2014, Marco Paoletti <marco1.paoletti@telecomitalia.it>,
 * https://my.telecomitalia.it/personal/02069823
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/
package it.telecomitalia.my.base_struct_apps;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

/* prima di usare questa classe ricordati di abilitare l'app con i permessi nel manifest file :)
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- questi non dovrebbero essere necessari...
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.DELETE_PACKAGES"/>-->
 */
public class VersionUpdate extends AsyncTask<String, Void, Void> {
    /* CONFIGURAZIONE: vengono usati solo in un metodo, ma li preferisco come campi perch
    * pi immediati da modificare per eventuali cambiamenti. */
    private final String SERVER = "http://10.98.6.129";
    private final String PATH = "/APPS/Base_Struct_Apps/";
    private final String VERSIONFILE = "versioning.xml";
    private Context context;
    private String currentVersion;
    private String currentApkName;
    private String runningVersion;

    /* costruttore */
    public VersionUpdate(Activity activity) {
        super();
        /* http://developer.android.com/tools/publishing/versioning.html
         * si comincia dal capire che versione sto utilizzando. Nella documentazione sopra
         * elencata, leggersi tutto e fare attenzione alla differenza tra Version Name e
         * Version Code. Con questo metodo mi leggo il versionName e lo salvo per
         * ulteriori elaborazioni. In caso qualcosa non funzionasse ritorno null */
        try {
            PackageManager manager = activity.getPackageManager();
            PackageInfo info = manager.getPackageInfo(activity.getPackageName(), 0);
            runningVersion = info.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            runningVersion = null;
        }
    }

    /* METODI */
    public void setContext(Context contx) {
        /* metodo per ricevere il Context dalla activity */
        context = contx;
    }

    public String versionRunning() {
        /* metodo dimostrativo, restituisce semplicemente la versione dell'app in esecuzione */
        return runningVersion;
    }

    /* leggi qui: http://stackoverflow.com/questions/6343166/android-os-networkonmainthreadexception
     * devo implementare un paio di cose per poter accedere a dei file remoti, tra le quali un thread
      * parallelo in cui fare le operazioni di rete come il controllo versione e il download del
      * eventuale aggiornamento */
    @Override
    protected Void doInBackground(String... urls) {
        /* metodo principale per aggiornamento */
        String xml = "";
        try {
            /* tento di leggermi il file XML remoto */
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(SERVER + PATH + VERSIONFILE);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();
            xml = EntityUtils.toString(httpEntity);
            /* ora in xml c' il codice della pagina degli aggiornamenti */
        } catch (IOException e) {
            e.printStackTrace();
        }
        // TODO: org.apache.http.conn.HttpHostConnectException ovvero host non raggiungibile
        try {
            /* nella variabile xml, c' il codice della pagina remota per gli aggiornamenti.
            * Per le mie esigenze, prendo dall'xml l'attributo value per vedere a che versione  la
            * applicazione sul server.*/
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            // http://stackoverflow.com/questions/1706493/java-net-malformedurlexception-no-protocol
            InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));
            Document dom = db.parse(is);
            Element element = dom.getDocumentElement();
            Element current = (Element) element.getElementsByTagName("current").item(0);
            currentVersion = current.getAttribute("value");
            currentApkName = current.getAttribute("apk");
        } catch (Exception e) {
            e.printStackTrace();
        }
        /* con il costruttore ho stabilito quale versione sta girando sul terminale, e con questi
        * due try, mi son letto XML remoto e preso la versione disponibile sul server e il relativo
        * nome dell'apk, caso ai dovesse servirmi. Ora li confronto e decido che fare */
        if (currentVersion != null & runningVersion != null) {
            /* esistono, li trasformo in double */
            Double serverVersion = Double.parseDouble(currentVersion);
            Double localVersion = Double.parseDouble(runningVersion);
            /* La versione server  superiore alla mia ! Occorre aggiornare */
            if (serverVersion > localVersion) {
                try {
                    /* connessione al server */
                    URL urlAPK = new URL(SERVER + PATH + currentApkName);
                    HttpURLConnection con = (HttpURLConnection) urlAPK.openConnection();
                    con.setRequestMethod("GET");
                    con.setDoOutput(true);
                    con.connect();
                    // qual' la tua directory di sistema Download ?
                    File downloadPath = Environment
                            .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
                    File outputFile = new File(downloadPath, currentApkName);
                    //Log.i("test", downloadPath.getAbsolutePath());
                    //Log.i("test", outputFile.getAbsolutePath());
                    // se esistono download parziali o vecchi, li elimino.
                    if (outputFile.exists())
                        outputFile.delete();
                    /* mi creo due File Stream uno di input, quello che sto scaricando dal server,
                    * e l'altro di output, quello che sto creando nella directory Download*/
                    InputStream input = con.getInputStream();
                    FileOutputStream output = new FileOutputStream(outputFile);
                    byte[] buffer = new byte[1024];
                    int count = 0;
                    while ((count = input.read(buffer)) != -1) {
                        output.write(buffer, 0, count);
                    }
                    output.close();
                    input.close();
                    /* una volta terminato il processo, attraverso un intent lancio il file che ho
                    * appena scaricato in modo da installare immediatamente l'aggiornamento come
                    * specificato qui
                    * http://stackoverflow.com/questions/4967669/android-install-apk-programmatically*/
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setDataAndType(Uri.fromFile(new File(outputFile.getAbsolutePath())),
                            "application/vnd.android.package-archive");
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.startActivity(intent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /* voglio comunque un metodo che abbia un nome riconoscibile, perch nell'activity richiamare il
     * metodo execute non mi piace esteticamente. Me ne creo uno, anche se fa esattamente la stessa cosa
     * di execute, ovvero eseguire doInBackground in un nuovo thread */
    public void checkUpdate() {
        // http://developer.android.com/training/basics/network-ops/xml.html
        this.execute(); // esegue doInBackground
    }
}