/**
* 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;
}
}