/*** Copyright (c) 2012-2013 Samuele Rini This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses *** https://github.com/dentex/ytdownloader/ https://sourceforge.net/projects/ytdownloader/ *** Different Licenses and Credits where noted in code comments. */ package dentex.youtube.downloader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.app.Activity; import android.app.AlertDialog; import android.app.DownloadManager; import android.app.DownloadManager.Query; import android.app.DownloadManager.Request; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager.NameNotFoundException; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.NavUtils; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.bugsense.trace.BugSenseHandler; import dentex.youtube.downloader.utils.PopUps; import dentex.youtube.downloader.utils.Utils; public class UpgradeApkActivity extends Activity { private ProgressBar progressBar2; private String currentVersion; private String apkFilename; private static final String DEBUG_TAG = "UpgradeApkActivity"; private boolean buttonClickedOnce = false; private TextView tv; private TextView cl; private Button upgradeButton; private DownloadManager downloadManager; private File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); private String webPage; private long enqueue; private Uri fileUri; private AsyncUpdate asyncUpdate; private String matchedVersion; private String matchedChangeLog; private String matchedMd5; private boolean isAsyncTaskRunning = false; private String compRes = "init"; private ContextThemeWrapper boxThemeContextWrapper = new ContextThemeWrapper(this, R.style.BoxTheme); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); BugSenseHandler.leaveBreadcrumb("UpgradeApkActivity_onCreate"); getWindow().requestFeature(Window.FEATURE_ACTION_BAR); // Theme init Utils.themeInit(this); // Language init Utils.langInit(this); setContentView(R.layout.activity_upgrade_apk); try { currentVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionName; Utils.logger("d", "current version: " + currentVersion, DEBUG_TAG); } catch (NameNotFoundException e) { Log.e(DEBUG_TAG, "version not read: " + e.getMessage()); currentVersion = "100"; } upgradeButton = (Button) findViewById(R.id.upgrade_button); progressBar2 = (ProgressBar) findViewById(R.id.progressBar2); progressBar2.setVisibility(View.GONE); tv = (TextView) findViewById(R.id.upgrade_upper_text); tv.setText(getString(R.string.upgrade_uppertext_init) + currentVersion); cl = (TextView) findViewById(R.id.upgrade_textview2); setupActionBar(); } private void setupActionBar() { getActionBar().setDisplayHomeAsUpEnabled(true); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: NavUtils.navigateUpFromSameTask(this); return true; } return super.onOptionsItemSelected(item); } public void upgradeButtonClick(View v) { ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected() && matchedVersion != "n.a.") { try { if (buttonClickedOnce == false) { buttonClickedOnce = true; //init version and changelog matchedChangeLog = null; matchedVersion = null; cl.setText(""); asyncUpdate = new AsyncUpdate(); webPage = "http://sourceforge.net/projects/ytdownloader/files/"; asyncUpdate.execute(webPage); } else { buttonClickedOnce = false; callDownloadApk(matchedVersion); upgradeButton.setEnabled(false); } } catch (NullPointerException e) { PopUps.showPopUp(getString(R.string.error), getString(R.string.upgrade_network_error), "alert", UpgradeApkActivity.this); Log.e(DEBUG_TAG, "unable to retrieve update data."); } } else { progressBar2.setVisibility(View.GONE); tv.setText(getString(R.string.no_net)); upgradeButton.setEnabled(false); PopUps.showPopUp(getString(R.string.no_net), getString(R.string.no_net_dialog_msg), "alert", this); } } @Override protected void onStart() { super.onStart(); registerReceiver(apkReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); Log.v(DEBUG_TAG, "_onStart"); } @Override protected void onRestart() { super.onRestart(); Log.v(DEBUG_TAG, "_onRestart"); } @Override public void onPause() { super.onPause(); Log.v(DEBUG_TAG, "_onPause"); } @Override protected void onStop() { super.onStop(); unregisterReceiver(apkReceiver); Log.v(DEBUG_TAG, "_onStop"); if (isAsyncTaskRunning) { asyncUpdate.cancel(true); isAsyncTaskRunning = false; } } private class AsyncUpdate extends AsyncTask<String, Void, Integer> { protected void onPreExecute() { upgradeButton.setEnabled(false); progressBar2.setVisibility(View.VISIBLE); tv.setText(R.string.upgrade_uppertext_searching); isAsyncTaskRunning = true; } protected Integer doInBackground(String... urls) { // params comes from the execute() call: params[0] is the url. try { Utils.logger("d", "doInBackground...", DEBUG_TAG); return downloadUrl(urls[0]); } catch (IOException e) { Log.e(DEBUG_TAG, "doInBackground: " + e.getMessage()); matchedVersion = "n.a."; return 1; } } private int downloadUrl(String myurl) throws IOException { InputStream is = null; int len = 100000; Utils.logger("d", "The link is: " + myurl, DEBUG_TAG); try { URL url = new URL(myurl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("User-Agent","<em>" + YTD.USER_AGENT_FIREFOX + "</em>"); conn.setReadTimeout(20000 /* milliseconds */); conn.setConnectTimeout(30000 /* milliseconds */); conn.setInstanceFollowRedirects(false); conn.setRequestMethod("GET"); conn.setDoInput(true); conn.connect(); int response = conn.getResponseCode(); Utils.logger("d", "The response is: " + response, DEBUG_TAG); is = conn.getInputStream(); if (!asyncUpdate.isCancelled()) { return readIt(is, len); } else { Utils.logger("d", "asyncUpdate cancelled @ 'return readIt'", DEBUG_TAG); return 3; } } finally { if (is != null) { is.close(); } } } public int readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException { Reader reader = null; reader = new InputStreamReader(stream, "UTF-8"); char[] buffer = new char[len]; reader.read(buffer); String content = new String(buffer); return OnlineUpdateCheck(content); } @Override protected void onPostExecute(Integer result) { progressBar2.setVisibility(View.GONE); tv.setText(getString(R.string.upgrade_latest) + matchedVersion + getString(R.string.upgrade_installed) + currentVersion); cl.setText(matchedChangeLog); if (matchedVersion.contentEquals("n.a.")) { Toast.makeText(UpgradeApkActivity.this, "Invalid HTTP server response", Toast.LENGTH_LONG).show(); } if (compRes.contentEquals(">")) { Utils.logger("d", "version comparison: downloading latest version...", DEBUG_TAG); upgradeButton.setEnabled(true); upgradeButton.setText(getString(R.string.upgrade_button_download)); } else if (compRes.contentEquals("==")) { PopUps.showPopUp(getString(R.string.information), getString(R.string.upgrade_latest_installed), "info", UpgradeApkActivity.this); Utils.logger("d", "version comparison: latest version is already installed!", DEBUG_TAG); upgradeButton.setEnabled(false); } else if (compRes.contentEquals("<")) { // No need for a popup... Utils.logger("d", "version comparison: installed higher than the one online? ...this should not happen...", DEBUG_TAG); upgradeButton.setEnabled(false); } else if (compRes.contentEquals("init")) { Utils.logger("d", "version comparison not tested", DEBUG_TAG); upgradeButton.setEnabled(false); } } } private int OnlineUpdateCheck(String content) { Utils.logger("d", "OnlineUpdateCheck", DEBUG_TAG); int res = 3; if (asyncUpdate.isCancelled()) { Utils.logger("d", "asyncUpdate cancelled @ 'OnlineUpdateCheck' begin", DEBUG_TAG); return 3; } // match version name Pattern v_pattern = Pattern.compile("versionName=\\\"(.*)\\\""); Matcher v_matcher = v_pattern.matcher(content); if (v_matcher.find() && !asyncUpdate.isCancelled()) { matchedVersion = v_matcher.group(1); Utils.logger("i", "_on-line version: " + matchedVersion, DEBUG_TAG); res = res - 1; } else { matchedVersion = "not_found"; Log.e(DEBUG_TAG, "_online version: not found!"); } // match changelog Pattern cl_pattern = Pattern.compile("<pre><code> v(.*?)</code></pre>", Pattern.DOTALL); Matcher cl_matcher = cl_pattern.matcher(content); if (cl_matcher.find() && !asyncUpdate.isCancelled()) { matchedChangeLog = " v" + cl_matcher.group(1); Utils.logger("i", "_online changelog...", DEBUG_TAG); res = res - 1; } else { matchedChangeLog = "not_found"; Log.e(DEBUG_TAG, "_online changelog not found!"); } // match md5 // checksum: <code>d7ef1e4668b24517fb54231571b4a74f</code> dentex.youtube.downloader_v1.4 Pattern md5_pattern = Pattern.compile("checksum: <code>(.{32})</code> dentex.youtube.downloader_v"); Matcher md5_matcher = md5_pattern.matcher(content); if (md5_matcher.find() && !asyncUpdate.isCancelled()) { matchedMd5 = md5_matcher.group(1); Utils.logger("i", "_online md5sum: " + matchedMd5, DEBUG_TAG); res = res - 1; } else { matchedMd5 = "not_found"; Log.e(DEBUG_TAG, "_online md5sum not found!"); } compRes = Utils.VersionComparator.compare(matchedVersion, currentVersion); Utils.logger("d", "version comparison: " + matchedVersion + " " + compRes + " " + currentVersion, DEBUG_TAG); return res; } void callDownloadApk(String ver) { String apklink = "http://sourceforge.net/projects/ytdownloader/files/dentex.youtube.downloader_v" + ver + ".apk/download"; apkFilename = "dentex.youtube.downloader_v" + ver + ".apk"; downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); Request request = new Request(Uri.parse(apklink)); fileUri = Uri.parse(dir.toURI() + apkFilename); request.setDestinationUri(fileUri); request.allowScanningByMediaScanner(); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.setTitle("YouTube Downloader v" + ver); try { enqueue = downloadManager.enqueue(request); } catch (IllegalArgumentException e) { Log.e(DEBUG_TAG, "callDownloadApk: " + e.getMessage()); BugSenseHandler.sendExceptionMessage(DEBUG_TAG + "-> callDownloadApk: ", e.getMessage(), e); YTD.NoDownProvPopUp(this); } catch (NullPointerException ne) { Log.e(DEBUG_TAG, "callDownloadApk: " + ne.getMessage()); BugSenseHandler.sendExceptionMessage(DEBUG_TAG + "-> callDownloadApk: ", ne.getMessage(), ne); Toast.makeText(this, getString(R.string.error), Toast.LENGTH_LONG).show(); } } BroadcastReceiver apkReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -2); if (enqueue != -1 && id != -2 && id == enqueue) { Query query = new Query(); query.setFilterById(id); Cursor c = downloadManager.query(query); if (c.moveToFirst()) { int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS); int status = c.getInt(columnIndex); if (status == DownloadManager.STATUS_SUCCESSFUL) { upgradeButton.setText(getString(R.string.upgrade_button_init)); upgradeButton.setEnabled(true); if (Utils.checkMD5(matchedMd5, new File(dir, apkFilename))) { AlertDialog.Builder helpBuilder = new AlertDialog.Builder(boxThemeContextWrapper); helpBuilder.setIcon(android.R.drawable.ic_dialog_info); helpBuilder.setTitle(getString(R.string.information)); helpBuilder.setMessage(getString(R.string.upgraded_dialog_msg)); helpBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(); intent.setAction(android.content.Intent.ACTION_VIEW); intent.setDataAndType(fileUri, "application/vnd.android.package-archive"); startActivity(intent); } }); helpBuilder.setNegativeButton(getString(R.string.upgraded_dialog_negative), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // cancel } }); AlertDialog helpDialog = helpBuilder.create(); if (! ((Activity) context).isFinishing()) { helpDialog.show(); } } else { AlertDialog.Builder helpBuilder = new AlertDialog.Builder(boxThemeContextWrapper); helpBuilder.setIcon(android.R.drawable.ic_dialog_info); helpBuilder.setTitle(getString(R.string.information)); helpBuilder.setMessage(getString(R.string.upgrade_bad_md5_dialog_msg)); helpBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { deleteBadDownload(id); callDownloadApk(matchedVersion); upgradeButton.setEnabled(false); } }); helpBuilder.setNegativeButton(getString(R.string.dialogs_negative), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { deleteBadDownload(id); // cancel } }); AlertDialog helpDialog = helpBuilder.create(); if (! ((Activity) context).isFinishing()) { helpDialog.show(); } } } else if (status == DownloadManager.STATUS_FAILED) { deleteBadDownload(id); } } } } }; private void deleteBadDownload (long id) { downloadManager.remove(id); Toast.makeText(this, getString(R.string.download_failed), Toast.LENGTH_LONG).show(); } }