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