mobile base

This commit is contained in:
2026-02-11 15:58:30 +01:00
parent e03329cedb
commit e254554532
14 changed files with 1007 additions and 2 deletions

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@@ -0,0 +1,193 @@
kotlin version: 2.0.21
error message: org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering
File being compiled: /Volumes/SED/Code/OpenTimeTracker/AndroidApp/app/src/main/java/com/opentimetracker/MainActivity.kt
The root cause java.lang.RuntimeException was thrown at: org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException(CodegenUtil.kt:253)
at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException$default(CodegenUtil.kt:236)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:65)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:52)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:38)
at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.phaseBody(CompilerPhase.kt:166)
at org.jetbrains.kotlin.backend.common.phaser.AbstractNamedCompilerPhase.invoke(CompilerPhase.kt:113)
at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:27)
at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:14)
at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.phaseBody(CompilerPhase.kt:166)
at org.jetbrains.kotlin.backend.common.phaser.AbstractNamedCompilerPhase.invoke(CompilerPhase.kt:113)
at org.jetbrains.kotlin.backend.common.phaser.CompilerPhaseKt.invokeToplevel(CompilerPhase.kt:62)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.invokeCodegen(JvmIrCodegenFactory.kt:371)
at org.jetbrains.kotlin.codegen.CodegenFactory.generateModule(CodegenFactory.kt:47)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.generateModuleInFrontendIRMode(JvmIrCodegenFactory.kt:433)
at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.generateCodeFromIr(jvmCompilerPipeline.kt:246)
at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:142)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.RuntimeException: Exception while generating code for:
FUN name:LoginScreen visibility:public modality:FINAL <> (viewModel:com.opentimetracker.ui.AuthViewModel, onLoginSuccess:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit
annotations:
Composable
VALUE_PARAMETER name:viewModel index:0 type:com.opentimetracker.ui.AuthViewModel
VALUE_PARAMETER name:onLoginSuccess index:1 type:kotlin.Function0<kotlin.Unit>
BLOCK_BODY
WHEN type=kotlin.Unit origin=IF
BRANCH
if: CALL 'public abstract fun <get-value> (): T of androidx.compose.runtime.MutableState declared in androidx.compose.runtime.MutableState' type=kotlin.Boolean origin=GET_PROPERTY
$this: CALL 'public final fun <get-isLoggedIn> (): androidx.compose.runtime.MutableState<kotlin.Boolean> declared in com.opentimetracker.ui.AuthViewModel' type=androidx.compose.runtime.MutableState<kotlin.Boolean> origin=GET_PROPERTY
$this: GET_VAR 'viewModel: com.opentimetracker.ui.AuthViewModel declared in com.opentimetracker.MainActivityKt.LoginScreen' type=com.opentimetracker.ui.AuthViewModel origin=null
then: BLOCK type=kotlin.Unit origin=null
CALL 'public final fun LaunchedEffect (key1: kotlin.Any?, block: @[ExtensionFunctionType] kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope, kotlin.Unit>): kotlin.Unit declared in androidx.compose.runtime.EffectsKt' type=kotlin.Unit origin=null
key1: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:INSTANCE type:kotlin.Unit visibility:public [final,static] declared in kotlin.Unit' type=kotlin.Unit origin=null
block: BLOCK type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
CLASS SUSPEND_LAMBDA CLASS name:<no name provided> modality:FINAL visibility:public/*package*/ superTypes:[kotlin.coroutines.jvm.internal.SuspendLambda; kotlin.jvm.functions.Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<kotlin.Unit>?, kotlin.Any?>]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
FIELD name:label type:kotlin.Int visibility:public/*package*/
CONSTRUCTOR SUSPEND_LAMBDA visibility:public/*package*/ <> ($onLoginSuccess:kotlin.Function0<kotlin.Unit>, $completion:kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?) returnType:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> [primary]
VALUE_PARAMETER BOUND_VALUE_PARAMETER name:$onLoginSuccess index:0 type:kotlin.Function0<kotlin.Unit>
VALUE_PARAMETER name:$completion index:1 type:kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?
BLOCK_BODY
SET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Unit origin=INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE
receiver: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
value: GET_VAR '$onLoginSuccess: kotlin.Function0<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.<init>' type=kotlin.Function0<kotlin.Unit> origin=null
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> (arity: kotlin.Int, $completion: kotlin.coroutines.Continuation<kotlin.Any?>?) declared in kotlin.coroutines.jvm.internal.SuspendLambda'
arity: CONST Int type=kotlin.Int value=2
$completion: GET_VAR '$completion: kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.<init>' type=kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>? origin=null
BLOCK type=kotlin.Unit origin=null
FUN name:invokeSuspend visibility:public modality:FINAL <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, $result:kotlin.Result<kotlin.Any?>) returnType:kotlin.Any?
overridden:
protected abstract fun invokeSuspend ($result: kotlin.Result<kotlin.Any?>): kotlin.Any? declared in kotlin.coroutines.jvm.internal.SuspendLambda
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER name:$result index:0 type:kotlin.Result<kotlin.Any?>
BLOCK_BODY
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE
$this: GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Function0<kotlin.Unit> origin=null
receiver: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invokeSuspend' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
FUN name:create visibility:public modality:FINAL <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, value:kotlin.Any?, $completion:kotlin.coroutines.Continuation<kotlin.Nothing>) returnType:kotlin.coroutines.Continuation<kotlin.Unit>
overridden:
public open fun create (value: kotlin.Any?, $completion: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.coroutines.Continuation<kotlin.Unit> declared in kotlin.coroutines.jvm.internal.SuspendLambda
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER name:value index:0 type:kotlin.Any?
VALUE_PARAMETER name:$completion index:1 type:kotlin.coroutines.Continuation<kotlin.Nothing>
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun create (value: kotlin.Any?, $completion: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.coroutines.Continuation<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>'
CONSTRUCTOR_CALL 'public/*package*/ constructor <init> ($onLoginSuccess: kotlin.Function0<kotlin.Unit>, $completion: kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?) [primary] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
$onLoginSuccess: GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Function0<kotlin.Unit> origin=null
receiver: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.create' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
$completion: GET_VAR '$completion: kotlin.coroutines.Continuation<kotlin.Nothing> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.create' type=kotlin.coroutines.Continuation<kotlin.Nothing> origin=null
FUN name:invoke visibility:public modality:FINAL <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, p1:kotlinx.coroutines.CoroutineScope, p2:kotlin.coroutines.Continuation<kotlin.Unit>?) returnType:kotlin.Any?
overridden:
public abstract fun invoke (p1: P1 of kotlin.jvm.functions.Function2, p2: P2 of kotlin.jvm.functions.Function2): R of kotlin.jvm.functions.Function2 declared in kotlin.jvm.functions.Function2
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER name:p1 index:0 type:kotlinx.coroutines.CoroutineScope
VALUE_PARAMETER name:p2 index:1 type:kotlin.coroutines.Continuation<kotlin.Unit>?
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun invoke (p1: kotlinx.coroutines.CoroutineScope, p2: kotlin.coroutines.Continuation<kotlin.Unit>?): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>'
CALL 'public final fun invokeSuspend ($result: kotlin.Result<kotlin.Any?>): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Any? origin=null
$this: TYPE_OP type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=IMPLICIT_CAST typeOperand=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
CALL 'public final fun create (value: kotlin.Any?, $completion: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.coroutines.Continuation<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.coroutines.Continuation<kotlin.Unit> origin=null
$this: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
value: GET_VAR 'p1: kotlinx.coroutines.CoroutineScope declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlinx.coroutines.CoroutineScope origin=null
$completion: GET_VAR 'p2: kotlin.coroutines.Continuation<kotlin.Unit>? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlin.coroutines.Continuation<kotlin.Unit>? origin=null
$result: CALL 'public final fun <unsafe-coerce> <T, R> (v: T of kotlin.jvm.internal.<unsafe-coerce>): R of kotlin.jvm.internal.<unsafe-coerce> declared in kotlin.jvm.internal' type=kotlin.Result<kotlin.Any?> origin=null
<T>: kotlin.Any?
<R>: kotlin.Result<kotlin.Any?>
v: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:INSTANCE type:kotlin.Unit visibility:public [final,static] declared in kotlin.Unit' type=kotlin.Unit origin=null
FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final]
FUN BRIDGE name:invoke visibility:public modality:OPEN <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, p1:kotlin.Any?, p2:kotlin.Any?) returnType:kotlin.Any?
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER BRIDGE name:p1 index:0 type:kotlin.Any?
VALUE_PARAMETER BRIDGE name:p2 index:1 type:kotlin.Any?
EXPRESSION_BODY
RETURN type=kotlin.Nothing from='public open fun invoke (p1: kotlin.Any?, p2: kotlin.Any?): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>'
CALL 'public final fun invoke (p1: kotlinx.coroutines.CoroutineScope, p2: kotlin.coroutines.Continuation<kotlin.Unit>?): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Any? origin=BRIDGE_DELEGATION
$this: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
p1: TYPE_OP type=kotlinx.coroutines.CoroutineScope origin=IMPLICIT_CAST typeOperand=kotlinx.coroutines.CoroutineScope
GET_VAR 'p1: kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlin.Any? origin=null
p2: TYPE_OP type=kotlin.coroutines.Continuation<*> origin=IMPLICIT_CAST typeOperand=kotlin.coroutines.Continuation<*>
GET_VAR 'p2: kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlin.Any? origin=null
CONSTRUCTOR_CALL 'public/*package*/ constructor <init> ($onLoginSuccess: kotlin.Function0<kotlin.Unit>, $completion: kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?) [primary] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
$onLoginSuccess: GET_VAR 'onLoginSuccess: kotlin.Function0<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen' type=kotlin.Function0<kotlin.Unit> origin=null
$completion: CONST Null type=kotlin.Nothing? value=null
CALL 'public final fun Column (modifier: androidx.compose.ui.Modifier, verticalArrangement: androidx.compose.foundation.layout.Arrangement.Vertical, horizontalAlignment: androidx.compose.ui.Alignment.Horizontal, content: @[Composable] @[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit>): kotlin.Unit [inline] declared in androidx.compose.foundation.layout.ColumnKt' type=kotlin.Unit origin=null
modifier: CALL 'public final fun padding-3ABfNKs (all: androidx.compose.ui.unit.Dp): androidx.compose.ui.Modifier declared in androidx.compose.foundation.layout.PaddingKt' type=androidx.compose.ui.Modifier origin=null
$receiver: CALL 'public final fun fillMaxSize$default (fraction: kotlin.Float, $mask0: kotlin.Int, $handler: kotlin.Any?): androidx.compose.ui.Modifier declared in androidx.compose.foundation.layout.SizeKt' type=androidx.compose.ui.Modifier origin=DEFAULT_DISPATCH_CALL
$receiver: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:Companion type:androidx.compose.ui.Modifier.Companion visibility:public [final,static] declared in androidx.compose.ui.Modifier' type=androidx.compose.ui.Modifier.Companion origin=null
fraction: COMPOSITE type=kotlin.Float origin=DEFAULT_VALUE
CONST Float type=kotlin.Float value=0.0
$mask0: CONST Int type=kotlin.Int value=1
$handler: CONST Null type=kotlin.Any? value=null
all: CALL 'public final fun <get-dp> (): androidx.compose.ui.unit.Dp [inline] declared in androidx.compose.ui.unit.DpKt' type=androidx.compose.ui.unit.Dp origin=GET_PROPERTY
$receiver: CONST Int type=kotlin.Int value=16
verticalArrangement: CALL 'public final fun <get-Center> (): androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical declared in androidx.compose.foundation.layout.Arrangement' type=androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical origin=GET_PROPERTY
$this: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:INSTANCE type:androidx.compose.foundation.layout.Arrangement visibility:public [final,static] declared in androidx.compose.foundation.layout.Arrangement' type=androidx.compose.foundation.layout.Arrangement origin=null
horizontalAlignment: CALL 'public final fun <get-CenterHorizontally> (): androidx.compose.ui.Alignment.Horizontal declared in androidx.compose.ui.Alignment.Companion' type=androidx.compose.ui.Alignment.Horizontal origin=GET_PROPERTY
$this: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:Companion type:androidx.compose.ui.Alignment.Companion visibility:public [final,static] declared in androidx.compose.ui.Alignment' type=androidx.compose.ui.Alignment.Companion origin=null
content: BLOCK type=@[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit> origin=LAMBDA
COMPOSITE type=kotlin.Unit origin=null
FUNCTION_REFERENCE 'private final fun LoginScreen$lambda$14 ($viewModel: com.opentimetracker.ui.AuthViewModel): kotlin.Unit declared in com.opentimetracker.MainActivityKt' type=@[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit> origin=INLINE_LAMBDA reflectionTarget=null
$viewModel: GET_VAR 'viewModel: com.opentimetracker.ui.AuthViewModel declared in com.opentimetracker.MainActivityKt.LoginScreen' type=com.opentimetracker.ui.AuthViewModel origin=null
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate$default(FunctionCodegen.kt:40)
at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethodNode(ClassCodegen.kt:406)
at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethod(ClassCodegen.kt:423)
at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:168)
at org.jetbrains.kotlin.backend.jvm.FileCodegen.lower(JvmPhases.kt:39)
at org.jetbrains.kotlin.backend.common.phaser.PhaseFactoriesKt.createFilePhase$lambda$4(PhaseFactories.kt:71)
at org.jetbrains.kotlin.backend.common.phaser.PhaseBuildersKt$createSimpleNamedCompilerPhase$1.phaseBody(PhaseBuilders.kt:69)
at org.jetbrains.kotlin.backend.common.phaser.SimpleNamedCompilerPhase.phaseBody(CompilerPhase.kt:226)
at org.jetbrains.kotlin.backend.common.phaser.AbstractNamedCompilerPhase.invoke(CompilerPhase.kt:113)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:62)
... 43 more
Caused by: org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Couldn't inline method call: CALL 'public final fun Column (modifier: androidx.compose.ui.Modifier, verticalArrangement: androidx.compose.foundation.layout.Arrangement.Vertical, horizontalAlignment: androidx.compose.ui.Alignment.Horizontal, content: @[Composable] @[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit>): kotlin.Unit [inline] declared in androidx.compose.foundation.layout.ColumnKt' type=kotlin.Unit origin=null
Method: null
File is unknown
The root cause java.lang.IllegalStateException was thrown at: org.jetbrains.kotlin.codegen.inline.SourceCompilerForInlineKt.getMethodNode(SourceCompilerForInline.kt:118)
at org.jetbrains.kotlin.codegen.inline.InlineCodegen.performInline(InlineCodegen.kt:65)
at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCodegen.genInlineCall(IrInlineCodegen.kt:163)
at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCallGenerator.genCall(IrInlineCallGenerator.kt:36)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:653)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:138)
at org.jetbrains.kotlin.ir.expressions.IrCall.accept(IrCall.kt:24)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitStatementContainer(ExpressionCodegen.kt:579)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:584)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:138)
at org.jetbrains.kotlin.ir.expressions.IrBlockBody.accept(IrBlockBody.kt:20)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.generate(ExpressionCodegen.kt:240)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.doGenerate(FunctionCodegen.kt:123)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:44)
... 53 more
Caused by: java.lang.IllegalStateException: couldn't find inline method Landroidx/compose/foundation/layout/ColumnKt;.Column(Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/ui/Alignment$Horizontal;Lkotlin/jvm/functions/Function1;)V
at org.jetbrains.kotlin.codegen.inline.SourceCompilerForInlineKt.getMethodNode(SourceCompilerForInline.kt:118)
at org.jetbrains.kotlin.codegen.inline.SourceCompilerForInlineKt.loadCompiledInlineFunction(SourceCompilerForInline.kt:96)
at org.jetbrains.kotlin.backend.jvm.codegen.IrSourceCompilerForInline.compileInlineFunction(IrSourceCompilerForInline.kt:91)
at org.jetbrains.kotlin.codegen.inline.InlineCodegen.compileInline(InlineCodegen.kt:43)
at org.jetbrains.kotlin.codegen.inline.InlineCodegen.performInline(InlineCodegen.kt:51)
... 65 more

View File

@@ -0,0 +1,193 @@
kotlin version: 2.0.21
error message: org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering
File being compiled: /Volumes/SED/Code/OpenTimeTracker/AndroidApp/app/src/main/java/com/opentimetracker/MainActivity.kt
The root cause java.lang.RuntimeException was thrown at: org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException(CodegenUtil.kt:253)
at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException$default(CodegenUtil.kt:236)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:65)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:52)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:38)
at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.phaseBody(CompilerPhase.kt:166)
at org.jetbrains.kotlin.backend.common.phaser.AbstractNamedCompilerPhase.invoke(CompilerPhase.kt:113)
at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:27)
at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:14)
at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.phaseBody(CompilerPhase.kt:166)
at org.jetbrains.kotlin.backend.common.phaser.AbstractNamedCompilerPhase.invoke(CompilerPhase.kt:113)
at org.jetbrains.kotlin.backend.common.phaser.CompilerPhaseKt.invokeToplevel(CompilerPhase.kt:62)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.invokeCodegen(JvmIrCodegenFactory.kt:371)
at org.jetbrains.kotlin.codegen.CodegenFactory.generateModule(CodegenFactory.kt:47)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.generateModuleInFrontendIRMode(JvmIrCodegenFactory.kt:433)
at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.generateCodeFromIr(jvmCompilerPipeline.kt:246)
at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:142)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.RuntimeException: Exception while generating code for:
FUN name:LoginScreen visibility:public modality:FINAL <> (viewModel:com.opentimetracker.ui.AuthViewModel, onLoginSuccess:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit
annotations:
Composable
VALUE_PARAMETER name:viewModel index:0 type:com.opentimetracker.ui.AuthViewModel
VALUE_PARAMETER name:onLoginSuccess index:1 type:kotlin.Function0<kotlin.Unit>
BLOCK_BODY
WHEN type=kotlin.Unit origin=IF
BRANCH
if: CALL 'public abstract fun <get-value> (): T of androidx.compose.runtime.MutableState declared in androidx.compose.runtime.MutableState' type=kotlin.Boolean origin=GET_PROPERTY
$this: CALL 'public final fun <get-isLoggedIn> (): androidx.compose.runtime.MutableState<kotlin.Boolean> declared in com.opentimetracker.ui.AuthViewModel' type=androidx.compose.runtime.MutableState<kotlin.Boolean> origin=GET_PROPERTY
$this: GET_VAR 'viewModel: com.opentimetracker.ui.AuthViewModel declared in com.opentimetracker.MainActivityKt.LoginScreen' type=com.opentimetracker.ui.AuthViewModel origin=null
then: BLOCK type=kotlin.Unit origin=null
CALL 'public final fun LaunchedEffect (key1: kotlin.Any?, block: @[ExtensionFunctionType] kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope, kotlin.Unit>): kotlin.Unit declared in androidx.compose.runtime.EffectsKt' type=kotlin.Unit origin=null
key1: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:INSTANCE type:kotlin.Unit visibility:public [final,static] declared in kotlin.Unit' type=kotlin.Unit origin=null
block: BLOCK type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
CLASS SUSPEND_LAMBDA CLASS name:<no name provided> modality:FINAL visibility:public/*package*/ superTypes:[kotlin.coroutines.jvm.internal.SuspendLambda; kotlin.jvm.functions.Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<kotlin.Unit>?, kotlin.Any?>]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
FIELD name:label type:kotlin.Int visibility:public/*package*/
CONSTRUCTOR SUSPEND_LAMBDA visibility:public/*package*/ <> ($onLoginSuccess:kotlin.Function0<kotlin.Unit>, $completion:kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?) returnType:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> [primary]
VALUE_PARAMETER BOUND_VALUE_PARAMETER name:$onLoginSuccess index:0 type:kotlin.Function0<kotlin.Unit>
VALUE_PARAMETER name:$completion index:1 type:kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?
BLOCK_BODY
SET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Unit origin=INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE
receiver: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
value: GET_VAR '$onLoginSuccess: kotlin.Function0<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.<init>' type=kotlin.Function0<kotlin.Unit> origin=null
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> (arity: kotlin.Int, $completion: kotlin.coroutines.Continuation<kotlin.Any?>?) declared in kotlin.coroutines.jvm.internal.SuspendLambda'
arity: CONST Int type=kotlin.Int value=2
$completion: GET_VAR '$completion: kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.<init>' type=kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>? origin=null
BLOCK type=kotlin.Unit origin=null
FUN name:invokeSuspend visibility:public modality:FINAL <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, $result:kotlin.Result<kotlin.Any?>) returnType:kotlin.Any?
overridden:
protected abstract fun invokeSuspend ($result: kotlin.Result<kotlin.Any?>): kotlin.Any? declared in kotlin.coroutines.jvm.internal.SuspendLambda
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER name:$result index:0 type:kotlin.Result<kotlin.Any?>
BLOCK_BODY
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE
$this: GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Function0<kotlin.Unit> origin=null
receiver: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invokeSuspend' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
FUN name:create visibility:public modality:FINAL <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, value:kotlin.Any?, $completion:kotlin.coroutines.Continuation<kotlin.Nothing>) returnType:kotlin.coroutines.Continuation<kotlin.Unit>
overridden:
public open fun create (value: kotlin.Any?, $completion: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.coroutines.Continuation<kotlin.Unit> declared in kotlin.coroutines.jvm.internal.SuspendLambda
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER name:value index:0 type:kotlin.Any?
VALUE_PARAMETER name:$completion index:1 type:kotlin.coroutines.Continuation<kotlin.Nothing>
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun create (value: kotlin.Any?, $completion: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.coroutines.Continuation<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>'
CONSTRUCTOR_CALL 'public/*package*/ constructor <init> ($onLoginSuccess: kotlin.Function0<kotlin.Unit>, $completion: kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?) [primary] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
$onLoginSuccess: GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Function0<kotlin.Unit> origin=null
receiver: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.create' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
$completion: GET_VAR '$completion: kotlin.coroutines.Continuation<kotlin.Nothing> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.create' type=kotlin.coroutines.Continuation<kotlin.Nothing> origin=null
FUN name:invoke visibility:public modality:FINAL <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, p1:kotlinx.coroutines.CoroutineScope, p2:kotlin.coroutines.Continuation<kotlin.Unit>?) returnType:kotlin.Any?
overridden:
public abstract fun invoke (p1: P1 of kotlin.jvm.functions.Function2, p2: P2 of kotlin.jvm.functions.Function2): R of kotlin.jvm.functions.Function2 declared in kotlin.jvm.functions.Function2
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER name:p1 index:0 type:kotlinx.coroutines.CoroutineScope
VALUE_PARAMETER name:p2 index:1 type:kotlin.coroutines.Continuation<kotlin.Unit>?
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun invoke (p1: kotlinx.coroutines.CoroutineScope, p2: kotlin.coroutines.Continuation<kotlin.Unit>?): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>'
CALL 'public final fun invokeSuspend ($result: kotlin.Result<kotlin.Any?>): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Any? origin=null
$this: TYPE_OP type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=IMPLICIT_CAST typeOperand=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
CALL 'public final fun create (value: kotlin.Any?, $completion: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.coroutines.Continuation<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.coroutines.Continuation<kotlin.Unit> origin=null
$this: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
value: GET_VAR 'p1: kotlinx.coroutines.CoroutineScope declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlinx.coroutines.CoroutineScope origin=null
$completion: GET_VAR 'p2: kotlin.coroutines.Continuation<kotlin.Unit>? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlin.coroutines.Continuation<kotlin.Unit>? origin=null
$result: CALL 'public final fun <unsafe-coerce> <T, R> (v: T of kotlin.jvm.internal.<unsafe-coerce>): R of kotlin.jvm.internal.<unsafe-coerce> declared in kotlin.jvm.internal' type=kotlin.Result<kotlin.Any?> origin=null
<T>: kotlin.Any?
<R>: kotlin.Result<kotlin.Any?>
v: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:INSTANCE type:kotlin.Unit visibility:public [final,static] declared in kotlin.Unit' type=kotlin.Unit origin=null
FIELD FIELD_FOR_CAPTURED_VALUE name:$onLoginSuccess type:kotlin.Function0<kotlin.Unit> visibility:public/*package*/ [final]
FUN BRIDGE name:invoke visibility:public modality:OPEN <> ($this:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>, p1:kotlin.Any?, p2:kotlin.Any?) returnType:kotlin.Any?
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>
VALUE_PARAMETER BRIDGE name:p1 index:0 type:kotlin.Any?
VALUE_PARAMETER BRIDGE name:p2 index:1 type:kotlin.Any?
EXPRESSION_BODY
RETURN type=kotlin.Nothing from='public open fun invoke (p1: kotlin.Any?, p2: kotlin.Any?): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>'
CALL 'public final fun invoke (p1: kotlinx.coroutines.CoroutineScope, p2: kotlin.coroutines.Continuation<kotlin.Unit>?): kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=kotlin.Any? origin=BRIDGE_DELEGATION
$this: GET_VAR '<this>: com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
p1: TYPE_OP type=kotlinx.coroutines.CoroutineScope origin=IMPLICIT_CAST typeOperand=kotlinx.coroutines.CoroutineScope
GET_VAR 'p1: kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlin.Any? origin=null
p2: TYPE_OP type=kotlin.coroutines.Continuation<*> origin=IMPLICIT_CAST typeOperand=kotlin.coroutines.Continuation<*>
GET_VAR 'p2: kotlin.Any? declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>.invoke' type=kotlin.Any? origin=null
CONSTRUCTOR_CALL 'public/*package*/ constructor <init> ($onLoginSuccess: kotlin.Function0<kotlin.Unit>, $completion: kotlin.coroutines.Continuation<com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>>?) [primary] declared in com.opentimetracker.MainActivityKt.LoginScreen.<no name provided>' type=com.opentimetracker.MainActivityKt.LoginScreen.<no name provided> origin=null
$onLoginSuccess: GET_VAR 'onLoginSuccess: kotlin.Function0<kotlin.Unit> declared in com.opentimetracker.MainActivityKt.LoginScreen' type=kotlin.Function0<kotlin.Unit> origin=null
$completion: CONST Null type=kotlin.Nothing? value=null
CALL 'public final fun Column (modifier: androidx.compose.ui.Modifier, verticalArrangement: androidx.compose.foundation.layout.Arrangement.Vertical, horizontalAlignment: androidx.compose.ui.Alignment.Horizontal, content: @[Composable] @[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit>): kotlin.Unit [inline] declared in androidx.compose.foundation.layout.ColumnKt' type=kotlin.Unit origin=null
modifier: CALL 'public final fun padding-3ABfNKs (all: androidx.compose.ui.unit.Dp): androidx.compose.ui.Modifier declared in androidx.compose.foundation.layout.PaddingKt' type=androidx.compose.ui.Modifier origin=null
$receiver: CALL 'public final fun fillMaxSize$default (fraction: kotlin.Float, $mask0: kotlin.Int, $handler: kotlin.Any?): androidx.compose.ui.Modifier declared in androidx.compose.foundation.layout.SizeKt' type=androidx.compose.ui.Modifier origin=DEFAULT_DISPATCH_CALL
$receiver: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:Companion type:androidx.compose.ui.Modifier.Companion visibility:public [final,static] declared in androidx.compose.ui.Modifier' type=androidx.compose.ui.Modifier.Companion origin=null
fraction: COMPOSITE type=kotlin.Float origin=DEFAULT_VALUE
CONST Float type=kotlin.Float value=0.0
$mask0: CONST Int type=kotlin.Int value=1
$handler: CONST Null type=kotlin.Any? value=null
all: CALL 'public final fun <get-dp> (): androidx.compose.ui.unit.Dp [inline] declared in androidx.compose.ui.unit.DpKt' type=androidx.compose.ui.unit.Dp origin=GET_PROPERTY
$receiver: CONST Int type=kotlin.Int value=16
verticalArrangement: CALL 'public final fun <get-Center> (): androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical declared in androidx.compose.foundation.layout.Arrangement' type=androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical origin=GET_PROPERTY
$this: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:INSTANCE type:androidx.compose.foundation.layout.Arrangement visibility:public [final,static] declared in androidx.compose.foundation.layout.Arrangement' type=androidx.compose.foundation.layout.Arrangement origin=null
horizontalAlignment: CALL 'public final fun <get-CenterHorizontally> (): androidx.compose.ui.Alignment.Horizontal declared in androidx.compose.ui.Alignment.Companion' type=androidx.compose.ui.Alignment.Horizontal origin=GET_PROPERTY
$this: GET_FIELD 'FIELD FIELD_FOR_OBJECT_INSTANCE name:Companion type:androidx.compose.ui.Alignment.Companion visibility:public [final,static] declared in androidx.compose.ui.Alignment' type=androidx.compose.ui.Alignment.Companion origin=null
content: BLOCK type=@[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit> origin=LAMBDA
COMPOSITE type=kotlin.Unit origin=null
FUNCTION_REFERENCE 'private final fun LoginScreen$lambda$14 ($viewModel: com.opentimetracker.ui.AuthViewModel): kotlin.Unit declared in com.opentimetracker.MainActivityKt' type=@[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit> origin=INLINE_LAMBDA reflectionTarget=null
$viewModel: GET_VAR 'viewModel: com.opentimetracker.ui.AuthViewModel declared in com.opentimetracker.MainActivityKt.LoginScreen' type=com.opentimetracker.ui.AuthViewModel origin=null
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate$default(FunctionCodegen.kt:40)
at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethodNode(ClassCodegen.kt:406)
at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethod(ClassCodegen.kt:423)
at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:168)
at org.jetbrains.kotlin.backend.jvm.FileCodegen.lower(JvmPhases.kt:39)
at org.jetbrains.kotlin.backend.common.phaser.PhaseFactoriesKt.createFilePhase$lambda$4(PhaseFactories.kt:71)
at org.jetbrains.kotlin.backend.common.phaser.PhaseBuildersKt$createSimpleNamedCompilerPhase$1.phaseBody(PhaseBuilders.kt:69)
at org.jetbrains.kotlin.backend.common.phaser.SimpleNamedCompilerPhase.phaseBody(CompilerPhase.kt:226)
at org.jetbrains.kotlin.backend.common.phaser.AbstractNamedCompilerPhase.invoke(CompilerPhase.kt:113)
at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:62)
... 43 more
Caused by: org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Couldn't inline method call: CALL 'public final fun Column (modifier: androidx.compose.ui.Modifier, verticalArrangement: androidx.compose.foundation.layout.Arrangement.Vertical, horizontalAlignment: androidx.compose.ui.Alignment.Horizontal, content: @[Composable] @[ExtensionFunctionType] kotlin.Function1<androidx.compose.foundation.layout.ColumnScope, kotlin.Unit>): kotlin.Unit [inline] declared in androidx.compose.foundation.layout.ColumnKt' type=kotlin.Unit origin=null
Method: null
File is unknown
The root cause java.lang.IllegalStateException was thrown at: org.jetbrains.kotlin.codegen.inline.SourceCompilerForInlineKt.getMethodNode(SourceCompilerForInline.kt:118)
at org.jetbrains.kotlin.codegen.inline.InlineCodegen.performInline(InlineCodegen.kt:65)
at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCodegen.genInlineCall(IrInlineCodegen.kt:163)
at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCallGenerator.genCall(IrInlineCallGenerator.kt:36)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:653)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:138)
at org.jetbrains.kotlin.ir.expressions.IrCall.accept(IrCall.kt:24)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitStatementContainer(ExpressionCodegen.kt:579)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:584)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:138)
at org.jetbrains.kotlin.ir.expressions.IrBlockBody.accept(IrBlockBody.kt:20)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.generate(ExpressionCodegen.kt:240)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.doGenerate(FunctionCodegen.kt:123)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:44)
... 53 more
Caused by: java.lang.IllegalStateException: couldn't find inline method Landroidx/compose/foundation/layout/ColumnKt;.Column(Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/ui/Alignment$Horizontal;Lkotlin/jvm/functions/Function1;)V
at org.jetbrains.kotlin.codegen.inline.SourceCompilerForInlineKt.getMethodNode(SourceCompilerForInline.kt:118)
at org.jetbrains.kotlin.codegen.inline.SourceCompilerForInlineKt.loadCompiledInlineFunction(SourceCompilerForInline.kt:96)
at org.jetbrains.kotlin.backend.jvm.codegen.IrSourceCompilerForInline.compileInlineFunction(IrSourceCompilerForInline.kt:91)
at org.jetbrains.kotlin.codegen.inline.InlineCodegen.compileInline(InlineCodegen.kt:43)
at org.jetbrains.kotlin.codegen.inline.InlineCodegen.performInline(InlineCodegen.kt:51)
... 65 more

View File

@@ -1,6 +1,12 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
// Kotlin 2.0+ requires the dedicated Compose Compiler plugin
id("org.jetbrains.kotlin.plugin.compose")
// Version constraint removed to allow sync with main Kotlin version (2.0.0+)
id("org.jetbrains.kotlin.plugin.serialization")
}
android {
@@ -33,6 +39,10 @@ android {
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
}
dependencies {
@@ -43,4 +53,22 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
// Networking
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
// Storage
implementation("androidx.datastore:datastore-preferences:1.0.0")
// Navigation
implementation("androidx.navigation:navigation-compose:2.7.7")
// Compose
implementation(platform("androidx.compose:compose-bom:2024.02.01"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
}

View File

@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -11,6 +13,19 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.OpenTimeTracker"
tools:targetApi="31" />
tools:targetApi="31">
<activity
android:name="MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.OpenTimeTracker">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,236 @@
package calvin.erfmann.opentimetracker
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import calvin.erfmann.opentimetracker.data.Activity
import calvin.erfmann.opentimetracker.data.UserPreferences
import calvin.erfmann.opentimetracker.di.NetworkModule
import calvin.erfmann.opentimetracker.ui.AuthViewModel
import calvin.erfmann.opentimetracker.ui.DashboardViewModel
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val prefs = UserPreferences(this)
setContent {
MaterialTheme {
AppNavigation(prefs)
}
}
}
}
@Composable
fun AppNavigation(prefs: UserPreferences) {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "login") {
composable("login") {
LoginScreen(
viewModel = viewModel(factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AuthViewModel(prefs) as T
}
}),
onLoginSuccess = { navController.navigate("dashboard") {
popUpTo("login") { inclusive = true }
}}
)
}
composable("dashboard") {
// Using remember to avoid recreating the API definition
val api = remember { NetworkModule.provideApiService(prefs) }
// CORRECT: Using viewModel factory to keep the instance alive across recompositions
// This prevents the infinite loop of API requests
val viewModel: DashboardViewModel = viewModel(
factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return DashboardViewModel(api) as T
}
}
)
DashboardScreen(viewModel = viewModel)
}
}
}
@Composable
fun LoginScreen(viewModel: AuthViewModel, onLoginSuccess: () -> Unit) {
if (viewModel.isLoggedIn.value) {
LaunchedEffect(Unit) { onLoginSuccess() }
}
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Open Time Tracker", style = MaterialTheme.typography.headlineMedium)
Spacer(Modifier.height(32.dp))
OutlinedTextField(
value = viewModel.baseUrlInput.value,
onValueChange = { viewModel.baseUrlInput.value = it },
label = { Text("Server URL (Self-hosted)") },
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = viewModel.username.value,
onValueChange = { viewModel.username.value = it },
label = { Text("Username") },
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = viewModel.password.value,
onValueChange = { viewModel.password.value = it },
label = { Text("Password") },
modifier = Modifier.fillMaxWidth(),
visualTransformation = PasswordVisualTransformation()
)
Spacer(Modifier.height(16.dp))
if (viewModel.error.value != null) {
Text(viewModel.error.value!!, color = Color.Red)
Spacer(Modifier.height(8.dp))
}
Button(
onClick = { viewModel.login() },
enabled = !viewModel.isLoading.value,
modifier = Modifier.fillMaxWidth()
) {
Text(if (viewModel.isLoading.value) "Logging in..." else "Login")
}
}
}
@Composable
fun DashboardScreen(viewModel: DashboardViewModel) {
val state = viewModel.state.value
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
// --- Timer Section ---
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
) {
Column(
modifier = Modifier.padding(24.dp).fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (state?.isTracking == true) {
val activityName = state.activities.find { it.id == state.entry?.activityId }?.name ?: "Unknown"
Text(activityName, style = MaterialTheme.typography.titleMedium)
if (!state.entry?.subcategory.isNullOrEmpty()) {
SuggestionChip(onClick = {}, label = { Text(state.entry?.subcategory!!) })
}
Text(viewModel.elapsedTime.value, style = MaterialTheme.typography.displayLarge)
Spacer(Modifier.height(16.dp))
Button(
onClick = { viewModel.stopActivity() },
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.error)
) {
Text("STOP")
}
} else {
Text("Ready to work?", style = MaterialTheme.typography.headlineSmall)
Text("Select an activity below", style = MaterialTheme.typography.bodyMedium)
}
}
}
Spacer(Modifier.height(24.dp))
// --- Content Switching ---
// Always show Active Tasks if present
if (!state?.activeTasks.isNullOrEmpty()) {
Text("Active Tasks", style = MaterialTheme.typography.titleMedium)
LazyColumn(modifier = Modifier.heightIn(max = 200.dp)) { // Limit height to not push activities off screen
items(state!!.activeTasks) { task ->
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(
checked = task.status == "completed",
onCheckedChange = { /* Call toggle api */ })
Text(task.name)
}
}
}
Spacer(Modifier.height(16.dp))
}
// Always show Activities Grid
Text("Activities", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(8.dp))
// Show loading or empty state if null
if (state == null) {
Box(modifier = Modifier.fillMaxWidth().height(100.dp), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
} else {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.weight(1f) // Fill remaining space
) {
items(state.activities) { activity ->
ActivityCard(activity) { viewModel.startActivity(activity.id) }
}
}
}
}
}
@Composable
fun ActivityCard(activity: Activity, onClick: () -> Unit) {
// Parse hex color lightly
val color = try {
Color(android.graphics.Color.parseColor(activity.color))
} catch (e: Exception) { Color.Gray }
Card(
modifier = Modifier
.height(100.dp)
.clickable { onClick() },
colors = CardDefaults.cardColors(containerColor = color)
) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Text(
activity.name,
color = Color.White,
style = MaterialTheme.typography.titleMedium
)
}
}
}

View File

@@ -0,0 +1,23 @@
package calvin.erfmann.opentimetracker.data
import retrofit2.http.*
interface ApiService {
// Auth
@POST("api/login")
suspend fun login(@Body request: LoginRequest): AuthResponse
// Dashboard
@GET("api/status")
suspend fun getStatus(): StatusResponse
@POST("api/timer/start/{activity_id}")
suspend fun startActivity(@Path("activity_id") activityId: String): StartTimerResponse
@POST("api/timer/stop")
suspend fun stopActivity()
@POST("api/tasks/toggle")
suspend fun toggleTask(@Body body: Map<String, Any>): Any // Simplified
}

View File

@@ -0,0 +1,64 @@
package calvin.erfmann.opentimetracker.data
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class AuthResponse(
val token: String
)
@Serializable
data class LoginRequest(
val username: String,
val password: String
)
@Serializable
data class StatusResponse(
@SerialName("is_tracking") val isTracking: Boolean = false,
val entry: TimeEntry? = null,
@SerialName("active_tasks") val activeTasks: List<Task> = emptyList(),
val activities: List<Activity> = emptyList()
)
@Serializable
data class TimeEntry(
val id: String,
@SerialName("start_time") val startTime: String,
val note: String? = null,
val subcategory: String? = null,
@SerialName("activity_id") val activityId: String? = null,
@SerialName("activity_name") val activityName: String? = null,
@SerialName("activity_color") val activityColor: String? = null
)
@Serializable
data class Activity(
val id: String,
val name: String,
val color: String,
val subcategories: List<String> = emptyList()
)
@Serializable
data class Task(
val id: String,
val name: String,
val status: String = "open", // Werte vom Server: "open" oder "completed"
val subcategory: String? = null,
@SerialName("activity_id") val activityId: String? = null,
@SerialName("completed_at") val completedAt: String? = null,
@SerialName("due_date") val dueDate: String? = null
)
@Serializable
data class StartTimerResponse(
val id: String,
@SerialName("start_time") val startTime: String
)

View File

@@ -0,0 +1,35 @@
package calvin.erfmann.opentimetracker.data
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
private val Context.dataStore by preferencesDataStore(name = "settings")
class UserPreferences(private val context: Context) {
companion object {
val KEY_BASE_URL = stringPreferencesKey("base_url")
val KEY_JWT = stringPreferencesKey("jwt")
const val DEFAULT_URL = "https://opentimetracker.app/"
}
val baseUrl: Flow<String> = context.dataStore.data.map { it[KEY_BASE_URL] ?: DEFAULT_URL }
val authToken: Flow<String?> = context.dataStore.data.map { it[KEY_JWT] }
suspend fun saveBaseUrl(url: String) {
// Ensure trailing slash
val formattedUrl = if (url.endsWith("/")) url else "$url/"
context.dataStore.edit { it[KEY_BASE_URL] = formattedUrl }
}
suspend fun saveToken(token: String) {
context.dataStore.edit { it[KEY_JWT] = token }
}
suspend fun clearToken() {
context.dataStore.edit { it.remove(KEY_JWT) }
}
}

View File

@@ -0,0 +1,53 @@
package calvin.erfmann.opentimetracker.di
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import calvin.erfmann.opentimetracker.data.ApiService
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Interceptor
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import calvin.erfmann.opentimetracker.data.UserPreferences
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.InternalSerializationApi
object NetworkModule {
private val json = Json {
ignoreUnknownKeys = true
isLenient = true
coerceInputValues = true
}
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
fun provideApiService(preferences: UserPreferences): ApiService {
val authInterceptor = Interceptor { chain ->
val token = runBlocking { preferences.authToken.first() }
val requestBuilder = chain.request().newBuilder()
if (token != null) {
requestBuilder.addHeader("Authorization", "Bearer $token")
}
chain.proceed(requestBuilder.build())
}
val client = OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.connectTimeout(10, TimeUnit.SECONDS)
.build()
// Note: For dynamic Base URL changes in a real App, you'd reimplement this
// to rebuild Retrofit when prefs change, or use an OkHttp Interceptor to switch Host.
// For simplicity, we assume URL is set before Login and app restarts or re-initis logic.
val currentUrl = runBlocking { preferences.baseUrl.first() }
return Retrofit.Builder()
.baseUrl(currentUrl)
.client(client)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
.create(ApiService::class.java)
}
}

View File

@@ -0,0 +1,47 @@
package calvin.erfmann.opentimetracker.ui
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import calvin.erfmann.opentimetracker.data.LoginRequest
import calvin.erfmann.opentimetracker.data.UserPreferences
import calvin.erfmann.opentimetracker.di.NetworkModule
import kotlinx.coroutines.launch
class AuthViewModel(private val prefs: UserPreferences) : ViewModel() {
var baseUrlInput = mutableStateOf(UserPreferences.DEFAULT_URL)
var username = mutableStateOf("")
var password = mutableStateOf("")
var isLoading = mutableStateOf(false)
var error = mutableStateOf<String?>(null)
var isLoggedIn = mutableStateOf(false)
init {
viewModelScope.launch {
prefs.baseUrl.collect { baseUrlInput.value = it }
}
}
fun login() {
viewModelScope.launch {
try {
isLoading.value = true
error.value = null
// 1. Save URL first so NetworkModule picks it up
prefs.saveBaseUrl(baseUrlInput.value)
// 2. Create temp service
val api = NetworkModule.provideApiService(prefs)
// 3. Call Login
val response = api.login(LoginRequest(username.value, password.value))
prefs.saveToken(response.token)
isLoggedIn.value = true
} catch (e: Exception) {
error.value = "Login failed: ${e.message}"
} finally {
isLoading.value = false
}
}
}
}

View File

@@ -0,0 +1,87 @@
package calvin.erfmann.opentimetracker.ui
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import calvin.erfmann.opentimetracker.data.ApiService
import calvin.erfmann.opentimetracker.data.StatusResponse
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.time.Duration
import java.time.Instant
class DashboardViewModel(private val api: ApiService) : ViewModel() {
var state = mutableStateOf<StatusResponse?>(null)
var elapsedTime = mutableStateOf("00:00:00")
init {
startPolling()
startLocalTimer()
}
private fun startPolling() {
viewModelScope.launch {
while (isActive) {
try {
val status = api.getStatus()
state.value = status
} catch (e: Exception) {
// Handle error (e.g., 401 -> Logout)
e.printStackTrace()
}
delay(3000) // Poll every 3 seconds
}
}
}
private fun startLocalTimer() {
viewModelScope.launch {
while (isActive) {
state.value?.entry?.startTime?.let { isoTime ->
try {
// Assuming ISO format like "2023-10-27T10:00:00Z"
val start = Instant.parse(isoTime)
val now = Instant.now()
val seconds = Duration.between(start, now).seconds
if (seconds >= 0) {
val h = seconds / 3600
val m = (seconds % 3600) / 60
val s = seconds % 60
elapsedTime.value = String.format("%02d:%02d:%02d", h, m, s)
}
} catch (e: Exception) {
elapsedTime.value = "--:--"
}
}
delay(1000)
}
}
}
fun startActivity(activityId: String) {
viewModelScope.launch {
// Optimistic UI Update possible here
try {
api.startActivity(activityId)
// Polling will update the full state shortly
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun stopActivity() {
viewModelScope.launch {
try {
api.stopActivity()
// Explicitly refresh state immediately for better UX
state.value = api.getStatus()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}

View File

@@ -2,4 +2,6 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose)
kotlin("plugin.serialization") version "2.1.20"
}

View File

@@ -8,6 +8,25 @@ espressoCore = "3.5.1"
appcompat = "1.6.1"
material = "1.10.0"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
composeBom = "2024.04.01"
navigationRuntimeKtx = "2.8.8"
navigationCompose = "2.8.8"
places = "4.1.0"
okhttp = "4.12.0"
streamLog = "1.1.4"
landscapist = "2.4.6"
webrtc = "1.3.6"
kotlinxSerializationJson = "1.7.3"
ktlint = "0.43.0"
retrofit = "2.11.0"
retrofitResultAdapter = "1.0.10"
retrofitKotlinxSerializationJson = "1.0.0"
spotless = "6.7.0"
credentials = "1.5.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -19,4 +38,4 @@ material = { group = "com.google.android.material", name = "material", version.r
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }