Skip to content

Commit 0fd7338

Browse files
xserxseswbaklazec-schbogdan-niculescu-schfilip-misztal
authored
Feature - login prompt (schibsted#87)
* feat: login prompt content provider implementation * refactor: content provider interaction logic moved to SessionInfoManager class * feat: fetching content provider authorities from the package manager; checking is session on the device exists * chore: refactored loginPrompt classes code style * refactor: move sessionInfoManager to sharedPreferencesStorage class * refactor: fix compatibility issues with PackageManager.MATCH_ALL * refactor: login prompt content provider refactor on xserxses comments * Add loginPromptManager (schibsted#73) * Add loginPromptManager * Add translations (schibsted#82) * call loginPrompt from Client (schibsted#84) * call loginPrompt from Client clean main activity Run requestLoginPrompt in background thread * Login promp tracking (schibsted#80) * Propose SchibstedAccountTracking public and internal API * Present tracking API in ExampleApp * Document API * More readable logging * Initial events for show/hide login prompt * Tracking events for clicks * Update events (schibsted#86) * Add final tracking events --------- Co-authored-by: filip-misztal <filip.misztal@schibsted.com> Co-authored-by: bogdan-niculescu-sch <104439589+bogdan-niculescu-sch@users.noreply.github.com> * Fill in readme (schibsted#89) - apply outstanding review remark * Throw different error type if user cancels login (schibsted#83) * Throw different error type if user cancels login * Check error before state - throw NotAuthed.CancelledByUser error * Fix typo * Update webflows/src/main/java/com/schibsted/account/webflows/util/Util.kt Co-authored-by: Filip Misztal <filip.jan.misztal@gmail.com> * Review remarks before merge (schibsted#90) * initial cleanup * make tracking thread safe - small review remarks * cleanup layout * code cleanup * add localized logos * Update logos * fix dialog showing check * apply review remark * Fix query period on content provider getSessions * Change DB primary key to packageName for content provider (schibsted#91) * Change db primary key to packagename for content provider * On conclict - replace with new values * user writable database for writting --------- Co-authored-by: filip-misztal <filip.misztal@schibsted.com> * Use "use" to be more safe in case of failures + Nice syntax (schibsted#92) * Use use to be more safe * Even more idiomatic Kotlin --------- Co-authored-by: filip-misztal <filip.misztal@schibsted.com> * Send cancel event on eid user cancel (schibsted#93) * Send cancel event on eid user cancel * Small Readme update * Login prompt crash (schibsted#94) * Pass intent via argument instead of whole client * Prevent adding twice --------- Co-authored-by: filip-misztal <filip.misztal@schibsted.com> * add support for norsk bokmal and norsk nynorsk (schibsted#96) * add serverUrl to content provider query (schibsted#95) * check for local session before showing login prompt (schibsted#97) * check for local session before showing login prompt * apply review remark * Check also for presence - not only callback type (schibsted#98) Co-authored-by: filip-misztal <filip.misztal@schibsted.com> * Dismiss prompt when login is initiated (schibsted#99) Co-authored-by: filip-misztal <filip.misztal@schibsted.com> * Remove login promp on login click (schibsted#100) * Dismiss prompt when login is initiated * Better place * This is no longer needed --------- Co-authored-by: filip-misztal <filip.misztal@schibsted.com> * add extra properties for events (schibsted#101) * add extra properties for events * Update readme and minor cleanup --------- Co-authored-by: wbaklazec-sch <105283956+wbaklazec-sch@users.noreply.github.com> Co-authored-by: bogdan-niculescu-sch <104439589+bogdan-niculescu-sch@users.noreply.github.com> Co-authored-by: filip-misztal <filip.misztal@schibsted.com>
1 parent 695f214 commit 0fd7338

38 files changed

+1096
-28
lines changed

README.md

+44-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ New implementation of the Schibsted account Android SDK using the web flows via
99
[Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs/overview/):
1010

1111
* API documentation can be
12-
found [here](https://pages.github.schibsted.io/spt-identity/account-sdk-android-web/).
12+
found [here](https://pages.github.schibsted.io/spt-identity/account-sdk-android-web/).
1313
* An example implementation of the SDK can be found
14-
[here](https://github.schibsted.io/spt-identity/account-sdk-android-web/tree/master/app/src/main/java/com/schibsted/account/example).
14+
[here](https://github.schibsted.io/spt-identity/account-sdk-android-web/tree/master/app/src/main/java/com/schibsted/account/example).
1515

1616
## Getting started
1717

@@ -33,6 +33,7 @@ will help you create a client and configure the necessary data.
3333

3434
The SDK is available via
3535
[Maven Central](https://search.maven.org/artifact/com.schibsted.account/account-sdk-android-web):
36+
3637
```
3738
implementation 'com.schibsted.account:account-sdk-android-web:<version>'
3839
```
@@ -217,11 +218,51 @@ by following these steps:
217218
.create(<Your service interface>)
218219
}
219220
```
220-
221+
221222
The SDK will automatically inject the user access token as a Bearer token in the HTTP
222223
Authorization request header. If the access token is rejected with a `401 Unauthorized`
223224
response (e.g. due to having expired), the SDK will try to use the refresh token to obtain a
224225
new access token and then retry the request once more.
225226

226227
**Note:** If the refresh token request fails, due to the refresh token itself having expired
227228
or been invalidated by the user, the SDK will log the user out.
229+
230+
#### Login prompt
231+
232+
This is a light version of simplified-login, allowing mobile app developers integrating this
233+
SDK to prompt users for log in if a valid session was already detected on the device.
234+
This feature is making use of the single-sign on feature from web, allowing users to log in with
235+
only two taps.
236+
237+
**Note** that for this feature to work, both the app where the user has a valid session, and the app
238+
that implements and
239+
requests the login prompt need to use Android SDK Web version 6.1.0 or newer.
240+
241+
**Note** that it is the calling app's responsibility to request the login prompt only if the user is
242+
not
243+
already logged in, or if any other specific conditions are met.
244+
245+
Example:
246+
247+
```kotlin
248+
if (!user?.isLoggedIn()) {
249+
lifecycleScope.launch {
250+
ExampleApp.client.requestLoginPrompt(applicationContext, supportFragmentManager, true)
251+
}
252+
}
253+
```
254+
255+
#### Pulse tracking
256+
257+
We have also added a way of integrating the app's Pulse instance into the SDK allowing internal
258+
events to be sent.
259+
260+
**Important:** When integrating with the SDK defined events, please keep in mind that the Pulse
261+
instance needs to map `[SchibstedAccountTrackingEvent].providerComponent` to `provider.component`
262+
and `[SchibstedAccountTrackingEvent].deployTag` to `deploy_tag` before pushing the created events.
263+
264+
This is a temporary solution and will be subject to changes in the future, but in the meanwhile
265+
you can use the provided example from ExampleApp to connect your Pulse event transmitter, which
266+
will be then used internally to track login-prompt flows and also send events if the login was
267+
successful or not.
268+

app/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ apply plugin: 'com.android.application'
22
apply plugin: 'kotlin-android'
33

44
android {
5-
compileSdkVersion 32
5+
compileSdkVersion 34
66

77
defaultConfig {
88
applicationId "com.schibsted.account"
99
minSdkVersion 21
10-
targetSdkVersion 30
10+
targetSdkVersion 32
1111
versionCode 1
1212
versionName "1.0"
1313

app/src/main/java/com/schibsted/account/example/ExampleApp.kt

+14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import com.schibsted.account.example.MainActivity.Companion.LOGIN_FAILED_EXTRA
1111
import com.schibsted.account.webflows.activities.AuthorizationManagementActivity
1212
import com.schibsted.account.webflows.client.Client
1313
import com.schibsted.account.webflows.client.ClientConfiguration
14+
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackerStore
15+
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingEvent
16+
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingListener
1417
import timber.log.Timber
1518

1619
class ExampleApp : Application() {
@@ -22,6 +25,17 @@ class ExampleApp : Application() {
2225
initManualClient()
2326
initAuthorizationManagement()
2427
initTimber()
28+
initTracking()
29+
}
30+
31+
private fun initTracking() {
32+
val listener = object : SchibstedAccountTrackingListener {
33+
override fun onEvent(event: SchibstedAccountTrackingEvent) {
34+
Timber.d("Tracked event ${event::class.simpleName.toString()}")
35+
}
36+
}
37+
38+
SchibstedAccountTrackerStore.addTrackingListener(listener)
2539
}
2640

2741
private fun initTimber() {

app/src/main/java/com/schibsted/account/example/MainActivity.kt

+17-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import android.os.Bundle
55
import android.widget.Toast
66
import androidx.appcompat.app.AppCompatActivity
77
import androidx.lifecycle.Observer
8+
import androidx.lifecycle.lifecycleScope
89
import com.schibsted.account.databinding.ActivityMainBinding
910
import com.schibsted.account.webflows.activities.AuthResultLiveData
1011
import com.schibsted.account.webflows.activities.NotAuthed
1112
import com.schibsted.account.webflows.user.User
1213
import com.schibsted.account.webflows.util.Either
14+
import kotlinx.coroutines.launch
1315
import timber.log.Timber
1416

1517
class MainActivity : AppCompatActivity() {
@@ -19,7 +21,6 @@ class MainActivity : AppCompatActivity() {
1921
super.onCreate(savedInstanceState)
2022

2123
binding = ActivityMainBinding.inflate(layoutInflater)
22-
2324
setContentView(binding.root)
2425

2526
if (intent.getBooleanExtra(LOGIN_FAILED_EXTRA, false)) {
@@ -31,6 +32,14 @@ class MainActivity : AppCompatActivity() {
3132
observeAuthResultLiveData()
3233
}
3334

35+
override fun onResume() {
36+
super.onResume()
37+
38+
lifecycleScope.launch {
39+
ExampleApp.client.requestLoginPrompt(applicationContext, supportFragmentManager, true)
40+
}
41+
}
42+
3443

3544
private fun initializeButtons() {
3645
binding.loginButton.setOnClickListener {
@@ -73,7 +82,13 @@ class MainActivity : AppCompatActivity() {
7382
}
7483

7584
private fun startLoggedInActivity(user: User) {
76-
startActivity(LoggedInActivity.intentWithUser(this, user, LoggedInActivity.Companion.Flow.AUTOMATIC))
85+
startActivity(
86+
LoggedInActivity.intentWithUser(
87+
this,
88+
user,
89+
LoggedInActivity.Companion.Flow.AUTOMATIC
90+
)
91+
)
7792
}
7893

7994
companion object {

app/src/main/res/layout/activity_main.xml

+1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@
2525
app:layout_constraintLeft_toLeftOf="parent"
2626
app:layout_constraintRight_toRightOf="parent"
2727
app:layout_constraintTop_toTopOf="@id/loginButton" />
28+
2829
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values/colors.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
3-
<color name="colorPrimary">#6200EE</color>
4-
<color name="colorPrimaryDark">#3700B3</color>
3+
<color name="colorPrimary">#3274D4</color>
4+
<color name="colorPrimaryDark">#2196F3</color>
55
<color name="colorAccent">#03DAC5</color>
66
</resources>

app/src/main/res/values/styles.xml

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
<resources>
22
<!-- Base application theme. -->
3-
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
3+
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
44
<!-- Customize your theme here. -->
55
<item name="colorPrimary">@color/colorPrimary</item>
66
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
77
<item name="colorAccent">@color/colorAccent</item>
8+
<item name="toolbarStyle">@style/AppToolbar</item>
9+
</style>
10+
11+
<style name="AppToolbar" parent="Widget.MaterialComponents.Toolbar.Primary">
12+
<item name="titleTextColor">@android:color/white</item>
813
</style>
914

1015
</resources>

webflows/build.gradle

+13-3
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,26 @@ apply plugin: 'maven-publish'
66
apply plugin: 'signing'
77

88
android {
9-
compileSdkVersion 32
9+
compileSdkVersion 34
1010

1111
defaultConfig {
1212
minSdkVersion 21
13-
targetSdkVersion 30
13+
targetSdkVersion 33
1414

1515
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1616
consumerProguardFiles "consumer-rules.pro"
17+
resConfigs "en", "sv", "no", "nn", "nb", "fi", "dk"
1718
}
1819

1920
compileOptions {
2021
sourceCompatibility JavaVersion.VERSION_1_8
2122
targetCompatibility JavaVersion.VERSION_1_8
2223
}
2324

25+
buildFeatures {
26+
viewBinding true
27+
}
28+
2429
buildTypes {
2530
release {
2631
minifyEnabled false
@@ -60,15 +65,20 @@ dependencies {
6065

6166
api "androidx.browser:browser:1.4.0"
6267
implementation 'com.nimbusds:nimbus-jose-jwt:9.24.3'
68+
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
6369

6470
//Logging
6571
implementation "com.jakewharton.timber:timber:$timber_version"
6672

73+
//Login prompt
74+
implementation 'androidx.constraintlayout:constraintlayout:2.2.0-alpha12'
75+
implementation 'com.google.android.material:material:1.9.0'
76+
6777
testImplementation 'junit:junit:4.13.2'
6878
testImplementation "io.mockk:mockk:${mockkVersion}"
6979
testImplementation "com.squareup.okhttp3:mockwebserver:${okHttpVersion}"
7080

71-
testImplementation 'org.robolectric:robolectric:4.8.2'
81+
testImplementation 'org.robolectric:robolectric:4.9.1'
7282
testImplementation 'androidx.test.ext:junit:1.1.3'
7383
testImplementation "androidx.test.espresso:espresso-core:${espressoVersion}"
7484
testImplementation "androidx.test.espresso:espresso-intents:${espressoVersion}"

webflows/src/main/AndroidManifest.xml

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
<uses-permission android:name="android.permission.INTERNET" />
55

66
<application>
7+
<provider
8+
android:name="com.schibsted.account.webflows.loginPrompt.LoginPromptContentProvider"
9+
android:authorities="${applicationId}.contentprovider"
10+
android:enabled="true"
11+
android:exported="true">
12+
<intent-filter>
13+
<action android:name="com.schibsted.account.LOGIN_PROMPT_CONTENT_PROVIDER" />
14+
</intent-filter>
15+
</provider>
716
<activity
817
android:name="com.schibsted.account.webflows.activities.AuthorizationManagementActivity"
918
android:exported="false"
@@ -15,5 +24,8 @@
1524
<intent>
1625
<action android:name="android.support.customtabs.action.CustomTabsService" />
1726
</intent>
27+
<intent>
28+
<action android:name="com.schibsted.account.LOGIN_PROMPT_CONTENT_PROVIDER" />
29+
</intent>
1830
</queries>
19-
</manifest>
31+
</manifest>

webflows/src/main/java/com/schibsted/account/webflows/activities/AuthResultLiveData.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import androidx.annotation.MainThread
55
import androidx.lifecycle.LiveData
66
import com.schibsted.account.webflows.client.Client
77
import com.schibsted.account.webflows.client.LoginError
8+
import com.schibsted.account.webflows.tracking.SchibstedAccountTracker
9+
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingEvent
810
import com.schibsted.account.webflows.user.User
911
import com.schibsted.account.webflows.util.Either
1012
import com.schibsted.account.webflows.util.Either.Left
@@ -61,7 +63,10 @@ class AuthResultLiveData private constructor(private val client: Client) :
6163
is Left -> update(
6264
Left(
6365
when (result.value) {
64-
is LoginError.CancelledByUser -> NotAuthed.CancelledByUser
66+
is LoginError.CancelledByUser -> {
67+
SchibstedAccountTracker.track(SchibstedAccountTrackingEvent.UserLoginCanceled)
68+
NotAuthed.CancelledByUser
69+
}
6570
else -> NotAuthed.LoginFailed(result.value)
6671
}
6772
)

webflows/src/main/java/com/schibsted/account/webflows/activities/AuthorizationManagementActivity.kt

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import android.content.Intent
3434
import android.net.Uri
3535
import android.os.Bundle
3636
import com.schibsted.account.webflows.client.Client
37+
import com.schibsted.account.webflows.tracking.SchibstedAccountTracker
38+
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingEvent
3739
import com.schibsted.account.webflows.util.Either.Left
3840
import timber.log.Timber
3941

@@ -177,6 +179,7 @@ class AuthorizationManagementActivity : Activity() {
177179

178180
private fun handleAuthorizationCanceled() {
179181
Timber.d("Authorization flow canceled by user")
182+
SchibstedAccountTracker.track(SchibstedAccountTrackingEvent.UserLoginCanceled)
180183
AuthResultLiveData.get().update(Left(NotAuthed.CancelledByUser))
181184
cancelIntent?.send()
182185
}

webflows/src/main/java/com/schibsted/account/webflows/api/UserProfileResponse.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ data class Address(
9292
@SerializedName("invoice")
9393
INVOICE;
9494

95-
override fun toString(): String = super.toString().toLowerCase(Locale.ROOT)
95+
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
9696
}
9797
}
9898

0 commit comments

Comments
 (0)