Skip to content

Commit 7fa9207

Browse files
authored
Merge pull request #180 from android/revert-179-delete_shrinewear
Revert "Delete shrinewear"
2 parents b6c6980 + fb1875a commit 7fa9207

32 files changed

+2801
-0
lines changed

Shrine/gradle/libs.versions.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ googleid = "1.1.1"
1414
hilt = "2.57.1"
1515
hiltCompiler = "1.2.0"
1616
hiltNavigationCompose = "1.2.0"
17+
horologist = "0.7.15"
1718
kotlin = "2.2.0"
1819
kotlinCoroutines = "1.0"
1920
kotlinxCoroutines = "1.9.0"
@@ -27,9 +28,12 @@ okHttp = "4.12.0"
2728
playServicesAuth = "21.1.1"
2829
playServicesFido = "21.0.0"
2930
playServicesLocation = "21.2.0"
31+
playServicesWearable = "19.0.0"
3032
retrofit = "2.9.0"
3133
spotless = "6.21.0"
3234
sysUiController = "0.28.0"
35+
wearComposeMaterial3 = "1.5.0-beta04"
36+
wearRemoteInteractions = "1.1.0"
3337

3438
# BOM Versions
3539
composeBom = "2025.05.01"
@@ -41,6 +45,7 @@ espressoCore = "3.5.1"
4145
extJUnit = "1.1.5"
4246
jUnit = "4.13.2"
4347
composeMaterial = "1.2.1"
48+
wearToolingPreview = "1.0.0"
4449
foundation = "1.8.3"
4550

4651

@@ -104,15 +109,34 @@ kotlin-coroutines = { group = "ru.gildor.coroutines", name = "kotlin-coroutines-
104109
okhttp-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okHttp" }
105110
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okHttp" }
106111

112+
# For Wear Module
113+
horologist-auth-ui = { module = "com.google.android.horologist:horologist-auth-ui", version.ref = "horologist" }
114+
horologist-compose-layout = { module = "com.google.android.horologist:horologist-compose-layout", version.ref = "horologist" }
115+
wear-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeMaterial3" }
116+
wear-compose-material3 = { module = "androidx.wear.compose:compose-material3", version.ref = "wearComposeMaterial3" }
117+
wear-compose-navigation = { module = "androidx.wear.compose:compose-navigation", version.ref = "wearComposeMaterial3" }
118+
wear-compose-ui-tooling = { group = "androidx.wear.compose", name = "compose-ui-tooling"}
119+
wear-remote-interactions = { group = "androidx.wear", name = "wear-remote-interactions", version.ref = "wearRemoteInteractions" }
120+
kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlinBom" }
121+
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
122+
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
123+
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerialization" }
124+
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
125+
playServicesWearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearable" }
126+
127+
107128
# Testing
108129
compose-ui-test-junit = { group = "androidx.compose.ui", name = "ui-test-junit4" }
109130
compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
110131
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
111132
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
112133
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "extJUnit" }
113134
junit = { group = "junit", name = "junit", version.ref = "jUnit"}
135+
androidx-compose-material = { group = "androidx.wear.compose", name = "compose-material", version.ref = "composeMaterial" }
136+
androidx-wear-tooling-preview = { group = "androidx.wear", name = "wear-tooling-preview", version.ref = "wearToolingPreview" }
114137
androidx-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" }
115138

139+
116140
[plugins]
117141
android-application = { id = "com.android.application", version.ref = "androidPlugin" }
118142
android-library = { id = "com.android.library", version.ref = "androidPlugin" }

Shrine/settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pluginManagement {
77
}
88
rootProject.name = "Shrine"
99
include(":app")
10+
include(":wear")

Shrine/wear/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

Shrine/wear/build.gradle.kts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
plugins {
17+
alias(libs.plugins.android.application)
18+
alias(libs.plugins.kotlin.android)
19+
alias(libs.plugins.kotlin.serialization)
20+
alias(libs.plugins.kotlin.compose)
21+
}
22+
23+
android {
24+
namespace = "com.authentication.shrinewear"
25+
compileSdk = 36
26+
27+
defaultConfig {
28+
applicationId = "com.authentication.shrine"
29+
minSdk = 30
30+
targetSdk = 36
31+
versionCode = 1
32+
versionName = "1.0"
33+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
34+
buildConfigField(
35+
"String", "API_BASE_URL",
36+
"\"https://project-sesame-426206.appspot.com\""
37+
)
38+
buildConfigField(
39+
"String", "GOOGLE_SIGN_IN_SERVER_CLIENT_ID",
40+
"\"PASTE_YOUR_SERVER_CLIENT_ID_HERE\""
41+
)
42+
}
43+
44+
signingConfigs {
45+
getByName("debug") {
46+
storeFile = file(project.rootProject.file("debug.keystore"))
47+
}
48+
}
49+
50+
buildTypes {
51+
release {
52+
isMinifyEnabled = false
53+
}
54+
debug {
55+
signingConfig = signingConfigs.getByName("debug")
56+
}
57+
}
58+
59+
compileOptions {
60+
sourceCompatibility = JavaVersion.VERSION_1_8
61+
targetCompatibility = JavaVersion.VERSION_1_8
62+
}
63+
64+
kotlin {
65+
compilerOptions {
66+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8)
67+
}
68+
}
69+
buildFeatures {
70+
compose = true
71+
buildConfig = true
72+
}
73+
packaging {
74+
resources {
75+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
76+
}
77+
}
78+
79+
dependencies {
80+
// The trailing comments indicate version change status post migration.
81+
// TODO(johnzoeller): Remove trailing comments once mobile dependency versions are updated.
82+
implementation(platform(libs.compose.bom)) // "2025.05.00" to .01
83+
implementation(libs.core.ktx) // 1.15.0 to 1.13.1
84+
implementation(libs.activity.compose) // bom 2025.05.00 to specific 1.9
85+
implementation(libs.appcompat) // NO CHANGE
86+
implementation(libs.compose.material.icons) // Bom "2025.05.00" to .01
87+
implementation(libs.compose.ui) // bom "2025.05.00" to .01
88+
implementation(libs.credentials) // NO CHANGE
89+
implementation(libs.credentials.play.services.auth) // NO CHANGE
90+
implementation(libs.lifecycle.runtime.ktx) // 2.8.7 -> 2.7.0
91+
implementation(libs.lifecycle.viewmodel.compose) // 2.8.7 -> 2.7.0
92+
implementation(libs.navigation.compose) // 2.9.0 -> 2.7.7
93+
94+
// Wear Androidx Dependencies
95+
implementation(libs.wear.compose.material3) // New
96+
implementation(libs.wear.compose.navigation) // New
97+
implementation(libs.wear.compose.ui.tooling) // New
98+
implementation(libs.wear.compose.foundation) // New
99+
implementation(libs.wear.remote.interactions) // New
100+
101+
// KotlinX
102+
implementation(platform(libs.kotlin.bom)) // New
103+
implementation(libs.kotlinx.coroutines.core) // New
104+
implementation(libs.kotlinx.coroutines.android) // New
105+
implementation(libs.kotlinx.serialization.core) // New
106+
implementation(libs.kotlinx.serialization.json) // New
107+
108+
// Wear Horologist SIWG composables
109+
implementation(libs.horologist.auth.ui)
110+
implementation(libs.horologist.compose.layout)
111+
112+
// GMS
113+
implementation(libs.google.id) // NO CHANGE
114+
implementation(libs.playServicesWearable) // New
115+
116+
// Http Server
117+
implementation(libs.okhttp) // NO CHANGE
118+
implementation(libs.okhttp.logging.interceptor) // NO CHANGE
119+
120+
// For Legacy Sign in With Google
121+
implementation(libs.play.services.auth) // 21.1.1 -> 21.3.0
122+
}
123+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
<uses-feature android:name="android.hardware.type.watch" />
4+
<uses-permission android:name="android.permission.INTERNET"/>
5+
<uses-permission android:name="android.permission.WAKE_LOCK" />
6+
7+
<application
8+
android:name=".ShrineApplication"
9+
android:icon="@drawable/shrine"
10+
android:roundIcon="@drawable/shrine"
11+
android:label="@string/app_name"
12+
android:supportsRtl="true"
13+
android:theme="@android:style/Theme.DeviceDefault">
14+
<meta-data
15+
android:name="com.google.android.wearable.standalone"
16+
android:value="true" />
17+
<activity
18+
android:name=".MainActivity"
19+
android:exported="true"
20+
android:taskAffinity="">
21+
<intent-filter>
22+
<action android:name="android.intent.action.MAIN" />
23+
<category android:name="android.intent.category.LAUNCHER" />
24+
</intent-filter>
25+
</activity>
26+
</application>
27+
</manifest>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.authentication.shrinewear
17+
18+
import android.content.Context
19+
import com.authentication.shrinewear.authenticator.AuthenticationServer
20+
import com.authentication.shrinewear.authenticator.CredentialManagerAuthenticator
21+
import com.authentication.shrinewear.network.AuthNetworkClient
22+
import kotlinx.coroutines.flow.MutableStateFlow
23+
import kotlinx.coroutines.flow.StateFlow
24+
import kotlinx.coroutines.flow.asStateFlow
25+
26+
/**
27+
* Represents the possible authentication states of the application.
28+
*/
29+
enum class AuthenticationState {
30+
LOGGED_OUT,
31+
LOGGED_IN,
32+
DISMISSED_BY_USER,
33+
MISSING_CREDENTIALS,
34+
FAILED,
35+
UNKNOWN_ERROR,
36+
}
37+
38+
/**
39+
* A simple, manual dependency injection container for application-wide singletons.
40+
*
41+
* This object serves as a central point to provide and access core services
42+
* and dependencies that are shared across the application.
43+
*
44+
* It requires a [Context] to be [provided][provide] during application startup
45+
* to initialize its dependencies.
46+
*/
47+
object Graph {
48+
49+
private val authNetworkClient: AuthNetworkClient by lazy {
50+
AuthNetworkClient()
51+
}
52+
val authenticationServer: AuthenticationServer by lazy {
53+
AuthenticationServer(authNetworkClient)
54+
}
55+
56+
/**
57+
* The authenticated instance of [CredentialManagerAuthenticator].
58+
* This property is initialized once via the [provide] method and
59+
* provides access to credential management and authentication services.
60+
*
61+
* It's a `lateinit var` because it's initialized after object creation (e.g., in `Application.onCreate()`).
62+
* The `private set` ensures that it can only be set once internally by the `Graph` object.
63+
*/
64+
lateinit var credentialManagerAuthenticator: CredentialManagerAuthenticator
65+
private set
66+
67+
private val _authenticationState = MutableStateFlow(AuthenticationState.LOGGED_OUT)
68+
69+
/**
70+
* Stores the current authentication status code. Defaults to [AuthenticationState.LOGGED_OUT].
71+
*/
72+
val authenticationState: StateFlow<AuthenticationState> = _authenticationState.asStateFlow()
73+
74+
/**
75+
* Provides and initializes the core dependencies for the application's [Graph].
76+
*
77+
* This method should be called once during the application's lifecycle (e.g., in the `Application.onCreate()` method)
78+
* to ensure all necessary services are set up.
79+
*
80+
* @param context The application [Context] required to initialize services like [CredentialManagerAuthenticator].
81+
*/
82+
fun provide(context: Context) {
83+
credentialManagerAuthenticator = CredentialManagerAuthenticator(
84+
context,
85+
authenticationServer,
86+
)
87+
}
88+
89+
fun updateAuthenticationState(newState: AuthenticationState) {
90+
_authenticationState.value = newState
91+
}
92+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.authentication.shrinewear
17+
18+
import android.os.Build
19+
import android.os.Bundle
20+
import android.util.Log
21+
import androidx.activity.ComponentActivity
22+
import androidx.activity.compose.setContent
23+
import androidx.wear.compose.material3.MaterialTheme
24+
import com.authentication.shrinewear.ui.ShrineApp
25+
26+
private const val TAG = "MainActivity"
27+
28+
/**
29+
* The main activity for the Shrine Wear OS application.
30+
*
31+
* This activity serves as the entry point of the application, initializing the Compose UI
32+
* and performing a diagnostic check for the Credential Manager API availability.
33+
*/
34+
class MainActivity : ComponentActivity() {
35+
36+
/**
37+
* Called when the activity is first created.
38+
*
39+
* This method performs the following:
40+
* - Logs the Android SDK version and whether the Credential Manager service is available.
41+
* - Calls the superclass's `onCreate` method.
42+
* - Sets the Compose content of the activity, applying [MaterialTheme] and
43+
* displaying the [ShrineApp] composable.
44+
*
45+
* @param savedInstanceState A [Bundle] containing the activity's previously saved state,
46+
* or null if the activity is being created for the first time.
47+
*/
48+
override fun onCreate(savedInstanceState: Bundle?) {
49+
Log.v(
50+
TAG,
51+
"Verifying Credential Manager availability. Build.VERSION.SDK_INT: ${Build.VERSION.SDK_INT}, has credential manager:" +
52+
"${getSystemService("credential") != null}",
53+
)
54+
super.onCreate(savedInstanceState)
55+
setContent {
56+
MaterialTheme {
57+
ShrineApp()
58+
}
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)