Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve score widget #106

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fastadapter-extensions-binding = { group = "com.mikepenz", name = "fastadapter-e
fastadapter-extensions-diff = { group = "com.mikepenz", name = "fastadapter-extensions-diff", version.ref = "fastadapter" }
arklib = { group = "dev.arkbuilders", name = "arklib", version.ref = "arkLib" }
orbit-mvi-viewmodel = { group = "org.orbit-mvi", name = "orbit-viewmodel", version.ref = "orbitMvi" }
orbit-mvi-compose = { group = "org.orbit-mvi", name = "orbit-compose", version.ref = "orbitMvi" }
viewbinding-property-delegate = { group = "com.github.kirich1409", name = "viewbindingpropertydelegate-noreflection", version.ref = "viewbindingPropertyDelegate" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidXCore" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidXAppcompat" }
Expand All @@ -47,3 +48,4 @@ androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graph
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
9 changes: 9 additions & 0 deletions sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ android {
buildFeatures {
buildConfig = true
viewBinding = true
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
}

splits {
Expand All @@ -72,11 +76,16 @@ android {
dependencies {
implementation(project(":filepicker"))
implementation(project(":about"))
implementation(project(":scorewidget"))

implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.material3)
implementation(libraries.arklib)
implementation("androidx.core:core-ktx:1.12.0")
implementation(libraries.androidx.appcompat)
implementation(libraries.android.material)
implementation(libraries.orbit.mvi.viewmodel)
testImplementation(libraries.junit)
androidTestImplementation(libraries.androidx.test.junit)
androidTestImplementation(libraries.androidx.test.espresso)
Expand Down
1 change: 1 addition & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
</intent-filter>
</activity>
<activity android:name="dev.arkbuilders.sample.about.AboutActivity" />
<activity android:name="dev.arkbuilders.sample.ScoreActivity" />

</application>

Expand Down
6 changes: 6 additions & 0 deletions sample/src/main/java/dev/arkbuilders/sample/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ class MainActivity : AppCompatActivity() {
val intent = Intent(this, AboutActivity::class.java)
startActivity(intent)
}

findViewById<MaterialButton>(R.id.btn_score).setOnClickListener {
resolvePermissions()
val intent = Intent(this, ScoreActivity::class.java)
startActivity(intent)
}
}

private fun getFilePickerConfig(mode: ArkFilePickerMode? = null) = ArkFilePickerConfig(
Expand Down
119 changes: 119 additions & 0 deletions sample/src/main/java/dev/arkbuilders/sample/ScoreActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package dev.arkbuilders.sample

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
import com.google.android.material.button.MaterialButton
import dev.arkbuilders.arklib.data.folders.FoldersRepo
import dev.arkbuilders.arklib.data.index.ResourceIndex
import dev.arkbuilders.arklib.data.index.ResourceIndexRepo
import dev.arkbuilders.arklib.user.score.ScoreStorage
import dev.arkbuilders.arklib.user.score.ScoreStorageRepo
import dev.arkbuilders.components.filepicker.ArkFilePickerConfig
import dev.arkbuilders.components.filepicker.ArkFilePickerFragment
import dev.arkbuilders.components.filepicker.ArkFilePickerMode
import dev.arkbuilders.components.filepicker.onArkPathPicked
import dev.arkbuilders.components.scorewidget.HorizontalScoreWidgetComposable
import dev.arkbuilders.components.scorewidget.ScoreWidgetController
import dev.arkbuilders.components.scorewidget.VerticalScoreWidgetComposable
import kotlinx.coroutines.launch
import java.nio.file.Path
import kotlin.io.path.name

class ScoreActivity : AppCompatActivity() {
private var rootFolder: Path? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_score)

val btnPickRoot = findViewById<MaterialButton>(R.id.btn_pick_root)
val btnPickResource = findViewById<MaterialButton>(R.id.btn_pick_resource)

supportFragmentManager.onArkPathPicked(
this,
customRequestKey = PICK_ROOT_KEY
) { root ->
rootFolder = root
btnPickRoot.text = root.name
}

supportFragmentManager.onArkPathPicked(
this,
customRequestKey = PICK_RESOURCE_KEY
) { resourcePath ->
btnPickResource.text = resourcePath.name
rootFolder?.let {
onResourcePicked(root = it, resourcePath)
}
}

btnPickRoot.setOnClickListener {
ArkFilePickerFragment
.newInstance(ArkFilePickerConfig(pathPickedRequestKey = PICK_ROOT_KEY))
.show(supportFragmentManager, null)
}

btnPickResource.setOnClickListener {
ArkFilePickerFragment
.newInstance(
ArkFilePickerConfig(
pathPickedRequestKey = PICK_RESOURCE_KEY,
mode = ArkFilePickerMode.FILE
)
).show(supportFragmentManager, null)
}
}

private fun onResourcePicked(
root: Path,
resourcePath: Path
) = lifecycleScope.launch {
val (index, scoreStorage) = setupIndexAndScoreStorage(root)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setupIndexAndScoreStorage is a suspend function so it should be scheduled in background thread. It's now running in main.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was sure that initialization of Index and ScoreStorage occurs on Dispatcher.IO, but ScoreStorage does not change dispatcher :/

val id = index.allPaths().toList()
.find { it.second == resourcePath }!!.first
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result of find could be null, and app will crash in that case because of !! assertion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. This can happen if file is selected from outside root

val scoreWidgetController = ScoreWidgetController(
lifecycleScope,
getCurrentId = { id },
onScoreChanged = {}
)
scoreWidgetController.init(scoreStorage)

findViewById<ComposeView>(R.id.score_widget_horizontal).setContent {
HorizontalScoreWidgetComposable(
size = DpSize(200.dp, 80.dp),
controller = scoreWidgetController
)
}

findViewById<ComposeView>(R.id.score_widget_vertical).setContent {
VerticalScoreWidgetComposable(
modifier = Modifier.padding(40.dp),
size = DpSize(50.dp, 120.dp),
controller = scoreWidgetController
)
}

scoreWidgetController.setVisible(true)
scoreWidgetController.displayScore()
}

private suspend fun setupIndexAndScoreStorage(
root: Path
): Pair<ResourceIndex, ScoreStorage> {
val foldersRepo = FoldersRepo(applicationContext)
val index = ResourceIndexRepo(foldersRepo).provide(root)
val scoreStorage = ScoreStorageRepo(lifecycleScope).provide(index)
return index to scoreStorage
}

companion object {
private val PICK_ROOT_KEY = "pickRootKey"
private val PICK_RESOURCE_KEY = "pickResourceKey"
}
}
14 changes: 11 additions & 3 deletions sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_open"
Expand Down Expand Up @@ -50,4 +52,10 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_storage_demo"/>

</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_score"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Open score" />

</LinearLayout>
46 changes: 46 additions & 0 deletions sample/src/main/res/layout/activity_score.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_pick_root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="Pick root"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_pick_resource"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="Pick resource"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_pick_root" />

<androidx.compose.ui.platform.ComposeView
android:id="@+id/score_widget_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:padding="50dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_pick_resource" />

<androidx.compose.ui.platform.ComposeView
android:id="@+id/score_widget_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/score_widget_horizontal" />


</androidx.constraintlayout.widget.ConstraintLayout>
21 changes: 16 additions & 5 deletions scorewidget/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,30 @@ android {
kotlinOptions {
jvmTarget = "17"
}

buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
}
buildFeatures {
viewBinding = true
}
}

dependencies {

implementation(libraries.androidx.core.ktx)
implementation(libraries.androidx.appcompat)
implementation(libraries.android.material)
implementation(libraries.androidx.compose.activity)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.icons.extended)
implementation(libraries.arklib)
implementation(libraries.orbit.mvi.viewmodel)
implementation(libs.orbit.mvi.compose)
implementation("com.github.mdrlzy:ComposeCounterSlider:0.1.4")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this dependency be added in predefined version management scheme that we already have?

testImplementation(libraries.junit)
androidTestImplementation(libraries.androidx.test.junit)
androidTestImplementation(libraries.androidx.test.espresso)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package dev.arkbuilders.components.scorewidget

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.mdrlzy.counterslider.HorizontalCounterSlider
import com.mdrlzy.counterslider.VerticalCounterSlider
import org.orbitmvi.orbit.compose.collectAsState


@Composable
fun HorizontalScoreWidgetComposable(
modifier: Modifier = Modifier,
size: DpSize = DpSize(200.dp, 80.dp),
allowTopToReset: Boolean = true,
allowBottomToReset: Boolean = true,
controller: ScoreWidgetController,
) {
val state by controller.collectAsState()
if (state.visible.not())
return
HorizontalCounterSlider(
modifier = modifier,
size = size,
value = state.score.toString(),
allowTopToReset = allowTopToReset,
allowBottomToReset = allowBottomToReset,
onValueIncreaseClick = { controller.onIncrease() },
onValueDecreaseClick = { controller.onDecrease() },
onValueClearClick = { controller.onReset() }
)
}

@Composable
fun VerticalScoreWidgetComposable(
modifier: Modifier = Modifier,
size: DpSize = DpSize(80.dp, 200.dp),
allowLeftToReset: Boolean = true,
allowRightToReset: Boolean = true,
controller: ScoreWidgetController,
) {
val state by controller.collectAsState()
if (state.visible.not())
return
VerticalCounterSlider(
modifier = modifier,
size = size,
value = state.score.toString(),
allowLeftToReset = allowLeftToReset,
allowRightToReset = allowRightToReset,
onValueIncreaseClick = { controller.onIncrease() },
onValueDecreaseClick = { controller.onDecrease() },
onValueClearClick = { controller.onReset() }
)
}
Loading
Loading