/** * Copyright (C) 2011-2013, Karsten Priegnitz * * Permission to use, copy, modify, and distribute this piece of software * for any purpose with or without fee is hereby granted, provided that * the above copyright notice and this permission notice appear in the * source code of all copies. * * It would be appreciated if you mention the author in your change log, * contributors list or the like. * * @author: Karsten Priegnitz * @see: http://code.google.com/p/android-change-log/ */ package de.theknut.xposedgelsettings.ui; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Color; import android.preference.PreferenceManager; import android.util.Log; import android.view.View; import android.webkit.WebView; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.Theme; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import de.theknut.xposedgelsettings.R; import de.theknut.xposedgelsettings.hooks.Utils; public class Changelog { private final Context context; private String lastVersion, thisVersion; // this is the key for storing the version name in SharedPreferences private static final String VERSION_KEY = "PREFS_VERSION_KEY"; private static final String NO_VERSION = ""; /** * Constructor * * Retrieves the version names and stores the new version name in * SharedPreferences * * @param context */ public Changelog(Context context) { this(context, PreferenceManager.getDefaultSharedPreferences(context)); } /** * Constructor * * Retrieves the version names and stores the new version name in * SharedPreferences * * @param context * @param sp * the shared preferences to store the last version name into */ public Changelog(Context context, SharedPreferences sp) { this.context = context; // get version numbers this.lastVersion = sp.getString(VERSION_KEY, NO_VERSION); Log.d(TAG, "lastVersion: " + lastVersion); try { this.thisVersion = context.getPackageManager().getPackageInfo( context.getPackageName(), 0).versionName; } catch (NameNotFoundException e) { this.thisVersion = NO_VERSION; Log.e(TAG, "could not get version name from manifest!"); e.printStackTrace(); } Log.d(TAG, "appVersion: " + this.thisVersion); } /** * @return The version name of the last installation of this app (as * described in the former manifest). This will be the same as * returned by <code>getThisVersion()</code> the second time this * version of the app is launched (more precisely: the second time * ChangeLog is instantiated). * @see AndroidManifest.xml#android:versionName */ public String getLastVersion() { return this.lastVersion; } /** * @return The version name of this app as described in the manifest. * @see AndroidManifest.xml#android:versionName */ public String getThisVersion() { return this.thisVersion; } /** * @return <code>true</code> if this version of your app is started the * first time */ public boolean firstRun() { return !this.lastVersion.equals(this.thisVersion); } /** * @return <code>true</code> if your app including ChangeLog is started the * first time ever. Also <code>true</code> if your app was * deinstalled and installed again. */ public boolean firstRunEver() { return NO_VERSION.equals(this.lastVersion); } /** * @return An AlertDialog displaying the changes since the previous * installed version of your app (what's new). But when this is the * first run of your app including ChangeLog then the full log * dialog is show. */ public MaterialDialog getLogDialog() { return this.getDialog(this.firstRunEver()); } /** * @return an AlertDialog with a full change log displayed */ public MaterialDialog getFullLogDialog() { return this.getDialog(true); } private MaterialDialog getDialog(boolean full) { WebView wv = new WebView(this.context); wv.setPadding(0, Utils.dpToPx(5), 0, 0); wv.setBackgroundColor(Color.TRANSPARENT); wv.loadDataWithBaseURL(null, this.getLog(full), "text/html", "UTF-8", null); return new MaterialDialog.Builder(this.context) .theme(Theme.DARK) .autoDismiss(true) .title( context.getResources().getString( full ? R.string.changelog_full_title : R.string.changelog_title)) .customView(wv, false) .positiveText(android.R.string.ok) .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog materialDialog) { updateVersionInPreferences(); showSupportDialog(context); } }).build(); } public void showSupportDialog(final Context context) { MaterialDialog.Builder dialog = new MaterialDialog.Builder(context) .theme(Theme.DARK) .autoDismiss(true) .title("I need your support!") .content("Please consider XGELS Premium to support the development of this app! Thanks so much!") .neutralText(android.R.string.ok) .positiveText("Donate") .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog materialDialog) { final String[] entries = context.getResources().getStringArray(R.array.donationArray); try { InAppPurchase.mHelper.getPrices(entries, false); } catch (Exception ex) { Toast.makeText(context, R.string.toast_getprices_fail, Toast.LENGTH_LONG).show(); Log.w("XGELS", "Something went wrong when trying to get the prices"); ex.printStackTrace(); } new MaterialDialog.Builder(context) .theme(Theme.DARK) .title("Support me! Buy me") .items(entries) .itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() { @Override public boolean onSelection(MaterialDialog materialDialog, View view, int i, CharSequence sku) { InAppPurchase.launchPurchaseFlow(context.getResources().getStringArray(R.array.donationValues)[i]); return true; } }) .build() .show(); } @Override public void onNegative(MaterialDialog materialDialog) { final String[] entries = context.getResources().getStringArray(R.array.premiumArray); try { InAppPurchase.mHelper.getPrices(entries, true); } catch (Exception ex) { Toast.makeText(context, R.string.toast_getprices_fail, Toast.LENGTH_LONG).show(); Log.w("XGELS", "Something went wrong when trying to get the prices"); ex.printStackTrace(); } new MaterialDialog.Builder(context) .theme(Theme.DARK) .title("Support me! Buy me") .items(entries) .itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() { @Override public boolean onSelection(MaterialDialog materialDialog, View view, int i, CharSequence sku) { InAppPurchase.launchPurchaseFlow(context.getResources().getStringArray(R.array.premiumValues)[i]); return true; } }) .build() .show(); } @Override public void onNeutral(MaterialDialog materialDialog) { } }); if (!InAppPurchase.isPremium) { dialog.negativeText("Go Premium"); } dialog.build().show(); } private void updateVersionInPreferences() { // save new version number to preferences SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); editor.putString(VERSION_KEY, thisVersion); editor.commit(); } /** * @return HTML displaying the changes since the previous installed version * of your app (what's new) */ public String getLog() { return this.getLog(false); } /** * @return HTML which displays full change log */ public String getFullLog() { return this.getLog(true); } /** modes for HTML-Lists (bullet, numbered) */ private enum Listmode { NONE, ORDERED, UNORDERED, }; private Listmode listMode = Listmode.NONE; private StringBuffer sb = null; private static final String EOCL = "END_OF_CHANGE_LOG"; private String getLog(boolean full) { // read changelog.txt file sb = new StringBuffer(); try { InputStream ins = context.getResources().openRawResource( R.raw.changelog); BufferedReader br = new BufferedReader(new InputStreamReader(ins)); String line = null; boolean advanceToEOVS = false; // if true: ignore further version // sections while ((line = br.readLine()) != null) { line = line.trim(); char marker = line.length() > 0 ? line.charAt(0) : 0; if (marker == '$') { // begin of a version section this.closeList(); String version = line.substring(1).trim(); // stop output? if (!full) { if (this.lastVersion.equals(version)) { advanceToEOVS = true; } else if (version.equals(EOCL)) { advanceToEOVS = false; } } } else if (!advanceToEOVS) { switch (marker) { case '%': // line contains version title this.closeList(); sb.append("<div class='title'>" + line.substring(1).trim() + "</div>\n"); break; case '_': // line contains version title this.closeList(); sb.append("<div class='subtitle'>" + line.substring(1).trim() + "</div>\n"); break; case '!': // line contains free text this.closeList(); sb.append("<div class='freetext'>" + line.substring(1).trim() + "</div>\n"); break; case '#': // line contains numbered list item this.openList(Listmode.ORDERED); sb.append("<li>" + line.substring(1).trim() + "</li>\n"); break; case '*': // line contains bullet list item this.openList(Listmode.UNORDERED); sb.append("<li>" + line.substring(1).trim() + "</li>\n"); break; default: // no special character: just use line as is this.closeList(); sb.append(line + "\n"); } } } this.closeList(); br.close(); } catch (IOException e) { e.printStackTrace(); } return sb.toString(); } private void openList(Listmode listMode) { if (this.listMode != listMode) { closeList(); if (listMode == Listmode.ORDERED) { sb.append("<div class='list'><ol>\n"); } else if (listMode == Listmode.UNORDERED) { sb.append("<div class='list'><ul>\n"); } this.listMode = listMode; } } private void closeList() { if (this.listMode == Listmode.ORDERED) { sb.append("</ol></div>\n"); } else if (this.listMode == Listmode.UNORDERED) { sb.append("</ul></div>\n"); } this.listMode = Listmode.NONE; } private static final String TAG = "XGELS Changelog"; /** * manually set the last version name - for testing purposes only * * @param lastVersion */ public void dontuseSetLastVersion(String lastVersion) { this.lastVersion = lastVersion; } }