Skip to content

Commit

Permalink
Merge pull request #111 from kaleidot725/develop-v1.4.0
Browse files Browse the repository at this point in the history
Merge develop v1.4.0
  • Loading branch information
kaleidot725 authored Jan 3, 2025
2 parents 104c8e8 + dc8c7ac commit 1cd8918
Show file tree
Hide file tree
Showing 23 changed files with 550 additions and 127 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ kotlin {
implementation(compose.material)
implementation(compose.materialIconsExtended)
implementation(libs.adam)
implementation(libs.lucide)
implementation(libs.kotlin.coroutines)
implementation(libs.kotlinx.coroutines.swing)
implementation(libs.kotlin.serialization)
implementation(libs.koin)
implementation(compose.desktop.currentOs) { exclude(group = "org.jetbrains.compose.material") }
implementation(libs.ktor.core)
implementation(libs.ktor.client.okhttp)
implementation(libs.jSystemThemeDetectorVer)
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ junit="5.11.3"
koin="4.0.0"
system_theme_detector="3.8"
ktor = "3.0.1"
lucide = "1.0.0"

[libraries]
adam = { module = "com.malinskiy.adam:adam", version.ref = "adam" }
Expand All @@ -20,6 +21,7 @@ koin = { module = "io.insert-koin:koin-core", version.ref = "koin" }
jSystemThemeDetectorVer = { module = "com.github.Dansoftowner:jSystemThemeDetector", version.ref = "system_theme_detector" }
ktor-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
lucide = { module = "com.composables:icons-lucide", version.ref = "lucide" }

[plugins]
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
Expand Down
81 changes: 7 additions & 74 deletions src/jvmMain/kotlin/jp/kaleidot725/adbpad/Main.kt
Original file line number Diff line number Diff line change
@@ -1,64 +1,40 @@
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Colors
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowScope
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.application
import jp.kaleidot725.adbpad.MainCategory
import jp.kaleidot725.adbpad.MainState
import jp.kaleidot725.adbpad.MainStateHolder
import jp.kaleidot725.adbpad.domain.di.domainModule
import jp.kaleidot725.adbpad.domain.model.Dialog
import jp.kaleidot725.adbpad.domain.model.UserColor
import jp.kaleidot725.adbpad.domain.model.device.Device
import jp.kaleidot725.adbpad.domain.model.language.Language
import jp.kaleidot725.adbpad.domain.model.setting.WindowSize
import jp.kaleidot725.adbpad.domain.model.setting.getWindowSize
import jp.kaleidot725.adbpad.repository.di.repositoryModule
import jp.kaleidot725.adbpad.ui.common.resource.clickableBackground
import jp.kaleidot725.adbpad.ui.component.NavigationRail
import jp.kaleidot725.adbpad.ui.di.stateHolderModule
import jp.kaleidot725.adbpad.ui.screen.CommandScreen
import jp.kaleidot725.adbpad.ui.screen.ScreenLayout
import jp.kaleidot725.adbpad.ui.screen.error.AdbErrorScreen
import jp.kaleidot725.adbpad.ui.screen.menu.component.DropDownDeviceMenu
import jp.kaleidot725.adbpad.ui.screen.screenshot.ScreenshotScreen
import jp.kaleidot725.adbpad.ui.screen.setting.SettingScreen
import jp.kaleidot725.adbpad.ui.screen.setting.SettingStateHolder
import jp.kaleidot725.adbpad.ui.screen.text.TextCommandScreen
import jp.kaleidot725.adbpad.ui.section.TopSection
import org.koin.core.context.GlobalContext
import org.koin.core.context.startKoin

Expand Down Expand Up @@ -112,9 +88,12 @@ fun WindowScope.App(mainStateHolder: MainStateHolder) {
Surface {
ScreenLayout(
top = {
TitleBarView(
state = state,
onSelectDevice = mainStateHolder::selectDevice,
val topStateHolder = mainStateHolder.topStateHolder
val topState by topStateHolder.state.collectAsState()
TopSection(
state = topState,
onExecuteCommand = topStateHolder::executeCommand,
onSelectDevice = topStateHolder::selectDevice,
onRefresh = mainStateHolder::refresh,
)
},
Expand Down Expand Up @@ -247,52 +226,6 @@ fun WindowScope.App(mainStateHolder: MainStateHolder) {
}
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun TitleBarView(
state: MainState,
onSelectDevice: (Device) -> Unit,
onRefresh: () -> Unit,
) {
Surface(
color = MaterialTheme.colors.background,
modifier = Modifier.fillMaxWidth(),
) {
Box {
Row(Modifier.align(Alignment.CenterStart).wrapContentSize()) {
DropDownDeviceMenu(
devices = state.devices,
selectedDevice = state.selectedDevice,
onSelectDevice = onSelectDevice,
modifier = Modifier.wrapContentWidth(),
)
}

Row(Modifier.align(Alignment.CenterEnd).wrapContentSize().padding(4.dp)) {
var isPress: Boolean by remember { mutableStateOf(false) }
val degrees: Float by animateFloatAsState(if (isPress) -90f else 0f)
Box(
modifier =
Modifier
.size(28.dp)
.clip(RoundedCornerShape(4.dp))
.clickableBackground(isDarker = true)
.onPointerEvent(PointerEventType.Press) { isPress = true }
.onPointerEvent(PointerEventType.Release) { isPress = false }
.clickable { onRefresh() },
) {
Icon(
imageVector = Icons.Default.RestartAlt,
tint = MaterialTheme.colors.onBackground,
contentDescription = null,
modifier = Modifier.rotate(degrees).align(Alignment.Center),
)
}
}
}
}
}

private val LightColors =
Colors(
primary = UserColor.Light.PRIMARY,
Expand Down
52 changes: 4 additions & 48 deletions src/jvmMain/kotlin/jp/kaleidot725/adbpad/MainStateHolder.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package jp.kaleidot725.adbpad

import jp.kaleidot725.adbpad.domain.model.Dialog
import jp.kaleidot725.adbpad.domain.model.device.Device
import jp.kaleidot725.adbpad.domain.model.language.Language
import jp.kaleidot725.adbpad.domain.model.setting.WindowSize
import jp.kaleidot725.adbpad.domain.usecase.adb.StartAdbUseCase
import jp.kaleidot725.adbpad.domain.usecase.device.GetSelectedDeviceFlowUseCase
import jp.kaleidot725.adbpad.domain.usecase.device.SelectDeviceUseCase
import jp.kaleidot725.adbpad.domain.usecase.device.UpdateDevicesUseCase
import jp.kaleidot725.adbpad.domain.usecase.language.GetLanguageUseCase
import jp.kaleidot725.adbpad.domain.usecase.refresh.RefreshUseCase
import jp.kaleidot725.adbpad.domain.usecase.theme.GetDarkModeFlowUseCase
Expand All @@ -18,34 +14,30 @@ import jp.kaleidot725.adbpad.ui.common.ParentStateHolder
import jp.kaleidot725.adbpad.ui.screen.command.CommandStateHolder
import jp.kaleidot725.adbpad.ui.screen.screenshot.ScreenshotStateHolder
import jp.kaleidot725.adbpad.ui.screen.text.TextCommandStateHolder
import jp.kaleidot725.adbpad.ui.section.TopStateHolder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch

class MainStateHolder(
val commandStateHolder: CommandStateHolder,
val textCommandStateHolder: TextCommandStateHolder,
val screenshotStateHolder: ScreenshotStateHolder,
val topStateHolder: TopStateHolder,
private val getWindowSizeUseCase: GetWindowSizeUseCase,
private val saveWindowSizeUseCase: SaveWindowSizeUseCase,
private val startAdbUseCase: StartAdbUseCase,
private val getDarkModeFlowUseCase: GetDarkModeFlowUseCase,
private val getLanguageUseCase: GetLanguageUseCase,
private val refreshUseCase: RefreshUseCase,
private val updateDevicesUseCase: UpdateDevicesUseCase,
private val getSelectedDeviceFlowUseCase: GetSelectedDeviceFlowUseCase,
private val selectDeviceUseCase: SelectDeviceUseCase,
) : ParentStateHolder {
private val coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main + Dispatchers.IO)
private val language: MutableStateFlow<Language.Type> = MutableStateFlow(Language.Type.ENGLISH)
Expand All @@ -54,24 +46,14 @@ class MainStateHolder(
private val dialog: MutableStateFlow<Dialog?> = MutableStateFlow(null)
private val category: MutableStateFlow<MainCategory> = MutableStateFlow(MainCategory.Command)

private var deviceJob: Job? = null
private val _devices: MutableStateFlow<List<Device>> = MutableStateFlow(emptyList())
private val devices: StateFlow<List<Device>> = _devices.asStateFlow()

private var selectedDeviceJob: Job? = null
private val _selectedDevice: MutableStateFlow<Device?> = MutableStateFlow(null)
private val selectedDevice: StateFlow<Device?> = _selectedDevice.asStateFlow()

val state: StateFlow<MainState> =
combine(language, isDark, windowSize, dialog, category, devices, selectedDevice) { data ->
combine(language, isDark, windowSize, dialog, category) { data ->
MainState(
data[0] as Language.Type,
data[1] as Boolean?,
data[2] as WindowSize,
data[3] as Dialog?,
data[4] as MainCategory,
data[5] as List<Device>,
data[6] as Device?,
)
}.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), MainState())

Expand All @@ -80,14 +62,14 @@ class MainStateHolder(
commandStateHolder,
textCommandStateHolder,
screenshotStateHolder,
topStateHolder,
)

init {
startSyncDarkMode()
restoreWindowSize()
checkAdbServer()
syncLanguage()
collectDevices()
}

override fun setup() {
Expand All @@ -98,7 +80,6 @@ class MainStateHolder(
startSyncDarkMode()
checkAdbServer()
syncLanguage()
collectDevices()
refreshUseCase()
children.forEach { it.refresh() }
}
Expand All @@ -119,12 +100,6 @@ class MainStateHolder(
this.category.value = category
}

fun selectDevice(device: Device) {
coroutineScope.launch {
selectDeviceUseCase(device)
}
}

private var themeFlowJob: Job? = null

private fun startSyncDarkMode() {
Expand Down Expand Up @@ -163,23 +138,4 @@ class MainStateHolder(
language.value = type
}
}

private fun collectDevices() {
deviceJob?.cancel()
deviceJob =
coroutineScope.launch {
while (isActive) {
_devices.value = updateDevicesUseCase()
delay(1000)
}
}

selectedDeviceJob?.cancel()
selectedDeviceJob =
coroutineScope.launch {
getSelectedDeviceFlowUseCase().collect {
_selectedDevice.value = it
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import jp.kaleidot725.adbpad.domain.usecase.adb.StartAdbUseCase
import jp.kaleidot725.adbpad.domain.usecase.appearance.GetAppearanceUseCase
import jp.kaleidot725.adbpad.domain.usecase.appearance.SaveAppearanceUseCase
import jp.kaleidot725.adbpad.domain.usecase.command.ExecuteCommandUseCase
import jp.kaleidot725.adbpad.domain.usecase.command.ExecuteDeviceControlCommandUseCase
import jp.kaleidot725.adbpad.domain.usecase.command.GetNormalCommandGroup
import jp.kaleidot725.adbpad.domain.usecase.device.GetSelectedDeviceFlowUseCase
import jp.kaleidot725.adbpad.domain.usecase.device.SelectDeviceUseCase
Expand Down Expand Up @@ -40,6 +41,9 @@ val domainModule =
factory {
ExecuteCommandUseCase(get())
}
factory {
ExecuteDeviceControlCommandUseCase(get())
}
factory {
GetNormalCommandGroup(get())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package jp.kaleidot725.adbpad.domain.model.command

import com.malinskiy.adam.request.shell.v1.ShellCommandRequest

interface DeviceControlCommand {
val requests: List<ShellCommandRequest>

data object Power : DeviceControlCommand {
override val requests: List<ShellCommandRequest> =
listOf(
ShellCommandRequest("input keyevent 26"),
)
}

data object VolumeUp : DeviceControlCommand {
override val requests: List<ShellCommandRequest> =
listOf(
ShellCommandRequest("input keyevent 24"),
)
}

data object VolumeDown : DeviceControlCommand {
override val requests: List<ShellCommandRequest> =
listOf(
ShellCommandRequest("input keyevent 25"),
)
}

data object VolumeMute : DeviceControlCommand {
override val requests: List<ShellCommandRequest> =
listOf(
ShellCommandRequest("input keyevent 164"),
)
}

data object Back : DeviceControlCommand {
override val requests: List<ShellCommandRequest> =
listOf(
ShellCommandRequest("input keyevent 4"),
)
}

data object Home : DeviceControlCommand {
override val requests: List<ShellCommandRequest> =
listOf(
ShellCommandRequest("input keyevent 3"),
)
}

data object Recents : DeviceControlCommand {
override val requests: List<ShellCommandRequest> =
listOf(
ShellCommandRequest("input keyevent 187"),
)
}
}
Loading

0 comments on commit 1cd8918

Please sign in to comment.