diff --git a/wear/build.gradle b/wear/build.gradle deleted file mode 100644 index 0a2064e1..00000000 --- a/wear/build.gradle +++ /dev/null @@ -1,71 +0,0 @@ -plugins { - id 'com.android.application' - id 'org.jetbrains.kotlin.android' -} - -android { - namespace 'com.example.util.simpletimetracker' - compileSdk 34 - - defaultConfig { - applicationId "com.razeeman.util.simpletimetracker" - minSdk 26 - targetSdk 34 - versionCode 1 - versionName "1.0" - vectorDrawables { - useSupportLibrary true - } - - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion '1.4.0-alpha02' - } - packagingOptions { - resources { - excludes += '/META-INF/{AL2.0,LGPL2.1}' - } - } -} - -dependencies { - var compose_version = "1.3.1" - var wear_compose_version = "1.1.0" - // Custom Dependencies - implementation 'com.google.android.gms:play-services-wearable:18.0.0' - implementation 'com.google.android.horologist:horologist-compose-layout:0.2.7' - - // Default Dependencies - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'com.google.android.gms:play-services-wearable:17.1.0' - implementation 'androidx.percentlayout:percentlayout:1.0.0' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.recyclerview:recyclerview:1.2.1' - implementation "androidx.compose.ui:ui:$compose_version" - implementation "androidx.wear.compose:compose-material:$wear_compose_version" - implementation "androidx.wear.compose:compose-foundation:$wear_compose_version" - implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' - implementation 'androidx.activity:activity-compose:1.3.1' - androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" - debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" - debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" - wearApp project(":wear") -} \ No newline at end of file diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts new file mode 100644 index 00000000..6838683d --- /dev/null +++ b/wear/build.gradle.kts @@ -0,0 +1,77 @@ +plugins { + id("com.android.application") + id("kotlin-android") +} + +android { + namespace = "com.example.util.simpletimetracker" + compileSdk = 34 + + defaultConfig { + applicationId = "com.razeeman.util.simpletimetracker" + minSdk = 26 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + vectorDrawables { + useSupportLibrary = true + } + + } + + buildTypes { + getByName("debug") { + applicationIdSuffix = ".debug" + } + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.4.0-alpha02" + } + packagingOptions { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + var compose_version = "1.3.1" + var wear_compose_version = "1.1.0" + // Custom Dependencies + implementation("com.google.android.horologist:horologist-compose-layout:0.2.7") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") + implementation(project(":wearrpc")) + + // Default Dependencies + implementation("androidx.core:core-ktx:1.7.0") + implementation("com.google.android.gms:play-services-wearable:17.1.0") + implementation("androidx.percentlayout:percentlayout:1.0.0") + implementation("androidx.legacy:legacy-support-v4:1.0.0") + implementation("androidx.recyclerview:recyclerview:1.2.1") + implementation("androidx.compose.ui:ui:$compose_version") + implementation("androidx.wear.compose:compose-material:$wear_compose_version") + implementation("androidx.wear.compose:compose-foundation:$wear_compose_version") + implementation("androidx.compose.ui:ui-tooling-preview:$compose_version") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1") + implementation("androidx.activity:activity-compose:1.3.1") + androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") + debugImplementation("androidx.compose.ui:ui-tooling:$compose_version") + debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version") +} \ No newline at end of file diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro index 481bb434..ff59496d 100644 --- a/wear/proguard-rules.pro +++ b/wear/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. +# proguardFiles setting in build.gradle.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/Messaging.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/Messaging.kt deleted file mode 100644 index 9d4a4b72..00000000 --- a/wear/src/main/java/com/example/util/simpletimetracker/data/Messaging.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -package com.example.util.simpletimetracker.data - -import android.content.Context -import android.util.Log -import com.google.android.gms.tasks.Task -import com.google.android.gms.tasks.Tasks -import com.google.android.gms.wearable.CapabilityClient -import com.google.android.gms.wearable.CapabilityInfo -import com.google.android.gms.wearable.Wearable -import com.example.util.simpletimetracker.presentation.LOG_TAG - -const val START_TIME_TRACKING_ACTIVITY_CAPABILITY_NAME = "start_time_tracking_activity" - -class Messaging { - fun startTimeTracking( - context: Context, - activity: String, - tag: String, - ) { - Thread( - Runnable { - startTimeTrackingTask(context, activity, tag) - }, - ).start() - } - - private fun startTimeTrackingTask( - context: Context, - activity: String, - tag: String, - ) { - // Find all nodes which support the time tracking message - val capabilityInfo: CapabilityInfo = - Tasks.await( - Wearable.getCapabilityClient(context) - .getCapability( - START_TIME_TRACKING_ACTIVITY_CAPABILITY_NAME, - CapabilityClient.FILTER_REACHABLE, - ), - ) - - // Choose the best node (the closest one connected to the watch - val nodes = capabilityInfo.nodes - val bestNode = nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id - - // Send the message - val message = "$activity|$tag" - bestNode?.also { nodeId -> - Log.i(LOG_TAG, "Sending message to $bestNode") - val sendTask: Task<*> = - Wearable.getMessageClient(context).sendMessage( - nodeId, - "/$START_TIME_TRACKING_ACTIVITY_CAPABILITY_NAME", - message.toByteArray(), - ).apply { - addOnSuccessListener { - Log.i( - LOG_TAG, - "Sent $START_TIME_TRACKING_ACTIVITY_CAPABILITY_NAME message: $message", - ) - } - addOnFailureListener { - Log.e( - LOG_TAG, - "Failed to send $START_TIME_TRACKING_ACTIVITY_CAPABILITY_NAME message: $message", - ) - } - } - } ?: run { - Log.e( - LOG_TAG, - "No nodes found with the capability $START_TIME_TRACKING_ACTIVITY_CAPABILITY_NAME", - ) - } - } -} diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/MainActivity.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/MainActivity.kt index 31944722..320632ba 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/MainActivity.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/MainActivity.kt @@ -7,6 +7,7 @@ package com.example.util.simpletimetracker.presentation import android.content.Context import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.background @@ -25,9 +26,13 @@ import com.google.android.horologist.compose.focus.rememberActiveFocusRequester import com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi import com.google.android.horologist.compose.rotaryinput.* import com.example.util.simpletimetracker.R -import com.example.util.simpletimetracker.data.Messaging import com.example.util.simpletimetracker.data.getTimeTrackingActivities import com.example.util.simpletimetracker.presentation.theme.SimpleTimeTrackerForWearOSTheme +import com.example.util.simpletimetracker.wearrpc.Messenger +import com.example.util.simpletimetracker.wearrpc.WearRPCClient +//import com.example.util.simpletimetracker.wearrpc.CurrentActivity +import kotlinx.coroutines.* +//import java.util.Date const val LOG_TAG = "com.razeeman.util.simpletimetracker" @@ -42,6 +47,8 @@ class MainActivity : ComponentActivity() { @Composable fun WearApp() { + val context = LocalContext.current + val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) SimpleTimeTrackerForWearOSTheme { val scrollState = rememberScalingLazyListState() Scaffold( @@ -55,11 +62,72 @@ fun WearApp() { PositionIndicator(scalingLazyListState = scrollState) }, ) { - ActivityList(scrollState) + ScalingLazyColumn ( + modifier = Modifier.fillMaxSize(), + autoCentering = AutoCenteringParams(itemIndex = 0) + ){ + item { + DebugButton( + label = "Ping", + onClick = { + Log.i(LOG_TAG, "Pinging mobile ...") + scope.async { + val response = WearRPCClient(Messenger(context)).ping("Ping button") + Log.i(LOG_TAG, response) + } + } + ) + } + item { + DebugButton(label="Query Activities", onClick = { + Log.i(LOG_TAG, "Querying activities ...") + scope.async { + val response = WearRPCClient(Messenger(context)).queryActivities() + Log.i(LOG_TAG, "Received ${response.size} activities") + for (activity in response) { + Log.i(LOG_TAG, " - ${activity.id}: ${activity.name}") + } + } + }) + } + } + +// ActivityList(scrollState) } } } +@Composable +fun DebugButton(label: String, onClick: () -> Unit) { + Chip( + modifier = Modifier + .fillMaxWidth(0.9f) + .padding(top = 10.dp), + label = { Text(label) }, + onClick = onClick, + ) +} + +@OptIn(ExperimentalHorologistComposeLayoutApi::class) +@Composable +fun ScrollingColumn(scrollState: ScalingLazyListState = rememberScalingLazyListState(), content: @Composable () -> Unit) { + val focusRequester = rememberActiveFocusRequester() + val rotaryHapticFeedback = rememberRotaryHapticFeedback() + ScalingLazyColumn( + modifier = + Modifier + .rotaryWithScroll(focusRequester, scrollState, rotaryHapticFeedback) + .fillMaxSize() + .background(MaterialTheme.colors.background) + .selectableGroup(), + contentPadding = PaddingValues(10.dp), + verticalArrangement = Arrangement.Center, + state = scrollState, + ) { + content + } +} + @OptIn(ExperimentalHorologistComposeLayoutApi::class) @Composable fun ActivityList(scrollState: ScalingLazyListState = rememberScalingLazyListState()) { @@ -68,11 +136,11 @@ fun ActivityList(scrollState: ScalingLazyListState = rememberScalingLazyListStat val rotaryHapticFeedback = rememberRotaryHapticFeedback() ScalingLazyColumn( modifier = - Modifier - .rotaryWithScroll(focusRequester, scrollState, rotaryHapticFeedback) - .fillMaxSize() - .background(MaterialTheme.colors.background) - .selectableGroup(), + Modifier + .rotaryWithScroll(focusRequester, scrollState, rotaryHapticFeedback) + .fillMaxSize() + .background(MaterialTheme.colors.background) + .selectableGroup(), contentPadding = PaddingValues(10.dp), verticalArrangement = Arrangement.Center, state = scrollState, @@ -125,11 +193,12 @@ fun ActivityWithTag( icon: Int = R.drawable.baseline_question_mark_24, ) { val context = LocalContext.current + val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) Chip( modifier = - Modifier - .fillMaxWidth(0.9f) - .padding(top = 10.dp), + Modifier + .fillMaxWidth(0.9f) + .padding(top = 10.dp), icon = { ActivityIcon(iconId = icon) }, @@ -151,7 +220,7 @@ fun ActivityWithTag( ChipDefaults.chipColors( backgroundColor = color, ), - onClick = { startTimeTracking(context, name, tag) }, + onClick = { startTimeTracking(scope, context) }, ) } @@ -162,11 +231,12 @@ fun ActivityWithoutTag( icon: Int = R.drawable.baseline_question_mark_24, ) { val context = LocalContext.current + val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) Chip( modifier = - Modifier - .fillMaxWidth(0.9f) - .padding(top = 10.dp), + Modifier + .fillMaxWidth(0.9f) + .padding(top = 10.dp), icon = { ActivityIcon(iconId = icon) }, @@ -181,7 +251,7 @@ fun ActivityWithoutTag( ChipDefaults.chipColors( backgroundColor = color, ), - onClick = { startTimeTracking(context, name, "") }, + onClick = { startTimeTracking(scope, context) }, ) } @@ -191,16 +261,22 @@ fun ActivityIcon(iconId: Int) { painter = painterResource(id = iconId), contentDescription = "activity icon", modifier = - Modifier - .size(ChipDefaults.IconSize) - .wrapContentSize(align = Alignment.Center), + Modifier + .size(ChipDefaults.IconSize) + .wrapContentSize(align = Alignment.Center), ) } -fun startTimeTracking( - context: Context, - activity: String, - tag: String, -) { - Messaging().startTimeTracking(context, activity, tag) -} +fun startTimeTracking(scope: CoroutineScope, context: Context) { +// val activities = arrayOf( +// CurrentActivity( +// id, +// name, +// startedAt = Date(), +// arrayOf(/* tags */), +// ) +// ) +// scope.async { +// WearRPCClient(context).setCurrentActivities(activities) +// } +} \ No newline at end of file