Skip to content

Commit

Permalink
Merge pull request #13 from UrAvgCode/compose
Browse files Browse the repository at this point in the history
Use Jetpack Compose for UI
  • Loading branch information
UrAvgCode authored Jan 24, 2025
2 parents 195be26 + c84a30c commit 2942cf9
Show file tree
Hide file tree
Showing 22 changed files with 357 additions and 298 deletions.
39 changes: 30 additions & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}

android {
namespace = "com.uravgcode.chooser"
compileSdk = 34

buildFeatures {
viewBinding = true
}
compileSdk = 35

defaultConfig {
applicationId = "com.uravgcode.chooser"
Expand All @@ -19,6 +16,9 @@ android {
versionName = "1.3.2"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
Expand All @@ -44,9 +44,30 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}

buildFeatures {
compose = true
}

composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
}

dependencies {
implementation("androidx.activity:activity-ktx:1.9.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}
82 changes: 8 additions & 74 deletions app/src/main/java/com/uravgcode/chooser/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,89 +1,23 @@
package com.uravgcode.chooser

import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import com.uravgcode.chooser.databinding.ActivityMainBinding
import com.uravgcode.chooser.utilities.Mode
import com.uravgcode.chooser.composables.screens.MainScreen
import com.uravgcode.chooser.utilities.SettingsManager

class MainActivity : ComponentActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var preferences: SharedPreferences

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.chooser.motionLayout = binding.motionLayout

preferences = getSharedPreferences("Settings", Context.MODE_PRIVATE)
loadPreferences()

binding.btnCount.setOnClickListener { updateCount() }
binding.btnMode.setOnClickListener { updateMode() }
binding.btnMode.setOnLongClickListener { toggleSound() }
}

private fun loadPreferences() {
with(binding.chooser) {
count = preferences.getInt("count", 1)
mode = Mode.valueOf(preferences.getString("mode", "SINGLE")!!)

updateModeUI()
updateCountUI()
motionLayout.jumpToState(mode.state())
}
}

private fun savePreference(key: String, value: Any) {
with(preferences.edit()) {
when (value) {
is Int -> putInt(key, value)
is String -> putString(key, value)
else -> throw IllegalArgumentException("Invalid type for SharedPreferences")
}
apply()
}
}

private fun updateMode() {
with(binding.chooser) {
if (motionLayout.currentState != -1) {
mode = mode.next()
count = mode.initialCount()
updateModeUI()
updateCountUI()
}
}
}
val preferences = getSharedPreferences("settings", Context.MODE_PRIVATE)
val settings = SettingsManager(preferences)

private fun updateCount() {
with(binding.chooser) {
count = mode.nextCount(count)
updateCountUI()
setContent {
MainScreen(settings)
}
}

private fun toggleSound(): Boolean {
binding.chooser.toggleSound()
return true
}

private fun updateModeUI() {
val mode = binding.chooser.mode
val drawable = mode.drawable()

binding.btnMode.foreground = getDrawable(drawable)
savePreference("mode", mode.toString())
binding.motionLayout.transitionToState(mode.state())
}

private fun updateCountUI() {
val count = binding.chooser.count
binding.btnCount.text = count.toString()
savePreference("count", count)
}
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/uravgcode/chooser/composables/Screen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.uravgcode.chooser.composables

sealed class Screen {
data object Chooser : Screen()
data object Settings : Screen()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.uravgcode.chooser.composables.buttons

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun AnimatedButton(
visible: Boolean,
onClick: () -> Unit,
onLongClick: (() -> Unit)? = null,
content: @Composable () -> Unit,
alignment: Alignment
) {
Box(modifier = Modifier.fillMaxSize()) {
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
initialOffsetY = { fullHeight -> -2 * fullHeight },
animationSpec = tween(durationMillis = 400)
),
exit = slideOutVertically(
targetOffsetY = { fullHeight -> -2 * fullHeight },
animationSpec = tween(durationMillis = 400)
),
modifier = Modifier
.align(alignment)
.padding(24.dp)
.size(56.dp)
) {
BaseButton(
onClick = onClick,
onLongClick = onLongClick,
content = content,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.uravgcode.chooser.composables.buttons

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics


@Composable
@OptIn(ExperimentalFoundationApi::class)
fun BaseButton(
onClick: () -> Unit,
onLongClick: (() -> Unit)? = null,
content: @Composable () -> Unit
) {
val haptic = LocalHapticFeedback.current
Surface(shape = CircleShape,
color = Color(0xFF2B2B2B),
contentColor = Color.White,
modifier = Modifier
.semantics {
role = Role.Button
}
.combinedClickable(
onClick = onClick,
onLongClick = {
onLongClick?.let {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
it()
}
}, onLongClickLabel = "Settings"
)
) {
Box(
contentAlignment = Alignment.Center,
) {
content()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.uravgcode.chooser.composables.screens

import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import com.uravgcode.chooser.composables.buttons.AnimatedButton
import com.uravgcode.chooser.utilities.Mode
import com.uravgcode.chooser.utilities.SettingsManager
import com.uravgcode.chooser.views.Chooser

@Composable
fun ChooserScreen(
settings: SettingsManager,
onNavigate: () -> Unit
) {
val chooserMode = remember { mutableStateOf(settings.getMode()) }
val chooserCount = remember { mutableIntStateOf(settings.getCount()) }
val isVisible = remember { mutableStateOf(true) }

AndroidView(
factory = { context ->
Chooser(context, setButtonVisibility = {
isVisible.value = it
})
},
update = { view ->
view.mode = chooserMode.value
view.count = chooserCount.intValue
},
modifier = Modifier
)

AnimatedButton(
visible = isVisible.value,
onClick = {
if (isVisible.value) {
chooserMode.value = chooserMode.value.next()
chooserCount.intValue = chooserMode.value.initialCount()
settings.setMode(chooserMode.value)
}
},
onLongClick = {
// onNavigate()
},
content = {
Icon(
painter = painterResource(id = chooserMode.value.drawable()),
contentDescription = "Mode"
)
},
alignment = Alignment.TopStart
)

AnimatedButton(
visible = chooserMode.value != Mode.ORDER && isVisible.value,
onClick = {
if (isVisible.value) {
chooserCount.intValue = chooserMode.value.nextCount(chooserCount.intValue)
settings.setCount(chooserCount.intValue)
}
},
content = {
Text(
text = chooserCount.intValue.toString(),
fontSize = 36.sp
)
},
alignment = Alignment.TopEnd
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.uravgcode.chooser.composables.screens

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import com.uravgcode.chooser.composables.Screen
import com.uravgcode.chooser.utilities.SettingsManager

@Composable
fun MainScreen(
settings: SettingsManager
) {
var currentScreen by remember { mutableStateOf<Screen>(Screen.Chooser) }

when (currentScreen) {
is Screen.Chooser -> ChooserScreen(
settings = settings,
onNavigate = { currentScreen = Screen.Settings }
)

is Screen.Settings -> SettingsScreen(
settings = settings,
onNavigateBack = { currentScreen = Screen.Chooser }
)
}
}
Loading

0 comments on commit 2942cf9

Please sign in to comment.