Java tutorial
/******************************************************************************* * 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 } }