Skip to content

Commit 27e7df4

Browse files
authored
Merge pull request #469 from Iterable/jay/MOB-4549-keychain
[MOB-4549] keychain
2 parents 1cf2dd7 + eab0e9f commit 27e7df4

File tree

11 files changed

+169
-32
lines changed

11 files changed

+169
-32
lines changed

app/build.gradle

+12
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,25 @@ android {
1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
multiDexEnabled true
1818
}
19+
1920
buildTypes {
2021
release {
2122
minifyEnabled false
2223
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
2324
}
25+
2426
debug {
2527
testCoverageEnabled true
2628
}
2729
}
2830

2931
testOptions.unitTests.includeAndroidResources = true
32+
3033
compileOptions {
3134
sourceCompatibility = 1.8
3235
targetCompatibility = 1.8
3336
}
37+
3438
kotlinOptions {
3539
jvmTarget = "1.8"
3640
}
@@ -69,9 +73,11 @@ dependencies {
6973
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0'
7074
androidTestImplementation 'br.com.concretesolutions:kappuccino:1.2.1'
7175
}
76+
7277
tasks.withType(Test) {
7378
jacoco.includeNoLocationClasses = true
7479
}
80+
7581
task jacocoDebugTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
7682
group = "reporting"
7783
description = "Generate unified Jacoco code coverage report"
@@ -99,12 +105,14 @@ task jacocoDebugTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest'])
99105
'**/*$ModuleAdapter.class',
100106
'**/*$ViewInjector*.class',
101107
]
108+
102109
def debugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug/classes", excludes: fileFilter) //we use "debug" build type for test coverage (can be other)
103110
def sdkTree = fileTree(dir: "${buildDir}/../../iterableapi/build/intermediates/javac/debug/classes", excludes: fileFilter)
104111
def sdkUiTree = fileTree(dir: "${buildDir}/../../iterableapi-ui/build/intermediates/javac/debug/classes", excludes: fileFilter)
105112
def mainSrc = "${project.projectDir}/src/main/java"
106113
def sdkSrc = "${project.projectDir}/../iterableapi/src/main/java"
107114
def sdkUiSrc = "${project.projectDir}/../iterableapi-ui/src/main/java"
115+
108116
sourceDirectories.from = files([mainSrc])
109117
classDirectories.from = files([debugTree])
110118
additionalSourceDirs.from = files([sdkSrc, sdkUiSrc])
@@ -117,11 +125,13 @@ task jacocoDebugTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest'])
117125
task jacocoDebugAndroidTestReport(type: JacocoReport, dependsOn: ['connectedCheck']) {
118126
group = "reporting"
119127
description = "Generate Jacoco code coverage report for instumentation tests"
128+
120129
reports {
121130
xml.enabled = true
122131
html.enabled = true
123132
csv.enabled = false
124133
}
134+
125135
def fileFilter = [
126136
'**/*Test*.*',
127137
'**/AutoValue_*.*',
@@ -141,12 +151,14 @@ task jacocoDebugAndroidTestReport(type: JacocoReport, dependsOn: ['connectedChec
141151
'**/*$ModuleAdapter.class',
142152
'**/*$ViewInjector*.class',
143153
]
154+
144155
def debugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug/classes", excludes: fileFilter) //we use "debug" build type for test coverage (can be other)
145156
def sdkTree = fileTree(dir: "${buildDir}/../../iterableapi/build/intermediates/javac/debug/classes", excludes: fileFilter)
146157
def sdkUiTree = fileTree(dir: "${buildDir}/../../iterableapi-ui/build/intermediates/javac/debug/classes", excludes: fileFilter)
147158
def mainSrc = "${project.projectDir}/src/main/java"
148159
def sdkSrc = "${project.projectDir}/../iterableapi/src/main/java"
149160
def sdkUiSrc = "${project.projectDir}/../iterableapi-ui/src/main/java"
161+
150162
sourceDirectories.from = files([mainSrc])
151163
classDirectories.from = files([debugTree])
152164
additionalSourceDirs.from = files([sdkSrc, sdkUiSrc])
+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<manifest xmlns:tools="http://schemas.android.com/tools"
3-
package="iterable.com.iterableapi">
2+
<manifest xmlns:tools="http://schemas.android.com/tools" package="iterable.com.iterableapi">
43
<uses-sdk tools:overrideLibrary="br.com.concretesolutions.kappuccino,android_libs.ub_uiautomator"/>
54
</manifest>

app/src/main/AndroidManifest.xml

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3-
package="com.iterable.iterableapi.testapp">
4-
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iterable.iterableapi.testapp">
53
<application
64
android:allowBackup="true"
75
android:icon="@mipmap/ic_launcher"
@@ -14,10 +12,8 @@
1412
android:theme="@style/AppTheme.NoActionBar">
1513
<intent-filter>
1614
<action android:name="android.intent.action.MAIN" />
17-
1815
<category android:name="android.intent.category.LAUNCHER" />
1916
</intent-filter>
2017
</activity>
2118
</application>
22-
23-
</manifest>
19+
</manifest>

iterableapi-ui/build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ ext {
6060
}
6161

6262
apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
63-
if(hasProperty("mavenPublishEnabled")) {
63+
64+
if (hasProperty("mavenPublishEnabled")) {
6465
apply from: '../maven-push.gradle'
6566
}
6667

iterableapi/build.gradle

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ android {
1515

1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
}
18+
1819
buildTypes {
1920
release {
2021
minifyEnabled false
@@ -25,11 +26,13 @@ android {
2526
multiDexEnabled true
2627
}
2728
}
29+
2830
testOptions.unitTests.all {
2931
testLogging {
3032
exceptionFormat "full"
3133
}
3234
}
35+
3336
testOptions.unitTests.includeAndroidResources = true
3437
}
3538

@@ -39,6 +42,7 @@ dependencies {
3942
api 'androidx.appcompat:appcompat:1.0.0'
4043
api 'androidx.annotation:annotation:1.0.0'
4144
api 'com.google.firebase:firebase-messaging:20.3.0'
45+
implementation "androidx.security:security-crypto:1.0.0"
4246

4347
testImplementation 'junit:junit:4.12'
4448
testImplementation 'androidx.test:runner:1.3.0'
@@ -83,12 +87,14 @@ ext {
8387
}
8488

8589
apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
86-
if(hasProperty("mavenPublishEnabled")) {
90+
91+
if (hasProperty("mavenPublishEnabled")) {
8792
apply from: '../maven-push.gradle'
8893
}
8994

9095
task javadoc(type: Javadoc) {
9196
source = android.sourceSets.main.java.srcDirs
97+
excludes = ['**/*.kt']
9298
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
9399
}
94100

Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<manifest xmlns:tools="http://schemas.android.com/tools"
3-
xmlns:android="http://schemas.android.com/apk/res/android"
4-
package="iterable.com.iterableapi">
2+
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" package="iterable.com.iterableapi">
53
<uses-sdk tools:overrideLibrary="android_libs.ub_uiautomator"/>
64
<application
75
android:label="IterableAPI"
8-
android:supportsRtl="true"/>
6+
android:supportsRtl="true" />
97
</manifest>

iterableapi/src/main/AndroidManifest.xml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:tools="http://schemas.android.com/tools"
23
package="com.iterable.iterableapi">
34

45
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
5-
<application>
66

7+
<application>
78
<!--FCM-->
89
<service
910
android:name="com.iterable.iterableapi.IterableFirebaseMessagingService"
@@ -27,6 +28,8 @@
2728
android:exported="false"
2829
android:launchMode="singleTop"
2930
android:theme="@style/TrampolineActivity.Transparent"/>
31+
32+
<uses-library android:name="androidx.security" android:required="false" />
3033
</application>
3134

3235
<queries>

iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java

+81-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.content.Context;
55
import android.content.Intent;
66
import android.content.SharedPreferences;
7+
import android.os.Build;
78
import android.os.Bundle;
89

910
import androidx.annotation.NonNull;
@@ -44,6 +45,7 @@ public class IterableApi {
4445
private String inboxSessionId;
4546
private IterableAuthManager authManager;
4647
private HashMap<String, String> deviceAttributes = new HashMap<>();
48+
private IterableKeychain keychain;
4749

4850
void fetchRemoteConfiguration() {
4951
apiClient.getRemoteConfiguration(new IterableHelper.IterableActionHandler() {
@@ -130,6 +132,15 @@ IterableAuthManager getAuthManager() {
130132
return authManager;
131133
}
132134

135+
@NonNull
136+
IterableKeychain getKeychain() {
137+
if (keychain == null) {
138+
keychain = new IterableKeychain(getMainActivityContext());
139+
}
140+
141+
return keychain;
142+
}
143+
133144
static void loadLastSavedConfiguration(Context context) {
134145
SharedPreferences sharedPref = context.getSharedPreferences(IterableConstants.SHARED_PREFS_SAVED_CONFIGURATION, Context.MODE_PRIVATE);
135146
boolean offlineMode = sharedPref.getBoolean(IterableConstants.SHARED_PREFS_OFFLINE_MODE_KEY, false);
@@ -330,31 +341,85 @@ private String getDeviceId() {
330341
}
331342

332343
private void storeAuthData() {
333-
try {
334-
SharedPreferences.Editor editor = getPreferences().edit();
335-
editor.putString(IterableConstants.SHARED_PREFS_EMAIL_KEY, _email);
336-
editor.putString(IterableConstants.SHARED_PREFS_USERID_KEY, _userId);
337-
editor.putString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, _authToken);
338-
editor.commit();
339-
} catch (Exception e) {
340-
IterableLogger.e(TAG, "Error while persisting email/userId", e);
344+
if (hasEncryptionDependency()) {
345+
getKeychain().saveEmail(_email);
346+
getKeychain().saveUserId(_userId);
347+
getKeychain().saveAuthToken(_authToken);
348+
} else {
349+
try {
350+
SharedPreferences.Editor editor = getPreferences().edit();
351+
editor.putString(IterableConstants.SHARED_PREFS_EMAIL_KEY, _email);
352+
editor.putString(IterableConstants.SHARED_PREFS_USERID_KEY, _userId);
353+
editor.putString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, _authToken);
354+
editor.commit();
355+
} catch (Exception e) {
356+
IterableLogger.e(TAG, "Error while persisting email/userId", e);
357+
}
341358
}
342359
}
343360

344361
private void retrieveEmailAndUserId() {
345-
try {
362+
if (hasEncryptionDependency()) {
363+
_email = getKeychain().getEmail();
364+
_userId = getKeychain().getUserId();
365+
_authToken = getKeychain().getAuthToken();
366+
} else {
346367
SharedPreferences prefs = getPreferences();
347368
_email = prefs.getString(IterableConstants.SHARED_PREFS_EMAIL_KEY, null);
348369
_userId = prefs.getString(IterableConstants.SHARED_PREFS_USERID_KEY, null);
349370
_authToken = prefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null);
350-
if (_authToken != null) {
351-
getAuthManager().queueExpirationRefresh(_authToken);
352-
}
353-
} catch (Exception e) {
354-
IterableLogger.e(TAG, "Error while retrieving email/userId/authToken", e);
371+
}
372+
373+
if (_authToken != null) {
374+
getAuthManager().queueExpirationRefresh(_authToken);
375+
}
376+
}
377+
378+
private void updateSDKVersion() {
379+
if (hasEncryptionDependency()) {
380+
migrateAuthDataFromSharedPrefsToKeychain();
355381
}
356382
}
357383

384+
private void migrateAuthDataFromSharedPrefsToKeychain() {
385+
SharedPreferences prefs = getPreferences();
386+
String sharedPrefsEmail = prefs.getString(IterableConstants.SHARED_PREFS_EMAIL_KEY, null);
387+
String sharedPrefsUserId = prefs.getString(IterableConstants.SHARED_PREFS_USERID_KEY, null);
388+
String sharedPrefsAuthToken = prefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null);
389+
390+
SharedPreferences.Editor editor = getPreferences().edit();
391+
392+
if (getKeychain().getEmail() == null && sharedPrefsEmail != null) {
393+
getKeychain().saveEmail(sharedPrefsEmail);
394+
editor.remove(IterableConstants.SHARED_PREFS_EMAIL_KEY);
395+
IterableLogger.v(TAG, "UPDATED: migrated email from SharedPreferences to IterableKeychain");
396+
} else if (sharedPrefsEmail != null) {
397+
editor.remove(IterableConstants.SHARED_PREFS_EMAIL_KEY);
398+
}
399+
400+
if (getKeychain().getUserId() == null && sharedPrefsUserId != null) {
401+
getKeychain().saveUserId(sharedPrefsUserId);
402+
editor.remove(IterableConstants.SHARED_PREFS_USERID_KEY);
403+
IterableLogger.v(TAG, "UPDATED: migrated userId from SharedPreferences to IterableKeychain");
404+
} else if (sharedPrefsUserId != null) {
405+
editor.remove(IterableConstants.SHARED_PREFS_USERID_KEY);
406+
}
407+
408+
if (getKeychain().getAuthToken() == null && sharedPrefsAuthToken != null) {
409+
getKeychain().saveAuthToken(sharedPrefsAuthToken);
410+
editor.remove(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY);
411+
IterableLogger.v(TAG, "UPDATED: migrated authToken from SharedPreferences to IterableKeychain");
412+
} else if (sharedPrefsAuthToken != null) {
413+
editor.remove(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY);
414+
}
415+
416+
editor.apply();
417+
}
418+
419+
private boolean hasEncryptionDependency() {
420+
return Build.VERSION.SDK_INT >= 23;
421+
}
422+
358423
private class IterableApiAuthProvider implements IterableApiClient.AuthProvider {
359424
@Nullable
360425
@Override
@@ -488,6 +553,8 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey,
488553
sharedInstance.config = new IterableConfig.Builder().build();
489554
}
490555

556+
sharedInstance.updateSDKVersion();
557+
491558
sharedInstance.retrieveEmailAndUserId();
492559

493560
IterableActivityMonitor.getInstance().registerLifecycleCallbacks(context);

0 commit comments

Comments
 (0)