diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ec791f8..74ec68d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -46,4 +46,5 @@ dependencies { implementation(libs.room.runtime) annotationProcessor(libs.room.compiler) implementation(libs.preferences) + implementation(libs.konfetti) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 192a615..e1ca4a6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,8 @@ --> + + 0 && turnTotal == 180) { + final Animation shakeAnimation = AnimationUtils.loadAnimation(this, R.anim.shake); + final View main = findViewById(R.id.main); + main.startAnimation(shakeAnimation); + + final Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + if (vibrator != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vibrator.vibrate(VibrationEffect.createOneShot(300, VibrationEffect.DEFAULT_AMPLITUDE)); + } + mSoundEngine.playOneHundredAndEightySound(); + } // Re-check logic for non-double finish or score of 1 int lastDartValue = mCurrentTurnDarts.get(mCurrentTurnDarts.size() - 1); @@ -579,17 +609,47 @@ public class GameActivity extends AppCompatActivity { updatePlayerStats(winner, dartsThrown, pointsMade, false); // Show win notification Toast.makeText(this, winner.name + " WINS!", Toast.LENGTH_LONG).show(); - - // End game and return to previous screen - finish(); + + playWinnerAnimation(winner.name); + mSoundEngine.playWinnerSound(); // TODO: Consider adding: - // - Win animation/sound // - Statistics display // - Save match to database // - Offer rematch } + private void playWinnerAnimation(final String winnerName) { + final KonfettiView konfettiView = findViewById(R.id.konfetti_view); + final View dimmerLayout = findViewById(R.id.dimmer); + final TextView winnerText = findViewById(R.id.winner_text); + + dimmerLayout.setVisibility(View.VISIBLE); + final EmitterConfig emitterConfig = new Emitter(300, TimeUnit.MILLISECONDS).max(300); + konfettiView.start( + new PartyFactory(emitterConfig) + .shapes(Shape.Circle.INSTANCE, Shape.Square.INSTANCE) + .spread(360) + .position(0.5, 0.5) // Center of screen + .colors(Arrays.asList(0xfce18a, 0xff726d, 0xf4306d, 0xb48def)) // Gold/Festive colors + .build() + ); + + winnerText.setText(winnerName + " WINS!"); + winnerText.setVisibility(View.VISIBLE); + winnerText.setScaleX(0f); + winnerText.setScaleY(0f); + winnerText.setAlpha(0f); + + winnerText.animate() + .scaleX(1.2f) + .scaleY(1.2f) + .alpha(1.0f) + .setDuration(500) + .setInterpolator(new OvershootInterpolator()) + .start(); + } + /** * State holder for a single player's X01 game progress. * Tracks name, remaining score, and darts thrown. diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/utils/SoundEngine.java b/app/src/main/java/com/aldo/apps/ochecompanion/utils/SoundEngine.java new file mode 100644 index 0000000..c9772d5 --- /dev/null +++ b/app/src/main/java/com/aldo/apps/ochecompanion/utils/SoundEngine.java @@ -0,0 +1,76 @@ +package com.aldo.apps.ochecompanion.utils; + +import android.content.Context; +import android.media.SoundPool; +import android.util.Log; + +import com.aldo.apps.ochecompanion.R; + +import java.lang.ref.WeakReference; +import java.util.SimpleTimeZone; + +public final class SoundEngine { + + /** + * Tag for debugging purpose. + */ + private static final String TAG = "SoundEngine"; + + private Context mContext; + + private static SoundEngine sInstance; + + private final SoundPool mSoundPool; + + private boolean mIsReady; + + private int mWinnerSoundId; + + private int mBustedSoundId; + + private int m180SoundId; + + private SoundEngine(final Context context) { + mContext = context; + mSoundPool = new SoundPool.Builder().setMaxStreams(5).build(); + mSoundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> mIsReady = true); + mWinnerSoundId = mSoundPool.load(context, R.raw.winner, 1); + mBustedSoundId = mSoundPool.load(context, R.raw.busted, 1); + m180SoundId = mSoundPool.load(context, R.raw.onehundredandeighty, 1); + + } + + public static SoundEngine getInstance(final Context context) { + if (sInstance == null) { + sInstance = new SoundEngine(context); + } + return sInstance; + } + + public void playWinnerSound() { + if (mIsReady) { + mSoundPool.play(mWinnerSoundId, 1.0f, 1.0f, 0, 0, 1.0f); + } else { + Log.w(TAG, "playWinnerSound: Not yet ready, cannot play sounds."); + } + } + + public void playBustedSound() { + if (mIsReady) { + mSoundPool.play(mBustedSoundId, 1.0f, 1.0f, 0, 0, 1.0f); + } else { + Log.w(TAG, "playBustedSound: Not yet ready, cannot play sounds."); + } + } + + public void playOneHundredAndEightySound() { + if (mIsReady) { + mSoundPool.play(m180SoundId, 1.0f, 1.0f, 0, 0, 1.0f); + } else { + Log.w(TAG, "playOneHundredAndEightySound: Not yet ready, cannot play sounds."); + } + } + + + +} diff --git a/app/src/main/res/anim/shake.xml b/app/src/main/res/anim/shake.xml new file mode 100644 index 0000000..f3d3989 --- /dev/null +++ b/app/src/main/res/anim/shake.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_game.xml b/app/src/main/res/layout/activity_game.xml index 2ee68cd..0a23158 100644 --- a/app/src/main/res/layout/activity_game.xml +++ b/app/src/main/res/layout/activity_game.xml @@ -157,4 +157,30 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/raw/busted.mp3 b/app/src/main/res/raw/busted.mp3 new file mode 100644 index 0000000..6c4c70a Binary files /dev/null and b/app/src/main/res/raw/busted.mp3 differ diff --git a/app/src/main/res/raw/onehundredandeighty.mp3 b/app/src/main/res/raw/onehundredandeighty.mp3 new file mode 100644 index 0000000..d2b319f Binary files /dev/null and b/app/src/main/res/raw/onehundredandeighty.mp3 differ diff --git a/app/src/main/res/raw/sad_busted.mp3 b/app/src/main/res/raw/sad_busted.mp3 new file mode 100644 index 0000000..f9c3c1f Binary files /dev/null and b/app/src/main/res/raw/sad_busted.mp3 differ diff --git a/app/src/main/res/raw/winner.mp3 b/app/src/main/res/raw/winner.mp3 new file mode 100644 index 0000000..9a18576 Binary files /dev/null and b/app/src/main/res/raw/winner.mp3 differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index b8947fa..6d7b333 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,6 +6,7 @@ #FFFFFF #E5E5EA #0A0A0A + #BF313131 #0056B3 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e9f7907..e843518 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,8 +9,10 @@ activity = "1.12.2" constraintlayout = "2.2.1" glide = "5.0.5" room = "2.8.4" -preferences = "1.2.0" +preferences = "1.2.1" preference = "1.2.1" +konfetti = "2.0.5" + [libraries] junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -25,7 +27,7 @@ room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = " room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room"} preferences = { group = "androidx.preference", name="preference-ktx", version.ref="preferences" } preference = { group = "androidx.preference", name = "preference", version.ref = "preference" } +konfetti = { group = "nl.dionsegijn", name = "konfetti-xml", version.ref = "konfetti" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } -