Release notes are now shown in wizard like UI

This commit is contained in:
serso 2015-02-06 00:59:34 +01:00
parent 7e5c5975c4
commit 2540ec0b37
12 changed files with 290 additions and 24 deletions

View File

@ -31,7 +31,6 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.text.Html;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.util.Log; import android.util.Log;
import android.view.*; import android.view.*;
@ -40,7 +39,6 @@ import android.widget.TextView;
import org.solovyev.android.Activities; import org.solovyev.android.Activities;
import org.solovyev.android.Android; import org.solovyev.android.Android;
import org.solovyev.android.Threads; import org.solovyev.android.Threads;
import org.solovyev.android.calculator.about.CalculatorReleaseNotesFragment;
import org.solovyev.android.calculator.plot.CalculatorPlotActivity; import org.solovyev.android.calculator.plot.CalculatorPlotActivity;
import org.solovyev.android.calculator.wizard.CalculatorWizards; import org.solovyev.android.calculator.wizard.CalculatorWizards;
import org.solovyev.android.fragments.FragmentUtils; import org.solovyev.android.fragments.FragmentUtils;
@ -49,7 +47,6 @@ import org.solovyev.android.wizard.Wizard;
import org.solovyev.android.wizard.Wizards; import org.solovyev.android.wizard.Wizards;
import org.solovyev.common.Objects; import org.solovyev.common.Objects;
import org.solovyev.common.history.HistoryAction; import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.text.Strings;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -58,7 +55,9 @@ import static android.os.Build.VERSION_CODES.GINGERBREAD_MR1;
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static org.solovyev.android.calculator.Preferences.Gui.preventScreenFromFading; import static org.solovyev.android.calculator.Preferences.Gui.preventScreenFromFading;
import static org.solovyev.android.calculator.about.CalculatorReleaseNotesFragment.hasReleaseNotes;
import static org.solovyev.android.wizard.WizardUi.continueWizard; import static org.solovyev.android.wizard.WizardUi.continueWizard;
import static org.solovyev.android.wizard.WizardUi.createLaunchIntent;
import static org.solovyev.android.wizard.WizardUi.startWizard; import static org.solovyev.android.wizard.WizardUi.startWizard;
public class CalculatorActivity extends BaseActivity implements SharedPreferences.OnSharedPreferenceChangeListener, CalculatorEventListener { public class CalculatorActivity extends BaseActivity implements SharedPreferences.OnSharedPreferenceChangeListener, CalculatorEventListener {
@ -164,23 +163,19 @@ public class CalculatorActivity extends BaseActivity implements SharedPreference
} else { } else {
if (savedVersion < appVersion) { if (savedVersion < appVersion) {
final boolean showReleaseNotes = Preferences.Gui.showReleaseNotes.getPreference(preferences); final boolean showReleaseNotes = Preferences.Gui.showReleaseNotes.getPreference(preferences);
if (showReleaseNotes) { if (showReleaseNotes && hasReleaseNotes(context, savedVersion + 1)) {
final String releaseNotes = CalculatorReleaseNotesFragment.getReleaseNotes(context, savedVersion + 1); final Bundle bundle = new Bundle();
if (!Strings.isEmpty(releaseNotes)) { bundle.putInt(CalculatorWizards.RELEASE_NOTES_VERSION, savedVersion);
final AlertDialog.Builder builder = new AlertDialog.Builder(context).setMessage(Html.fromHtml(releaseNotes)); context.startActivity(createLaunchIntent(wizards, CalculatorWizards.RELEASE_NOTES, context, bundle));
builder.setPositiveButton(android.R.string.ok, null);
builder.setTitle(R.string.c_release_notes);
builder.create().show();
dialogShown = true; dialogShown = true;
} }
} }
} }
} }
}
//Log.d(this.getClass().getName(), "Application was opened " + appOpenedCounter + " time!"); //Log.d(this.getClass().getName(), "Application was opened " + appOpenedCounter + " time!");
if (!dialogShown) { if (!dialogShown) {
if (appOpenedCounter != null && appOpenedCounter > 100) { if (appOpenedCounter != null && appOpenedCounter > 30) {
dialogShown = showSpecialWindow(preferences, Preferences.Gui.feedbackWindowShown, R.layout.feedback, R.id.feedbackText, context); dialogShown = showSpecialWindow(preferences, Preferences.Gui.feedbackWindowShown, R.layout.feedback, R.id.feedbackText, context);
} }
} }

View File

@ -35,6 +35,8 @@ import org.solovyev.android.calculator.R;
import org.solovyev.common.text.Strings; import org.solovyev.common.text.Strings;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import static org.solovyev.android.Android.getAppVersionCode; import static org.solovyev.android.Android.getAppVersionCode;
@ -61,11 +63,11 @@ public class CalculatorReleaseNotesFragment extends CalculatorFragment {
@Nonnull @Nonnull
public static String getReleaseNotes(@Nonnull Context context) { public static String getReleaseNotes(@Nonnull Context context) {
return getReleaseNotes(context, 0); return getReleaseNotesString(context, 0);
} }
@Nonnull @Nonnull
public static String getReleaseNotes(@Nonnull Context context, int minVersion) { public static String getReleaseNotesString(@Nonnull Context context, int minVersion) {
final StringBuilder result = new StringBuilder(); final StringBuilder result = new StringBuilder();
final String releaseNotesForTitle = context.getString(R.string.c_release_notes_for_title); final String releaseNotesForTitle = context.getString(R.string.c_release_notes_for_title);
@ -78,7 +80,6 @@ public class CalculatorReleaseNotesFragment extends CalculatorFragment {
final String versionName = getVersionName(textHelper, versionCode); final String versionName = getVersionName(textHelper, versionCode);
String releaseNotesForVersion = textHelper.getText(makeReleaseNotesResourceId(versionCode)); String releaseNotesForVersion = textHelper.getText(makeReleaseNotesResourceId(versionCode));
if (!Strings.isEmpty(releaseNotesForVersion)) { if (!Strings.isEmpty(releaseNotesForVersion)) {
if (releaseNotesForVersion == null) throw new AssertionError();
if (!first) { if (!first) {
result.append("<br/><br/>"); result.append("<br/><br/>");
} else { } else {
@ -94,7 +95,38 @@ public class CalculatorReleaseNotesFragment extends CalculatorFragment {
} }
@Nonnull @Nonnull
private static String getVersionName(@Nonnull TextHelper textHelper, int versionCode) { public static List<Integer> getReleaseNotesVersions(@Nonnull Context context, int minVersion) {
final List<Integer> releaseNotes = new ArrayList<>();
final int currentVersionCode = getAppVersionCode(context);
final TextHelper textHelper = new TextHelper(context.getResources(), CalculatorApplication.class.getPackage().getName());
for (int versionCode = currentVersionCode; versionCode >= minVersion; versionCode--) {
final String releaseNotesForVersion = textHelper.getText(makeReleaseNotesResourceId(versionCode));
if (!Strings.isEmpty(releaseNotesForVersion)) {
releaseNotes.add(versionCode);
}
}
return releaseNotes;
}
public static boolean hasReleaseNotes(@Nonnull Context context, int minVersion) {
final int currentVersionCode = getAppVersionCode(context);
final TextHelper textHelper = new TextHelper(context.getResources(), CalculatorApplication.class.getPackage().getName());
for (int versionCode = currentVersionCode; versionCode >= minVersion; versionCode--) {
String releaseNotesForVersion = textHelper.getText(makeReleaseNotesResourceId(versionCode));
if (!Strings.isEmpty(releaseNotesForVersion)) {
return true;
}
}
return false;
}
@Nonnull
public static String getVersionName(@Nonnull TextHelper textHelper, int versionCode) {
final String versionName = textHelper.getText(makeVersionResourceId(versionCode)); final String versionName = textHelper.getText(makeVersionResourceId(versionCode));
if (versionName != null) { if (versionName != null) {
return versionName; return versionName;
@ -103,7 +135,7 @@ public class CalculatorReleaseNotesFragment extends CalculatorFragment {
} }
} }
private static String makeReleaseNotesResourceId(int versionCode) { public static String makeReleaseNotesResourceId(int versionCode) {
return "c_release_notes_for_" + versionCode; return "c_release_notes_for_" + versionCode;
} }

View File

@ -49,6 +49,9 @@ public class TextHelper {
public String getText(@Nonnull String stringName) { public String getText(@Nonnull String stringName) {
final int stringId = this.resources.getIdentifier(stringName, "string", this.packageName); final int stringId = this.resources.getIdentifier(stringName, "string", this.packageName);
try { try {
if (stringId == 0) {
return null;
}
return resources.getString(stringId); return resources.getString(stringId);
} catch (Resources.NotFoundException e) { } catch (Resources.NotFoundException e) {
return null; return null;

View File

@ -0,0 +1,56 @@
package org.solovyev.android.calculator.release;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.google.common.base.Strings;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.about.CalculatorReleaseNotesFragment;
import org.solovyev.android.calculator.about.TextHelper;
import org.solovyev.android.calculator.wizard.WizardFragment;
import javax.annotation.Nonnull;
public class ReleaseNoteFragment extends WizardFragment {
@Nonnull
public static final String ARG_VERSION = "version";
private int version;
@Override
protected int getViewResId() {
return R.layout.cpp_release_note_step;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
version = getArguments().getInt(ARG_VERSION, 0);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = super.onCreateView(inflater, container, savedInstanceState);
final TextView title = (TextView) view.findViewById(R.id.release_note_title);
final TextHelper textHelper = new TextHelper(getActivity().getResources(), CalculatorApplication.class.getPackage().getName());
title.setText(getString(R.string.cpp_new_in_version, getReleaseNoteVersion(textHelper)));
final TextView message = (TextView) view.findViewById(R.id.release_note_message);
message.setText(Html.fromHtml(getReleaseNote(textHelper)));
return view;
}
@Nonnull
private String getReleaseNoteVersion(@Nonnull TextHelper textHelper) {
return CalculatorReleaseNotesFragment.getVersionName(textHelper, version);
}
@Nonnull
private String getReleaseNote(@Nonnull TextHelper textHelper) {
final String resourceId = CalculatorReleaseNotesFragment.makeReleaseNotesResourceId(version);
return Strings.nullToEmpty(textHelper.getText(resourceId)).replace("\n", "<br/>");
}
}

View File

@ -0,0 +1,86 @@
package org.solovyev.android.calculator.release;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import org.solovyev.android.wizard.WizardStep;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ReleaseNoteStep implements WizardStep {
private final int version;
public ReleaseNoteStep(int version) {
this.version = version;
}
public ReleaseNoteStep(@Nonnull Bundle arguments) {
this(arguments.getInt(ReleaseNoteFragment.ARG_VERSION, 0));
}
@Nonnull
@Override
public String getFragmentTag() {
return getName();
}
@Nonnull
@Override
public Class<? extends Fragment> getFragmentClass() {
return ReleaseNoteFragment.class;
}
@Nullable
@Override
public Bundle getFragmentArgs() {
final Bundle bundle = new Bundle();
bundle.putInt(ReleaseNoteFragment.ARG_VERSION, version);
return bundle;
}
@Override
public int getTitleResId() {
return 0;
}
@Override
public int getNextButtonTitleResId() {
return 0;
}
@Override
public boolean onNext(@Nonnull Fragment fragment) {
return false;
}
@Override
public boolean onPrev(@Nonnull Fragment fragment) {
return false;
}
@Override
public boolean isVisible() {
return false;
}
@Nonnull
@Override
public String getName() {
return "release-note-" + version;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ReleaseNoteStep that = (ReleaseNoteStep) o;
return version == that.version;
}
@Override
public int hashCode() {
return version;
}
}

View File

@ -2,6 +2,9 @@ package org.solovyev.android.calculator.wizard;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle;
import org.solovyev.android.calculator.about.CalculatorReleaseNotesFragment;
import org.solovyev.android.calculator.release.ReleaseNoteStep;
import org.solovyev.android.wizard.*; import org.solovyev.android.wizard.*;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -15,6 +18,8 @@ import static org.solovyev.android.calculator.wizard.CalculatorWizardStep.welcom
public class CalculatorWizards implements Wizards { public class CalculatorWizards implements Wizards {
public static final String FIRST_TIME_WIZARD = "first-wizard"; public static final String FIRST_TIME_WIZARD = "first-wizard";
public static final String RELEASE_NOTES = "release-notes";
public static final String RELEASE_NOTES_VERSION = "version";
public static final String DEFAULT_WIZARD_FLOW = "app-wizard"; public static final String DEFAULT_WIZARD_FLOW = "app-wizard";
@Nonnull @Nonnull
@ -32,20 +37,28 @@ public class CalculatorWizards implements Wizards {
@Nonnull @Nonnull
@Override @Override
public Wizard getWizard(@Nullable String name) { public Wizard getWizard(@Nullable String name, @Nullable Bundle arguments) {
if (name == null) { if (name == null) {
return getWizard(FIRST_TIME_WIZARD); return getWizard(FIRST_TIME_WIZARD, arguments);
} }
if (FIRST_TIME_WIZARD.equals(name)) { if (FIRST_TIME_WIZARD.equals(name)) {
return newBaseWizard(FIRST_TIME_WIZARD, newFirstTimeWizardFlow()); return newBaseWizard(FIRST_TIME_WIZARD, newFirstTimeWizardFlow());
} else if (DEFAULT_WIZARD_FLOW.equals(name)) { } else if (DEFAULT_WIZARD_FLOW.equals(name)) {
return newBaseWizard(DEFAULT_WIZARD_FLOW, newDefaultWizardFlow()); return newBaseWizard(DEFAULT_WIZARD_FLOW, newDefaultWizardFlow());
} else if (RELEASE_NOTES.equals(name)) {
return newBaseWizard(RELEASE_NOTES, newReleaseNotesWizardFlow(context, arguments));
} else { } else {
throw new IllegalArgumentException("Wizard flow " + name + " is not supported"); throw new IllegalArgumentException("Wizard flow " + name + " is not supported");
} }
} }
@Nonnull
@Override
public Wizard getWizard(@Nullable String name) throws IllegalArgumentException {
return getWizard(name, null);
}
@Nonnull @Nonnull
private BaseWizard newBaseWizard(@Nonnull String name, @Nonnull WizardFlow flow) { private BaseWizard newBaseWizard(@Nonnull String name, @Nonnull WizardFlow flow) {
return new BaseWizard(name, context, flow); return new BaseWizard(name, context, flow);
@ -62,6 +75,21 @@ public class CalculatorWizards implements Wizards {
return new ListWizardFlow(wizardSteps); return new ListWizardFlow(wizardSteps);
} }
@Nonnull
static WizardFlow newReleaseNotesWizardFlow(@Nonnull Context context, @Nullable Bundle arguments) {
final List<WizardStep> wizardSteps = new ArrayList<WizardStep>();
final int startVersion = arguments != null ? arguments.getInt(RELEASE_NOTES_VERSION, 0) : 0;
List<Integer> versions = CalculatorReleaseNotesFragment.getReleaseNotesVersions(context, startVersion);
final int size = versions.size();
if (size > 7) {
versions = versions.subList(0, 7);
}
for (Integer version : versions) {
wizardSteps.add(new ReleaseNoteStep(version));
}
return new ListWizardFlow(wizardSteps);
}
@Nonnull @Nonnull
static WizardFlow newFirstTimeWizardFlow() { static WizardFlow newFirstTimeWizardFlow() {
final List<WizardStep> wizardSteps = new ArrayList<WizardStep>(); final List<WizardStep> wizardSteps = new ArrayList<WizardStep>();
@ -72,4 +100,5 @@ public class CalculatorWizards implements Wizards {
} }
return new ListWizardFlow(wizardSteps); return new ListWizardFlow(wizardSteps);
} }
} }

View File

@ -102,7 +102,8 @@ public class WizardActivity extends BaseActivity implements WizardsAware {
} }
public void finishWizardAbruptly() { public void finishWizardAbruptly() {
finishWizardAbruptly(false); final boolean confirmed = wizardUi.getWizard().getName().equals(CalculatorWizards.RELEASE_NOTES);
finishWizardAbruptly(confirmed);
} }
public void finishWizardAbruptly(boolean confirmed) { public void finishWizardAbruptly(boolean confirmed) {

View File

@ -9,6 +9,8 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.release.ReleaseNoteFragment;
import org.solovyev.android.calculator.release.ReleaseNoteStep;
import org.solovyev.android.wizard.Wizard; import org.solovyev.android.wizard.Wizard;
import org.solovyev.android.wizard.WizardFlow; import org.solovyev.android.wizard.WizardFlow;
import org.solovyev.android.wizard.WizardStep; import org.solovyev.android.wizard.WizardStep;
@ -34,7 +36,11 @@ public abstract class WizardFragment extends Fragment implements View.OnClickLis
} }
@Nonnull @Nonnull
private CalculatorWizardStep findStepByClassName() { private WizardStep findStepByClassName() {
if (this instanceof ReleaseNoteFragment) {
return new ReleaseNoteStep(getArguments());
}
for (CalculatorWizardStep step : CalculatorWizardStep.values()) { for (CalculatorWizardStep step : CalculatorWizardStep.values()) {
if (step.getFragmentClass().equals(getClass())) { if (step.getFragmentClass().equals(getClass())) {
return step; return step;

View File

@ -12,6 +12,7 @@ import javax.annotation.Nullable;
public class WizardUi<A extends FragmentActivity> { public class WizardUi<A extends FragmentActivity> {
private static final String FLOW = "flow"; private static final String FLOW = "flow";
private static final String ARGUMENTS = "arguments";
private static final String STEP = "step"; private static final String STEP = "step";
protected WizardStep step; protected WizardStep step;
@ -44,7 +45,8 @@ public class WizardUi<A extends FragmentActivity> {
stepName = savedInstanceState.getString(STEP); stepName = savedInstanceState.getString(STEP);
} }
wizard = wizardsAware.getWizards().getWizard(wizardName); final Bundle arguments = intent.getBundleExtra(ARGUMENTS);
wizard = wizardsAware.getWizards().getWizard(wizardName, arguments);
if (stepName != null) { if (stepName != null) {
step = wizard.getFlow().getStepByName(stepName); step = wizard.getFlow().getStepByName(stepName);
@ -131,10 +133,16 @@ public class WizardUi<A extends FragmentActivity> {
} }
@Nonnull @Nonnull
private static Intent createLaunchIntent(@Nonnull Wizards wizards, @Nullable String name, @Nonnull Context context) { public static Intent createLaunchIntent(@Nonnull Wizards wizards, @Nullable String name, @Nonnull Context context) {
return createLaunchIntent(wizards, name, context, null);
}
@Nonnull
public static Intent createLaunchIntent(@Nonnull Wizards wizards, @Nullable String name, @Nonnull Context context, @Nullable Bundle arguments) {
final Intent intent = new Intent(context, wizards.getActivityClassName()); final Intent intent = new Intent(context, wizards.getActivityClassName());
intent.putExtra(FLOW, name); intent.putExtra(FLOW, name);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(ARGUMENTS, arguments);
return intent; return intent;
} }
} }

View File

@ -1,6 +1,7 @@
package org.solovyev.android.wizard; package org.solovyev.android.wizard;
import android.app.Activity; import android.app.Activity;
import android.os.Bundle;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -10,6 +11,9 @@ public interface Wizards {
@Nonnull @Nonnull
Class<? extends Activity> getActivityClassName(); Class<? extends Activity> getActivityClassName();
@Nonnull
public Wizard getWizard(@Nullable String name, @Nullable Bundle arguments) throws IllegalArgumentException;
@Nonnull @Nonnull
public Wizard getWizard(@Nullable String name) throws IllegalArgumentException; public Wizard getWizard(@Nullable String name) throws IllegalArgumentException;
} }

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2013 serso aka se.solovyev
~
~ 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.
~
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Contact details
~
~ Email: se.solovyev@gmail.com
~ Site: http://se.solovyev.org
-->
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="match_parent"
a:layout_height="match_parent"
a:orientation="vertical"
a:gravity="center">
<TextView
style="@style/WizardLabel"
a:id="@+id/release_note_title"
a:layout_height="wrap_content"
a:layout_width="match_parent"
a:textAppearance="@android:style/TextAppearance.Large" />
<TextView
style="@style/WizardLabel.Last"
a:id="@+id/release_note_message"
a:layout_height="wrap_content"
a:layout_width="match_parent"
a:textAppearance="@android:style/TextAppearance.Medium" />
</LinearLayout>

View File

@ -15,4 +15,5 @@
<string name="wizard_final">The app is set up and ready to use.</string> <string name="wizard_final">The app is set up and ready to use.</string>
<string name="wizard_skip">Skip</string> <string name="wizard_skip">Skip</string>
<string name="wizard_finish">Finish</string> <string name="wizard_finish">Finish</string>
<string name="cpp_new_in_version">New in %1$s version</string>
</resources> </resources>