Skip to content

Commit e261af0

Browse files
authored
Fallback to legacy Google signIn on WearOS 34 and below (Automattic#4633)
1 parent 09af292 commit e261af0

File tree

6 files changed

+177
-25
lines changed

6 files changed

+177
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* Updates
44
* Improved login experience on WearOS
55
([#4631](https://github.com/Automattic/pocket-casts-android/pull/4631))
6+
* Bring back legacy Google login on WearOS 14 and below
7+
([#4633](https://github.com/Automattic/pocket-casts-android/pull/4633))
68

79
7.100
810
-----

wear/src/main/AndroidManifest.xml

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5-
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
6-
<uses-permission android:name="android.permission.BLUETOOTH"/>
7-
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
8-
<uses-permission android:name="android.permission.INTERNET"/>
9-
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
10-
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
5+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
6+
<uses-permission android:name="android.permission.BLUETOOTH" />
7+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
8+
<uses-permission android:name="android.permission.INTERNET" />
9+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
10+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
1111
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
12-
<uses-permission android:name="android.permission.VIBRATE"/>
12+
<uses-permission android:name="android.permission.VIBRATE" />
1313
<uses-permission android:name="android.permission.WAKE_LOCK" />
1414
<uses-permission android:name="com.android.vending.BILLING" />
15-
<uses-permission android:name="com.android.vending.CHECK_LICENSE"/>
15+
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
1616
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
1717
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
1818

19-
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove"/>
19+
<uses-permission
20+
android:name="com.google.android.gms.permission.AD_ID"
21+
tools:node="remove" />
2022

2123
<uses-feature android:name="android.hardware.type.watch" />
2224

@@ -62,24 +64,37 @@
6264
<action android:name="au.com.shiftyjelly.pocketcasts.MAIN" />
6365
<category android:name="android.intent.category.DEFAULT" />
6466
</intent-filter>
67+
<intent-filter android:autoVerify="true">
68+
<action android:name="android.intent.action.VIEW" />
69+
70+
<category android:name="android.intent.category.DEFAULT" />
71+
<category android:name="android.intent.category.BROWSABLE" />
72+
73+
<data android:scheme="http" />
74+
<data android:scheme="https" />
75+
<data android:host="www.pocketcasts.com" />
76+
<data android:host="pocketcasts.com" />
77+
</intent-filter>
6578
</activity>
6679

6780
<service
6881
android:name=".repositories.playback.PlaybackService"
6982
android:exported="true"
70-
android:label="@string/app_name"
71-
android:foregroundServiceType="mediaPlayback">
83+
android:foregroundServiceType="mediaPlayback"
84+
android:label="@string/app_name">
7285
<intent-filter>
73-
<action android:name="android.media.browse.MediaBrowserService"/>
86+
<action android:name="android.media.browse.MediaBrowserService" />
7487
</intent-filter>
7588
</service>
7689

77-
<service android:name="au.com.shiftyjelly.pocketcasts.repositories.download.UpdateEpisodeDetailsJob"
78-
android:permission="android.permission.BIND_JOB_SERVICE"
79-
android:exported="true"/>
80-
<service android:name="au.com.shiftyjelly.pocketcasts.repositories.refresh.RefreshPodcastsJob"
81-
android:permission="android.permission.BIND_JOB_SERVICE"
82-
android:exported="true"/>
90+
<service
91+
android:name="au.com.shiftyjelly.pocketcasts.repositories.download.UpdateEpisodeDetailsJob"
92+
android:exported="true"
93+
android:permission="android.permission.BIND_JOB_SERVICE" />
94+
<service
95+
android:name="au.com.shiftyjelly.pocketcasts.repositories.refresh.RefreshPodcastsJob"
96+
android:exported="true"
97+
android:permission="android.permission.BIND_JOB_SERVICE" />
8398

8499
<!-- File sharing / Opml export -->
85100
<provider
@@ -89,7 +104,7 @@
89104
android:grantUriPermissions="true">
90105
<meta-data
91106
android:name="android.support.FILE_PROVIDER_PATHS"
92-
android:resource="@xml/file_paths"/>
107+
android:resource="@xml/file_paths" />
93108
</provider>
94109

95110
<!-- Work Manager -->

wear/src/main/kotlin/au/com/shiftyjelly/pocketcasts/wear/ui/authentication/AuthenticationNavGraph.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
package au.com.shiftyjelly.pocketcasts.wear.ui.authentication
44

5+
import android.os.Build
56
import android.widget.Toast
67
import androidx.activity.compose.LocalActivity
78
import androidx.compose.runtime.Composable
@@ -23,6 +24,7 @@ const val AUTHENTICATION_SUB_GRAPH = "authentication_graph"
2324
private object AuthenticationNavRoutes {
2425
const val LOGIN_SCREEN = "login_screen"
2526
const val LOGIN_WITH_GOOGLE = "login_with_google"
27+
const val LOGIN_WITH_GOOGLE_LEGACY = "login_with_google_legacy"
2628
const val LOGIN_WITH_PHONE = "login_with_phone"
2729
const val LOGIN_WITH_EMAIL = "login_with_email"
2830
}
@@ -46,7 +48,12 @@ fun NavGraphBuilder.authenticationNavGraph(
4648
) {
4749
LoginScreen(
4850
onLoginWithGoogleClick = {
49-
navController.navigate(AuthenticationNavRoutes.LOGIN_WITH_GOOGLE)
51+
val route = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
52+
AuthenticationNavRoutes.LOGIN_WITH_GOOGLE
53+
} else {
54+
AuthenticationNavRoutes.LOGIN_WITH_GOOGLE_LEGACY
55+
}
56+
navController.navigate(route)
5057
},
5158
onLoginWithPhoneClick = {
5259
navController.navigate(AuthenticationNavRoutes.LOGIN_WITH_PHONE)
@@ -87,7 +94,6 @@ fun NavGraphBuilder.authenticationNavGraph(
8794
navController.popBackStack()
8895
}
8996
}
90-
9197
val defaultErrorMessage = stringResource(LR.string.onboarding_continue_with_google_error)
9298

9399
LoginWithGoogleScreen(
@@ -105,5 +111,16 @@ fun NavGraphBuilder.authenticationNavGraph(
105111
},
106112
)
107113
}
114+
115+
composable(
116+
route = AuthenticationNavRoutes.LOGIN_WITH_GOOGLE_LEGACY,
117+
) {
118+
LegacyLoginWithGoogleScreen(
119+
signInSuccessScreen = {
120+
googleSignInSuccessScreen(GoogleAccountData(name = it?.givenName.orEmpty(), avatarUrl = it?.photoUrl?.toString()))
121+
},
122+
onCancel = { navController.popBackStack() },
123+
)
124+
}
108125
}
109126
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@file:Suppress("DEPRECATION")
2+
3+
package au.com.shiftyjelly.pocketcasts.wear.ui.authentication
4+
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.collectAsState
7+
import androidx.compose.runtime.getValue
8+
import androidx.compose.ui.Modifier
9+
import androidx.compose.ui.platform.LocalContext
10+
import androidx.compose.ui.res.stringResource
11+
import androidx.hilt.navigation.compose.hiltViewModel
12+
import au.com.shiftyjelly.pocketcasts.compose.CallOnce
13+
import au.com.shiftyjelly.pocketcasts.utils.Network
14+
import au.com.shiftyjelly.pocketcasts.wear.ui.component.ErrorScreen
15+
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
16+
import com.google.android.horologist.auth.ui.googlesignin.signin.GoogleSignInScreen
17+
import com.google.android.horologist.compose.layout.ScreenScaffold
18+
import timber.log.Timber
19+
import au.com.shiftyjelly.pocketcasts.localization.R as LR
20+
import com.google.android.horologist.auth.composables.R as HR
21+
22+
@Composable
23+
fun LegacyLoginWithGoogleScreen(
24+
onCancel: () -> Unit,
25+
modifier: Modifier = Modifier,
26+
viewModel: LegacyLoginWithGoogleScreenViewModel = hiltViewModel(),
27+
signInSuccessScreen: @Composable (GoogleSignInAccount?) -> Unit,
28+
) {
29+
ScreenScaffold(
30+
modifier = modifier,
31+
) {
32+
val state by viewModel.state.collectAsState()
33+
val context = LocalContext.current
34+
35+
CallOnce {
36+
// Allow the user to sign in with a different account
37+
viewModel.clearPreviousSignIn()
38+
}
39+
40+
GoogleSignInScreen(
41+
viewModel = viewModel.googleSignInViewModel,
42+
onAuthCancelled = {
43+
Timber.i("Google sign in cancelled")
44+
onCancel()
45+
},
46+
failedContent = {
47+
val message = if (Network.isConnected(context)) {
48+
HR.string.horologist_auth_error_message
49+
} else {
50+
LR.string.log_in_no_network
51+
}
52+
ErrorScreen(stringResource(message))
53+
},
54+
content = {
55+
signInSuccessScreen(state.googleSignInAccount)
56+
},
57+
)
58+
}
59+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
@file:Suppress("DEPRECATION")
2+
3+
package au.com.shiftyjelly.pocketcasts.wear.ui.authentication
4+
5+
import androidx.lifecycle.ViewModel
6+
import au.com.shiftyjelly.pocketcasts.repositories.podcast.PodcastManager
7+
import au.com.shiftyjelly.pocketcasts.repositories.sync.LoginResult
8+
import au.com.shiftyjelly.pocketcasts.repositories.sync.SignInSource
9+
import au.com.shiftyjelly.pocketcasts.repositories.sync.SyncManager
10+
import au.com.shiftyjelly.pocketcasts.utils.log.LogBuffer
11+
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
12+
import com.google.android.gms.auth.api.signin.GoogleSignInClient
13+
import com.google.android.horologist.auth.data.googlesignin.GoogleSignInEventListener
14+
import com.google.android.horologist.auth.ui.googlesignin.signin.GoogleSignInViewModel
15+
import dagger.hilt.android.lifecycle.HiltViewModel
16+
import javax.inject.Inject
17+
import kotlinx.coroutines.flow.MutableStateFlow
18+
import kotlinx.coroutines.flow.asStateFlow
19+
import kotlinx.coroutines.flow.update
20+
21+
@HiltViewModel
22+
class LegacyLoginWithGoogleScreenViewModel @Inject constructor(
23+
googleSignInClient: GoogleSignInClient,
24+
private val podcastManager: PodcastManager,
25+
private val syncManager: SyncManager,
26+
) : ViewModel(),
27+
GoogleSignInEventListener {
28+
29+
data class State(
30+
val googleSignInAccount: GoogleSignInAccount?,
31+
)
32+
33+
val googleSignInViewModel = GoogleSignInViewModel(googleSignInClient, this)
34+
35+
private val _state = MutableStateFlow(
36+
State(googleSignInAccount = null),
37+
)
38+
val state = _state.asStateFlow()
39+
40+
override suspend fun onSignedIn(account: GoogleSignInAccount) {
41+
_state.update {
42+
it.copy(
43+
googleSignInAccount = account,
44+
)
45+
}
46+
47+
account.idToken?.let { idToken ->
48+
val loginResult = syncManager.loginWithGoogle(idToken, SignInSource.UserInitiated.Watch)
49+
when (loginResult) {
50+
is LoginResult.Failed -> {
51+
LogBuffer.i(LogBuffer.TAG_BACKGROUND_TASKS, "Failed to login with Google: ${loginResult.message}")
52+
}
53+
is LoginResult.Success -> {
54+
podcastManager.refreshPodcastsAfterSignIn()
55+
}
56+
}
57+
}
58+
}
59+
60+
fun clearPreviousSignIn() {
61+
googleSignInViewModel.googleSignInClient.signOut()
62+
}
63+
}

wear/src/main/kotlin/au/com/shiftyjelly/pocketcasts/wear/ui/authentication/LoginWithGoogleViewModel.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import android.content.Context
55
import androidx.credentials.CredentialManager
66
import androidx.credentials.CustomCredential
77
import androidx.credentials.GetCredentialRequest
8-
import androidx.credentials.GetPasswordOption
98
import androidx.credentials.PasswordCredential
109
import androidx.credentials.exceptions.GetCredentialCancellationException
11-
import androidx.credentials.exceptions.GetCredentialException
1210
import androidx.credentials.exceptions.GetCredentialInterruptedException
1311
import androidx.credentials.exceptions.NoCredentialException
1412
import androidx.lifecycle.ViewModel
@@ -21,11 +19,9 @@ import au.com.shiftyjelly.pocketcasts.repositories.sync.SyncManager
2119
import au.com.shiftyjelly.pocketcasts.utils.extensions.isGooglePlayServicesAvailableSuccess
2220
import au.com.shiftyjelly.pocketcasts.utils.log.LogBuffer
2321
import com.google.android.gms.common.GoogleApiAvailability
24-
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
2522
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
2623
import dagger.hilt.android.lifecycle.HiltViewModel
2724
import dagger.hilt.android.qualifiers.ApplicationContext
28-
import java.util.UUID
2925
import javax.inject.Inject
3026
import kotlinx.coroutines.flow.MutableStateFlow
3127
import kotlinx.coroutines.flow.asStateFlow

0 commit comments

Comments
 (0)