@iternio/react-native-auto-play 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/android/build.gradle +4 -2
- package/android/src/automotive/AndroidManifest-nonnav.xml +0 -2
- package/android/src/automotive/AndroidManifest.xml +0 -2
- package/android/src/main/AndroidManifest-nonnav.xml +0 -2
- package/android/src/main/AndroidManifest.xml +0 -2
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoService.kt +3 -27
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AutoPlayError.kt +4 -0
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlayTiming.kt +122 -0
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridSignInTemplate.kt +23 -0
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/SignInWithGoogleActivity.kt +79 -27
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/Parser.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/SignInTemplate.kt +35 -21
- package/installTimers.ts +3 -0
- package/installTimers.web.ts +2 -0
- package/ios/hybrid/HybridAutoPlayTiming.swift +123 -0
- package/lib/components/SafeAreaView.js +1 -1
- package/lib/hybrid/HybridAutoPlayTiming.d.ts +2 -0
- package/lib/hybrid/HybridAutoPlayTiming.js +2 -0
- package/lib/hybrid/HybridVoice.js +2 -2
- package/lib/index.js +0 -2
- package/lib/index.web.d.ts +4 -2
- package/lib/installTimers.d.ts +1 -0
- package/lib/installTimers.js +8 -0
- package/lib/installTimers.web.d.ts +1 -0
- package/lib/installTimers.web.js +2 -0
- package/lib/scenes/CarPlayDashboardScene.d.ts +3 -3
- package/lib/specs/AutoPlayTiming.nitro.d.ts +27 -0
- package/lib/specs/AutoPlayTiming.nitro.js +1 -0
- package/lib/specs/SignInTemplate.nitro.d.ts +1 -0
- package/lib/templates/SignInTemplate.d.ts +5 -0
- package/lib/templates/SignInTemplate.js +7 -0
- package/lib/types/Telemetry.d.ts +1 -1
- package/lib/utils/AutoPlayTimers.d.ts +21 -0
- package/lib/utils/AutoPlayTimers.js +45 -0
- package/lib/utils/NitroImage.d.ts +5 -2
- package/lib/utils/NitroImage.js +1 -1
- package/lib-cjs/components/SafeAreaView.js +1 -1
- package/lib-cjs/hybrid/HybridVoice.js +2 -2
- package/lib-cjs/index.js +0 -5
- package/lib-cjs/templates/SignInTemplate.js +7 -0
- package/lib-cjs/utils/NitroImage.js +1 -1
- package/nitro.json +10 -0
- package/nitrogen/generated/android/ReactNativeAutoPlay+autolinking.cmake +2 -0
- package/nitrogen/generated/android/ReactNativeAutoPlayOnLoad.cpp +16 -0
- package/nitrogen/generated/android/c++/JActiveCarUxRestrictions.hpp +1 -1
- package/nitrogen/generated/android/c++/JAlertActionStyle.hpp +1 -1
- package/nitrogen/generated/android/c++/JAlertDismissalReason.hpp +1 -1
- package/nitrogen/generated/android/c++/JAppFocusState.hpp +1 -1
- package/nitrogen/generated/android/c++/JAssetImage.hpp +18 -18
- package/nitrogen/generated/android/c++/JAutoText.hpp +1 -1
- package/nitrogen/generated/android/c++/JBooleanTelemetryItem.hpp +1 -1
- package/nitrogen/generated/android/c++/JCarUxRestrictions.hpp +1 -1
- package/nitrogen/generated/android/c++/JChargingConnector.hpp +1 -1
- package/nitrogen/generated/android/c++/JClusterEventName.hpp +1 -1
- package/nitrogen/generated/android/c++/JColorScheme.hpp +1 -1
- package/nitrogen/generated/android/c++/JDistance.hpp +1 -1
- package/nitrogen/generated/android/c++/JDistanceUnits.hpp +1 -1
- package/nitrogen/generated/android/c++/JDurationWithTimeZone.hpp +1 -1
- package/nitrogen/generated/android/c++/JEventName.hpp +1 -1
- package/nitrogen/generated/android/c++/JForkType.hpp +1 -1
- package/nitrogen/generated/android/c++/JGlyphImage.hpp +1 -1
- package/nitrogen/generated/android/c++/JGoogleSignIn.hpp +1 -1
- package/nitrogen/generated/android/c++/JGoogleSignInAccount.hpp +1 -1
- package/nitrogen/generated/android/c++/JGridImageSize.hpp +1 -1
- package/nitrogen/generated/android/c++/JGridTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JHybridAutoPlayTimingSpec.cpp +59 -0
- package/nitrogen/generated/android/c++/JHybridAutoPlayTimingSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JHybridSignInTemplateSpec.cpp +5 -0
- package/nitrogen/generated/android/c++/JHybridSignInTemplateSpec.hpp +1 -0
- package/nitrogen/generated/android/c++/JImageLane.hpp +1 -1
- package/nitrogen/generated/android/c++/JInformationTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JInputSignIn.hpp +1 -1
- package/nitrogen/generated/android/c++/JKeepType.hpp +1 -1
- package/nitrogen/generated/android/c++/JKeyboardType.hpp +1 -1
- package/nitrogen/generated/android/c++/JLaneGuidance.hpp +1 -1
- package/nitrogen/generated/android/c++/JListImageType.hpp +1 -1
- package/nitrogen/generated/android/c++/JListTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JLocation.hpp +1 -1
- package/nitrogen/generated/android/c++/JManeuverState.hpp +1 -1
- package/nitrogen/generated/android/c++/JManeuverType.hpp +1 -1
- package/nitrogen/generated/android/c++/JMapTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JMessageTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JNavigationAlertAction.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroAction.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroActionType.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroAlignment.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroAttributedString.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroAttributedStringImage.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroBaseMapTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroButtonStyle.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroChargerLocation.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroChargerOutlet.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroColor.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroGridButton.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroLoadingManeuver.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroMapButton.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroMapButtonType.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroMessageManeuver.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroNavigationAlert.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroOptionsPanelChargerSection.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroOptionsPanelConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroOptionsPanelGridSection.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroRoutingManeuver.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroRow.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroSection.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroSectionType.hpp +1 -1
- package/nitrogen/generated/android/c++/JNumericTelemetryItem.hpp +1 -1
- package/nitrogen/generated/android/c++/JOffRampType.hpp +1 -1
- package/nitrogen/generated/android/c++/JOnRampType.hpp +1 -1
- package/nitrogen/generated/android/c++/JPermissionRequestResult.hpp +1 -1
- package/nitrogen/generated/android/c++/JPinSignIn.hpp +1 -1
- package/nitrogen/generated/android/c++/JPoint.hpp +1 -1
- package/nitrogen/generated/android/c++/JPreferredImageLane.hpp +1 -1
- package/nitrogen/generated/android/c++/JQrSignIn.hpp +1 -1
- package/nitrogen/generated/android/c++/JRemoteImage.hpp +1 -1
- package/nitrogen/generated/android/c++/JRouteChoice.hpp +1 -1
- package/nitrogen/generated/android/c++/JSafeAreaInsets.hpp +1 -1
- package/nitrogen/generated/android/c++/JSearchTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JSignInMethods.hpp +1 -1
- package/nitrogen/generated/android/c++/JSignInTemplateConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JStringTelemetryItem.hpp +1 -1
- package/nitrogen/generated/android/c++/JTelemetry.hpp +1 -1
- package/nitrogen/generated/android/c++/JTextInputType.hpp +1 -1
- package/nitrogen/generated/android/c++/JTrafficSide.hpp +1 -1
- package/nitrogen/generated/android/c++/JTravelEstimates.hpp +1 -1
- package/nitrogen/generated/android/c++/JTripConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JTripPoint.hpp +1 -1
- package/nitrogen/generated/android/c++/JTripPreviewTextConfiguration.hpp +1 -1
- package/nitrogen/generated/android/c++/JTripSelectorCallback.hpp +1 -1
- package/nitrogen/generated/android/c++/JTripsConfig.hpp +1 -1
- package/nitrogen/generated/android/c++/JTurnType.hpp +1 -1
- package/nitrogen/generated/android/c++/JVehicleTelemetryItem.hpp +1 -1
- package/nitrogen/generated/android/c++/JVisibilityState.hpp +1 -1
- package/nitrogen/generated/android/c++/JVisibleTravelEstimate.hpp +1 -1
- package/nitrogen/generated/android/c++/JVoiceAudioEncoding.hpp +1 -1
- package/nitrogen/generated/android/c++/JVoiceInputChunk.hpp +1 -1
- package/nitrogen/generated/android/c++/JVoiceInputResult.hpp +1 -1
- package/nitrogen/generated/android/c++/JWaypointCoordinate.hpp +1 -1
- package/nitrogen/generated/android/c++/JWindowInformation.hpp +1 -1
- package/nitrogen/generated/android/c++/JZoomEvent.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/AssetImage.kt +15 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_ActiveCarUxRestrictions.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_AlertDismissalReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_AppFocusState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_ColorScheme.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_Point.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_Point_double.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_Point_std__optional_Point_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_SafeAreaInsets.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_VisibilityState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_VoiceInputChunk.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_WindowInformation.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_bool.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__optional_Location__std__optional_std__string_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__optional_Telemetry_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__optional_bool_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__optional_std__string__std__optional_GoogleSignInAccount_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__string_ColorScheme.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__string_ZoomEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__string_bool.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_std__string_std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutoTelemetrySpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutomotiveSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidWindowInformationSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlaySpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlayTimingSpec.kt +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridClusterSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridGridTemplateSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridInformationTemplateSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridListTemplateSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridMapTemplateSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridMessageTemplateSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridSearchTemplateSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridSignInTemplateSpec.kt +6 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridVoiceSpec.kt +2 -0
- package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.hpp +35 -9
- package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Umbrella.hpp +5 -0
- package/nitrogen/generated/ios/ReactNativeAutoPlayAutolinking.mm +8 -0
- package/nitrogen/generated/ios/ReactNativeAutoPlayAutolinking.swift +12 -0
- package/nitrogen/generated/ios/c++/HybridAutoPlayTimingSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridAutoPlayTimingSpecSwift.hpp +88 -0
- package/nitrogen/generated/ios/swift/AssetImage.swift +13 -13
- package/nitrogen/generated/ios/swift/HybridAutoPlayTimingSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridAutoPlayTimingSpec_cxx.swift +154 -0
- package/nitrogen/generated/shared/c++/AssetImage.hpp +18 -18
- package/nitrogen/generated/shared/c++/HybridAutoPlayTimingSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridAutoPlayTimingSpec.hpp +63 -0
- package/nitrogen/generated/shared/c++/HybridSignInTemplateSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridSignInTemplateSpec.hpp +1 -0
- package/package.json +6 -4
- package/src/components/SafeAreaView.tsx +1 -1
- package/src/hybrid/HybridAutoPlayTiming.ts +5 -0
- package/src/hybrid/HybridVoice.ts +2 -2
- package/src/index.ts +0 -3
- package/src/installTimers.ts +9 -0
- package/src/installTimers.web.ts +2 -0
- package/src/specs/AutoPlayTiming.nitro.ts +25 -0
- package/src/specs/SignInTemplate.nitro.ts +1 -0
- package/src/templates/SignInTemplate.ts +8 -0
- package/src/types/Telemetry.ts +1 -1
- package/src/utils/AutoPlayTimers.ts +60 -0
- package/src/utils/NitroImage.ts +13 -3
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HeadlessTaskService.kt +0 -36
- package/lib/AutoPlayHeadlessJsTask.d.ts +0 -5
- package/lib/AutoPlayHeadlessJsTask.js +0 -14
- package/lib-cjs/AutoPlayHeadlessJsTask.js +0 -16
- package/src/AutoPlayHeadlessJsTask.ts +0 -22
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
## Requirements
|
|
26
26
|
|
|
27
27
|
- **iOS builds require Xcode 27+** (the iOS 27 SDK), even for apps that don't use any `mapConfig`/panel features — the library references `CPMapPanel`/`CPPanel` types internally behind `@available(iOS 27.0, *)` checks, but `@available` only defers *runtime* execution, not compile-time symbol resolution, so the SDK must be present to build at all.
|
|
28
|
+
- **`react-native-nitro-modules` 0.37.1 or newer** — the generated native bindings target that runtime shape; an older nitro-modules install fails at native build time.
|
|
28
29
|
|
|
29
30
|
## Installation
|
|
30
31
|
|
|
@@ -474,10 +475,11 @@ Use the color `'default'` for every monochrome icon that has to stay readable in
|
|
|
474
475
|
|
|
475
476
|
### 1. Register the AutoPlay Components
|
|
476
477
|
|
|
477
|
-
You need to register your AutoPlay components in your app's entry file (e.g., `index.js`).
|
|
478
|
+
You need to register your AutoPlay components in your app's entry file (e.g., `index.js`). Import `@iternio/react-native-auto-play/installTimers` — a side-effect-only module that replaces the global `setTimeout`/`setInterval`/`requestAnimationFrame` (and their `clear*`/`cancel*` counterparts) with versions that keep running while CarPlay/Android Auto is actively driving the car screen, even if the phone itself is backgrounded or its screen is locked. React Native's own timers throttle or pause in that state regardless of whether the app process is actually still alive, which would otherwise stall ETA updates and telemetry polling. It must run before any other module has a chance to capture a reference to the original globals, which means it must be your entry file's **first import** — ES import declarations are hoisted and evaluated in source order, so it needs to come before everything else, including `react-native` itself:
|
|
478
479
|
|
|
479
480
|
```javascript
|
|
480
481
|
// index.js
|
|
482
|
+
import '@iternio/react-native-auto-play/installTimers';
|
|
481
483
|
import { AppRegistry } from 'react-native';
|
|
482
484
|
import { name as appName } from './app.json';
|
|
483
485
|
import App from './src/App';
|
|
@@ -680,7 +682,7 @@ Below is a concise overview of the most important props per template. Optional p
|
|
|
680
682
|
|
|
681
683
|
| Prop | Type | Required | Notes |
|
|
682
684
|
| --- | --- | --- | --- |
|
|
683
|
-
| `signInMethod` | `SignInMethod` | ✅ | The sign-in method configuration. Can be QRSignIn, PinSignIn, InputSignIn. |
|
|
685
|
+
| `signInMethod` | `SignInMethod` | ✅ | The sign-in method configuration. Can be QRSignIn, PinSignIn, InputSignIn, GoogleSignIn. |
|
|
684
686
|
| `title` | `string` | ❌ | Header title. |
|
|
685
687
|
| `additionalText` | `string` | ❌ | Additional descriptive text. |
|
|
686
688
|
| `instructions` | `string` | ❌ | Sign-in Instructions text. |
|
|
@@ -694,6 +696,19 @@ Below is a concise overview of the most important props per template. Optional p
|
|
|
694
696
|
| QR | `QrSignIn` | QR Code sign in |
|
|
695
697
|
| PIN | `PinSignIn` | PIN Code sign in (1–12 characters) |
|
|
696
698
|
| Input | `InputSignIn` | Text sign in, for example mail/username and password |
|
|
699
|
+
| Google | `GoogleSignIn` | Sign in with Google. Requires Google Play services 23.08 or newer — check `SignInTemplate.isGoogleSignInAvailable()` first. |
|
|
700
|
+
|
|
701
|
+
`GoogleSignIn` takes a `serverClientId` (your **Web** OAuth client ID), a `signInButtonText` and a `callback(error?, account?)`. On success the returned `GoogleSignInAccount` always carries both `idToken` and `serverAuthCode` (offline access, `openid` `profile` `email` scopes) for your backend. If the user cancels, or either the account picker or the authorization step fails, `callback` is called with an `error` and no account.
|
|
702
|
+
|
|
703
|
+
**Sign in with Google needs a recent Play services.** Constructing a `SignInTemplate` with `GoogleSignIn` throws `playServicesUnavailable` (and `updateTemplate` rejects with it) when Google Play services is missing or older than 23.08 (e.g. Android Automotive builds without GMS). Pick a different method up front instead:
|
|
704
|
+
|
|
705
|
+
```ts
|
|
706
|
+
const signInMethod: SignInMethod = SignInTemplate.isGoogleSignInAvailable()
|
|
707
|
+
? { method: SignInMethods.GOOGLE, serverClientId, signInButtonText: 'Sign in with Google', callback }
|
|
708
|
+
: { method: SignInMethods.QR, url: loginUrl };
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
`isGoogleSignInAvailable()` always returns `false` on iOS.
|
|
697
712
|
|
|
698
713
|
For InputSignIn the keyboard and input fields can be configured with following properties:
|
|
699
714
|
|
|
@@ -1314,8 +1329,6 @@ The same no-op surface is what `react-native-web` builds get automatically via `
|
|
|
1314
1329
|
|
|
1315
1330
|
- **Broken exceptions with `react-native-skia`**: When using `react-native-skia` exceptions on iOS are not reported correctly. This is fixed since version `2.4.19` of `react-native-skia`. For more details, see this [pull request](https://github.com/Shopify/react-native-skia/pull/3595) and [issue](https://github.com/Shopify/react-native-skia/issues/3635).
|
|
1316
1331
|
- **AppState on iOS**: The `AppState` module from React Native does not work correctly on iOS because this library uses scenes, which are not supported by the stock `AppState` module. This library provides a custom state listener that works for both Android and iOS. Use `HybridAutoPlay.addListenerRenderState` instead of `AppState`.
|
|
1317
|
-
- **Timers stop on screen lock**: iOS stops all timers when the device main screen is turned off. To ensure timers continue to run (which is often necessary for background tasks related to autoplay), a patch for `react-native` is required. A patch is included in the root `patches/` directory and can be applied using `patch-package`.
|
|
1318
|
-
In case you are using Expo SDK >= 56 make sure to set `buildReactNativeFromSource` to `true` in your app config for [expo-build-properties](https://docs.expo.dev/versions/latest/sdk/build-properties/#sharedbuildconfigfields), otherwise the patch can't be applied.
|
|
1319
1332
|
- **expo-splash-screen stuck on iOS**: The `expo-splash-screen` module is broken on iOS because it does not support scenes, which are used by this library. This can cause the splash screen to be stuck on either the mobile device or on CarPlay. To fix this, a patch for `expo-splash-screen` is included in the root `patches/` directory and can be applied using `patch-package`. After applying the patch, you can hide the splash screen for a specific scene by passing the module name to the `hide` or `hideAsync` function. The module name can be one of the values from the `AutoPlayModules` enum or the UUID of a cluster screen.
|
|
1320
1333
|
```tsx
|
|
1321
1334
|
import { hideAsync } from 'expo-splash-screen';
|
package/android/build.gradle
CHANGED
|
@@ -165,7 +165,9 @@ dependencies {
|
|
|
165
165
|
implementation "androidx.car.app:app-projected:1.7.0"
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
implementation 'com.google.android.gms:play-services-
|
|
169
|
-
implementation '
|
|
168
|
+
implementation 'com.google.android.gms:play-services-auth:22.0.0'
|
|
169
|
+
implementation 'androidx.credentials:credentials:1.6.0'
|
|
170
|
+
implementation 'androidx.credentials:credentials-play-services-auth:1.6.0'
|
|
171
|
+
implementation 'com.google.android.libraries.identity.googleid:googleid:1.1.1'
|
|
170
172
|
}
|
|
171
173
|
|
|
@@ -58,8 +58,6 @@
|
|
|
58
58
|
</intent-filter>
|
|
59
59
|
</service>
|
|
60
60
|
|
|
61
|
-
<service android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.HeadlessTaskService" />
|
|
62
|
-
|
|
63
61
|
<activity android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.SignInWithGoogleActivity" />
|
|
64
62
|
|
|
65
63
|
<provider
|
|
@@ -74,8 +74,6 @@
|
|
|
74
74
|
</intent-filter>
|
|
75
75
|
</service>
|
|
76
76
|
|
|
77
|
-
<service android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.HeadlessTaskService" />
|
|
78
|
-
|
|
79
77
|
<activity android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.SignInWithGoogleActivity" />
|
|
80
78
|
|
|
81
79
|
<provider
|
|
@@ -28,8 +28,6 @@
|
|
|
28
28
|
<meta-data android:name="com.google.android.gms.car.application" android:resource="@xml/automotive_app_desc" tools:ignore="MetadataTagInsideApplicationTag" />
|
|
29
29
|
<meta-data android:name="androidx.car.app.minCarApiLevel" android:value="1" />
|
|
30
30
|
|
|
31
|
-
<service android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.HeadlessTaskService" />
|
|
32
|
-
|
|
33
31
|
<provider android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.ActivityRenderStateProvider" android:authorities="${applicationId}.ActivityRenderStateProvider" android:exported="false" />
|
|
34
32
|
</application>
|
|
35
33
|
</manifest>
|
|
@@ -37,8 +37,6 @@
|
|
|
37
37
|
<meta-data android:name="com.google.android.gms.car.application" android:resource="@xml/automotive_app_desc" tools:ignore="MetadataTagInsideApplicationTag" />
|
|
38
38
|
<meta-data android:name="androidx.car.app.minCarApiLevel" android:value="1" />
|
|
39
39
|
|
|
40
|
-
<service android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.HeadlessTaskService" />
|
|
41
|
-
|
|
42
40
|
<provider android:name="com.margelo.nitro.swe.iternio.reactnativeautoplay.ActivityRenderStateProvider" android:authorities="${applicationId}.ActivityRenderStateProvider" android:exported="false" />
|
|
43
41
|
</application>
|
|
44
42
|
</manifest>
|
|
@@ -5,14 +5,10 @@ import android.annotation.SuppressLint
|
|
|
5
5
|
import android.app.Notification
|
|
6
6
|
import android.app.NotificationChannel
|
|
7
7
|
import android.app.NotificationManager
|
|
8
|
-
import android.content.ComponentName
|
|
9
|
-
import android.content.Intent
|
|
10
|
-
import android.content.ServiceConnection
|
|
11
8
|
import android.content.pm.ApplicationInfo
|
|
12
9
|
import android.content.pm.PackageManager
|
|
13
10
|
import android.graphics.Bitmap
|
|
14
11
|
import android.os.Build
|
|
15
|
-
import android.os.IBinder
|
|
16
12
|
import android.util.Log
|
|
17
13
|
import androidx.car.app.CarAppService
|
|
18
14
|
import androidx.car.app.Session
|
|
@@ -23,6 +19,7 @@ import androidx.core.app.NotificationCompat
|
|
|
23
19
|
import androidx.core.app.NotificationManagerCompat
|
|
24
20
|
import androidx.lifecycle.DefaultLifecycleObserver
|
|
25
21
|
import androidx.lifecycle.LifecycleOwner
|
|
22
|
+
import com.facebook.react.ReactApplication
|
|
26
23
|
import com.facebook.react.bridge.LifecycleEventListener
|
|
27
24
|
import com.margelo.nitro.NitroModules
|
|
28
25
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.AppInfo
|
|
@@ -30,7 +27,6 @@ import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.AppInfo
|
|
|
30
27
|
class AndroidAutoService : CarAppService() {
|
|
31
28
|
private lateinit var notificationManager: NotificationManager
|
|
32
29
|
|
|
33
|
-
private var isServiceBound = false
|
|
34
30
|
private var isSessionStarted = false
|
|
35
31
|
private var isReactAppStarted = false
|
|
36
32
|
|
|
@@ -48,6 +44,8 @@ class AndroidAutoService : CarAppService() {
|
|
|
48
44
|
super.onCreate()
|
|
49
45
|
instance = this
|
|
50
46
|
|
|
47
|
+
(application as? ReactApplication)?.reactHost?.start()
|
|
48
|
+
|
|
51
49
|
NitroModules.applicationContext?.addLifecycleEventListener(reactLifecycleObserver)
|
|
52
50
|
|
|
53
51
|
notificationManager = getSystemService(NotificationManager::class.java)
|
|
@@ -98,11 +96,6 @@ class AndroidAutoService : CarAppService() {
|
|
|
98
96
|
}
|
|
99
97
|
|
|
100
98
|
private val sessionLifecycleObserver = object : DefaultLifecycleObserver {
|
|
101
|
-
override fun onCreate(owner: LifecycleOwner) {
|
|
102
|
-
val serviceIntent = Intent(applicationContext, HeadlessTaskService::class.java)
|
|
103
|
-
bindService(serviceIntent, connection, BIND_AUTO_CREATE)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
99
|
override fun onResume(owner: LifecycleOwner) {
|
|
107
100
|
isSessionStarted = true
|
|
108
101
|
}
|
|
@@ -112,27 +105,10 @@ class AndroidAutoService : CarAppService() {
|
|
|
112
105
|
}
|
|
113
106
|
|
|
114
107
|
override fun onDestroy(owner: LifecycleOwner) {
|
|
115
|
-
if (isServiceBound) {
|
|
116
|
-
unbindService(connection)
|
|
117
|
-
isServiceBound = false
|
|
118
|
-
}
|
|
119
|
-
|
|
120
108
|
this@AndroidAutoService.stopForeground(STOP_FOREGROUND_REMOVE)
|
|
121
109
|
}
|
|
122
110
|
}
|
|
123
111
|
|
|
124
|
-
private val connection: ServiceConnection = object : ServiceConnection {
|
|
125
|
-
override fun onServiceConnected(
|
|
126
|
-
className: ComponentName, service: IBinder
|
|
127
|
-
) {
|
|
128
|
-
isServiceBound = true
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
override fun onServiceDisconnected(arg0: ComponentName) {
|
|
132
|
-
isServiceBound = false
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
112
|
private fun createNotification(
|
|
137
113
|
title: String?, text: String?, largeIcon: Bitmap?
|
|
138
114
|
): Notification {
|
package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AutoPlayError.kt
CHANGED
|
@@ -8,3 +8,7 @@ class TemplateNotFoundException(private val templateId: String) :
|
|
|
8
8
|
class VoiceInputCancelledException : Exception("voiceInputCancelled") {
|
|
9
9
|
override fun toString(): String = "voiceInputCancelled"
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
class PlayServicesUnavailableException : Exception("playServicesUnavailable") {
|
|
13
|
+
override fun toString(): String = "playServicesUnavailable"
|
|
14
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
package com.margelo.nitro.swe.iternio.reactnativeautoplay
|
|
2
|
+
|
|
3
|
+
import android.os.Handler
|
|
4
|
+
import android.os.Looper
|
|
5
|
+
import android.os.SystemClock
|
|
6
|
+
import java.lang.ref.WeakReference
|
|
7
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
8
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
9
|
+
import java.util.concurrent.atomic.AtomicLong
|
|
10
|
+
|
|
11
|
+
private class ScheduledTimer(
|
|
12
|
+
val callback: () -> Unit,
|
|
13
|
+
val interval: Long,
|
|
14
|
+
val repeats: Boolean,
|
|
15
|
+
var target: Long,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// Weak ref, so a stale instance (dev reload / host restart) can't tick forever in the queue.
|
|
19
|
+
private class TickRunnable(target: HybridAutoPlayTiming) : Runnable {
|
|
20
|
+
private val ref = WeakReference(target)
|
|
21
|
+
|
|
22
|
+
override fun run() {
|
|
23
|
+
ref.get()?.tick()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Backs setTimeout/setInterval (see AutoPlayTimers.ts) with a plain Handler loop on the main
|
|
29
|
+
* Looper -- not Choreographer, which JavaTimerManager itself uses and only keeps ticking while
|
|
30
|
+
* the host is resumed or a headless JS task is running. See AutoPlayTiming.nitro.ts.
|
|
31
|
+
*
|
|
32
|
+
* Ticks at a fixed ~60Hz while any timer is pending and stops entirely once idle, rather than
|
|
33
|
+
* scheduling a delay to the next due target: postDelayed's dispatch is gated on uptimeMillis(),
|
|
34
|
+
* which pauses during deep sleep, so a long pre-sleep delay would need that much *awake* time
|
|
35
|
+
* after waking. A short, repeated delay only needs ~16ms of awake time, so it fires within
|
|
36
|
+
* about one frame of the device waking regardless of sleep length -- due timers are then found
|
|
37
|
+
* correctly because target/now use elapsedRealtime(), which counts straight through sleep.
|
|
38
|
+
*/
|
|
39
|
+
class HybridAutoPlayTiming : HybridAutoPlayTimingSpec() {
|
|
40
|
+
companion object {
|
|
41
|
+
private const val FRAME_DURATION_MS = 1000L / 60L
|
|
42
|
+
|
|
43
|
+
// RN's TimerManager also numbers ids from 1; offsetting avoids collisions with ids a
|
|
44
|
+
// pre-install caller (e.g. React's own scheduler) still holds against the original
|
|
45
|
+
// clearTimeout, now replaced.
|
|
46
|
+
private const val ID_OFFSET = 1_000_000_000L
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private val handler = Handler(Looper.getMainLooper())
|
|
50
|
+
private val timers = ConcurrentHashMap<Double, ScheduledTimer>()
|
|
51
|
+
private val nextId = AtomicLong(ID_OFFSET)
|
|
52
|
+
private val isTicking = AtomicBoolean(false)
|
|
53
|
+
private val tickRunnable = TickRunnable(this)
|
|
54
|
+
|
|
55
|
+
private fun ensureTicking() {
|
|
56
|
+
if (isTicking.compareAndSet(false, true)) {
|
|
57
|
+
handler.postDelayed(tickRunnable, FRAME_DURATION_MS)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
internal fun tick() {
|
|
62
|
+
// elapsedRealtime(), not uptimeMillis(): both are immune to NTP/manual clock changes,
|
|
63
|
+
// but unlike uptimeMillis() this one keeps counting through deep sleep.
|
|
64
|
+
val now = SystemClock.elapsedRealtime()
|
|
65
|
+
|
|
66
|
+
// Sort by (target, id): ConcurrentHashMap iteration order is unspecified, so
|
|
67
|
+
// setTimeout(a, 0); setTimeout(b, 0) could otherwise fire b before a.
|
|
68
|
+
val dueIds = timers.entries.filter { it.value.target <= now }
|
|
69
|
+
.sortedWith(compareBy({ it.value.target }, { it.key }))
|
|
70
|
+
.map { it.key }
|
|
71
|
+
|
|
72
|
+
for (id in dueIds) {
|
|
73
|
+
// Re-check membership: a same-tick callback may have cleared a later timer in
|
|
74
|
+
// this snapshotted list (e.g. `clearTimeout(t); t = setTimeout(...)`).
|
|
75
|
+
val timer = timers[id] ?: continue
|
|
76
|
+
|
|
77
|
+
if (timer.repeats) {
|
|
78
|
+
timer.target = now + timer.interval
|
|
79
|
+
} else {
|
|
80
|
+
timers.remove(id)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
timer.callback()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (timers.isNotEmpty()) {
|
|
87
|
+
handler.postDelayed(tickRunnable, FRAME_DURATION_MS)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isTicking.set(false)
|
|
92
|
+
// Reclaim: a concurrent createTimer() may have inserted an entry and found `isTicking`
|
|
93
|
+
// still true (skipping its own post) in the gap before the flag was cleared above.
|
|
94
|
+
if (timers.isNotEmpty()) {
|
|
95
|
+
ensureTicking()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
override fun createTimer(callback: () -> Unit, durationMs: Double, repeats: Boolean): Double {
|
|
100
|
+
val interval = durationMs.toLong().coerceAtLeast(0)
|
|
101
|
+
val id = nextId.getAndIncrement().toDouble()
|
|
102
|
+
timers[id] = ScheduledTimer(
|
|
103
|
+
callback = callback,
|
|
104
|
+
interval = interval,
|
|
105
|
+
repeats = repeats,
|
|
106
|
+
target = SystemClock.elapsedRealtime() + interval,
|
|
107
|
+
)
|
|
108
|
+
ensureTicking()
|
|
109
|
+
return id
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override fun deleteTimer(id: Double) {
|
|
113
|
+
timers.remove(id)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
override fun dispose() {
|
|
117
|
+
handler.removeCallbacks(tickRunnable)
|
|
118
|
+
isTicking.set(false)
|
|
119
|
+
timers.clear()
|
|
120
|
+
super.dispose()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
package com.margelo.nitro.swe.iternio.reactnativeautoplay
|
|
2
2
|
|
|
3
|
+
import com.google.android.gms.common.ConnectionResult
|
|
4
|
+
import com.google.android.gms.common.GoogleApiAvailability
|
|
5
|
+
import com.margelo.nitro.NitroModules
|
|
3
6
|
import com.margelo.nitro.core.Promise
|
|
4
7
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.AndroidAutoTemplate
|
|
5
8
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.SignInTemplate
|
|
@@ -9,14 +12,34 @@ class HybridSignInTemplate : HybridSignInTemplateSpec() {
|
|
|
9
12
|
val context = AndroidAutoSession.getRootContext()
|
|
10
13
|
?: throw IllegalArgumentException("createSignInTemplate failed, no carContext found")
|
|
11
14
|
|
|
15
|
+
assertSignInMethodAvailable(config)
|
|
16
|
+
|
|
12
17
|
val template = SignInTemplate(context, config)
|
|
13
18
|
AndroidAutoTemplate.setTemplate(config.id, template)
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
override fun updateTemplate(templateId: String, config: SignInTemplateConfig): Promise<Unit> {
|
|
17
22
|
return Promise.async {
|
|
23
|
+
assertSignInMethodAvailable(config)
|
|
18
24
|
val template = AndroidAutoTemplate.getTemplate<SignInTemplate>(templateId)
|
|
19
25
|
template.updateTemplate(config)
|
|
20
26
|
}
|
|
21
27
|
}
|
|
28
|
+
|
|
29
|
+
override fun isGoogleSignInAvailable(): Boolean {
|
|
30
|
+
val context = NitroModules.applicationContext ?: return false
|
|
31
|
+
|
|
32
|
+
// on older versions sign in with google is no longer supported
|
|
33
|
+
val googleServiceMinVersion = 230815045
|
|
34
|
+
val available = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(
|
|
35
|
+
context, googleServiceMinVersion
|
|
36
|
+
)
|
|
37
|
+
return ConnectionResult.SUCCESS == available
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private fun assertSignInMethodAvailable(config: SignInTemplateConfig) {
|
|
41
|
+
if (config.signInMethod?.asFourthOrNull() != null && !isGoogleSignInAvailable()) {
|
|
42
|
+
throw PlayServicesUnavailableException()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
22
45
|
}
|
|
@@ -4,42 +4,90 @@ import android.os.Binder
|
|
|
4
4
|
import android.os.Bundle
|
|
5
5
|
import android.os.IBinder
|
|
6
6
|
import androidx.activity.ComponentActivity
|
|
7
|
-
import androidx.activity.result.
|
|
7
|
+
import androidx.activity.result.IntentSenderRequest
|
|
8
8
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
9
|
+
import androidx.credentials.CredentialManager
|
|
10
|
+
import androidx.credentials.GetCredentialRequest
|
|
11
|
+
import androidx.lifecycle.lifecycleScope
|
|
12
|
+
import com.google.android.gms.auth.api.identity.AuthorizationRequest
|
|
13
|
+
import com.google.android.gms.auth.api.identity.Identity
|
|
14
|
+
import com.google.android.gms.common.Scopes
|
|
15
|
+
import com.google.android.gms.common.api.Scope
|
|
16
|
+
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
|
|
17
|
+
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
|
18
|
+
import kotlinx.coroutines.launch
|
|
12
19
|
import java.security.InvalidParameterException
|
|
13
|
-
import javax.annotation.Nullable
|
|
14
20
|
|
|
15
21
|
class SignInWithGoogleActivity : ComponentActivity() {
|
|
16
|
-
override fun onCreate(savedInstanceState: Bundle?) {
|
|
17
|
-
super.onCreate(savedInstanceState)
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
private var callback: OnSignInComplete? = null
|
|
24
|
+
private var idCredential: GoogleIdTokenCredential? = null
|
|
25
|
+
|
|
26
|
+
private val authLauncher = registerForActivityResult(
|
|
27
|
+
ActivityResultContracts.StartIntentSenderForResult()
|
|
28
|
+
) { result ->
|
|
29
|
+
val auth = runCatching {
|
|
30
|
+
Identity.getAuthorizationClient(this)
|
|
31
|
+
.getAuthorizationResultFromIntent(result.data)
|
|
32
|
+
}.getOrNull()
|
|
33
|
+
finishWith(auth?.serverAuthCode)
|
|
34
|
+
}
|
|
20
35
|
|
|
36
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
37
|
+
super.onCreate(savedInstanceState)
|
|
38
|
+
callback = intent.extras?.getBinder(BINDER_KEY) as OnSignInComplete?
|
|
21
39
|
val serverClientId = intent.extras?.getString("serverClientId")
|
|
22
40
|
?: throw InvalidParameterException("missing serverClientId parameter")
|
|
23
41
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
lifecycleScope.launch {
|
|
43
|
+
// 1. Authentication: ID token
|
|
44
|
+
val request = GetCredentialRequest.Builder()
|
|
45
|
+
.addCredentialOption(GetSignInWithGoogleOption.Builder(serverClientId).build())
|
|
46
|
+
.build()
|
|
47
|
+
try {
|
|
48
|
+
val result = CredentialManager.create(this@SignInWithGoogleActivity)
|
|
49
|
+
.getCredential(this@SignInWithGoogleActivity, request)
|
|
50
|
+
idCredential = GoogleIdTokenCredential.createFrom(result.credential.data)
|
|
51
|
+
} catch (_: Exception) {
|
|
52
|
+
callback?.onSignInComplete(null, null)
|
|
53
|
+
finish()
|
|
54
|
+
return@launch
|
|
55
|
+
}
|
|
33
56
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.
|
|
39
|
-
|
|
57
|
+
// 2. Authorization: server auth code
|
|
58
|
+
val authRequest = AuthorizationRequest.builder()
|
|
59
|
+
// matches the legacy DEFAULT_SIGN_IN + requestEmail() scopes, so the exchanged
|
|
60
|
+
// server auth code grants the same access as before the Credential Manager migration
|
|
61
|
+
.setRequestedScopes(
|
|
62
|
+
listOf(Scope(Scopes.OPEN_ID), Scope(Scopes.PROFILE), Scope(Scopes.EMAIL))
|
|
63
|
+
)
|
|
64
|
+
.requestOfflineAccess(serverClientId)
|
|
40
65
|
.build()
|
|
41
|
-
|
|
42
|
-
|
|
66
|
+
Identity.getAuthorizationClient(this@SignInWithGoogleActivity)
|
|
67
|
+
.authorize(authRequest)
|
|
68
|
+
.addOnSuccessListener { res ->
|
|
69
|
+
if (res.hasResolution()) {
|
|
70
|
+
authLauncher.launch(
|
|
71
|
+
IntentSenderRequest.Builder(res.pendingIntent!!.intentSender).build()
|
|
72
|
+
)
|
|
73
|
+
} else {
|
|
74
|
+
finishWith(res.serverAuthCode)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
.addOnFailureListener { finishWith(null) }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private fun finishWith(serverAuthCode: String?) {
|
|
82
|
+
// a cancelled or failed authorization step yields no server auth code; report it as a
|
|
83
|
+
// failed sign in so callers get either an error or a complete account, as with the
|
|
84
|
+
// legacy GoogleSignIn flow, never an account without a server auth code
|
|
85
|
+
if (serverAuthCode == null) {
|
|
86
|
+
callback?.onSignInComplete(null, null)
|
|
87
|
+
} else {
|
|
88
|
+
callback?.onSignInComplete(idCredential, serverAuthCode)
|
|
89
|
+
}
|
|
90
|
+
finish()
|
|
43
91
|
}
|
|
44
92
|
|
|
45
93
|
/**
|
|
@@ -49,9 +97,13 @@ class SignInWithGoogleActivity : ComponentActivity() {
|
|
|
49
97
|
/**
|
|
50
98
|
* Notifies that sign in flow completed.
|
|
51
99
|
*
|
|
52
|
-
* @param
|
|
100
|
+
* @param credential the account signed in or `null` if there were issues signing in.
|
|
101
|
+
* @param serverAuthCode the server auth code, always non-null when [credential] is non-null.
|
|
53
102
|
*/
|
|
54
|
-
abstract fun onSignInComplete(
|
|
103
|
+
abstract fun onSignInComplete(
|
|
104
|
+
credential: GoogleIdTokenCredential?,
|
|
105
|
+
serverAuthCode: String?
|
|
106
|
+
)
|
|
55
107
|
}
|
|
56
108
|
|
|
57
109
|
companion object {
|
package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/Parser.kt
CHANGED
|
@@ -378,7 +378,7 @@ object Parser {
|
|
|
378
378
|
}
|
|
379
379
|
row.onPress?.let {
|
|
380
380
|
setOnClickListener {
|
|
381
|
-
|
|
381
|
+
it(null)
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
384
|
row.browsable?.let {
|
|
@@ -650,7 +650,7 @@ object Parser {
|
|
|
650
650
|
// whose backing bitmap has already been recycled or failed to allocate;
|
|
651
651
|
// copy() can also throw (e.g., OOM on very large remote images). Either
|
|
652
652
|
// way we return null so the caller falls back to a placeholder icon.
|
|
653
|
-
return image.underlyingBitmap
|
|
653
|
+
return image.underlyingBitmap?.copy(Bitmap.Config.ARGB_8888, false)
|
|
654
654
|
} else if (image is CloseableXml) {
|
|
655
655
|
val drawable = image.buildDrawable()
|
|
656
656
|
return drawable?.toBitmap(
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
package com.margelo.nitro.swe.iternio.reactnativeautoplay.template
|
|
2
2
|
|
|
3
3
|
import android.content.Intent
|
|
4
|
-
import android.net.Uri
|
|
5
4
|
import android.os.Bundle
|
|
5
|
+
import android.util.Base64
|
|
6
6
|
import androidx.car.app.CarContext
|
|
7
7
|
import androidx.car.app.model.Action
|
|
8
8
|
import androidx.car.app.model.ActionStrip
|
|
@@ -18,7 +18,9 @@ import androidx.car.app.model.signin.ProviderSignInMethod
|
|
|
18
18
|
import androidx.car.app.model.signin.QRCodeSignInMethod
|
|
19
19
|
import androidx.car.app.model.signin.SignInTemplate
|
|
20
20
|
import androidx.core.graphics.drawable.IconCompat
|
|
21
|
-
import
|
|
21
|
+
import androidx.core.net.toUri
|
|
22
|
+
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
|
23
|
+
import com.margelo.nitro.NitroModules
|
|
22
24
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.KeyboardType
|
|
23
25
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.NitroAction
|
|
24
26
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.NitroActionType
|
|
@@ -26,8 +28,8 @@ import com.margelo.nitro.swe.iternio.reactnativeautoplay.R
|
|
|
26
28
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.SignInTemplateConfig
|
|
27
29
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.SignInWithGoogleActivity
|
|
28
30
|
import com.margelo.nitro.swe.iternio.reactnativeautoplay.TextInputType
|
|
31
|
+
import org.json.JSONObject
|
|
29
32
|
import java.security.InvalidParameterException
|
|
30
|
-
import androidx.core.net.toUri
|
|
31
33
|
|
|
32
34
|
class SignInTemplate(
|
|
33
35
|
context: CarContext, config: SignInTemplateConfig
|
|
@@ -115,32 +117,35 @@ class SignInTemplate(
|
|
|
115
117
|
).build()
|
|
116
118
|
)
|
|
117
119
|
setOnClickListener(ParkedOnlyOnClickListener.create {
|
|
118
|
-
val extras = Bundle(
|
|
120
|
+
val extras = Bundle(2)
|
|
119
121
|
extras.putBinder(
|
|
120
122
|
SignInWithGoogleActivity.BINDER_KEY,
|
|
121
123
|
object : SignInWithGoogleActivity.OnSignInComplete() {
|
|
122
|
-
override fun onSignInComplete(
|
|
123
|
-
|
|
124
|
+
override fun onSignInComplete(
|
|
125
|
+
credential: GoogleIdTokenCredential?,
|
|
126
|
+
serverAuthCode: String?
|
|
127
|
+
) {
|
|
128
|
+
if (credential == null) {
|
|
124
129
|
googleSignIn.callback("Error signing in", null)
|
|
125
|
-
|
|
126
|
-
googleSignIn.callback(
|
|
127
|
-
null,
|
|
128
|
-
com.margelo.nitro.swe.iternio.reactnativeautoplay.GoogleSignInAccount(
|
|
129
|
-
serverAuthCode = account.serverAuthCode,
|
|
130
|
-
email = account.email,
|
|
131
|
-
id = account.id,
|
|
132
|
-
displayName = account.displayName,
|
|
133
|
-
photoUrl = account.photoUrl?.toString(),
|
|
134
|
-
idToken = account.idToken,
|
|
135
|
-
givenName = account.givenName,
|
|
136
|
-
familyName = account.familyName
|
|
137
|
-
)
|
|
138
|
-
)
|
|
130
|
+
return
|
|
139
131
|
}
|
|
132
|
+
googleSignIn.callback(
|
|
133
|
+
null,
|
|
134
|
+
com.margelo.nitro.swe.iternio.reactnativeautoplay.GoogleSignInAccount(
|
|
135
|
+
serverAuthCode = serverAuthCode,
|
|
136
|
+
email = credential.id,
|
|
137
|
+
id = subFromIdToken(credential.idToken),
|
|
138
|
+
displayName = credential.displayName,
|
|
139
|
+
photoUrl = credential.profilePictureUri?.toString(),
|
|
140
|
+
idToken = credential.idToken,
|
|
141
|
+
givenName = credential.givenName,
|
|
142
|
+
familyName = credential.familyName
|
|
143
|
+
)
|
|
144
|
+
)
|
|
140
145
|
}
|
|
141
146
|
})
|
|
142
147
|
extras.putString("serverClientId", googleSignIn.serverClientId)
|
|
143
|
-
|
|
148
|
+
NitroModules.applicationContext?.startActivity(
|
|
144
149
|
Intent().setClass(context, SignInWithGoogleActivity::class.java)
|
|
145
150
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).putExtras(extras)
|
|
146
151
|
)
|
|
@@ -211,4 +216,13 @@ class SignInTemplate(
|
|
|
211
216
|
config.onPopped?.let { it() }
|
|
212
217
|
templates.remove(templateId)
|
|
213
218
|
}
|
|
219
|
+
|
|
220
|
+
private fun subFromIdToken(idToken: String): String? = runCatching {
|
|
221
|
+
val payload = idToken.split(".")[1]
|
|
222
|
+
val json = String(
|
|
223
|
+
Base64.decode(payload, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
|
|
224
|
+
Charsets.UTF_8
|
|
225
|
+
)
|
|
226
|
+
JSONObject(json).getString("sub")
|
|
227
|
+
}.getOrNull()
|
|
214
228
|
}
|
package/installTimers.ts
ADDED