diff --git a/app/src/main/java/com/italankin/fifteen/Colors.java b/app/src/main/java/com/italankin/fifteen/Colors.java
index 413e21d..79cfc68 100644
--- a/app/src/main/java/com/italankin/fifteen/Colors.java
+++ b/app/src/main/java/com/italankin/fifteen/Colors.java
@@ -132,6 +132,8 @@ public class Colors {
CYAN_F3
};
public static final int ERROR = 0xffd24242;
+ public static final int NEW_APP = 0xff00639b;
+ public static final int NEW_APP_TEXT = 0xffffffff;
public static int getBackgroundColor() {
return background[Settings.getColorMode()];
diff --git a/app/src/main/java/com/italankin/fifteen/Defaults.java b/app/src/main/java/com/italankin/fifteen/Defaults.java
index c45b645..2f2081e 100644
--- a/app/src/main/java/com/italankin/fifteen/Defaults.java
+++ b/app/src/main/java/com/italankin/fifteen/Defaults.java
@@ -18,4 +18,5 @@ class Defaults {
static final boolean STATS = false;
static final boolean RANDOM_MISSING_TILE = false;
static final boolean NEW_GAME_DELAY = true;
+ static final boolean SHOW_NEW_APP_BANNER = true;
}
diff --git a/app/src/main/java/com/italankin/fifteen/GameSurface.java b/app/src/main/java/com/italankin/fifteen/GameSurface.java
index 73f048a..4e44e7b 100644
--- a/app/src/main/java/com/italankin/fifteen/GameSurface.java
+++ b/app/src/main/java/com/italankin/fifteen/GameSurface.java
@@ -1,12 +1,10 @@
package com.italankin.fifteen;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
-import android.net.Uri;
import android.view.MotionEvent;
import android.view.View;
import com.italankin.fifteen.export.ExportCallback;
@@ -31,6 +29,7 @@ public class GameSurface extends View implements TopPanelView.Callback {
private final Resources mResources;
+ private NewAppBannerView mNewAppBanner;
private TopPanelView mTopPanel;
private InfoPanelView mInfoPanel;
private FieldView mField;
@@ -82,6 +81,12 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
}
private void init() {
+ mNewAppBanner = new NewAppBannerView(getContext());
+ if (Settings.showNewAppBanner) {
+ Settings.showNewAppBanner = false;
+ Settings.save();
+ mNewAppBanner.show();
+ }
mTopPanel = new TopPanelView();
mTopPanel.addButton(BTN_NEW, mResources.getString(R.string.action_new));
mTopPanel.addButton(BTN_SETTINGS, mResources.getString(R.string.action_settings));
@@ -96,13 +101,7 @@ private void init() {
GameState.get().help = true;
RectF window = new RectF(0, 0, Dimensions.surfaceWidth, Dimensions.surfaceHeight);
mHelpOverlay = new HelpOverlay(getResources(), tileAppearAnimator, window, mRectField);
- mHelpOverlay.addCallback(() -> {
- Resources res = getResources();
- Intent intent = new Intent(Intent.ACTION_VIEW)
- .setData(Uri.parse(res.getString(R.string.help_how_to_play_url)));
- Intent chooser = Intent.createChooser(intent, res.getString(R.string.help_how_to_play));
- getContext().startActivity(chooser);
- });
+ mHelpOverlay.addCallback(() -> Tools.openUrl(getContext(), R.string.help_how_to_play_url));
mHelpOverlay.show();
});
@@ -157,6 +156,9 @@ public void onImportClicked() {
@Override
protected void onDraw(Canvas canvas) {
long current = System.currentTimeMillis();
+ if (lastOnDrawTimestamp == 0) {
+ lastOnDrawTimestamp = current - 1;
+ }
draw(canvas, current - lastOnDrawTimestamp);
lastOnDrawTimestamp = current;
if (Settings.postInvalidateDelay > 0) {
@@ -249,6 +251,9 @@ public boolean onTouchEvent(MotionEvent event) {
} else if (state.isSolved() && mRectField.contains(x, y)) {
createNewGame(true);
return true;
+ } else if (mNewAppBanner.isShown()) {
+ mNewAppBanner.onClick(x, y);
+ return true;
} else if (mTopPanel.onClick(x, y)) {
return true;
} else if (Settings.hardmode && mHardModeView.onClick(x, y)) {
@@ -362,6 +367,9 @@ private void draw(Canvas canvas, long elapsed) {
canvas.drawColor(Colors.getBackgroundColor());
mTopPanel.draw(canvas, elapsed);
+ if (mNewAppBanner.isShown()) {
+ mNewAppBanner.draw(canvas, elapsed);
+ }
mInfoPanel.draw(canvas, elapsed);
boolean helpShown = mHelpOverlay != null && mHelpOverlay.isShown();
if (!helpShown) {
diff --git a/app/src/main/java/com/italankin/fifteen/Settings.java b/app/src/main/java/com/italankin/fifteen/Settings.java
index 99101a8..a5c0c88 100644
--- a/app/src/main/java/com/italankin/fifteen/Settings.java
+++ b/app/src/main/java/com/italankin/fifteen/Settings.java
@@ -27,6 +27,7 @@ public class Settings {
private static final String KEY_TIME_FORMAT = "time_format";
private static final String KEY_STATS = "stats";
private static final String KEY_MISSING_RANDOM_TILE = "missing_random_tile";
+ private static final String KEY_SHOW_NEW_APP_BANNER = "show_new_app_banner";
static final String KEY_SAVED_GAME_ARRAY = "puzzle_prev";
static final String KEY_SAVED_GAME_MOVES = "puzzle_prev_moves";
@@ -49,6 +50,7 @@ public class Settings {
public static int ingameInfoTps = Defaults.INGAME_INFO_TPS;
public static int timeFormat = Defaults.TIME_FORMAT;
public static boolean stats = Defaults.STATS;
+ public static boolean showNewAppBanner = Defaults.SHOW_NEW_APP_BANNER;
/**
* Used for UI tests to limit event loop queue
@@ -85,6 +87,7 @@ static void load(Context context) {
timeFormat = prefs.getInt(KEY_TIME_FORMAT, Defaults.TIME_FORMAT);
stats = prefs.getBoolean(KEY_STATS, Defaults.STATS);
randomMissingTile = prefs.getBoolean(KEY_MISSING_RANDOM_TILE, Defaults.RANDOM_MISSING_TILE);
+ showNewAppBanner = prefs.getBoolean(KEY_SHOW_NEW_APP_BANNER, Defaults.SHOW_NEW_APP_BANNER);
if (prefs.contains("ingame_info")) {
// old logic for backward compatibility
@@ -146,6 +149,7 @@ static void save(boolean sync) {
editor.putInt(KEY_TIME_FORMAT, timeFormat);
editor.putBoolean(KEY_STATS, stats);
editor.putBoolean(KEY_MISSING_RANDOM_TILE, randomMissingTile);
+ editor.putBoolean(KEY_SHOW_NEW_APP_BANNER, showNewAppBanner);
SaveGameManager.saveGame(editor);
if (sync) {
editor.commit();
diff --git a/app/src/main/java/com/italankin/fifteen/Tools.java b/app/src/main/java/com/italankin/fifteen/Tools.java
index ad8c8b3..ac0730a 100644
--- a/app/src/main/java/com/italankin/fifteen/Tools.java
+++ b/app/src/main/java/com/italankin/fifteen/Tools.java
@@ -1,6 +1,9 @@
package com.italankin.fifteen;
+import android.content.Context;
+import android.content.Intent;
import android.graphics.Color;
+import android.net.Uri;
import java.util.Locale;
@@ -45,4 +48,11 @@ public static String formatFloat(float f) {
public static int manhattan(int x1, int y1, int x2, int y2) {
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
}
+
+ public static void openUrl(Context context, int urlResource) {
+ Intent intent = new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(context.getResources().getString(urlResource)));
+ Intent chooser = Intent.createChooser(intent, null);
+ context.startActivity(chooser);
+ }
}
diff --git a/app/src/main/java/com/italankin/fifteen/views/NewAppBannerView.java b/app/src/main/java/com/italankin/fifteen/views/NewAppBannerView.java
new file mode 100644
index 0000000..146d51e
--- /dev/null
+++ b/app/src/main/java/com/italankin/fifteen/views/NewAppBannerView.java
@@ -0,0 +1,88 @@
+package com.italankin.fifteen.views;
+
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.animation.DecelerateInterpolator;
+import com.italankin.fifteen.*;
+
+public class NewAppBannerView extends BaseView {
+
+ public static final long DELAY_TIME = 500;
+ public static final long APPEAR_TIME = 400;
+ public static final long DISAPPEAR_TIME = 400;
+ public static final long ACTIVE_TIME = 8000;
+
+ private final Context context;
+ private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final RectF bannerRect = new RectF();
+ private final RectF clipRect = new RectF();
+ private final float buttonTextYOffset;
+ private final String bannerText;
+ private final TimeInterpolator timeInterpolator = new DecelerateInterpolator(2f);
+ private long time;
+
+ public NewAppBannerView(Context context) {
+ this.context = context;
+ paint.setTextSize(Dimensions.interfaceFontSize);
+ paint.setTextAlign(Paint.Align.CENTER);
+ paint.setTypeface(Settings.typeface);
+ Rect r = new Rect();
+ paint.getTextBounds("A", 0, 1, r);
+ buttonTextYOffset = r.centerY();
+ bannerText = context.getString(R.string.new_app_banner_text);
+ bannerRect.set(0, 0, Dimensions.surfaceWidth, Dimensions.topBarHeight);
+ clipRect.set(bannerRect);
+ }
+
+ @Override
+ public boolean show() {
+ time = 0;
+ return super.show();
+ }
+
+ @Override
+ public void draw(Canvas canvas, long elapsedTime) {
+ time += elapsedTime;
+ if (time <= DELAY_TIME) {
+ return;
+ }
+ long realTime = time - DELAY_TIME;
+ int saveCount = canvas.save();
+ if (realTime < APPEAR_TIME) {
+ float p = Math.min(1f, (realTime / (float) APPEAR_TIME));
+ float w = bannerRect.width() / 2f * timeInterpolator.getInterpolation(p);
+ clipRect.left = bannerRect.centerX() - w;
+ clipRect.right = bannerRect.centerX() + w;
+ canvas.clipRect(clipRect);
+ } else if (realTime >= APPEAR_TIME + ACTIVE_TIME + DISAPPEAR_TIME) {
+ hide();
+ return;
+ } else if (realTime >= APPEAR_TIME + ACTIVE_TIME) {
+ float p = Math.max(0f, 1f - ((realTime - APPEAR_TIME - ACTIVE_TIME) / (float) APPEAR_TIME));
+ float w = bannerRect.width() / 2f * timeInterpolator.getInterpolation(p);
+ clipRect.left = bannerRect.centerX() - w;
+ clipRect.right = bannerRect.centerX() + w;
+ canvas.clipRect(clipRect);
+ }
+ paint.setColor(Colors.NEW_APP);
+ canvas.drawRect(bannerRect, paint);
+ paint.setColor(Colors.NEW_APP_TEXT);
+ canvas.drawText(bannerText, bannerRect.centerX(), bannerRect.centerY() - buttonTextYOffset, paint);
+ canvas.restoreToCount(saveCount);
+ }
+
+ public void onClick(float x, float y) {
+ if (bannerRect.contains(x, y)) {
+ Tools.openUrl(context, R.string.fifteen_app_url);
+ hide();
+ }
+ }
+
+ @Override
+ public void update() {
+ }
+}
diff --git a/app/src/main/java/com/italankin/fifteen/views/SettingsView.java b/app/src/main/java/com/italankin/fifteen/views/SettingsView.java
index 99c5cef..2d8e670 100644
--- a/app/src/main/java/com/italankin/fifteen/views/SettingsView.java
+++ b/app/src/main/java/com/italankin/fifteen/views/SettingsView.java
@@ -215,7 +215,7 @@ public boolean hide() {
}
private void initPages(Resources res) {
- pages.put(PAGE_BASIC, new BasicPage(mPaintText, mPaintValue, res));
+ pages.put(PAGE_BASIC, new BasicPage(context, mPaintText, mPaintValue));
pages.put(PAGE_ADVANCED, new AdvancedPage(this, mPaintText, mPaintValue, res));
pages.put(PAGE_INGAME_INFO, new IngameInfoPage(mPaintText, mPaintValue, res));
pages.put(PAGE_ABOUT, new AboutPage(context, mPaintText, mPaintValue));
diff --git a/app/src/main/java/com/italankin/fifteen/views/settings/AboutPage.java b/app/src/main/java/com/italankin/fifteen/views/settings/AboutPage.java
index e305038..89e142a 100644
--- a/app/src/main/java/com/italankin/fifteen/views/settings/AboutPage.java
+++ b/app/src/main/java/com/italankin/fifteen/views/settings/AboutPage.java
@@ -1,14 +1,13 @@
package com.italankin.fifteen.views.settings;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
-import android.net.Uri;
import com.italankin.fifteen.Dimensions;
import com.italankin.fifteen.R;
+import com.italankin.fifteen.Tools;
import com.italankin.fifteen.views.SettingsView;
public class AboutPage implements SettingsPage {
@@ -20,14 +19,11 @@ public class AboutPage implements SettingsPage {
private final String mTextSourceCode;
private final String mTextSourceCodeValue;
- private final String mTextNewApp;
- private final String mTextNewAppValue;
private final String mTextWebsite;
private final String mTextWebsiteValue;
private RectF mRectWebsite;
private RectF mRectSourceCode;
- private RectF mRectNewApp;
public AboutPage(Context context, Paint paintText, Paint paintValue) {
this.context = context;
@@ -38,8 +34,6 @@ public AboutPage(Context context, Paint paintText, Paint paintValue) {
Resources res = context.getResources();
mTextSourceCode = res.getString(R.string.pref_source_code);
mTextSourceCodeValue = res.getString(R.string.pref_source_code_value);
- mTextNewApp = res.getString(R.string.pref_new_app);
- mTextNewAppValue = res.getString(R.string.pref_new_app_value);
mTextWebsite = res.getString(R.string.pref_website);
mTextWebsiteValue = res.getString(R.string.pref_website_value);
}
@@ -53,8 +47,6 @@ public void init(int lineSpacing, int topMargin, int padding, int textHeight) {
mRectSourceCode = new RectF(0, topMargin, Dimensions.surfaceWidth, topMargin + textHeight);
mRectSourceCode.inset(0, padding);
topMargin += lineSpacing;
- mRectNewApp = new RectF(0, topMargin, Dimensions.surfaceWidth, topMargin + textHeight);
- mRectNewApp.inset(0, padding);
}
@Override
@@ -63,28 +55,17 @@ public void draw(Canvas canvas, float valueRight, float textLeft, float textYOff
canvas.drawText(mTextWebsiteValue, valueRight, mRectWebsite.bottom - textYOffset, mPaintValue);
canvas.drawText(mTextSourceCode, textLeft, mRectSourceCode.bottom - textYOffset, mPaintText);
canvas.drawText(mTextSourceCodeValue, valueRight, mRectSourceCode.bottom - textYOffset, mPaintValue);
- canvas.drawText(mTextNewApp, textLeft, mRectNewApp.bottom - textYOffset, mPaintText);
- canvas.drawText(mTextNewAppValue, valueRight, mRectNewApp.bottom - textYOffset, mPaintValue);
}
@Override
public void onClick(float x, float y, float dx) {
if (mRectWebsite.contains(x, y)) {
- openUrl(R.string.website_url);
+ Tools.openUrl(context, R.string.website_url);
} else if (mRectSourceCode.contains(x, y)) {
- openUrl(R.string.source_code_url);
- } else if (mRectNewApp.contains(x, y)) {
- openUrl(R.string.fifteen_app_url);
+ Tools.openUrl(context, R.string.source_code_url);
}
}
- private void openUrl(int urlResource) {
- Intent intent = new Intent(Intent.ACTION_VIEW)
- .setData(Uri.parse(context.getResources().getString(urlResource)));
- Intent chooser = Intent.createChooser(intent, null);
- context.startActivity(chooser);
- }
-
@Override
public void update() {
}
diff --git a/app/src/main/java/com/italankin/fifteen/views/settings/BasicPage.java b/app/src/main/java/com/italankin/fifteen/views/settings/BasicPage.java
index a371ed3..6b507c3 100644
--- a/app/src/main/java/com/italankin/fifteen/views/settings/BasicPage.java
+++ b/app/src/main/java/com/italankin/fifteen/views/settings/BasicPage.java
@@ -1,19 +1,16 @@
package com.italankin.fifteen.views.settings;
+import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
-
-import com.italankin.fifteen.Colors;
-import com.italankin.fifteen.Constants;
-import com.italankin.fifteen.Dimensions;
-import com.italankin.fifteen.R;
-import com.italankin.fifteen.Settings;
+import com.italankin.fifteen.*;
import com.italankin.fifteen.views.SettingsView;
public class BasicPage implements SettingsPage {
+ private final Context mContext;
private final Paint mPaintText;
private final Paint mPaintValue;
private final Paint mPaintIcon;
@@ -31,6 +28,8 @@ public class BasicPage implements SettingsPage {
private final String[] mTextColorModeValues;
private final String mTextType;
private final String[] mTextTypeValue;
+ private final String mTextNewApp;
+ private final String mTextNewAppValue;
private RectF mRectWidth;
private RectF mRectAnimations;
@@ -39,16 +38,19 @@ public class BasicPage implements SettingsPage {
private RectF mRectColor;
private RectF mRectColorMode;
private RectF mRectColorIcon;
+ private RectF mRectNewApp;
private SettingsView.Callbacks mCallbacks;
- public BasicPage(Paint paintText, Paint paintValue, Resources res) {
+ public BasicPage(Context context, Paint paintText, Paint paintValue) {
+ this.mContext = context;
this.mPaintText = paintText;
this.mPaintValue = paintValue;
mPaintIcon = new Paint();
mPaintIcon.setAntiAlias(Settings.antiAlias);
+ Resources res = context.getResources();
mTextHeight = res.getString(R.string.pref_height);
mTextHeightValue = Integer.toString(Settings.gameHeight);
mTextWidth = res.getString(R.string.pref_width);
@@ -62,6 +64,8 @@ public BasicPage(Paint paintText, Paint paintValue, Resources res) {
mTextColorMode = res.getString(R.string.pref_color_mode);
mTextColorModeValues = res.getStringArray(R.array.color_mode);
mTextColor = res.getString(R.string.pref_color);
+ mTextNewApp = res.getString(R.string.pref_new_app);
+ mTextNewAppValue = res.getString(R.string.pref_new_app_value);
}
public void addCallback(SettingsView.Callbacks callbacks) {
@@ -98,6 +102,10 @@ public void init(int lineSpacing, int topMargin, int padding, int textHeight) {
Dimensions.surfaceWidth / 2 + 2.0f * Dimensions.spacing + textHeight,
mRectColor.bottom + padding);
mRectColorIcon.inset(-mRectColorIcon.width() / 4, -mRectColorIcon.width() / 4);
+
+ topMargin += lineSpacing;
+ mRectNewApp = new RectF(0, topMargin, Dimensions.surfaceWidth, topMargin + textHeight);
+ mRectNewApp.inset(0, padding);
}
@Override
@@ -125,6 +133,12 @@ public void draw(Canvas canvas, float valueRight, float textLeft, float textYOff
canvas.drawText(mTextType, textLeft, mRectGameType.bottom - textYOffset, mPaintText);
canvas.drawText(mTextTypeValue[Settings.gameType],
valueRight, mRectGameType.bottom - textYOffset, mPaintValue);
+
+ canvas.drawText(mTextNewApp, textLeft, mRectNewApp.bottom - textYOffset, mPaintText);
+ int valueColor = mPaintValue.getColor();
+ mPaintValue.setColor(Colors.NEW_APP);
+ canvas.drawText(mTextNewAppValue, valueRight, mRectNewApp.bottom - textYOffset, mPaintValue);
+ mPaintValue.setColor(valueColor);
}
@Override
@@ -197,6 +211,10 @@ public void onClick(float x, float y, float dx) {
mCallbacks.onSettingsChanged(true);
}
}
+
+ if (mRectNewApp.contains(x, y)) {
+ Tools.openUrl(mContext, R.string.fifteen_app_url);
+ }
}
@Override
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 7823bd5..17e36b9 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -44,6 +44,8 @@
Сайт
Новая версия
+ НОВАЯ ВЕРСИЯ: FIFTEEN
+
выкл.
быстрые
обычные
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1aac5cb..b8d8929 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -47,6 +47,8 @@
New app
Fifteen
+ NEW VERSION: FIFTEEN
+
off
fast
normal