From 94f4412995a54df4e7eaa2a69a494f44eb182d69 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 18 Feb 2016 04:16:51 +0100 Subject: [PATCH 01/35] Rename library module to ckChangeLog-core --- {ckChangeLog => ckChangeLog-core}/build.gradle | 0 {ckChangeLog => ckChangeLog-core}/src/main/AndroidManifest.xml | 0 .../src/main/java/de/cketti/library/changelog/ChangeLog.java | 0 .../src/main/res/values-cs/strings.xml | 0 .../src/main/res/values-de/strings.xml | 0 .../src/main/res/values-el/strings.xml | 0 .../src/main/res/values-es/strings.xml | 0 .../src/main/res/values-pl/strings.xml | 0 .../src/main/res/values-ru/strings.xml | 0 .../src/main/res/values-sk/strings.xml | 0 .../src/main/res/values-uk/strings.xml | 0 .../src/main/res/values/ckChangeLog_strings.xml | 0 .../src/main/res/values/strings.xml | 0 .../src/main/res/xml/changelog.xml | 0 .../src/main/res/xml/changelog_master.xml | 0 sample/build.gradle | 2 +- settings.gradle | 2 +- 17 files changed, 2 insertions(+), 2 deletions(-) rename {ckChangeLog => ckChangeLog-core}/build.gradle (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/AndroidManifest.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/java/de/cketti/library/changelog/ChangeLog.java (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-cs/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-de/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-el/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-es/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-pl/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-ru/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-sk/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values-uk/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values/ckChangeLog_strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/values/strings.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/xml/changelog.xml (100%) rename {ckChangeLog => ckChangeLog-core}/src/main/res/xml/changelog_master.xml (100%) diff --git a/ckChangeLog/build.gradle b/ckChangeLog-core/build.gradle similarity index 100% rename from ckChangeLog/build.gradle rename to ckChangeLog-core/build.gradle diff --git a/ckChangeLog/src/main/AndroidManifest.xml b/ckChangeLog-core/src/main/AndroidManifest.xml similarity index 100% rename from ckChangeLog/src/main/AndroidManifest.xml rename to ckChangeLog-core/src/main/AndroidManifest.xml diff --git a/ckChangeLog/src/main/java/de/cketti/library/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java similarity index 100% rename from ckChangeLog/src/main/java/de/cketti/library/changelog/ChangeLog.java rename to ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java diff --git a/ckChangeLog/src/main/res/values-cs/strings.xml b/ckChangeLog-core/src/main/res/values-cs/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-cs/strings.xml rename to ckChangeLog-core/src/main/res/values-cs/strings.xml diff --git a/ckChangeLog/src/main/res/values-de/strings.xml b/ckChangeLog-core/src/main/res/values-de/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-de/strings.xml rename to ckChangeLog-core/src/main/res/values-de/strings.xml diff --git a/ckChangeLog/src/main/res/values-el/strings.xml b/ckChangeLog-core/src/main/res/values-el/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-el/strings.xml rename to ckChangeLog-core/src/main/res/values-el/strings.xml diff --git a/ckChangeLog/src/main/res/values-es/strings.xml b/ckChangeLog-core/src/main/res/values-es/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-es/strings.xml rename to ckChangeLog-core/src/main/res/values-es/strings.xml diff --git a/ckChangeLog/src/main/res/values-pl/strings.xml b/ckChangeLog-core/src/main/res/values-pl/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-pl/strings.xml rename to ckChangeLog-core/src/main/res/values-pl/strings.xml diff --git a/ckChangeLog/src/main/res/values-ru/strings.xml b/ckChangeLog-core/src/main/res/values-ru/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-ru/strings.xml rename to ckChangeLog-core/src/main/res/values-ru/strings.xml diff --git a/ckChangeLog/src/main/res/values-sk/strings.xml b/ckChangeLog-core/src/main/res/values-sk/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-sk/strings.xml rename to ckChangeLog-core/src/main/res/values-sk/strings.xml diff --git a/ckChangeLog/src/main/res/values-uk/strings.xml b/ckChangeLog-core/src/main/res/values-uk/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values-uk/strings.xml rename to ckChangeLog-core/src/main/res/values-uk/strings.xml diff --git a/ckChangeLog/src/main/res/values/ckChangeLog_strings.xml b/ckChangeLog-core/src/main/res/values/ckChangeLog_strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values/ckChangeLog_strings.xml rename to ckChangeLog-core/src/main/res/values/ckChangeLog_strings.xml diff --git a/ckChangeLog/src/main/res/values/strings.xml b/ckChangeLog-core/src/main/res/values/strings.xml similarity index 100% rename from ckChangeLog/src/main/res/values/strings.xml rename to ckChangeLog-core/src/main/res/values/strings.xml diff --git a/ckChangeLog/src/main/res/xml/changelog.xml b/ckChangeLog-core/src/main/res/xml/changelog.xml similarity index 100% rename from ckChangeLog/src/main/res/xml/changelog.xml rename to ckChangeLog-core/src/main/res/xml/changelog.xml diff --git a/ckChangeLog/src/main/res/xml/changelog_master.xml b/ckChangeLog-core/src/main/res/xml/changelog_master.xml similarity index 100% rename from ckChangeLog/src/main/res/xml/changelog_master.xml rename to ckChangeLog-core/src/main/res/xml/changelog_master.xml diff --git a/sample/build.gradle b/sample/build.gradle index 67e9cc1..abb6574 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -14,7 +14,7 @@ android { } dependencies { - compile project(':ckChangeLog') + compile project(':ckChangeLog-core') compile 'com.android.support:support-v4:21.0.3' } diff --git a/settings.gradle b/settings.gradle index cc5e2d8..295fce9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -include ':ckChangeLog' +include ':ckChangeLog-core' include ':sample' From 1c3e60e38e932cdb3174b9f66c8830bfa48de48a Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 18 Feb 2016 06:09:12 +0100 Subject: [PATCH 02/35] Extract dialog code to separate module --- build.gradle | 2 +- .../cketti/library/changelog/ChangeLog.java | 160 +---------------- ckChangeLog-dialog/build.gradle | 17 ++ .../src/main/AndroidManifest.xml | 5 + .../changelog/dialog/DialogChangeLog.java | 162 ++++++++++++++++++ .../src/main/res/values-cs/strings.xml | 0 .../src/main/res/values-de/strings.xml | 0 .../src/main/res/values-el/strings.xml | 0 .../src/main/res/values-es/strings.xml | 0 .../src/main/res/values-pl/strings.xml | 0 .../src/main/res/values-ru/strings.xml | 0 .../src/main/res/values-sk/strings.xml | 0 .../src/main/res/values-uk/strings.xml | 0 .../src/main/res/values/strings.xml | 0 sample/build.gradle | 2 +- .../cketti/sample/changelog/MainActivity.java | 25 ++- settings.gradle | 1 + 17 files changed, 201 insertions(+), 173 deletions(-) create mode 100644 ckChangeLog-dialog/build.gradle create mode 100644 ckChangeLog-dialog/src/main/AndroidManifest.xml create mode 100644 ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-cs/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-de/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-el/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-es/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-pl/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-ru/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-sk/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values-uk/strings.xml (100%) rename {ckChangeLog-core => ckChangeLog-dialog}/src/main/res/values/strings.xml (100%) diff --git a/build.gradle b/build.gradle index a26292d..ba16cbf 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:1.5.0' } } diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java index b87b3f0..2824257 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java @@ -42,9 +42,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; @@ -52,7 +50,6 @@ import android.preference.PreferenceManager; import android.util.Log; import android.util.SparseArray; -import android.webkit.WebView; /** @@ -75,25 +72,12 @@ public class ChangeLog { */ protected static final int NO_VERSION = -1; - /** - * Default CSS styles used to format the change log. - */ - public static final String DEFAULT_CSS = - "h1 { margin-left: 0px; font-size: 1.2em; }" + "\n" + - "li { margin-left: 0px; }" + "\n" + - "ul { padding-left: 2em; }"; - /** * Context that is used to access the resources and to create the ChangeLog dialogs. */ protected final Context mContext; - /** - * Contains the CSS rules used to format the change log. - */ - protected final String mCss; - /** * Last version code read from {@code SharedPreferences} or {@link #NO_VERSION}. */ @@ -140,19 +124,7 @@ protected interface ChangeTag { * Context that is used to access the resources and to create the ChangeLog dialogs. */ public ChangeLog(Context context) { - this(context, PreferenceManager.getDefaultSharedPreferences(context), DEFAULT_CSS); - } - - /** - * Create a {@code ChangeLog} instance using the default {@link SharedPreferences} file. - * - * @param context - * Context that is used to access the resources and to create the ChangeLog dialogs. - * @param css - * CSS styles that will be used to format the change log. - */ - public ChangeLog(Context context, String css) { - this(context, PreferenceManager.getDefaultSharedPreferences(context), css); + this(context, PreferenceManager.getDefaultSharedPreferences(context)); } /** @@ -162,14 +134,10 @@ public ChangeLog(Context context, String css) { * Context that is used to access the resources and to create the ChangeLog dialogs. * @param preferences * {@code SharedPreferences} instance that is used to persist the last version code. - * @param css - * CSS styles used to format the change log (excluding {@code }). * */ - public ChangeLog(Context context, SharedPreferences preferences, String css) { + public ChangeLog(Context context, SharedPreferences preferences) { mContext = context; - mCss = css; // Get last version code mLastVersionCode = preferences.getInt(VERSION_KEY, NO_VERSION); @@ -254,76 +222,10 @@ public void skipLogDialog() { updateVersionInPreferences(); } - /** - * Get the "What's New" dialog. - * - * @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 - * {@code ChangeLog} then the full log dialog is show. - */ - public AlertDialog getLogDialog() { - return getDialog(isFirstRunEver()); - } - - /** - * Get a dialog with the full change log. - * - * @return An AlertDialog with a full change log displayed. - */ - public AlertDialog getFullLogDialog() { - return getDialog(true); - } - - /** - * Create a dialog containing (parts of the) change log. - * - * @param full - * If this is {@code true} the full change log is displayed. Otherwise only changes for - * versions newer than the last version are displayed. - * - * @return A dialog containing the (partial) change log. - */ - protected AlertDialog getDialog(boolean full) { - WebView wv = new WebView(mContext); - //wv.setBackgroundColor(0); // transparent - wv.loadDataWithBaseURL(null, getLog(full), "text/html", "UTF-8", null); - - AlertDialog.Builder builder = new AlertDialog.Builder(mContext); - builder.setTitle( - mContext.getResources().getString( - full ? R.string.changelog_full_title : R.string.changelog_title)) - .setView(wv) - .setCancelable(false) - // OK button - .setPositiveButton( - mContext.getResources().getString(R.string.changelog_ok_button), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // The user clicked "OK" so save the current version code as - // "last version code". - updateVersionInPreferences(); - } - }); - - if (!full) { - // Show "More…" button if we're only displaying a partial change log. - builder.setNegativeButton(R.string.changelog_show_full, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - getFullLogDialog().show(); - } - }); - } - - return builder.create(); - } - /** * Write current version code to the preferences. */ - protected void updateVersionInPreferences() { + public void updateVersionInPreferences() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); SharedPreferences.Editor editor = sp.edit(); editor.putInt(VERSION_KEY, mCurrentVersionCode); @@ -332,62 +234,6 @@ protected void updateVersionInPreferences() { editor.commit(); } - /** - * Get changes since last version as HTML string. - * - * @return HTML string containing the changes since the previous installed version of your app - * (What's New). - */ - public String getLog() { - return getLog(false); - } - - /** - * Get full change log as HTML string. - * - * @return HTML string containing the full change log. - */ - public String getFullLog() { - return getLog(true); - } - - /** - * Get (partial) change log as HTML string. - * - * @param full - * If this is {@code true} the full change log is returned. Otherwise only changes for - * versions newer than the last version are returned. - * - * @return The (partial) change log. - */ - protected String getLog(boolean full) { - StringBuilder sb = new StringBuilder(); - - sb.append(""); - - String versionFormat = mContext.getResources().getString(R.string.changelog_version_format); - - List changelog = getChangeLog(full); - - for (ReleaseItem release : changelog) { - sb.append("

"); - sb.append(String.format(versionFormat, release.versionName)); - sb.append("

    "); - for (String change : release.changes) { - sb.append("
  • "); - sb.append(change); - sb.append("
  • "); - } - sb.append("
"); - } - - sb.append(""); - - return sb.toString(); - } - /** * Returns the merged change log. * diff --git a/ckChangeLog-dialog/build.gradle b/ckChangeLog-dialog/build.gradle new file mode 100644 index 0000000..6f4fe46 --- /dev/null +++ b/ckChangeLog-dialog/build.gradle @@ -0,0 +1,17 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion propCompileSdkVersion + buildToolsVersion propBuildToolsVersion + + defaultConfig { + minSdkVersion 4 + + versionCode 1 + versionName "1.0" + } +} + +dependencies { + compile project(':ckChangeLog-core') +} diff --git a/ckChangeLog-dialog/src/main/AndroidManifest.xml b/ckChangeLog-dialog/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e9e6814 --- /dev/null +++ b/ckChangeLog-dialog/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java new file mode 100644 index 0000000..4aeeaaa --- /dev/null +++ b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java @@ -0,0 +1,162 @@ +package de.cketti.library.changelog.dialog; + + +import java.util.List; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.webkit.WebView; + +import de.cketti.library.changelog.ChangeLog; +import de.cketti.library.changelog.ChangeLog.ReleaseItem; + + +public final class DialogChangeLog { + /** + * Default CSS styles used to format the change log. + */ + public static final String DEFAULT_CSS = "" + + "h1 { margin-left: 0px; font-size: 1.2em; }" + "\n" + + "li { margin-left: 0px; }" + "\n" + + "ul { padding-left: 2em; }"; + + + private final Context context; + private final ChangeLog changeLog; + private final String css; + + + public static DialogChangeLog newInstance(Context context) { + ChangeLog changeLog = new ChangeLog(context); + return new DialogChangeLog(context, changeLog, DEFAULT_CSS); + } + + public static DialogChangeLog newInstance(Context context, String css) { + ChangeLog changeLog = new ChangeLog(context); + return new DialogChangeLog(context, changeLog, css); + } + + private DialogChangeLog(Context context, ChangeLog changeLog, String css) { + this.context = context; + this.changeLog = changeLog; + this.css = css; + } + + /** + * Get the "What's New" dialog. + * + * @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 + * {@code ChangeLog} then the full log dialog is show. + */ + public AlertDialog getLogDialog() { + return getDialog(changeLog.isFirstRunEver()); + } + + /** + * Get a dialog with the full change log. + * + * @return An AlertDialog with a full change log displayed. + */ + public AlertDialog getFullLogDialog() { + return getDialog(true); + } + + private AlertDialog getDialog(boolean full) { + WebView wv = new WebView(context); + //wv.setBackgroundColor(0); // transparent + wv.loadDataWithBaseURL(null, getLog(full), "text/html", "UTF-8", null); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle( + context.getResources().getString( + full ? R.string.changelog_full_title : R.string.changelog_title)) + .setView(wv) + .setCancelable(false) + // OK button + .setPositiveButton( + context.getResources().getString(R.string.changelog_ok_button), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // The user clicked "OK" so save the current version code as + // "last version code". + changeLog.updateVersionInPreferences(); + } + }); + + if (!full) { + // Show "More…" button if we're only displaying a partial change log. + builder.setNegativeButton(R.string.changelog_show_full, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + getFullLogDialog().show(); + } + }); + } + + return builder.create(); + } + + /** + * Get changes since last version as HTML string. + * + * @return HTML string containing the changes since the previous installed version of your app + * (What's New). + */ + public String getLog() { + return getLog(false); + } + + /** + * Get full change log as HTML string. + * + * @return HTML string containing the full change log. + */ + public String getFullLog() { + return getLog(true); + } + + /** + * Get (partial) change log as HTML string. + * + * @param full + * If this is {@code true} the full change log is returned. Otherwise only changes for + * versions newer than the last version are returned. + * + * @return The (partial) change log. + */ + protected String getLog(boolean full) { + StringBuilder sb = new StringBuilder(); + + sb.append(""); + + String versionFormat = context.getResources().getString(R.string.changelog_version_format); + + List changelog = changeLog.getChangeLog(full); + + for (ReleaseItem release : changelog) { + sb.append("

"); + sb.append(String.format(versionFormat, release.versionName)); + sb.append("

    "); + for (String change : release.changes) { + sb.append("
  • "); + sb.append(change); + sb.append("
  • "); + } + sb.append("
"); + } + + sb.append(""); + + return sb.toString(); + } + + public boolean isFirstRun() { + return changeLog.isFirstRun(); + } +} diff --git a/ckChangeLog-core/src/main/res/values-cs/strings.xml b/ckChangeLog-dialog/src/main/res/values-cs/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-cs/strings.xml rename to ckChangeLog-dialog/src/main/res/values-cs/strings.xml diff --git a/ckChangeLog-core/src/main/res/values-de/strings.xml b/ckChangeLog-dialog/src/main/res/values-de/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-de/strings.xml rename to ckChangeLog-dialog/src/main/res/values-de/strings.xml diff --git a/ckChangeLog-core/src/main/res/values-el/strings.xml b/ckChangeLog-dialog/src/main/res/values-el/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-el/strings.xml rename to ckChangeLog-dialog/src/main/res/values-el/strings.xml diff --git a/ckChangeLog-core/src/main/res/values-es/strings.xml b/ckChangeLog-dialog/src/main/res/values-es/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-es/strings.xml rename to ckChangeLog-dialog/src/main/res/values-es/strings.xml diff --git a/ckChangeLog-core/src/main/res/values-pl/strings.xml b/ckChangeLog-dialog/src/main/res/values-pl/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-pl/strings.xml rename to ckChangeLog-dialog/src/main/res/values-pl/strings.xml diff --git a/ckChangeLog-core/src/main/res/values-ru/strings.xml b/ckChangeLog-dialog/src/main/res/values-ru/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-ru/strings.xml rename to ckChangeLog-dialog/src/main/res/values-ru/strings.xml diff --git a/ckChangeLog-core/src/main/res/values-sk/strings.xml b/ckChangeLog-dialog/src/main/res/values-sk/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-sk/strings.xml rename to ckChangeLog-dialog/src/main/res/values-sk/strings.xml diff --git a/ckChangeLog-core/src/main/res/values-uk/strings.xml b/ckChangeLog-dialog/src/main/res/values-uk/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values-uk/strings.xml rename to ckChangeLog-dialog/src/main/res/values-uk/strings.xml diff --git a/ckChangeLog-core/src/main/res/values/strings.xml b/ckChangeLog-dialog/src/main/res/values/strings.xml similarity index 100% rename from ckChangeLog-core/src/main/res/values/strings.xml rename to ckChangeLog-dialog/src/main/res/values/strings.xml diff --git a/sample/build.gradle b/sample/build.gradle index abb6574..a26be9e 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -14,7 +14,7 @@ android { } dependencies { - compile project(':ckChangeLog-core') + compile project(':ckChangeLog-dialog') compile 'com.android.support:support-v4:21.0.3' } diff --git a/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java b/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java index 01e8652..ced0113 100644 --- a/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java +++ b/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java @@ -1,6 +1,7 @@ package de.cketti.sample.changelog; -import de.cketti.library.changelog.ChangeLog; +import de.cketti.library.changelog.dialog.DialogChangeLog; + import android.content.Context; import android.os.Bundle; import android.support.v4.app.FragmentActivity; @@ -10,13 +11,16 @@ public class MainActivity extends FragmentActivity { + private static final String DARK_THEME_CSS = "body { color: #ffffff; background-color: #282828; }" + "\n" + + DialogChangeLog.DEFAULT_CSS; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - ChangeLog cl = new ChangeLog(this); + DialogChangeLog cl = DialogChangeLog.newInstance(this); if (cl.isFirstRun()) { cl.getLogDialog().show(); } @@ -32,11 +36,11 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_whats_new: { - new DarkThemeChangeLog(this).getLogDialog().show(); + createDarkThemeChangeLog(this).getLogDialog().show(); break; } case R.id.menu_full_changelog: { - new ChangeLog(this).getFullLogDialog().show(); + DialogChangeLog.newInstance(this).getFullLogDialog().show(); break; } } @@ -44,15 +48,8 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } - /** - * Example that shows how to create a themed dialog. - */ - public static class DarkThemeChangeLog extends ChangeLog { - public static final String DARK_THEME_CSS = - "body { color: #ffffff; background-color: #282828; }" + "\n" + DEFAULT_CSS; - - public DarkThemeChangeLog(Context context) { - super(new ContextThemeWrapper(context, R.style.DarkTheme), DARK_THEME_CSS); - } + private static DialogChangeLog createDarkThemeChangeLog(Context context) { + ContextThemeWrapper themedContext = new ContextThemeWrapper(context, R.style.DarkTheme); + return DialogChangeLog.newInstance(themedContext, DARK_THEME_CSS); } } diff --git a/settings.gradle b/settings.gradle index 295fce9..80b5e34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ include ':ckChangeLog-core' +include ':ckChangeLog-dialog' include ':sample' From 8f6183dc183c291e8d46a150817e8284bba2a263 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 18 Feb 2016 06:25:30 +0100 Subject: [PATCH 03/35] Make ReleaseItem a top-level class --- .../cketti/library/changelog/ChangeLog.java | 25 --------------- .../cketti/library/changelog/ReleaseItem.java | 32 +++++++++++++++++++ .../changelog/dialog/DialogChangeLog.java | 2 +- 3 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java index 2824257..fbd7a48 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java @@ -418,29 +418,4 @@ public int compare(ReleaseItem lhs, ReleaseItem rhs) { }; } - /** - * Container used to store information about a release/version. - */ - public static class ReleaseItem { - /** - * Version code of the release. - */ - public final int versionCode; - - /** - * Version name of the release. - */ - public final String versionName; - - /** - * List of changes introduced with that release. - */ - public final List changes; - - ReleaseItem(int versionCode, String versionName, List changes) { - this.versionCode = versionCode; - this.versionName = versionName; - this.changes = changes; - } - } } diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java new file mode 100644 index 0000000..be62e62 --- /dev/null +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java @@ -0,0 +1,32 @@ +package de.cketti.library.changelog; + + +import java.util.List; + + +/** + * Container used to store information about a release/version. + */ +public class ReleaseItem { + /** + * Version code of the release. + */ + public final int versionCode; + + /** + * Version name of the release. + */ + public final String versionName; + + /** + * List of changes introduced with that release. + */ + public final List changes; + + + ReleaseItem(int versionCode, String versionName, List changes) { + this.versionCode = versionCode; + this.versionName = versionName; + this.changes = changes; + } +} diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java index 4aeeaaa..5ab9b10 100644 --- a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java +++ b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java @@ -9,7 +9,7 @@ import android.webkit.WebView; import de.cketti.library.changelog.ChangeLog; -import de.cketti.library.changelog.ChangeLog.ReleaseItem; +import de.cketti.library.changelog.ReleaseItem; public final class DialogChangeLog { From 2af83292903bec76652c713d7d18ed03315188c5 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 18 Feb 2016 07:38:25 +0100 Subject: [PATCH 04/35] Clean up the code a bit --- .../cketti/library/changelog/ChangeLog.java | 221 +++++------------- .../changelog/dialog/DialogChangeLog.java | 45 +--- 2 files changed, 72 insertions(+), 194 deletions(-) diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java index fbd7a48..0bfaa0a 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java @@ -53,104 +53,65 @@ /** - * Display a dialog showing a full or partial (What's New) change log. + * Generate a full or partial (What's New) change log. */ @SuppressWarnings("UnusedDeclaration") -public class ChangeLog { - /** - * Tag that is used when sending error/debug messages to the log. - */ - protected static final String LOG_TAG = "ckChangeLog"; - - /** - * This is the key used when storing the version code in SharedPreferences. - */ - protected static final String VERSION_KEY = "ckChangeLog_last_version_code"; - - /** - * Constant that used when no version code is available. - */ - protected static final int NO_VERSION = -1; - - - /** - * Context that is used to access the resources and to create the ChangeLog dialogs. - */ - protected final Context mContext; - - /** - * Last version code read from {@code SharedPreferences} or {@link #NO_VERSION}. - */ - private int mLastVersionCode; +public final class ChangeLog { + private static final String LOG_TAG = "ckChangeLog"; + private static final String VERSION_KEY = "ckChangeLog_last_version_code"; + private static final int NO_VERSION = -1; - /** - * Version code of the current installation. - */ - private int mCurrentVersionCode; - /** - * Version name of the current installation. - */ - private String mCurrentVersionName; + private final Context context; + private int lastVersionCode; + private int currentVersionCode; + private String currentVersionName; - /** - * Contains constants for the root element of {@code changelog.xml}. - */ - protected interface ChangeLogTag { - static final String NAME = "changelog"; - } - - /** - * Contains constants for the release element of {@code changelog.xml}. - */ - protected interface ReleaseTag { - static final String NAME = "release"; - static final String ATTRIBUTE_VERSION = "version"; - static final String ATTRIBUTE_VERSION_CODE = "versioncode"; - } - - /** - * Contains constants for the change element of {@code changelog.xml}. - */ - protected interface ChangeTag { - static final String NAME = "change"; - } - /** * Create a {@code ChangeLog} instance using the default {@link SharedPreferences} file. * * @param context - * Context that is used to access the resources and to create the ChangeLog dialogs. + * Context that is used to access resources. */ - public ChangeLog(Context context) { - this(context, PreferenceManager.getDefaultSharedPreferences(context)); + public static ChangeLog newInstance(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + ChangeLog changeLog = new ChangeLog(context); + changeLog.init(preferences); + + return changeLog; } /** * Create a {@code ChangeLog} instance using the supplied {@code SharedPreferences} instance. * * @param context - * Context that is used to access the resources and to create the ChangeLog dialogs. + * Context that is used to access resources. * @param preferences * {@code SharedPreferences} instance that is used to persist the last version code. * */ - public ChangeLog(Context context, SharedPreferences preferences) { - mContext = context; + public static ChangeLog newInstance(Context context, SharedPreferences preferences) { + ChangeLog changeLog = new ChangeLog(context); + changeLog.init(preferences); - // Get last version code - mLastVersionCode = preferences.getInt(VERSION_KEY, NO_VERSION); + return changeLog; + } + + private ChangeLog(Context context) { + this.context = context; + } + + private void init(SharedPreferences preferences) { + lastVersionCode = preferences.getInt(VERSION_KEY, NO_VERSION); - // Get current version code and version name try { - PackageInfo packageInfo = context.getPackageManager().getPackageInfo( - context.getPackageName(), 0); + PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - mCurrentVersionCode = packageInfo.versionCode; - mCurrentVersionName = packageInfo.versionName; + currentVersionCode = packageInfo.versionCode; + currentVersionName = packageInfo.versionName; } catch (NameNotFoundException e) { - mCurrentVersionCode = NO_VERSION; + currentVersionCode = NO_VERSION; Log.e(LOG_TAG, "Could not get version information from manifest!", e); } } @@ -166,7 +127,7 @@ public ChangeLog(Context context, SharedPreferences preferences) { * @see android:versionCode */ public int getLastVersionCode() { - return mLastVersionCode; + return lastVersionCode; } /** @@ -177,7 +138,7 @@ public int getLastVersionCode() { * @see android:versionCode */ public int getCurrentVersionCode() { - return mCurrentVersionCode; + return currentVersionCode; } /** @@ -188,7 +149,7 @@ public int getCurrentVersionCode() { * @see android:versionName */ public String getCurrentVersionName() { - return mCurrentVersionName; + return currentVersionName; } /** @@ -197,7 +158,7 @@ public String getCurrentVersionName() { * @return {@code true} if this version of your app is started the first time. */ public boolean isFirstRun() { - return mLastVersionCode < mCurrentVersionCode; + return lastVersionCode < currentVersionCode; } /** @@ -207,28 +168,21 @@ public boolean isFirstRun() { * Also {@code true} if your app was uninstalled and installed again. */ public boolean isFirstRunEver() { - return mLastVersionCode == NO_VERSION; + return lastVersionCode == NO_VERSION; } /** - * Skip the "What's new" dialog for this app version. + * Write current version code to the preferences. * *

* Future calls to {@link #isFirstRun()} and {@link #isFirstRunEver()} will return {@code false} * for the current app version. *

*/ - public void skipLogDialog() { - updateVersionInPreferences(); - } - - /** - * Write current version code to the preferences. - */ - public void updateVersionInPreferences() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + public void writeCurrentVersion() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); - editor.putInt(VERSION_KEY, mCurrentVersionCode); + editor.putInt(VERSION_KEY, currentVersionCode); // TODO: Update preferences from a background thread editor.commit(); @@ -250,8 +204,7 @@ public List getChangeLog(boolean full) { SparseArray masterChangelog = getMasterChangeLog(full); SparseArray changelog = getLocalizedChangeLog(full); - List mergedChangeLog = - new ArrayList(masterChangelog.size()); + List mergedChangeLog = new ArrayList(masterChangelog.size()); for (int i = 0, len = masterChangelog.size(); i < len; i++) { int key = masterChangelog.keyAt(i); @@ -268,38 +221,16 @@ public List getChangeLog(boolean full) { return mergedChangeLog; } - /** - * Read master change log from {@code xml/changelog_master.xml} - * - * @see #readChangeLogFromResource(int, boolean) - */ - protected SparseArray getMasterChangeLog(boolean full) { + private SparseArray getMasterChangeLog(boolean full) { return readChangeLogFromResource(R.xml.changelog_master, full); } - /** - * Read localized change log from {@code xml[-lang]/changelog.xml} - * - * @see #readChangeLogFromResource(int, boolean) - */ - protected SparseArray getLocalizedChangeLog(boolean full) { + private SparseArray getLocalizedChangeLog(boolean full) { return readChangeLogFromResource(R.xml.changelog, full); } - /** - * Read change log from XML resource file. - * - * @param resId - * Resource ID of the XML file to read the change log from. - * @param full - * If this is {@code true} the full change log is returned. Otherwise only changes for - * versions newer than the last version are returned. - * - * @return A {@code SparseArray} containing {@link ReleaseItem}s representing the (partial) - * change log. - */ - protected final SparseArray readChangeLogFromResource(int resId, boolean full) { - XmlResourceParser xml = mContext.getResources().getXml(resId); + private SparseArray readChangeLogFromResource(int resId, boolean full) { + XmlResourceParser xml = context.getResources().getXml(resId); try { return readChangeLog(xml, full); } finally { @@ -307,18 +238,7 @@ protected final SparseArray readChangeLogFromResource(int resId, bo } } - /** - * Read the change log from an XML file. - * - * @param xml - * The {@code XmlPullParser} instance used to read the change log. - * @param full - * If {@code true} the full change log is read. Otherwise only the changes since the - * last (saved) version are read. - * - * @return A {@code SparseArray} mapping the version codes to release information. - */ - protected SparseArray readChangeLog(XmlPullParser xml, boolean full) { + private SparseArray readChangeLog(XmlPullParser xml, boolean full) { SparseArray result = new SparseArray(); try { @@ -342,26 +262,6 @@ protected SparseArray readChangeLog(XmlPullParser xml, boolean full return result; } - /** - * Parse the {@code release} tag of a change log XML file. - * - * @param xml - * The {@code XmlPullParser} instance used to read the change log. - * @param full - * If {@code true} the contents of the {@code release} tag are always added to - * {@code changelog}. Otherwise only if the item's {@code versioncode} attribute is - * higher than the last version code. - * @param changelog - * The {@code SparseArray} to add a new {@link ReleaseItem} instance to. - * - * @return {@code true} if the {@code release} element is describing changes of a version older - * or equal to the last version. In that case {@code changelog} won't be modified and - * {@link #readChangeLog(XmlPullParser, boolean)} will stop reading more elements from - * the change log file. - * - * @throws XmlPullParserException - * @throws IOException - */ private boolean parseReleaseTag(XmlPullParser xml, boolean full, SparseArray changelog) throws XmlPullParserException, IOException { @@ -375,7 +275,7 @@ private boolean parseReleaseTag(XmlPullParser xml, boolean full, versionCode = NO_VERSION; } - if (!full && versionCode <= mLastVersionCode) { + if (!full && versionCode <= lastVersionCode) { return true; } @@ -396,14 +296,7 @@ private boolean parseReleaseTag(XmlPullParser xml, boolean full, return false; } - /** - * Returns a {@link Comparator} that specifies the sort order of the {@link ReleaseItem}s. - * - *

- * The default implementation returns the items in reverse order (latest version first). - *

- */ - protected Comparator getChangeLogComparator() { + private Comparator getChangeLogComparator() { return new Comparator() { @Override public int compare(ReleaseItem lhs, ReleaseItem rhs) { @@ -418,4 +311,18 @@ public int compare(ReleaseItem lhs, ReleaseItem rhs) { }; } + + private interface ChangeLogTag { + String NAME = "changelog"; + } + + private interface ReleaseTag { + String NAME = "release"; + String ATTRIBUTE_VERSION = "version"; + String ATTRIBUTE_VERSION_CODE = "versioncode"; + } + + private interface ChangeTag { + String NAME = "change"; + } } diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java index 5ab9b10..98fd768 100644 --- a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java +++ b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java @@ -28,12 +28,11 @@ public final class DialogChangeLog { public static DialogChangeLog newInstance(Context context) { - ChangeLog changeLog = new ChangeLog(context); - return new DialogChangeLog(context, changeLog, DEFAULT_CSS); + return DialogChangeLog.newInstance(context, DEFAULT_CSS); } public static DialogChangeLog newInstance(Context context, String css) { - ChangeLog changeLog = new ChangeLog(context); + ChangeLog changeLog = ChangeLog.newInstance(context); return new DialogChangeLog(context, changeLog, css); } @@ -63,6 +62,10 @@ public AlertDialog getFullLogDialog() { return getDialog(true); } + public boolean isFirstRun() { + return changeLog.isFirstRun(); + } + private AlertDialog getDialog(boolean full) { WebView wv = new WebView(context); //wv.setBackgroundColor(0); // transparent @@ -82,7 +85,7 @@ private AlertDialog getDialog(boolean full) { public void onClick(DialogInterface dialog, int which) { // The user clicked "OK" so save the current version code as // "last version code". - changeLog.updateVersionInPreferences(); + changeLog.writeCurrentVersion(); } }); @@ -100,35 +103,7 @@ public void onClick(DialogInterface dialog, int id) { return builder.create(); } - /** - * Get changes since last version as HTML string. - * - * @return HTML string containing the changes since the previous installed version of your app - * (What's New). - */ - public String getLog() { - return getLog(false); - } - - /** - * Get full change log as HTML string. - * - * @return HTML string containing the full change log. - */ - public String getFullLog() { - return getLog(true); - } - - /** - * Get (partial) change log as HTML string. - * - * @param full - * If this is {@code true} the full change log is returned. Otherwise only changes for - * versions newer than the last version are returned. - * - * @return The (partial) change log. - */ - protected String getLog(boolean full) { + private String getLog(boolean full) { StringBuilder sb = new StringBuilder(); sb.append(""); - - String versionFormat = context.getResources().getString(R.string.changelog_version_format); - + private String getChangeLogHtml(boolean full) { List changelog = full ? changeLog.getChangeLog() : changeLog.getRecentChanges(); - for (ReleaseItem release : changelog) { - sb.append("

"); - sb.append(String.format(versionFormat, release.versionName)); - sb.append("

    "); - for (String change : release.changes) { - sb.append("
  • "); - sb.append(change); - sb.append("
  • "); - } - sb.append("
"); - } - - sb.append(""); - - return sb.toString(); + return formatter.createHtmlChangeLog(changelog); } } diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/HtmlFormatter.java b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/HtmlFormatter.java new file mode 100644 index 0000000..f3ed73d --- /dev/null +++ b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/HtmlFormatter.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012-2017 cketti and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.cketti.library.changelog.dialog; + + +import java.util.List; + +import de.cketti.library.changelog.ReleaseItem; + + +class HtmlFormatter { + private final String versionFormat; + private final String css; + + + public HtmlFormatter(String versionFormat, String css) { + this.versionFormat = versionFormat; + this.css = css; + } + + public String createHtmlChangeLog(List changelog) { + StringBuilder sb = new StringBuilder(); + + sb.append(""); + + for (ReleaseItem release : changelog) { + sb.append("

"); + sb.append(String.format(versionFormat, release.versionName)); + sb.append("

    "); + for (String change : release.changes) { + sb.append("
  • "); + sb.append(change); + sb.append("
  • "); + } + sb.append("
"); + } + + sb.append(""); + + return sb.toString(); + } +} From 3b4f6733a83f74508c72102aa275e339ad4eeb96 Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 24 Mar 2017 06:06:15 +0100 Subject: [PATCH 19/35] Expose the used ChangeLog instance to users of DialogChangeLog --- .../cketti/library/changelog/dialog/DialogChangeLog.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java index 77c6864..b1fcef3 100644 --- a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java +++ b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java @@ -59,6 +59,13 @@ private DialogChangeLog(Context context, ChangeLog changeLog, HtmlFormatter form this.formatter = formatter; } + /** + * Get the {@link ChangeLog} instance backing this {@code DialogChangeLog}. + */ + public ChangeLog getChangeLog() { + return changeLog; + } + /** * Get the "What's New" dialog. * From 5cae803d60072ebd1979ae8f92faddd36b63f315 Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 24 Mar 2017 06:59:10 +0100 Subject: [PATCH 20/35] Clean up and add more JavaDoc --- .../cketti/library/changelog/ChangeLog.java | 20 ++++++-- .../library/changelog/ChangeLogProvider.java | 13 +++++ .../changelog/InvalidChangeLogException.java | 3 ++ .../changelog/MergedChangeLogProvider.java | 8 +++ .../cketti/library/changelog/ReleaseItem.java | 3 +- .../changelog/ResourceChangeLogProvider.java | 5 +- .../cketti/library/changelog/XmlParser.java | 9 ++-- .../changelog/dialog/DialogChangeLog.java | 51 ++++++++++++------- 8 files changed, 85 insertions(+), 27 deletions(-) diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java index d6ee960..e0bc1e6 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java @@ -45,9 +45,8 @@ /** - * Generate a full or partial (What's New) change log. + * Generate a full or partial (What's New) Change Log. */ -@SuppressWarnings("UnusedDeclaration") public final class ChangeLog { private static final String LOG_TAG = "ckChangeLog"; private static final String VERSION_KEY = "ckChangeLog_last_version_code"; @@ -91,7 +90,18 @@ public static ChangeLog newInstance(Context context, SharedPreferences preferenc return newInstance(context, preferences, changeLogProvider); } - public static ChangeLog newInstance(Context context, SharedPreferences preferences, + /** + * Create a {@code ChangeLog} instance using the supplied {@code SharedPreferences} and {@code ChangeLogProvider} + * instances. + * + * @param context + * Context that is used to access resources. + * @param preferences + * {@code SharedPreferences} instance that is used to persist the last version code. + * @param changeLogProvider + * {@code ChangeLogProvider} instance that is used to retrieve the Change Log. + */ + public static ChangeLog newInstance(Context context, SharedPreferences preferences, ChangeLogProvider changeLogProvider) { ChangeLog changeLog = new ChangeLog(context, preferences, changeLogProvider); changeLog.init(); @@ -189,9 +199,9 @@ public void writeCurrentVersion() { } /** - * Returns the full change log. + * Returns the full Change Log. * - * @return A sorted {@code List} containing {@link ReleaseItem}s representing the full change log. + * @return A sorted {@code List} containing {@link ReleaseItem}s representing the full Change Log. */ public List getChangeLog() { return changeLogProvider.getChangeLog(); diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLogProvider.java index 7c4bd79..1b95de8 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLogProvider.java @@ -19,7 +19,20 @@ import java.util.List; +/** + * A Change Log provider that can return information about all versions or only ones newer than a given version code. + */ public interface ChangeLogProvider { + /** + * Get all {@code ReleaseItem} entries of this Change Log. + */ List getChangeLog(); + + /** + * Get only {@code ReleaseItem} entries newer than the given version code. + * + * @param lastVersionCode + * {@code ReleaseItem} entries with a version code lower than or equal to this value won't be returned. + */ List getChangeLogSince(int lastVersionCode); } diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/InvalidChangeLogException.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/InvalidChangeLogException.java index 7c42f30..1935773 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/InvalidChangeLogException.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/InvalidChangeLogException.java @@ -16,6 +16,9 @@ package de.cketti.library.changelog; +/** + * Thrown by {@link XmlParser} when reading the Change Log XML fails. + */ public class InvalidChangeLogException extends RuntimeException { public InvalidChangeLogException(String detailMessage) { super(detailMessage); diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/MergedChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/MergedChangeLogProvider.java index 7b4ad48..797e803 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/MergedChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/MergedChangeLogProvider.java @@ -26,6 +26,14 @@ import static de.cketti.library.changelog.Preconditions.checkNotNull; +/** + * {@link ChangeLogProvider} that merges the data from two {@code ChangeLogProvider}s. + * + *

+ * Change Log entries from the provider with localized data are favored over the entries from the master provider. + * However, when a specific entry is missing in the localized provider the entry from the master provider will be used. + *

+ */ public final class MergedChangeLogProvider implements ChangeLogProvider { private final ChangeLogProvider masterChangeLogProvider; private final ChangeLogProvider localizedChangeLogProvider; diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java index dfdb459..0e35357 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java @@ -17,6 +17,7 @@ import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static de.cketti.library.changelog.Preconditions.checkNotNull; @@ -50,7 +51,7 @@ public static ReleaseItem newInstance(int versionCode, String versionName, List< ReleaseItem(int versionCode, String versionName, List changes) { this.versionCode = versionCode; this.versionName = checkNotNull(versionName, "versionName == null"); - this.changes = checkNotNull(changes, "changes == null"); + this.changes = Collections.unmodifiableList(checkNotNull(changes, "changes == null")); } @Override diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ResourceChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ResourceChangeLogProvider.java index d8cdc35..ab38a22 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ResourceChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ResourceChangeLogProvider.java @@ -23,7 +23,10 @@ import android.support.annotation.XmlRes; -public class ResourceChangeLogProvider implements ChangeLogProvider { +/** + * A {@link ChangeLogProvider} that reads data from an Android XML resource. + */ +class ResourceChangeLogProvider implements ChangeLogProvider { private final Context context; private final int resourceId; diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java index f08b156..89f00e2 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java @@ -24,6 +24,9 @@ import org.xmlpull.v1.XmlPullParserException; +/** + * The parser for ckChangeLog's XML file format. + */ public final class XmlParser { private static final int NO_VERSION = -1; private static final String TAG_CHANGELOG = "changelog"; @@ -54,7 +57,7 @@ private XmlParser(XmlPullParser xmlPullParser, int lastVersionCode) { result = new ArrayList<>(); } - public List readChangeLog() { + private List readChangeLog() { try { while (xmlPullParser.getEventType() != XmlPullParser.START_TAG) { xmlPullParser.next(); @@ -152,8 +155,8 @@ private void assertNextEventType(int expectedEventType, String message) throws X private void assertElementStart(String expectedElementName) throws XmlPullParserException, IOException { String tagName = xmlPullParser.getName(); if (!expectedElementName.equals(tagName)) { - throw new InvalidChangeLogException("Unexpected tag: " + tagName + - " (wanted: " + expectedElementName + ")"); + throw new InvalidChangeLogException( + "Unexpected tag: " + tagName + " (wanted: " + expectedElementName + ")"); } } diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java index b1fcef3..c292992 100644 --- a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java +++ b/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java @@ -27,9 +27,23 @@ import de.cketti.library.changelog.ReleaseItem; +/** + * Display a dialog showing a full or partial (What's New) Change Log. + * + *

+ * You can display a Change Log after app updates by putting the following code in your Activity's {@code onCreate()} + * method: + *

+ *
{@code
+ * DialogChangeLog changeLog = DialogChangeLog.newInstance(this);
+ * if (changeLog.isFirstRun()) {
+ *     changeLog.getLogDialog().show();
+ * }
+ * }
+ */ public final class DialogChangeLog { /** - * Default CSS styles used to format the change log. + * Default CSS styles used to format the Change Log. */ public static final String DEFAULT_CSS = "" + "h1 { margin-left: 0px; font-size: 1.2em; }" + "\n" + @@ -42,10 +56,16 @@ public final class DialogChangeLog { private final HtmlFormatter formatter; + /** + * Create an instance using the default CSS to format the Change Log. + */ public static DialogChangeLog newInstance(Context context) { return DialogChangeLog.newInstance(context, DEFAULT_CSS); } + /** + * Create an instance using the supplied CSS to format the Change Log. + */ public static DialogChangeLog newInstance(Context context, String css) { ChangeLog changeLog = ChangeLog.newInstance(context); String versionFormat = context.getResources().getString(R.string.changelog_version_format); @@ -60,7 +80,7 @@ private DialogChangeLog(Context context, ChangeLog changeLog, HtmlFormatter form } /** - * Get the {@link ChangeLog} instance backing this {@code DialogChangeLog}. + * Get the {@code ChangeLog} instance backing this {@code DialogChangeLog}. */ public ChangeLog getChangeLog() { return changeLog; @@ -78,43 +98,40 @@ public AlertDialog getLogDialog() { } /** - * Get a dialog with the full change log. + * Get a dialog with the full Change Log. * - * @return An AlertDialog with a full change log displayed. + * @return An AlertDialog with a full Change Log displayed. */ public AlertDialog getFullLogDialog() { return getDialog(true); } + /** + * Check if this is the first execution of this app version. + * + * @return {@code true} if this version of your app is started the first time. + */ public boolean isFirstRun() { return changeLog.isFirstRun(); } private AlertDialog getDialog(boolean full) { - WebView wv = new WebView(context); - //wv.setBackgroundColor(0); // transparent - wv.loadDataWithBaseURL(null, getChangeLogHtml(full), "text/html", "UTF-8", null); + WebView webView = new WebView(context); + webView.loadDataWithBaseURL(null, getChangeLogHtml(full), "text/html", "UTF-8", null); AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle( - context.getResources().getString( - full ? R.string.changelog_full_title : R.string.changelog_title)) - .setView(wv) + builder.setTitle(full ? R.string.changelog_full_title : R.string.changelog_title) + .setView(webView) .setCancelable(false) - // OK button - .setPositiveButton( - context.getResources().getString(R.string.changelog_ok_button), + .setPositiveButton(R.string.changelog_ok_button, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - // The user clicked "OK" so save the current version code as - // "last version code". changeLog.writeCurrentVersion(); } }); if (!full) { - // Show "More…" button if we're only displaying a partial change log. builder.setNegativeButton(R.string.changelog_show_full, new DialogInterface.OnClickListener() { @Override From ee69936c201865a622706be18457f4d6e1a70b25 Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 24 Mar 2017 08:07:44 +0100 Subject: [PATCH 21/35] Update Change Log with migration instructions --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63e2dff..505ae3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ ## Change Log +### Version 2.0.0 (unreleased) +**Breaking change!** The library was split into a core library (`ckchangelog-core`) and a library for the UI part +(`ckchangelog-dialog`). + +The core library provides the base functionality like parsing the XML file and remembering the version code of the last +app version. This allows users of the core library to easily provide their own visualization of the Change Log. + +The `ckchangelog-dialog` library provides the simple dialog from ckChangeLog 1.x that renders the Change Log in a +`WebView` inside an `AlertDialog`. + +#### Update from ckChangeLog 1.x + +Replace the old entry in the dependency block with this: + +```groovy +dependencies { + compile 'de.cketti.library.changelog:ckchangelog-dialog:2.0.0' +} +``` + +Then replace the old ckChangeLog code in your Activity's `onCreate()` method with this: + +```java +DialogChangeLog changeLog = DialogChangeLog.newInstance(this); +if (changeLog.isFirstRun()) { + changeLog.getLogDialog().show(); +} +``` + +Advanced functionality like getting the last version code is available via the `ChangeLog` instance that can be +retrieved by using `DialogChangeLog#getChangeLog()`. +Example: `dialogChangeLog.getChangeLog().isFirstRunEver()` + ### Version 1.2.2 (2015-01-09) * Added Ukrainian translation From 1752fac8f2687132a7c73d3f8d44dc919823aba9 Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 24 Mar 2017 08:13:50 +0100 Subject: [PATCH 22/35] Update README --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 436b1cc..a7a5d60 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ckChangeLog - An Android Library to display a Change Log [![Build status](https://api.travis-ci.org/cketti/ckChangeLog.svg)](https://travis-ci.org/cketti/ckChangeLog) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.cketti.library.changelog/ckchangelog/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.cketti.library.changelog/ckchangelog) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.cketti.library.changelog/ckchangelog-dialog/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.cketti.library.changelog/ckchangelog-dialog) ![Screenshot](screenshot_1.png) ![Screenshot](screenshot_2.png) @@ -14,7 +14,7 @@ This library provides an easy way to display a change log in your app. * Can display the complete change log history * Uses a simple XML file as source * Supports partial translations - * Easily extendable to use something other than a dialog + * Easily extensible to use something other than a dialog Repository at . @@ -42,12 +42,12 @@ Repository at . 2. Create translations of this `changelog_master.xml` file in files named `changelog.xml` under language-specific versions of `res/xml/`, e.g. `res/xml-de/changelog.xml`. -3. Display the change log dialog by putting the following code in your activity's `onCreate()` method: +3. Display the change log dialog by putting the following code in your Activity's `onCreate()` method: ```java - ChangeLog cl = new ChangeLog(this); - if (cl.isFirstRun()) { - cl.getLogDialog().show(); + DialogChangeLog changeLog = DialogChangeLog.newInstance(this); + if (changeLog.isFirstRun()) { + changeLog.getLogDialog().show(); } ``` @@ -57,7 +57,7 @@ The easiest way to add ckChangeLog to your project is via Gradle. Just add the f ```groovy dependencies { - compile 'de.cketti.library.changelog:ckchangelog:1.2.2' + compile 'de.cketti.library.changelog:ckchangelog-dialog:2.0.0' } ``` @@ -78,6 +78,7 @@ In order to change the labels of the dialog add the following items to your `str What\'s New OK More… +Version %s ``` ## Acknowledgments @@ -88,7 +89,7 @@ This library is based on: Other contributors: * [See here](https://github.com/cketti/ckChangeLog/graphs/contributors) -* You? Please create pull requests against the [dev](https://github.com/cketti/ckChangeLog/tree/dev) branch +* You? Create a [pull request](https://github.com/cketti/ckChangeLog/pulls). ## License From e6d9ee80be990d8cd09b50cbc516e33d9d3d851b Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 24 Mar 2017 08:51:32 +0100 Subject: [PATCH 23/35] Add Maven Central snapshot repository --- android-mvn-push.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android-mvn-push.gradle b/android-mvn-push.gradle index f49a930..85b5529 100644 --- a/android-mvn-push.gradle +++ b/android-mvn-push.gradle @@ -1,5 +1,5 @@ if (!(hasProperty("nexusUsername") && hasProperty("nexusPassword"))) { - return; + return } apply plugin: 'maven' @@ -19,6 +19,9 @@ afterEvaluate { project -> repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { authentication(userName: nexusUsername, password: nexusPassword) } + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots") { + authentication(userName: nexusUsername, password: nexusPassword) + } pom.project { name project.pom.name From 5e61cbd68c1ab23da3324adf7f1919e302b2de3d Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 9 Nov 2017 22:30:29 +0100 Subject: [PATCH 24/35] Make XmlParser return an unmodifiable list --- .../src/main/java/de/cketti/library/changelog/XmlParser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java index 89f00e2..9f3b208 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java +++ b/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.xmlpull.v1.XmlPullParser; @@ -79,7 +80,7 @@ private List readChangeLog() { throw new IllegalStateException(e); } - return result; + return Collections.unmodifiableList(result); } private boolean parseReleaseElement() throws XmlPullParserException, IOException { From 30dcb54b2e45f4ec79d8e6dad8f14b3086774e11 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 8 Mar 2018 22:36:53 +0100 Subject: [PATCH 25/35] Update build tools --- .travis.yml | 7 ++- build.gradle | 8 ++-- ckChangeLog-core/build.gradle | 8 ++-- ckChangeLog-dialog/build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 50514 -> 53324 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 52 +++++++++++------------ gradlew.bat | 8 ++-- sample/build.gradle | 6 +-- 9 files changed, 48 insertions(+), 47 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae1e685..6e6599c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: android android: components: - - build-tools-23.0.3 - - android-23 + - build-tools-27.0.3 + - android-27 - extra-android-m2repository licenses: - 'android-sdk-license-.+' @@ -10,7 +10,6 @@ android: jdk: oraclejdk8 before_install: - - mkdir "$ANDROID_HOME/licenses" || true - - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" + - yes | sdkmanager "platforms;android-27" script: ./gradlew assembleDebug testDebugUnitTest diff --git a/build.gradle b/build.gradle index f684f02..ae2e9bf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,24 @@ buildscript { repositories { + google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:3.0.1' } } allprojects { repositories { + google() mavenCentral() } } ext { - propBuildToolsVersion = "23.0.3" - propCompileSdkVersion = 23 + propBuildToolsVersion = "27.0.3" + propCompileSdkVersion = 27 libraryVersion = "2.0.0-SNAPSHOT" pom = [ diff --git a/ckChangeLog-core/build.gradle b/ckChangeLog-core/build.gradle index ec13915..609bd40 100644 --- a/ckChangeLog-core/build.gradle +++ b/ckChangeLog-core/build.gradle @@ -13,11 +13,11 @@ android { } dependencies { - compile 'com.android.support:support-annotations:25.0.0' + implementation 'com.android.support:support-annotations:27.1.0' - testCompile 'org.robolectric:robolectric:3.1.4' - testCompile 'junit:junit:4.12' - testCompile 'org.mockito:mockito-core:1.10.19' + testImplementation 'org.robolectric:robolectric:3.1.4' + testImplementation 'junit:junit:4.12' + testImplementation 'org.mockito:mockito-core:1.10.19' } project.ext.pom = rootProject.pom diff --git a/ckChangeLog-dialog/build.gradle b/ckChangeLog-dialog/build.gradle index b780aa5..cdd631c 100644 --- a/ckChangeLog-dialog/build.gradle +++ b/ckChangeLog-dialog/build.gradle @@ -11,7 +11,7 @@ android { } dependencies { - compile project(':ckChangeLog-core') + implementation project(':ckChangeLog-core') } project.ext.pom = rootProject.pom diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 667288ad6c2b3b87c990ece1267e56f0bcbf3622..3baa851b28c65f87dd36a6748e1a85cf360c1301 100644 GIT binary patch delta 44006 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 delta 41382 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≈_yKU)cX!o>arCU_YrY8v-7>A^3a}#x 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 94774db..b3bd290 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Aug 19 02:20:04 CEST 2016 +#Thu Mar 08 21:09:01 CET 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip diff --git a/gradlew b/gradlew index 91a7e26..27309d9 100755 --- a/gradlew +++ b/gradlew @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..832fdb6 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,7 +46,7 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args diff --git a/sample/build.gradle b/sample/build.gradle index 5eef8c7..b9d453a 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -9,13 +9,13 @@ android { versionName "1.0" minSdkVersion 14 - targetSdkVersion 21 + targetSdkVersion 27 } } dependencies { - compile project(':ckChangeLog-dialog') - compile 'com.android.support:support-v4:21.0.3' + implementation project(':ckChangeLog-dialog') + implementation 'com.android.support:support-v4:27.1.0' } From 0b40bcf809e8f3264d87341df0774ea7702265cb Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 8 Mar 2018 22:43:28 +0100 Subject: [PATCH 26/35] Update test dependencies --- ckChangeLog-core/build.gradle | 4 ++-- .../test/java/de/cketti/library/changelog/ChangeLogTest.java | 1 - .../cketti/library/changelog/MergedChangeLogProviderTest.java | 1 - .../test/java/de/cketti/library/changelog/XmlParserTest.java | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ckChangeLog-core/build.gradle b/ckChangeLog-core/build.gradle index 609bd40..a51a8a4 100644 --- a/ckChangeLog-core/build.gradle +++ b/ckChangeLog-core/build.gradle @@ -15,9 +15,9 @@ android { dependencies { implementation 'com.android.support:support-annotations:27.1.0' - testImplementation 'org.robolectric:robolectric:3.1.4' + testImplementation 'org.robolectric:robolectric:3.7.1' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:1.10.19' + testImplementation 'org.mockito:mockito-core:2.8.9' } project.ext.pom = rootProject.pom diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/ChangeLogTest.java b/ckChangeLog-core/src/test/java/de/cketti/library/changelog/ChangeLogTest.java index 3508f52..0445b0b 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/ChangeLogTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/library/changelog/ChangeLogTest.java @@ -27,7 +27,6 @@ @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 23) public class ChangeLogTest { private static final int APP_VERSION_CODE = 3; private static final String APP_VERSION_NAME = "1.2"; diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/MergedChangeLogProviderTest.java b/ckChangeLog-core/src/test/java/de/cketti/library/changelog/MergedChangeLogProviderTest.java index 80d0d35..f605a8b 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/MergedChangeLogProviderTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/library/changelog/MergedChangeLogProviderTest.java @@ -16,7 +16,6 @@ @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 23) public class MergedChangeLogProviderTest { @Test public void getChangeLog_shouldReturnMergedChangeLog() throws Exception { diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/XmlParserTest.java b/ckChangeLog-core/src/test/java/de/cketti/library/changelog/XmlParserTest.java index e859b58..7b7aa30 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/XmlParserTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/library/changelog/XmlParserTest.java @@ -19,7 +19,6 @@ @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 23) public class XmlParserTest { @Test public void parse_withEmptyChangeLog() throws Exception { From 2af0af4532919a4b84278a7f35c4e4b05b0c0976 Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 12 May 2018 17:49:23 +0200 Subject: [PATCH 27/35] Change package name to de.cketti.changelog --- ckChangeLog-core/src/main/AndroidManifest.xml | 2 +- .../java/de/cketti/{library => }/changelog/ChangeLog.java | 2 +- .../cketti/{library => }/changelog/ChangeLogProvider.java | 2 +- .../{library => }/changelog/InvalidChangeLogException.java | 2 +- .../{library => }/changelog/MergedChangeLogProvider.java | 4 ++-- .../de/cketti/{library => }/changelog/Preconditions.java | 2 +- .../java/de/cketti/{library => }/changelog/ReleaseItem.java | 4 ++-- .../{library => }/changelog/ResourceChangeLogProvider.java | 2 +- .../java/de/cketti/{library => }/changelog/XmlParser.java | 2 +- .../de/cketti/{library => }/changelog/ChangeLogTest.java | 2 +- .../changelog/MergedChangeLogProviderTest.java | 6 +++--- .../de/cketti/{library => }/changelog/XmlParserTest.java | 4 ++-- .../{library => }/changelog/helper/ChangeLogBuilder.java | 4 ++-- .../changelog/helper/ChangeLogProviderBuilder.java | 6 +++--- ckChangeLog-dialog/src/main/AndroidManifest.xml | 2 +- .../{library => }/changelog/dialog/DialogChangeLog.java | 6 +++--- .../{library => }/changelog/dialog/HtmlFormatter.java | 4 ++-- .../main/java/de/cketti/sample/changelog/MainActivity.java | 2 +- 18 files changed, 29 insertions(+), 29 deletions(-) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/ChangeLog.java (99%) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/ChangeLogProvider.java (97%) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/InvalidChangeLogException.java (96%) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/MergedChangeLogProvider.java (97%) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/Preconditions.java (95%) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/ReleaseItem.java (96%) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/ResourceChangeLogProvider.java (97%) rename ckChangeLog-core/src/main/java/de/cketti/{library => }/changelog/XmlParser.java (99%) rename ckChangeLog-core/src/test/java/de/cketti/{library => }/changelog/ChangeLogTest.java (99%) rename ckChangeLog-core/src/test/java/de/cketti/{library => }/changelog/MergedChangeLogProviderTest.java (95%) rename ckChangeLog-core/src/test/java/de/cketti/{library => }/changelog/XmlParserTest.java (98%) rename ckChangeLog-core/src/test/java/de/cketti/{library => }/changelog/helper/ChangeLogBuilder.java (88%) rename ckChangeLog-core/src/test/java/de/cketti/{library => }/changelog/helper/ChangeLogProviderBuilder.java (89%) rename ckChangeLog-dialog/src/main/java/de/cketti/{library => }/changelog/dialog/DialogChangeLog.java (97%) rename ckChangeLog-dialog/src/main/java/de/cketti/{library => }/changelog/dialog/HtmlFormatter.java (94%) diff --git a/ckChangeLog-core/src/main/AndroidManifest.xml b/ckChangeLog-core/src/main/AndroidManifest.xml index 03a7ba0..5a84ed3 100644 --- a/ckChangeLog-core/src/main/AndroidManifest.xml +++ b/ckChangeLog-core/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java similarity index 99% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java index e0bc1e6..111081d 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java @@ -31,7 +31,7 @@ * * http://code.google.com/p/android-change-log/ */ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.util.List; diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLogProvider.java similarity index 97% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLogProvider.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLogProvider.java index 1b95de8..7bea422 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLogProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.util.List; diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/InvalidChangeLogException.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/InvalidChangeLogException.java similarity index 96% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/InvalidChangeLogException.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/InvalidChangeLogException.java index 1935773..4a3a78b 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/InvalidChangeLogException.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/InvalidChangeLogException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog; +package de.cketti.changelog; /** diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/MergedChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/MergedChangeLogProvider.java similarity index 97% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/MergedChangeLogProvider.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/MergedChangeLogProvider.java index 797e803..dc425fa 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/MergedChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/MergedChangeLogProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.util.ArrayList; @@ -23,7 +23,7 @@ import android.util.SparseArray; -import static de.cketti.library.changelog.Preconditions.checkNotNull; +import static de.cketti.changelog.Preconditions.checkNotNull; /** diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/Preconditions.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/Preconditions.java similarity index 95% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/Preconditions.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/Preconditions.java index ec5c3bd..48abc18 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/Preconditions.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/Preconditions.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog; +package de.cketti.changelog; class Preconditions { diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java similarity index 96% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java index 0e35357..46021b6 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ReleaseItem.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static de.cketti.library.changelog.Preconditions.checkNotNull; +import static de.cketti.changelog.Preconditions.checkNotNull; /** diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ResourceChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java similarity index 97% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/ResourceChangeLogProvider.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java index ab38a22..dc4b5d9 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/ResourceChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.util.List; diff --git a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java similarity index 99% rename from ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java rename to ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java index 9f3b208..e1f9b2f 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/library/changelog/XmlParser.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.io.IOException; diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/ChangeLogTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java similarity index 99% rename from ckChangeLog-core/src/test/java/de/cketti/library/changelog/ChangeLogTest.java rename to ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java index 0445b0b..3a39eb6 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/ChangeLogTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java @@ -1,4 +1,4 @@ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.util.ArrayList; diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/MergedChangeLogProviderTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java similarity index 95% rename from ckChangeLog-core/src/test/java/de/cketti/library/changelog/MergedChangeLogProviderTest.java rename to ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java index f605a8b..33a864b 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/MergedChangeLogProviderTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java @@ -1,10 +1,10 @@ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.util.List; -import de.cketti.library.changelog.helper.ChangeLogBuilder; -import de.cketti.library.changelog.helper.ChangeLogProviderBuilder; +import de.cketti.changelog.helper.ChangeLogBuilder; +import de.cketti.changelog.helper.ChangeLogProviderBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/XmlParserTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java similarity index 98% rename from ckChangeLog-core/src/test/java/de/cketti/library/changelog/XmlParserTest.java rename to ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java index 7b7aa30..bc23433 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/XmlParserTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java @@ -1,10 +1,10 @@ -package de.cketti.library.changelog; +package de.cketti.changelog; import java.io.InputStream; import java.util.List; -import de.cketti.library.changelog.helper.ChangeLogBuilder; +import de.cketti.changelog.helper.ChangeLogBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/helper/ChangeLogBuilder.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogBuilder.java similarity index 88% rename from ckChangeLog-core/src/test/java/de/cketti/library/changelog/helper/ChangeLogBuilder.java rename to ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogBuilder.java index db093e2..3bdb543 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/helper/ChangeLogBuilder.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogBuilder.java @@ -1,10 +1,10 @@ -package de.cketti.library.changelog.helper; +package de.cketti.changelog.helper; import java.util.ArrayList; import java.util.List; -import de.cketti.library.changelog.ReleaseItem; +import de.cketti.changelog.ReleaseItem; import edu.emory.mathcs.backport.java.util.Collections; import static java.util.Arrays.asList; diff --git a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/helper/ChangeLogProviderBuilder.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogProviderBuilder.java similarity index 89% rename from ckChangeLog-core/src/test/java/de/cketti/library/changelog/helper/ChangeLogProviderBuilder.java rename to ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogProviderBuilder.java index 373baae..2316930 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/library/changelog/helper/ChangeLogProviderBuilder.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogProviderBuilder.java @@ -1,11 +1,11 @@ -package de.cketti.library.changelog.helper; +package de.cketti.changelog.helper; import java.util.ArrayList; import java.util.List; -import de.cketti.library.changelog.ChangeLogProvider; -import de.cketti.library.changelog.ReleaseItem; +import de.cketti.changelog.ChangeLogProvider; +import de.cketti.changelog.ReleaseItem; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; diff --git a/ckChangeLog-dialog/src/main/AndroidManifest.xml b/ckChangeLog-dialog/src/main/AndroidManifest.xml index e9e6814..7af476b 100644 --- a/ckChangeLog-dialog/src/main/AndroidManifest.xml +++ b/ckChangeLog-dialog/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java b/ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java similarity index 97% rename from ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java rename to ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java index c292992..e62edba 100644 --- a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/DialogChangeLog.java +++ b/ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog.dialog; +package de.cketti.changelog.dialog; import java.util.List; @@ -23,8 +23,8 @@ import android.content.DialogInterface; import android.webkit.WebView; -import de.cketti.library.changelog.ChangeLog; -import de.cketti.library.changelog.ReleaseItem; +import de.cketti.changelog.ChangeLog; +import de.cketti.changelog.ReleaseItem; /** diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/HtmlFormatter.java b/ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java similarity index 94% rename from ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/HtmlFormatter.java rename to ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java index f3ed73d..87b58ff 100644 --- a/ckChangeLog-dialog/src/main/java/de/cketti/library/changelog/dialog/HtmlFormatter.java +++ b/ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cketti.library.changelog.dialog; +package de.cketti.changelog.dialog; import java.util.List; -import de.cketti.library.changelog.ReleaseItem; +import de.cketti.changelog.ReleaseItem; class HtmlFormatter { diff --git a/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java b/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java index ced0113..f13b743 100644 --- a/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java +++ b/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java @@ -1,6 +1,6 @@ package de.cketti.sample.changelog; -import de.cketti.library.changelog.dialog.DialogChangeLog; +import de.cketti.changelog.dialog.DialogChangeLog; import android.content.Context; import android.os.Bundle; From 445a0ae5ec972abda8353fc0f681cfb8c6cbb36d Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 12 May 2018 18:19:21 +0200 Subject: [PATCH 28/35] Update dependencies --- ckChangeLog-core/build.gradle | 4 ++-- sample/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ckChangeLog-core/build.gradle b/ckChangeLog-core/build.gradle index a51a8a4..8f08be3 100644 --- a/ckChangeLog-core/build.gradle +++ b/ckChangeLog-core/build.gradle @@ -13,11 +13,11 @@ android { } dependencies { - implementation 'com.android.support:support-annotations:27.1.0' + implementation 'com.android.support:support-annotations:27.1.1' testImplementation 'org.robolectric:robolectric:3.7.1' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.8.9' + testImplementation 'org.mockito:mockito-core:2.18.0' } project.ext.pom = rootProject.pom diff --git a/sample/build.gradle b/sample/build.gradle index b9d453a..116e018 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -15,7 +15,7 @@ android { dependencies { implementation project(':ckChangeLog-dialog') - implementation 'com.android.support:support-v4:27.1.0' + implementation 'com.android.support:support-v4:27.1.1' } From 36c48f83ddf57901502e6bbbb1feddb0e9073e66 Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 12 May 2018 18:25:33 +0200 Subject: [PATCH 29/35] Clean up code to remove some warnings --- .../java/de/cketti/changelog/XmlParser.java | 2 +- .../de/cketti/changelog/ChangeLogTest.java | 25 +++++++++---------- .../MergedChangeLogProviderTest.java | 9 +++---- .../de/cketti/changelog/XmlParserTest.java | 1 - 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java index e1f9b2f..92089e9 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java @@ -153,7 +153,7 @@ private void assertNextEventType(int expectedEventType, String message) throws X } } - private void assertElementStart(String expectedElementName) throws XmlPullParserException, IOException { + private void assertElementStart(String expectedElementName) { String tagName = xmlPullParser.getName(); if (!expectedElementName.equals(tagName)) { throw new InvalidChangeLogException( diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java index 3a39eb6..07cea5b 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java @@ -14,14 +14,13 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -46,7 +45,7 @@ public void setUp() throws Exception { } @Test - public void getLastVersionCode() throws Exception { + public void getLastVersionCode() { setLastVersionCode(2); ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); @@ -56,7 +55,7 @@ public void getLastVersionCode() throws Exception { } @Test - public void getCurrentVersionCode() throws Exception { + public void getCurrentVersionCode() { ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); int currentVersionCode = changeLog.getCurrentVersionCode(); @@ -65,7 +64,7 @@ public void getCurrentVersionCode() throws Exception { } @Test - public void getCurrentVersionName() throws Exception { + public void getCurrentVersionName() { ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); String currentVersionName = changeLog.getCurrentVersionName(); @@ -74,7 +73,7 @@ public void getCurrentVersionName() throws Exception { } @Test - public void isFirstRun_withLastVersionCodeSmallerThanCurrentVersion_shouldReturnTrue() throws Exception { + public void isFirstRun_withLastVersionCodeSmallerThanCurrentVersion_shouldReturnTrue() { setLastVersionCode(1); ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); @@ -84,7 +83,7 @@ public void isFirstRun_withLastVersionCodeSmallerThanCurrentVersion_shouldReturn } @Test - public void isFirstRun_withLastVersionCodeEqualToCurrentVersion_shouldReturnFalse() throws Exception { + public void isFirstRun_withLastVersionCodeEqualToCurrentVersion_shouldReturnFalse() { setLastVersionCode(APP_VERSION_CODE); ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); @@ -94,7 +93,7 @@ public void isFirstRun_withLastVersionCodeEqualToCurrentVersion_shouldReturnFals } @Test - public void isFirstRunEver_withLastVersionCodeSet_shouldReturnFalse() throws Exception { + public void isFirstRunEver_withLastVersionCodeSet_shouldReturnFalse() { setLastVersionCode(1); ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); @@ -104,7 +103,7 @@ public void isFirstRunEver_withLastVersionCodeSet_shouldReturnFalse() throws Exc } @Test - public void isFirstRunEver_withLastVersionCodeUnset_shouldReturnTrue() throws Exception { + public void isFirstRunEver_withLastVersionCodeUnset_shouldReturnTrue() { setLastVersionCode(-1); ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); @@ -114,7 +113,7 @@ public void isFirstRunEver_withLastVersionCodeUnset_shouldReturnTrue() throws Ex } @Test - public void writeCurrentVersion() throws Exception { + public void writeCurrentVersion() { ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); changeLog.writeCurrentVersion(); @@ -124,7 +123,7 @@ public void writeCurrentVersion() throws Exception { } @Test - public void getChangeLog_shouldReturnDataFromChangeLogProvider() throws Exception { + public void getChangeLog_shouldReturnDataFromChangeLogProvider() { ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); List releaseItemsToReturn = new ArrayList<>(); when(changeLogProvider.getChangeLog()).thenReturn(releaseItemsToReturn); @@ -135,7 +134,7 @@ public void getChangeLog_shouldReturnDataFromChangeLogProvider() throws Exceptio } @Test - public void getRecentChanges_shouldReturnDataFromChangeLogProvider() throws Exception { + public void getRecentChanges_shouldReturnDataFromChangeLogProvider() { setLastVersionCode(2); ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); List releaseItemsToReturn = new ArrayList<>(); diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java index 33a864b..b3d5b65 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java @@ -8,7 +8,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -18,7 +17,7 @@ @RunWith(RobolectricTestRunner.class) public class MergedChangeLogProviderTest { @Test - public void getChangeLog_shouldReturnMergedChangeLog() throws Exception { + public void getChangeLog_shouldReturnMergedChangeLog() { ChangeLogProvider masterChangeLogProvider = new ChangeLogProviderBuilder() .addVersion(1, "1.0", "First version") .addVersion(2, "1.1", "Some new feature", "Small bugfix") @@ -41,7 +40,7 @@ public void getChangeLog_shouldReturnMergedChangeLog() throws Exception { } @Test - public void getChangeLogSince_shouldReturnMergedChangeLog() throws Exception { + public void getChangeLogSince_shouldReturnMergedChangeLog() { ChangeLogProvider masterChangeLogProvider = new ChangeLogProviderBuilder() .addVersion(1, "1.0", "First version") .addVersion(2, "1.1", "Some new feature", "Small bugfix") @@ -63,7 +62,7 @@ public void getChangeLogSince_shouldReturnMergedChangeLog() throws Exception { } @Test - public void constructor_withFirstArgumentNull_shouldThrow() throws Exception { + public void constructor_withFirstArgumentNull_shouldThrow() { ChangeLogProvider changeLogProvider = mock(ChangeLogProvider.class); try { @@ -75,7 +74,7 @@ public void constructor_withFirstArgumentNull_shouldThrow() throws Exception { } @Test - public void constructor_withSecondArgumentNull_shouldThrow() throws Exception { + public void constructor_withSecondArgumentNull_shouldThrow() { ChangeLogProvider changeLogProvider = mock(ChangeLogProvider.class); try { diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java index bc23433..c6e1819 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java @@ -8,7 +8,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import org.xmlpull.mxp1.MXParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; From c0db1c427bacf6d2e8da13f98bccae2ec3b9bb36 Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 12 May 2018 18:04:00 +0200 Subject: [PATCH 30/35] Move change log files from res/xml/ to res/raw/ This is mostly due to AAPT(2) optimizing files in res/xml/ and there's no guarantee this won't break our files. Sadly, there has been at least one bug that affected ckChangeLog. See issue #48. --- .../java/de/cketti/changelog/ChangeLog.java | 6 +- .../changelog/ResourceChangeLogProvider.java | 60 +++++++++++++------ .../src/main/res/{xml => raw}/changelog.xml | 0 .../src/main/res/raw/changelog_master.xml | 6 ++ .../src/main/res/xml/changelog_master.xml | 6 -- .../de/cketti/changelog/XmlParserTest.java | 4 +- .../main/res/{xml-de => raw-de}/changelog.xml | 0 .../res/{xml => raw}/changelog_master.xml | 0 8 files changed, 55 insertions(+), 27 deletions(-) rename ckChangeLog-core/src/main/res/{xml => raw}/changelog.xml (100%) create mode 100644 ckChangeLog-core/src/main/res/raw/changelog_master.xml delete mode 100644 ckChangeLog-core/src/main/res/xml/changelog_master.xml rename sample/src/main/res/{xml-de => raw-de}/changelog.xml (100%) rename sample/src/main/res/{xml => raw}/changelog_master.xml (100%) diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java index 111081d..6cad6d8 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java @@ -40,6 +40,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; import android.preference.PreferenceManager; import android.util.Log; @@ -82,8 +83,9 @@ public static ChangeLog newInstance(Context context) { * */ public static ChangeLog newInstance(Context context, SharedPreferences preferences) { - ChangeLogProvider masterChangeLogProvider = new ResourceChangeLogProvider(context, R.xml.changelog_master); - ChangeLogProvider localizedChangeLogProvider = new ResourceChangeLogProvider(context, R.xml.changelog); + Resources resources = context.getResources(); + ChangeLogProvider masterChangeLogProvider = new ResourceChangeLogProvider(resources, R.raw.changelog_master); + ChangeLogProvider localizedChangeLogProvider = new ResourceChangeLogProvider(resources, R.raw.changelog); ChangeLogProvider changeLogProvider = new MergedChangeLogProvider( masterChangeLogProvider, localizedChangeLogProvider); diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java index dc4b5d9..1d18cce 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2012-2017 cketti and contributors - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,43 +16,69 @@ package de.cketti.changelog; +import java.io.IOException; +import java.io.InputStream; import java.util.List; -import android.content.Context; -import android.content.res.XmlResourceParser; -import android.support.annotation.XmlRes; +import android.content.res.Resources; +import android.support.annotation.IdRes; +import android.support.annotation.RawRes; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; /** * A {@link ChangeLogProvider} that reads data from an Android XML resource. */ class ResourceChangeLogProvider implements ChangeLogProvider { - private final Context context; + private final Resources resources; private final int resourceId; - ResourceChangeLogProvider(Context context, @XmlRes int resourceId) { - this.context = context; + ResourceChangeLogProvider(Resources resources, @RawRes int resourceId) { + this.resources = resources; this.resourceId = resourceId; } @Override public List getChangeLog() { - XmlResourceParser xml = context.getResources().getXml(resourceId); try { - return XmlParser.parse(xml); - } finally { - xml.close(); + XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser(); + InputStream inputStream = resources.openRawResource(resourceId); + try { + xmlPullParser.setInput(inputStream, null); + return XmlParser.parse(xmlPullParser); + } finally { + inputStream.close(); + } + } catch (XmlPullParserException | IOException e) { + throw new InvalidChangeLogException("Error parsing XML resource: " + getResourceName(resourceId), e); } } @Override public List getChangeLogSince(int lastVersionCode) { - XmlResourceParser xml = context.getResources().getXml(resourceId); try { - return XmlParser.parse(xml, lastVersionCode); - } finally { - xml.close(); + XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser(); + InputStream inputStream = resources.openRawResource(resourceId); + try { + xmlPullParser.setInput(inputStream, null); + return XmlParser.parse(xmlPullParser, lastVersionCode); + } finally { + inputStream.close(); + } + } catch (XmlPullParserException | IOException e) { + throw new InvalidChangeLogException("Error parsing XML resource: " + getResourceName(resourceId), e); + } + } + + private String getResourceName(@IdRes int resourceId) { + try { + return resources.getResourceName(resourceId); + } catch (Resources.NotFoundException e) { + return Integer.toString(resourceId); } } } diff --git a/ckChangeLog-core/src/main/res/xml/changelog.xml b/ckChangeLog-core/src/main/res/raw/changelog.xml similarity index 100% rename from ckChangeLog-core/src/main/res/xml/changelog.xml rename to ckChangeLog-core/src/main/res/raw/changelog.xml diff --git a/ckChangeLog-core/src/main/res/raw/changelog_master.xml b/ckChangeLog-core/src/main/res/raw/changelog_master.xml new file mode 100644 index 0000000..3244cda --- /dev/null +++ b/ckChangeLog-core/src/main/res/raw/changelog_master.xml @@ -0,0 +1,6 @@ + + + diff --git a/ckChangeLog-core/src/main/res/xml/changelog_master.xml b/ckChangeLog-core/src/main/res/xml/changelog_master.xml deleted file mode 100644 index d83a85b..0000000 --- a/ckChangeLog-core/src/main/res/xml/changelog_master.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java index c6e1819..2f1a608 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java @@ -8,9 +8,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.xmlpull.mxp1.MXParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -140,8 +140,8 @@ public void parse_withChangeElementContainingElement_shouldThrow() throws Except } private XmlPullParser createParserForFile(String resourceName) throws XmlPullParserException { + XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser(); InputStream inputStream = getClass().getResourceAsStream(resourceName); - MXParser xmlPullParser = new MXParser(); xmlPullParser.setInput(inputStream, "utf-8"); return xmlPullParser; } diff --git a/sample/src/main/res/xml-de/changelog.xml b/sample/src/main/res/raw-de/changelog.xml similarity index 100% rename from sample/src/main/res/xml-de/changelog.xml rename to sample/src/main/res/raw-de/changelog.xml diff --git a/sample/src/main/res/xml/changelog_master.xml b/sample/src/main/res/raw/changelog_master.xml similarity index 100% rename from sample/src/main/res/xml/changelog_master.xml rename to sample/src/main/res/raw/changelog_master.xml From 89238f724085a04a7d05be94d1749513edef090a Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 12 May 2018 21:10:52 +0200 Subject: [PATCH 31/35] Add "date" attribute --- .../java/de/cketti/changelog/ReleaseItem.java | 22 +++++++++++--- .../java/de/cketti/changelog/XmlParser.java | 10 +++++-- .../MergedChangeLogProviderTest.java | 30 +++++++++---------- .../de/cketti/changelog/XmlParserTest.java | 22 +++++++++++--- .../changelog/helper/ChangeLogBuilder.java | 4 +-- .../helper/ChangeLogProviderBuilder.java | 5 ++-- .../valid_changelog_with_version_dates.xml | 16 ++++++++++ ...valid_changelog_without_version_dates.xml} | 0 8 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 ckChangeLog-core/src/test/resources/valid_changelog_with_version_dates.xml rename ckChangeLog-core/src/test/resources/{valid_sample_changelog.xml => valid_changelog_without_version_dates.xml} (100%) diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java index 46021b6..dca1e97 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java @@ -20,6 +20,8 @@ import java.util.Collections; import java.util.List; +import android.support.annotation.Nullable; + import static de.cketti.changelog.Preconditions.checkNotNull; @@ -37,20 +39,28 @@ public final class ReleaseItem { */ public final String versionName; + /** + * Date of the release (optional). + */ + @Nullable + public final String date; + /** * List of changes introduced with that release. */ public final List changes; - public static ReleaseItem newInstance(int versionCode, String versionName, List changes) { + public static ReleaseItem newInstance(int versionCode, String versionName, @Nullable String date, + List changes) { List copiedChanges = new ArrayList<>(changes); - return new ReleaseItem(versionCode, versionName, copiedChanges); + return new ReleaseItem(versionCode, versionName, date, copiedChanges); } - ReleaseItem(int versionCode, String versionName, List changes) { + ReleaseItem(int versionCode, String versionName, @Nullable String date, List changes) { this.versionCode = versionCode; this.versionName = checkNotNull(versionName, "versionName == null"); + this.date = date; this.changes = Collections.unmodifiableList(checkNotNull(changes, "changes == null")); } @@ -59,6 +69,7 @@ public String toString() { return "ReleaseItem{" + "versionCode=" + versionCode + ", versionName='" + versionName + '\'' + + ", date='" + date + '\'' + ", changes=" + changes + '}'; } @@ -80,14 +91,17 @@ public boolean equals(Object o) { if (!versionName.equals(that.versionName)) { return false; } + if (date != null ? !date.equals(that.date) : that.date != null) { + return false; + } return changes.equals(that.changes); - } @Override public int hashCode() { int result = versionCode; result = 31 * result + versionName.hashCode(); + result = 31 * result + (date != null ? date.hashCode() : 0); result = 31 * result + changes.hashCode(); return result; } diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java index 92089e9..daa3d44 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java @@ -34,6 +34,7 @@ public final class XmlParser { private static final String TAG_RELEASE = "release"; private static final String ATTRIBUTE_VERSION = "version"; private static final String ATTRIBUTE_VERSION_CODE = "versioncode"; + private static final String ATTRIBUTE_DATE = "date"; private static final String TAG_CHANGE = "change"; @@ -86,7 +87,8 @@ private List readChangeLog() { private boolean parseReleaseElement() throws XmlPullParserException, IOException { assertElementStart(TAG_RELEASE); - String version = parseVersionAttribute(); + String versionName = parseVersionAttribute(); + String date = parseDateAttribute(); int versionCode = parseVersionCodeAttribute(); if (lastVersionCode != NO_VERSION && versionCode <= lastVersionCode) { @@ -107,7 +109,7 @@ private boolean parseReleaseElement() throws XmlPullParserException, IOException throw new InvalidChangeLogException(" tag must contain at least one element"); } - ReleaseItem release = new ReleaseItem(versionCode, version, changes); + ReleaseItem release = new ReleaseItem(versionCode, versionName, date, changes); result.add(release); return false; @@ -118,6 +120,10 @@ private String parseVersionAttribute() { return xmlPullParser.getAttributeValue(null, ATTRIBUTE_VERSION); } + private String parseDateAttribute() { + return xmlPullParser.getAttributeValue(null, ATTRIBUTE_DATE); + } + private int parseVersionCodeAttribute() { assertAttributePresent(ATTRIBUTE_VERSION_CODE); diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java index b3d5b65..6d4a94b 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/MergedChangeLogProviderTest.java @@ -19,13 +19,13 @@ public class MergedChangeLogProviderTest { @Test public void getChangeLog_shouldReturnMergedChangeLog() { ChangeLogProvider masterChangeLogProvider = new ChangeLogProviderBuilder() - .addVersion(1, "1.0", "First version") - .addVersion(2, "1.1", "Some new feature", "Small bugfix") - .addVersion(3, "1.2", "The latest change") + .addVersion(1, "1.0", null, "First version") + .addVersion(2, "1.1", "2000-01-01", "Some new feature", "Small bugfix") + .addVersion(3, "1.2", "2010-02-01", "The latest change") .build(); ChangeLogProvider localizedChangeLogProvider = new ChangeLogProviderBuilder() - .addVersion(1, "1.0", "Erste Version") - .addVersion(2, "1.1", "Neue Funktionalität", "Kleiner Fehlerbehebung") + .addVersion(1, "1.0", null, "Erste Version") + .addVersion(2, "1.1", "2000-01-01", "Neue Funktionalität", "Kleiner Fehlerbehebung") .build(); MergedChangeLogProvider changeLogProvider = new MergedChangeLogProvider(masterChangeLogProvider, localizedChangeLogProvider); @@ -33,22 +33,22 @@ public void getChangeLog_shouldReturnMergedChangeLog() { List changeLog = changeLogProvider.getChangeLog(); assertEquals(changeLog, new ChangeLogBuilder() - .addVersion(1, "1.0", "Erste Version") - .addVersion(2, "1.1", "Neue Funktionalität", "Kleiner Fehlerbehebung") - .addVersion(3, "1.2", "The latest change") + .addVersion(1, "1.0", null, "Erste Version") + .addVersion(2, "1.1", "2000-01-01", "Neue Funktionalität", "Kleiner Fehlerbehebung") + .addVersion(3, "1.2", "2010-02-01", "The latest change") .build()); } @Test public void getChangeLogSince_shouldReturnMergedChangeLog() { ChangeLogProvider masterChangeLogProvider = new ChangeLogProviderBuilder() - .addVersion(1, "1.0", "First version") - .addVersion(2, "1.1", "Some new feature", "Small bugfix") - .addVersion(3, "1.2", "The latest change") + .addVersion(1, "1.0", null, "First version") + .addVersion(2, "1.1", null, "Some new feature", "Small bugfix") + .addVersion(3, "1.2", null, "The latest change") .build(); ChangeLogProvider localizedChangeLogProvider = new ChangeLogProviderBuilder() - .addVersion(1, "1.0", "Erste Version") - .addVersion(2, "1.1", "Neue Funktionalität", "Kleiner Fehlerbehebung") + .addVersion(1, "1.0", null, "Erste Version") + .addVersion(2, "1.1", null, "Neue Funktionalität", "Kleiner Fehlerbehebung") .build(); MergedChangeLogProvider changeLogProvider = new MergedChangeLogProvider(masterChangeLogProvider, localizedChangeLogProvider); @@ -56,8 +56,8 @@ public void getChangeLogSince_shouldReturnMergedChangeLog() { List changeLog = changeLogProvider.getChangeLogSince(1); assertEquals(changeLog, new ChangeLogBuilder() - .addVersion(2, "1.1", "Neue Funktionalität", "Kleiner Fehlerbehebung") - .addVersion(3, "1.2", "The latest change") + .addVersion(2, "1.1", null, "Neue Funktionalität", "Kleiner Fehlerbehebung") + .addVersion(3, "1.2", null, "The latest change") .build()); } diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java index 2f1a608..8886631 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/XmlParserTest.java @@ -30,14 +30,28 @@ public void parse_withEmptyChangeLog() throws Exception { @Test public void parse_withValidChangeLog() throws Exception { - XmlPullParser xmlPullParser = createParserForFile("/valid_sample_changelog.xml"); + XmlPullParser xmlPullParser = createParserForFile("/valid_changelog_with_version_dates.xml"); List releaseItems = XmlParser.parse(xmlPullParser); assertEquals(new ChangeLogBuilder() - .addVersion(1, "1.0", "First release") - .addVersion(10, "2.0", "Fixed: A bug fix", "Some other changes I can't quite remember") - .addVersion(11, "2.1", "Totally new and shiny version") + .addVersion(1, "1.0.0", "2018-01-01", "First release") + .addVersion(2, "2.0.0", "2018-02-01", "Second release") + .addVersion(3, "3.0.0", "2018-03-01", "Third release") + .build(), + releaseItems); + } + + @Test + public void parse_withValidChangeLogNotContainingDates() throws Exception { + XmlPullParser xmlPullParser = createParserForFile("/valid_changelog_without_version_dates.xml"); + + List releaseItems = XmlParser.parse(xmlPullParser); + + assertEquals(new ChangeLogBuilder() + .addVersion(1, "1.0", null, "First release") + .addVersion(10, "2.0", null, "Fixed: A bug fix", "Some other changes I can't quite remember") + .addVersion(11, "2.1", null, "Totally new and shiny version") .build(), releaseItems); } diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogBuilder.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogBuilder.java index 3bdb543..a1be405 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogBuilder.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogBuilder.java @@ -14,8 +14,8 @@ public class ChangeLogBuilder { private List releaseItems = new ArrayList<>(); - public ChangeLogBuilder addVersion(int versionCode, String versionName, String... changes) { - ReleaseItem releaseItem = ReleaseItem.newInstance(versionCode, versionName, asList(changes)); + public ChangeLogBuilder addVersion(int versionCode, String versionName, String date, String... changes) { + ReleaseItem releaseItem = ReleaseItem.newInstance(versionCode, versionName, date, asList(changes)); releaseItems.add(releaseItem); return this; } diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogProviderBuilder.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogProviderBuilder.java index 2316930..32643b4 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogProviderBuilder.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/helper/ChangeLogProviderBuilder.java @@ -15,8 +15,9 @@ public class ChangeLogProviderBuilder { private List releaseItems = new ArrayList<>(); - public ChangeLogProviderBuilder addVersion(int versionCode, String versionName, String... changes) { - ReleaseItem releaseItem = ReleaseItem.newInstance(versionCode, versionName, asList(changes)); + public ChangeLogProviderBuilder addVersion(int versionCode, String versionName, String date, + String... changes) { + ReleaseItem releaseItem = ReleaseItem.newInstance(versionCode, versionName, date, asList(changes)); releaseItems.add(releaseItem); return this; } diff --git a/ckChangeLog-core/src/test/resources/valid_changelog_with_version_dates.xml b/ckChangeLog-core/src/test/resources/valid_changelog_with_version_dates.xml new file mode 100644 index 0000000..0e78b48 --- /dev/null +++ b/ckChangeLog-core/src/test/resources/valid_changelog_with_version_dates.xml @@ -0,0 +1,16 @@ + + + + + Third release + + + + Second release + + + + First release + + + diff --git a/ckChangeLog-core/src/test/resources/valid_sample_changelog.xml b/ckChangeLog-core/src/test/resources/valid_changelog_without_version_dates.xml similarity index 100% rename from ckChangeLog-core/src/test/resources/valid_sample_changelog.xml rename to ckChangeLog-core/src/test/resources/valid_changelog_without_version_dates.xml From ce3de32f8626f4645cd86738c3075465af95220f Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 12 May 2018 21:34:53 +0200 Subject: [PATCH 32/35] Rename ckChangeLog-dialog to ckChangeLog-legacy-dialog Showing a dialog at app start is a very obtrusive way of showing the change log. Also using a WebView isn't great. The intended message of adding "legacy" is: Stay away from this artifact when writing new code. --- .../build.gradle | 2 +- .../src/main/AndroidManifest.xml | 0 .../de/cketti/changelog/dialog/DialogChangeLog.java | 0 .../de/cketti/changelog/dialog/HtmlFormatter.java | 0 .../src/main/res/values-cs/strings.xml | 0 .../src/main/res/values-de/strings.xml | 0 .../src/main/res/values-el/strings.xml | 0 .../src/main/res/values-es/strings.xml | 0 .../src/main/res/values-pl/strings.xml | 0 .../src/main/res/values-ru/strings.xml | 0 .../src/main/res/values-sk/strings.xml | 0 .../src/main/res/values-uk/strings.xml | 0 .../src/main/res/values/strings.xml | 0 {sample => sample-legacy-dialog}/build.gradle | 2 +- .../src/main/AndroidManifest.xml | 0 .../de/cketti/sample/changelog/MainActivity.java | 0 .../src/main/res/drawable-hdpi/ic_launcher.png | Bin .../src/main/res/drawable-mdpi/ic_launcher.png | Bin .../src/main/res/drawable-xhdpi/ic_launcher.png | Bin .../src/main/res/drawable-xxhdpi/ic_launcher.png | Bin .../src/main/res/drawable-xxxhdpi/ic_launcher.png | Bin .../src/main/res/layout/activity_main.xml | 0 .../src/main/res/menu/activity_main.xml | 0 .../src/main/res/raw-de/changelog.xml | 0 .../src/main/res/raw/changelog_master.xml | 0 .../src/main/res/values-de/strings.xml | 0 .../src/main/res/values-es/strings.xml | 0 .../src/main/res/values-v11/styles.xml | 0 .../src/main/res/values-v14/styles.xml | 0 .../src/main/res/values/strings.xml | 0 .../src/main/res/values/styles.xml | 0 settings.gradle | 4 ++-- 32 files changed, 4 insertions(+), 4 deletions(-) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/build.gradle (85%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/AndroidManifest.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-cs/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-de/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-el/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-es/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-pl/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-ru/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-sk/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values-uk/strings.xml (100%) rename {ckChangeLog-dialog => ckChangeLog-legacy-dialog}/src/main/res/values/strings.xml (100%) rename {sample => sample-legacy-dialog}/build.gradle (85%) rename {sample => sample-legacy-dialog}/src/main/AndroidManifest.xml (100%) rename {sample => sample-legacy-dialog}/src/main/java/de/cketti/sample/changelog/MainActivity.java (100%) rename {sample => sample-legacy-dialog}/src/main/res/drawable-hdpi/ic_launcher.png (100%) rename {sample => sample-legacy-dialog}/src/main/res/drawable-mdpi/ic_launcher.png (100%) rename {sample => sample-legacy-dialog}/src/main/res/drawable-xhdpi/ic_launcher.png (100%) rename {sample => sample-legacy-dialog}/src/main/res/drawable-xxhdpi/ic_launcher.png (100%) rename {sample => sample-legacy-dialog}/src/main/res/drawable-xxxhdpi/ic_launcher.png (100%) rename {sample => sample-legacy-dialog}/src/main/res/layout/activity_main.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/menu/activity_main.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/raw-de/changelog.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/raw/changelog_master.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/values-de/strings.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/values-es/strings.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/values-v11/styles.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/values-v14/styles.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/values/strings.xml (100%) rename {sample => sample-legacy-dialog}/src/main/res/values/styles.xml (100%) diff --git a/ckChangeLog-dialog/build.gradle b/ckChangeLog-legacy-dialog/build.gradle similarity index 85% rename from ckChangeLog-dialog/build.gradle rename to ckChangeLog-legacy-dialog/build.gradle index cdd631c..457c250 100644 --- a/ckChangeLog-dialog/build.gradle +++ b/ckChangeLog-legacy-dialog/build.gradle @@ -15,6 +15,6 @@ dependencies { } project.ext.pom = rootProject.pom -project.ext.pom['artifactId'] = "ckchangelog-dialog" +project.ext.pom['artifactId'] = "ckchangelog-legacy-dialog" apply from: '../android-mvn-push.gradle' diff --git a/ckChangeLog-dialog/src/main/AndroidManifest.xml b/ckChangeLog-legacy-dialog/src/main/AndroidManifest.xml similarity index 100% rename from ckChangeLog-dialog/src/main/AndroidManifest.xml rename to ckChangeLog-legacy-dialog/src/main/AndroidManifest.xml diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java b/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java similarity index 100% rename from ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java rename to ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java diff --git a/ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java b/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java similarity index 100% rename from ckChangeLog-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java rename to ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java diff --git a/ckChangeLog-dialog/src/main/res/values-cs/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-cs/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-cs/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-cs/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values-de/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-de/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-de/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-de/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values-el/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-el/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-el/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-el/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values-es/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-es/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-es/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-es/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values-pl/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-pl/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-pl/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-pl/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values-ru/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-ru/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-ru/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-ru/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values-sk/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-sk/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-sk/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-sk/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values-uk/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values-uk/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values-uk/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values-uk/strings.xml diff --git a/ckChangeLog-dialog/src/main/res/values/strings.xml b/ckChangeLog-legacy-dialog/src/main/res/values/strings.xml similarity index 100% rename from ckChangeLog-dialog/src/main/res/values/strings.xml rename to ckChangeLog-legacy-dialog/src/main/res/values/strings.xml diff --git a/sample/build.gradle b/sample-legacy-dialog/build.gradle similarity index 85% rename from sample/build.gradle rename to sample-legacy-dialog/build.gradle index 116e018..e7fa5e2 100644 --- a/sample/build.gradle +++ b/sample-legacy-dialog/build.gradle @@ -14,7 +14,7 @@ android { } dependencies { - implementation project(':ckChangeLog-dialog') + implementation project(':ckChangeLog-legacy-dialog') implementation 'com.android.support:support-v4:27.1.1' } diff --git a/sample/src/main/AndroidManifest.xml b/sample-legacy-dialog/src/main/AndroidManifest.xml similarity index 100% rename from sample/src/main/AndroidManifest.xml rename to sample-legacy-dialog/src/main/AndroidManifest.xml diff --git a/sample/src/main/java/de/cketti/sample/changelog/MainActivity.java b/sample-legacy-dialog/src/main/java/de/cketti/sample/changelog/MainActivity.java similarity index 100% rename from sample/src/main/java/de/cketti/sample/changelog/MainActivity.java rename to sample-legacy-dialog/src/main/java/de/cketti/sample/changelog/MainActivity.java diff --git a/sample/src/main/res/drawable-hdpi/ic_launcher.png b/sample-legacy-dialog/src/main/res/drawable-hdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/drawable-hdpi/ic_launcher.png rename to sample-legacy-dialog/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/sample/src/main/res/drawable-mdpi/ic_launcher.png b/sample-legacy-dialog/src/main/res/drawable-mdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/drawable-mdpi/ic_launcher.png rename to sample-legacy-dialog/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/sample/src/main/res/drawable-xhdpi/ic_launcher.png b/sample-legacy-dialog/src/main/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/drawable-xhdpi/ic_launcher.png rename to sample-legacy-dialog/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/sample/src/main/res/drawable-xxhdpi/ic_launcher.png b/sample-legacy-dialog/src/main/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/drawable-xxhdpi/ic_launcher.png rename to sample-legacy-dialog/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png b/sample-legacy-dialog/src/main/res/drawable-xxxhdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/drawable-xxxhdpi/ic_launcher.png rename to sample-legacy-dialog/src/main/res/drawable-xxxhdpi/ic_launcher.png diff --git a/sample/src/main/res/layout/activity_main.xml b/sample-legacy-dialog/src/main/res/layout/activity_main.xml similarity index 100% rename from sample/src/main/res/layout/activity_main.xml rename to sample-legacy-dialog/src/main/res/layout/activity_main.xml diff --git a/sample/src/main/res/menu/activity_main.xml b/sample-legacy-dialog/src/main/res/menu/activity_main.xml similarity index 100% rename from sample/src/main/res/menu/activity_main.xml rename to sample-legacy-dialog/src/main/res/menu/activity_main.xml diff --git a/sample/src/main/res/raw-de/changelog.xml b/sample-legacy-dialog/src/main/res/raw-de/changelog.xml similarity index 100% rename from sample/src/main/res/raw-de/changelog.xml rename to sample-legacy-dialog/src/main/res/raw-de/changelog.xml diff --git a/sample/src/main/res/raw/changelog_master.xml b/sample-legacy-dialog/src/main/res/raw/changelog_master.xml similarity index 100% rename from sample/src/main/res/raw/changelog_master.xml rename to sample-legacy-dialog/src/main/res/raw/changelog_master.xml diff --git a/sample/src/main/res/values-de/strings.xml b/sample-legacy-dialog/src/main/res/values-de/strings.xml similarity index 100% rename from sample/src/main/res/values-de/strings.xml rename to sample-legacy-dialog/src/main/res/values-de/strings.xml diff --git a/sample/src/main/res/values-es/strings.xml b/sample-legacy-dialog/src/main/res/values-es/strings.xml similarity index 100% rename from sample/src/main/res/values-es/strings.xml rename to sample-legacy-dialog/src/main/res/values-es/strings.xml diff --git a/sample/src/main/res/values-v11/styles.xml b/sample-legacy-dialog/src/main/res/values-v11/styles.xml similarity index 100% rename from sample/src/main/res/values-v11/styles.xml rename to sample-legacy-dialog/src/main/res/values-v11/styles.xml diff --git a/sample/src/main/res/values-v14/styles.xml b/sample-legacy-dialog/src/main/res/values-v14/styles.xml similarity index 100% rename from sample/src/main/res/values-v14/styles.xml rename to sample-legacy-dialog/src/main/res/values-v14/styles.xml diff --git a/sample/src/main/res/values/strings.xml b/sample-legacy-dialog/src/main/res/values/strings.xml similarity index 100% rename from sample/src/main/res/values/strings.xml rename to sample-legacy-dialog/src/main/res/values/strings.xml diff --git a/sample/src/main/res/values/styles.xml b/sample-legacy-dialog/src/main/res/values/styles.xml similarity index 100% rename from sample/src/main/res/values/styles.xml rename to sample-legacy-dialog/src/main/res/values/styles.xml diff --git a/settings.gradle b/settings.gradle index 80b5e34..e38665e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ include ':ckChangeLog-core' -include ':ckChangeLog-dialog' -include ':sample' +include ':ckChangeLog-legacy-dialog' +include ':sample-legacy-dialog' From 7c9f83892b6e7a69df9064cdf1f9e051a83932c0 Mon Sep 17 00:00:00 2001 From: cketti Date: Sun, 13 May 2018 13:52:29 +0200 Subject: [PATCH 33/35] Make writeCurrentVersion() update internal state --- .../java/de/cketti/changelog/ChangeLog.java | 2 ++ .../de/cketti/changelog/ChangeLogTest.java | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java index 6cad6d8..4b8bc61 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java @@ -195,6 +195,8 @@ public boolean isFirstRunEver() { *

*/ public void writeCurrentVersion() { + lastVersionCode = currentVersionCode; + SharedPreferences.Editor editor = preferences.edit(); editor.putInt(VERSION_KEY, currentVersionCode); editor.apply(); diff --git a/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java b/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java index 07cea5b..1634e54 100644 --- a/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java +++ b/ckChangeLog-core/src/test/java/de/cketti/changelog/ChangeLogTest.java @@ -92,6 +92,17 @@ public void isFirstRun_withLastVersionCodeEqualToCurrentVersion_shouldReturnFals assertFalse(firstRun); } + @Test + public void isFirstRun_afterCallingWriteCurrentVersion_shouldReturnFalse() { + setLastVersionCode(1); + ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); + changeLog.writeCurrentVersion(); + + boolean firstRun = changeLog.isFirstRun(); + + assertFalse(firstRun); + } + @Test public void isFirstRunEver_withLastVersionCodeSet_shouldReturnFalse() { setLastVersionCode(1); @@ -112,6 +123,17 @@ public void isFirstRunEver_withLastVersionCodeUnset_shouldReturnTrue() { assertTrue(firstRunEver); } + @Test + public void isFirstRunEver_afterCallingWriteCurrentVersion_shouldReturnFalse() { + setLastVersionCode(-1); + ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); + changeLog.writeCurrentVersion(); + + boolean firstRunEver = changeLog.isFirstRunEver(); + + assertFalse(firstRunEver); + } + @Test public void writeCurrentVersion() { ChangeLog changeLog = ChangeLog.newInstance(context, preferences, changeLogProvider); From c4ff3065358737211f94edc2612ddffe4f73d7e9 Mon Sep 17 00:00:00 2001 From: cketti Date: Sun, 13 May 2018 13:59:56 +0200 Subject: [PATCH 34/35] Update copyright headers --- NOTICE | 2 +- README.md | 2 +- .../src/main/java/de/cketti/changelog/ChangeLog.java | 2 +- .../main/java/de/cketti/changelog/ChangeLogProvider.java | 8 ++++---- .../de/cketti/changelog/InvalidChangeLogException.java | 2 +- .../java/de/cketti/changelog/MergedChangeLogProvider.java | 8 ++++---- .../src/main/java/de/cketti/changelog/Preconditions.java | 8 ++++---- .../src/main/java/de/cketti/changelog/ReleaseItem.java | 8 ++++---- .../de/cketti/changelog/ResourceChangeLogProvider.java | 2 +- .../src/main/java/de/cketti/changelog/XmlParser.java | 8 ++++---- .../java/de/cketti/changelog/dialog/DialogChangeLog.java | 2 +- .../java/de/cketti/changelog/dialog/HtmlFormatter.java | 2 +- 12 files changed, 27 insertions(+), 27 deletions(-) diff --git a/NOTICE b/NOTICE index f524874..4fac050 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,4 @@ -Copyright (C) 2012-2017 cketti and contributors +Copyright (C) 2012-2018 cketti and contributors https://github.com/cketti/ckChangeLog/graphs/contributors Portions Copyright (C) 2012 Martin van Zuilekom (http://martin.cubeactive.com) diff --git a/README.md b/README.md index a7a5d60..1b656de 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Other contributors: ## License - Copyright (C) 2012-2017 cketti and contributors + Copyright (C) 2012-2018 cketti and contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java index 4b8bc61..c845f73 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 cketti and contributors + * Copyright (C) 2012-2018 cketti and contributors * https://github.com/cketti/ckChangeLog/graphs/contributors * * Portions Copyright (C) 2012 Martin van Zuilekom (http://martin.cubeactive.com) diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLogProvider.java index 7bea422..8041e96 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ChangeLogProvider.java @@ -1,12 +1,12 @@ /* - * Copyright (C) 2012-2017 cketti and contributors - * + * Copyright (C) 2012-2018 cketti and contributors + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/InvalidChangeLogException.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/InvalidChangeLogException.java index 4a3a78b..f7e3e6b 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/InvalidChangeLogException.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/InvalidChangeLogException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 cketti and contributors + * Copyright (C) 2012-2018 cketti and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/MergedChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/MergedChangeLogProvider.java index dc425fa..40274ca 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/MergedChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/MergedChangeLogProvider.java @@ -1,12 +1,12 @@ /* - * Copyright (C) 2012-2017 cketti and contributors - * + * Copyright (C) 2012-2018 cketti and contributors + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/Preconditions.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/Preconditions.java index 48abc18..925ae5e 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/Preconditions.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/Preconditions.java @@ -1,12 +1,12 @@ /* - * Copyright (C) 2012-2017 cketti and contributors - * + * Copyright (C) 2012-2018 cketti and contributors + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java index dca1e97..e453934 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ReleaseItem.java @@ -1,12 +1,12 @@ /* - * Copyright (C) 2012-2017 cketti and contributors - * + * Copyright (C) 2012-2018 cketti and contributors + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java index 1d18cce..b44213a 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/ResourceChangeLogProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 cketti and contributors + * Copyright (C) 2012-2018 cketti and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java index daa3d44..fca69b3 100644 --- a/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java +++ b/ckChangeLog-core/src/main/java/de/cketti/changelog/XmlParser.java @@ -1,12 +1,12 @@ /* - * Copyright (C) 2012-2017 cketti and contributors - * + * Copyright (C) 2012-2018 cketti and contributors + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java b/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java index e62edba..3b05557 100644 --- a/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java +++ b/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/DialogChangeLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 cketti and contributors + * Copyright (C) 2012-2018 cketti and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java b/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java index 87b58ff..7a5addc 100644 --- a/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java +++ b/ckChangeLog-legacy-dialog/src/main/java/de/cketti/changelog/dialog/HtmlFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 cketti and contributors + * Copyright (C) 2012-2018 cketti and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From d483d5de7b62c6202852cb3be51d7cb198af4800 Mon Sep 17 00:00:00 2001 From: cketti Date: Sun, 13 May 2018 15:11:23 +0200 Subject: [PATCH 35/35] Update README and CHANGELOG --- CHANGELOG.md | 60 ++++++++++++++++++++++++++++++++++++---------------- README.md | 17 ++++++--------- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 505ae3f..4a202dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,38 +1,62 @@ ## Change Log ### Version 2.0.0 (unreleased) -**Breaking change!** The library was split into a core library (`ckchangelog-core`) and a library for the UI part -(`ckchangelog-dialog`). +**Breaking change!** Change log files are now located in `raw` resource directories rather than the `xml` ones +(see issue #48). + +The library was split into a core library (`ckchangelog-core`) and a library for the UI part +(`ckchangelog-legacy-dialog`). The core library provides the base functionality like parsing the XML file and remembering the version code of the last -app version. This allows users of the core library to easily provide their own visualization of the Change Log. +app version. This allows users of the core library to provide their own visualization of the change log. + +The `ckchangelog-legacy-dialog` library provides the simple dialog from ckChangeLog 1.x that renders the change log in a +`WebView` inside an `AlertDialog`. However, users are strongly encouraged to write their own UI to display the change +log. It is the author's belief that `ckchangelog-legacy-dialog` should be avoided because of the following issues: +* Displaying a dialog on app startup is quite obtrusive. +* WebView is a very resource-intensive UI widget. +* Creating a ChangeLog instance triggers a read from `SharedPreferences` and a query to `PackageManager`. Both + operations that should be performed in a background thread, but aren't if you're using `ckchangelog-legacy-dialog`. + Similarly, reading the change log resources are operations that should be performed in the background. -The `ckchangelog-dialog` library provides the simple dialog from ckChangeLog 1.x that renders the Change Log in a -`WebView` inside an `AlertDialog`. +Support for a `date` attribute on the `release` element has been added to `ckchangelog-core`. It's an optional attribute +that accepts arbitrary strings. It's up the user of the library whether to parse the string as a date in a certain +format, output the string as-is, or ignore the value. + +Example: +```xml + + First release + +``` +**Note:** `ckchangelog-legacy-dialog` does not support this new attribute. #### Update from ckChangeLog 1.x -Replace the old entry in the dependency block with this: +1. Replace the old entry in the dependency block with this: -```groovy -dependencies { - compile 'de.cketti.library.changelog:ckchangelog-dialog:2.0.0' -} -``` + ```groovy + dependencies { + compile 'de.cketti.library.changelog:ckchangelog-legacy-dialog:2.0.0' + } + ``` -Then replace the old ckChangeLog code in your Activity's `onCreate()` method with this: +2. Move the change log files from `res/xml/` and `res/xml-*` to `res/raw/` and `res/raw-*` respectively. -```java -DialogChangeLog changeLog = DialogChangeLog.newInstance(this); -if (changeLog.isFirstRun()) { - changeLog.getLogDialog().show(); -} -``` +3. Then replace the old ckChangeLog code in your Activity's `onCreate()` method with this: + + ```java + DialogChangeLog changeLog = DialogChangeLog.newInstance(this); + if (changeLog.isFirstRun()) { + changeLog.getLogDialog().show(); + } + ``` Advanced functionality like getting the last version code is available via the `ChangeLog` instance that can be retrieved by using `DialogChangeLog#getChangeLog()`. Example: `dialogChangeLog.getChangeLog().isFirstRunEver()` + ### Version 1.2.2 (2015-01-09) * Added Ukrainian translation diff --git a/README.md b/README.md index 1b656de..034ebff 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Repository at . ## Usage -1. Create the master change log in `res/xml/changelog_master.xml`. Formatted like this: +1. Create the master change log in `res/raw/changelog_master.xml`. Formatted like this: ```xml @@ -40,7 +40,7 @@ Repository at . ``` 2. Create translations of this `changelog_master.xml` file in files named `changelog.xml` under -language-specific versions of `res/xml/`, e.g. `res/xml-de/changelog.xml`. +language-specific versions of `res/raw/`, e.g. `res/raw-de/changelog.xml`. 3. Display the change log dialog by putting the following code in your Activity's `onCreate()` method: @@ -53,21 +53,16 @@ language-specific versions of `res/xml/`, e.g. `res/xml-de/changelog.xml`. ## Include the library -The easiest way to add ckChangeLog to your project is via Gradle. Just add the following lines to your `build.gradle`: +Add the following lines to your `build.gradle` file: ```groovy dependencies { - compile 'de.cketti.library.changelog:ckchangelog-dialog:2.0.0' + compile 'de.cketti.library.changelog:ckchangelog-legacy-dialog:2.0.0' } ``` -To tell Gradle where to find the library, make sure `build.gradle` also contains this: - -```groovy -repositories { - mavenCentral() -} -``` +The library is uploaded to Maven Central and should be mirrored by JCenter. Make sure your `repositories` block +contains either `mavenCentral()` or `jcenter()`. ## Customize labels