Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementScreen
import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementViewModel
import io.github.sds100.keymapper.base.constraints.ChooseConstraintScreen
import io.github.sds100.keymapper.base.constraints.ChooseConstraintViewModel
import io.github.sds100.keymapper.base.debug.GetEventScreen
import io.github.sds100.keymapper.base.expertmode.ExpertModeScreen
import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupScreen
import io.github.sds100.keymapper.base.logging.LogScreen
Expand Down Expand Up @@ -165,6 +166,14 @@ fun BaseMainNavHost(
)
}

composable<NavDestination.GetEvent> {
GetEventScreen(
modifier = Modifier.fillMaxSize(),
viewModel = hiltViewModel(),
onBackClick = { navController.popBackStack() },
)
}

composable<NavDestination.ChooseSetting> {
ChooseSettingScreen(
modifier = Modifier.fillMaxSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import io.github.sds100.keymapper.base.constraints.ConfigConstraintsUseCaseImpl
import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCase
import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCaseImpl
import io.github.sds100.keymapper.base.constraints.DisplayConstraintUseCase
import io.github.sds100.keymapper.base.debug.GetEventOutputUseCase
import io.github.sds100.keymapper.base.debug.GetEventOutputUseCaseImpl
import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupDelegateImpl
import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupDelegate
import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupUseCase
Expand Down Expand Up @@ -185,6 +187,10 @@ abstract class BaseViewModelHiltModule {
@ViewModelScoped
abstract fun bindShareLogcatUseCase(impl: ShareLogcatUseCaseImpl): ShareLogcatUseCase

@Binds
@ViewModelScoped
abstract fun bindGetEventOutputUseCase(impl: GetEventOutputUseCaseImpl): GetEventOutputUseCase

@Binds
@ViewModelScoped
abstract fun bindOnboardingTipDelegate(impl: OnboardingTipDelegateImpl): OnboardingTipDelegate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package io.github.sds100.keymapper.base.debug

import android.content.Context
import androidx.core.net.toUri
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.sds100.keymapper.base.actions.ExecuteShellCommandUseCase
import io.github.sds100.keymapper.base.utils.ShareUtils
import io.github.sds100.keymapper.base.utils.getFullMessage
import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
import io.github.sds100.keymapper.common.BuildConfigProvider
import io.github.sds100.keymapper.common.models.ShellExecutionMode
import io.github.sds100.keymapper.common.utils.handle
import io.github.sds100.keymapper.data.Keys
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
import io.github.sds100.keymapper.system.clipboard.ClipboardAdapter
import io.github.sds100.keymapper.system.files.FileAdapter
import io.github.sds100.keymapper.system.files.FileUtils
import io.github.sds100.keymapper.system.files.IFile
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

interface GetEventOutputUseCase {
val deviceInfoOutput: Flow<String>
val eventsOutput: Flow<String>

suspend fun refreshDeviceInfo()
suspend fun recordEvents()
suspend fun stopRecording()
fun copyOutput(output: String)
suspend fun shareOutput(output: String)
}

class GetEventOutputUseCaseImpl @Inject constructor(
@ApplicationContext private val context: Context,
private val executeShellCommandUseCase: ExecuteShellCommandUseCase,
private val preferenceRepository: PreferenceRepository,
private val clipboardAdapter: ClipboardAdapter,
private val fileAdapter: FileAdapter,
private val buildConfigProvider: BuildConfigProvider,
private val resourceProvider: ResourceProvider,
) : GetEventOutputUseCase {

companion object {
private const val MAX_COPY_OUTPUT_LENGTH = 150_000
}

override val deviceInfoOutput: Flow<String> = preferenceRepository
.get(Keys.getEventDeviceInfoOutput)
.map { it.orEmpty() }

override val eventsOutput: Flow<String> = preferenceRepository
.get(Keys.getEventEventsOutput)
.map { it.orEmpty() }

override suspend fun refreshDeviceInfo() {
val output = executeShellCommandUseCase.execute(
command = "getevent -il",
executionMode = ShellExecutionMode.ADB,
timeoutMillis = 30_000L,
).handle(
onSuccess = { it.stdout },
onError = { "Error: ${it.getFullMessage(resourceProvider)}" },
)
preferenceRepository.set(Keys.getEventDeviceInfoOutput, output)
}

override suspend fun recordEvents() {
val output = executeShellCommandUseCase.execute(
command = "getevent -lt",
executionMode = ShellExecutionMode.ADB,
timeoutMillis = 300_000L,
).handle(
onSuccess = { it.stdout },
onError = { "" },
)
if (output.isNotEmpty()) {
preferenceRepository.set(Keys.getEventEventsOutput, output)
}
}

override suspend fun stopRecording() {
executeShellCommandUseCase.execute(
command = "pkill -x getevent || true",
executionMode = ShellExecutionMode.ADB,
timeoutMillis = 5_000L,
)
}

override fun copyOutput(output: String) {
clipboardAdapter.copy(
"getevent output",
output.takeLast(MAX_COPY_OUTPUT_LENGTH),
)
}

override suspend fun shareOutput(output: String) {
withContext(Dispatchers.IO) {
val fileName = "getevent/key_mapper_getevent_${FileUtils.createFileDate()}.txt"
val file: IFile = fileAdapter.getPrivateFile(fileName)
file.createFile()
file.outputStream()?.bufferedWriter()?.use { it.write(output) }

val publicUri = fileAdapter.getPublicUriForPrivateFile(file).toUri()
ShareUtils.shareFile(context, publicUri, buildConfigProvider.packageName)
}
}
}
Loading
Loading