Skip to content

Commit cea4ec1

Browse files
WillieCubedsamtstern
authored andcommitted
Add support for privacy policy URLs in sign-up (#727)
1 parent cf96d39 commit cea4ec1

File tree

11 files changed

+197
-42
lines changed

11 files changed

+197
-42
lines changed

app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public class AuthUiActivity extends AppCompatActivity {
5252
private static final String UNCHANGED_CONFIG_VALUE = "CHANGE-ME";
5353
private static final String GOOGLE_TOS_URL = "https://www.google.com/policies/terms/";
5454
private static final String FIREBASE_TOS_URL = "https://firebase.google.com/terms/";
55+
private static final String GOOGLE_PRIVACY_POLICY_URL = "https://www.google.com/policies/privacy/";
56+
private static final String FIREBASE_PRIVACY_POLICY_URL = "https://firebase.google.com/terms/analytics/#7_privacy";
5557
private static final int RC_SIGN_IN = 100;
5658

5759
@BindView(R.id.default_theme)
@@ -84,6 +86,12 @@ public class AuthUiActivity extends AppCompatActivity {
8486
@BindView(R.id.firebase_tos)
8587
RadioButton mUseFirebaseTos;
8688

89+
@BindView(R.id.google_privacy)
90+
RadioButton mUseGooglePrivacyPolicy;
91+
92+
@BindView(R.id.firebase_privacy)
93+
RadioButton mUseFirebasePrivacyPolicy;
94+
8795
@BindView(R.id.sign_in)
8896
Button mSignIn;
8997

@@ -185,6 +193,7 @@ public void signIn(View view) {
185193
.setLogo(getSelectedLogo())
186194
.setAvailableProviders(getSelectedProviders())
187195
.setTosUrl(getSelectedTosUrl())
196+
.setPrivacyPolicyUrl(getSelectedPrivacyPolicyUrl())
188197
.setIsSmartLockEnabled(mEnableSmartLock.isChecked())
189198
.setAllowNewEmailAccounts(mAllowNewEmailAccounts.isChecked())
190199
.build(),
@@ -327,6 +336,15 @@ private String getSelectedTosUrl() {
327336
return FIREBASE_TOS_URL;
328337
}
329338

339+
@MainThread
340+
private String getSelectedPrivacyPolicyUrl() {
341+
if (mUseGooglePrivacyPolicy.isChecked()) {
342+
return GOOGLE_PRIVACY_POLICY_URL;
343+
}
344+
345+
return FIREBASE_PRIVACY_POLICY_URL;
346+
}
347+
330348
@MainThread
331349
private boolean isGoogleConfigured() {
332350
return !UNCHANGED_CONFIG_VALUE.equals(

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,34 @@
140140

141141
</RadioGroup>
142142

143+
<TextView
144+
style="@style/Base.TextAppearance.AppCompat.Subhead"
145+
android:layout_width="wrap_content"
146+
android:layout_height="wrap_content"
147+
android:layout_marginBottom="8dp"
148+
android:layout_marginTop="16dp"
149+
android:text="@string/privacy_policy_header"/>
150+
151+
<RadioGroup
152+
android:layout_width="wrap_content"
153+
android:layout_height="wrap_content"
154+
android:orientation="vertical">
155+
156+
<RadioButton
157+
android:id="@+id/google_privacy"
158+
android:layout_width="wrap_content"
159+
android:layout_height="wrap_content"
160+
android:checked="true"
161+
android:text="@string/google_privacy_label"/>
162+
163+
<RadioButton
164+
android:id="@+id/firebase_privacy"
165+
android:layout_width="wrap_content"
166+
android:layout_height="wrap_content"
167+
android:text="@string/firebase_privacy_label"/>
168+
169+
</RadioGroup>
170+
143171
<TextView
144172
style="@style/Base.TextAppearance.AppCompat.Subhead"
145173
android:layout_width="wrap_content"

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@
3131
<string name="google_label_missing_config">Google - configuration missing</string>
3232
<string name="google_tos_label">Google TOS</string>
3333
<string name="firebase_tos_label">Firebase TOS</string>
34+
<string name="google_privacy_label">Google Privacy Policy</string>
35+
<string name="firebase_privacy_label">Firebase Privacy Policy</string>
3436
<string name="tos_header">Terms of Service URL:</string>
37+
<string name="privacy_policy_header">Privacy Policy URL:</string>
3538
<string name="unknown_response">Unexpected onActivityResult response code</string>
3639
<string name="unknown_sign_in_response">Unknown response from AuthUI sign-in</string>
3740
<string name="sign_in_cancelled">Sign in cancelled</string>

auth/src/main/java/com/firebase/ui/auth/AuthUI.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ private abstract class AuthIntentBuilder<T extends AuthIntentBuilder> {
449449
int mTheme = getDefaultTheme();
450450
List<IdpConfig> mProviders = new ArrayList<>();
451451
String mTosUrl;
452+
String mPrivacyPolicyUrl;
452453
boolean mIsSmartLockEnabled = true;
453454

454455
private AuthIntentBuilder() {}
@@ -483,6 +484,14 @@ public T setTosUrl(@Nullable String tosUrl) {
483484
return (T) this;
484485
}
485486

487+
/**
488+
* Specifies the privacy policy URL for the application.
489+
*/
490+
public T setPrivacyPolicyUrl(@Nullable String privacyPolicyUrl) {
491+
mPrivacyPolicyUrl = privacyPolicyUrl;
492+
return (T) this;
493+
}
494+
486495
/**
487496
* Specified the set of supported authentication providers. At least one provider must
488497
* be specified. There may only be one instance of each provider.
@@ -631,6 +640,7 @@ protected FlowParameters getFlowParams() {
631640
mTheme,
632641
mLogo,
633642
mTosUrl,
643+
mPrivacyPolicyUrl,
634644
mIsSmartLockEnabled,
635645
false,
636646
true,
@@ -666,6 +676,7 @@ protected FlowParameters getFlowParams() {
666676
mTheme,
667677
mLogo,
668678
mTosUrl,
679+
mPrivacyPolicyUrl,
669680
mIsSmartLockEnabled,
670681
mAllowNewEmailAccounts,
671682
false,

auth/src/main/java/com/firebase/ui/auth/ui/FlowParameters.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ public class FlowParameters implements Parcelable {
4848
@Nullable
4949
public final String termsOfServiceUrl;
5050

51+
@Nullable
52+
public final String privacyPolicyUrl;
53+
5154
public final boolean smartLockEnabled;
5255

5356
public final boolean allowNewEmailAccounts;
@@ -62,6 +65,7 @@ public FlowParameters(
6265
@StyleRes int themeId,
6366
@DrawableRes int logoId,
6467
@Nullable String termsOfServiceUrl,
68+
@Nullable String privacyPolicyUrl,
6569
boolean smartLockEnabled,
6670
boolean allowNewEmailAccounts,
6771
boolean isReauth,
@@ -72,6 +76,7 @@ public FlowParameters(
7276
this.themeId = themeId;
7377
this.logoId = logoId;
7478
this.termsOfServiceUrl = termsOfServiceUrl;
79+
this.privacyPolicyUrl = privacyPolicyUrl;
7580
this.smartLockEnabled = smartLockEnabled;
7681
this.allowNewEmailAccounts = allowNewEmailAccounts;
7782
this.isReauth = isReauth;
@@ -85,6 +90,7 @@ public void writeToParcel(Parcel dest, int flags) {
8590
dest.writeInt(themeId);
8691
dest.writeInt(logoId);
8792
dest.writeString(termsOfServiceUrl);
93+
dest.writeString(privacyPolicyUrl);
8894
dest.writeInt(smartLockEnabled ? 1 : 0);
8995
dest.writeInt(allowNewEmailAccounts ? 1 : 0);
9096
dest.writeInt(isReauth ? 1 : 0);
@@ -104,6 +110,7 @@ public FlowParameters createFromParcel(Parcel in) {
104110
int themeId = in.readInt();
105111
int logoId = in.readInt();
106112
String termsOfServiceUrl = in.readString();
113+
String privacyPolicyUrl = in.readString();
107114
boolean smartLockEnabled = in.readInt() != 0;
108115
boolean allowNewEmailAccounts = in.readInt() != 0;
109116
boolean isReauth = in.readInt() != 0;
@@ -115,6 +122,7 @@ public FlowParameters createFromParcel(Parcel in) {
115122
themeId,
116123
logoId,
117124
termsOfServiceUrl,
125+
privacyPolicyUrl,
118126
smartLockEnabled,
119127
allowNewEmailAccounts,
120128
isReauth,
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.firebase.ui.auth.ui.email;
2+
3+
import android.content.Context;
4+
import android.net.Uri;
5+
import android.support.annotation.ColorInt;
6+
import android.support.annotation.StringRes;
7+
import android.support.customtabs.CustomTabsIntent;
8+
import android.support.v4.content.ContextCompat;
9+
import android.text.SpannableStringBuilder;
10+
import android.text.TextUtils;
11+
import android.text.method.LinkMovementMethod;
12+
import android.text.style.ClickableSpan;
13+
import android.text.style.ForegroundColorSpan;
14+
import android.util.TypedValue;
15+
import android.view.View;
16+
import android.widget.TextView;
17+
18+
import com.firebase.ui.auth.R;
19+
import com.firebase.ui.auth.ui.FlowParameters;
20+
21+
public class PreambleHandler {
22+
private static final String TOS_TARGET = "%TOS%";
23+
private static final String PP_TARGET = "%PP%";
24+
25+
private final Context mContext;
26+
private final FlowParameters mFlowParameters;
27+
private final ForegroundColorSpan mLinkSpan;
28+
29+
private SpannableStringBuilder mBuilder;
30+
31+
public PreambleHandler(Context context, FlowParameters parameters) {
32+
mContext = context;
33+
mFlowParameters = parameters;
34+
mLinkSpan = new ForegroundColorSpan(ContextCompat.getColor(mContext, R.color.linkColor));
35+
36+
setupCreateAccountPreamble();
37+
}
38+
39+
public void setPreamble(TextView textView) {
40+
textView.setMovementMethod(LinkMovementMethod.getInstance());
41+
textView.setText(mBuilder);
42+
}
43+
44+
private void setupCreateAccountPreamble() {
45+
int preambleType = getPreambleType();
46+
if (preambleType == -1) {
47+
return;
48+
}
49+
50+
String[] preambles =
51+
mContext.getResources().getStringArray(R.array.create_account_preamble);
52+
mBuilder = new SpannableStringBuilder(preambles[preambleType]);
53+
54+
replaceTarget(TOS_TARGET, R.string.terms_of_service, mFlowParameters.termsOfServiceUrl);
55+
replaceTarget(PP_TARGET, R.string.privacy_policy, mFlowParameters.privacyPolicyUrl);
56+
}
57+
58+
private void replaceTarget(String target, @StringRes int replacementRes, String url) {
59+
int targetIndex = mBuilder.toString().indexOf(target);
60+
if (targetIndex != -1) {
61+
String replacement = mContext.getString(replacementRes);
62+
mBuilder.replace(targetIndex, targetIndex + target.length(), replacement);
63+
64+
int end = targetIndex + replacement.length();
65+
mBuilder.setSpan(mLinkSpan, targetIndex, end, 0);
66+
mBuilder.setSpan(new CustomTabsSpan(url), targetIndex, end, 0);
67+
}
68+
}
69+
70+
/**
71+
* 0 means we have both a TOS and a PP
72+
* <p>1 means we only have a TOS
73+
* <p>2 means we only have a PP
74+
* <p>-1 means we have neither
75+
*/
76+
private int getPreambleType() {
77+
int preambleType;
78+
79+
boolean hasTos = !TextUtils.isEmpty(mFlowParameters.termsOfServiceUrl);
80+
boolean hasPp = !TextUtils.isEmpty(mFlowParameters.privacyPolicyUrl);
81+
82+
if (hasTos && hasPp) {
83+
preambleType = 0;
84+
} else if (hasTos) {
85+
preambleType = 1;
86+
} else if (hasPp) {
87+
preambleType = 2;
88+
} else {
89+
preambleType = -1;
90+
}
91+
92+
return preambleType;
93+
}
94+
95+
private class CustomTabsSpan extends ClickableSpan {
96+
private final String mUrl;
97+
private final CustomTabsIntent mCustomTabsIntent;
98+
99+
public CustomTabsSpan(String url) {
100+
mUrl = url;
101+
102+
// Getting default color
103+
TypedValue typedValue = new TypedValue();
104+
mContext.getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
105+
@ColorInt int color = typedValue.data;
106+
107+
mCustomTabsIntent = new CustomTabsIntent.Builder()
108+
.setToolbarColor(color)
109+
.setShowTitle(true)
110+
.build();
111+
}
112+
113+
@Override
114+
public void onClick(View widget) {
115+
mCustomTabsIntent.launchUrl(mContext, Uri.parse(mUrl));
116+
}
117+
}
118+
}

auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
package com.firebase.ui.auth.ui.email;
22

3-
import android.net.Uri;
43
import android.os.Bundle;
5-
import android.support.annotation.ColorInt;
64
import android.support.annotation.NonNull;
75
import android.support.annotation.Nullable;
86
import android.support.annotation.RestrictTo;
9-
import android.support.customtabs.CustomTabsIntent;
107
import android.support.design.widget.TextInputLayout;
11-
import android.support.v4.content.ContextCompat;
12-
import android.text.SpannableStringBuilder;
138
import android.text.TextUtils;
14-
import android.text.style.ForegroundColorSpan;
15-
import android.util.TypedValue;
169
import android.view.LayoutInflater;
1710
import android.view.View;
1811
import android.view.ViewGroup;
@@ -164,7 +157,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
164157
getActivity().setTitle(R.string.title_register_email);
165158

166159
mSaveSmartLock = mHelper.getSaveSmartLockInstance(getActivity());
167-
setUpTermsOfService();
160+
new PreambleHandler(getContext(), mHelper.getFlowParams()).setPreamble(mAgreementText);
168161
}
169162

170163
@Override
@@ -177,39 +170,6 @@ public void onSaveInstanceState(Bundle outState) {
177170
super.onSaveInstanceState(outState);
178171
}
179172

180-
private void setUpTermsOfService() {
181-
if (TextUtils.isEmpty(mHelper.getFlowParams().termsOfServiceUrl)) {
182-
return;
183-
}
184-
185-
ForegroundColorSpan foregroundColorSpan =
186-
new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.linkColor));
187-
188-
String preamble = getString(R.string.create_account_preamble);
189-
String link = getString(R.string.terms_of_service);
190-
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(preamble + link);
191-
int start = preamble.length();
192-
spannableStringBuilder.setSpan(foregroundColorSpan, start, start + link.length(), 0);
193-
194-
mAgreementText.setText(spannableStringBuilder);
195-
mAgreementText.setOnClickListener(new View.OnClickListener() {
196-
@Override
197-
public void onClick(View view) {
198-
// Getting default color
199-
TypedValue typedValue = new TypedValue();
200-
getActivity().getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
201-
@ColorInt int color = typedValue.data;
202-
203-
new CustomTabsIntent.Builder()
204-
.setToolbarColor(color)
205-
.build()
206-
.launchUrl(
207-
getActivity(),
208-
Uri.parse(mHelper.getFlowParams().termsOfServiceUrl));
209-
}
210-
});
211-
}
212-
213173
@Override
214174
public void onFocusChange(View view, boolean hasFocus) {
215175
if (hasFocus) return; // Only consider fields losing focus

auth/src/main/res/values/strings.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,13 @@
4242
</plurals>
4343
<string name="email_account_creation_error">Email account registration unsuccessful</string>
4444
<string name="error_user_collision">An account already exists with that email address.</string>
45-
<string name="create_account_preamble">"By tapping SAVE you are indicating that you agree to the "</string>
45+
<string-array name="create_account_preamble" formatted="false">
46+
<item>By tapping SAVE you are indicating that you agree to the %TOS% and the %PP%.</item>
47+
<item>By tapping SAVE you are indicating that you agree to the %TOS%.</item>
48+
<item>By tapping SAVE you are indicating that you agree to the %PP%.</item>
49+
</string-array>
4650
<string name="terms_of_service">Terms of Service</string>
51+
<string name="privacy_policy">Privacy Policy</string>
4752

4853
<!-- Idp/Email welcome back -->
4954
<string name="title_welcome_back_idp_prompt">@string/sign_in_default</string>

auth/src/test/java/com/firebase/ui/auth/AuthUITest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,14 @@ public void testCreatingStartIntent() {
8383
new IdpConfig.Builder(AuthUI.GOOGLE_PROVIDER).build(),
8484
new IdpConfig.Builder(AuthUI.FACEBOOK_PROVIDER).build()))
8585
.setTosUrl(TestConstants.TOS_URL)
86+
.setPrivacyPolicyUrl(TestConstants.PRIVACY_URL)
8687
.build()
8788
.getParcelableExtra(ExtraConstants.EXTRA_FLOW_PARAMS);
8889

8990
assertEquals(3, flowParameters.providerInfo.size());
9091
assertEquals(mFirebaseApp.getName(), flowParameters.appName);
9192
assertEquals(TestConstants.TOS_URL, flowParameters.termsOfServiceUrl);
93+
assertEquals(TestConstants.PRIVACY_URL, flowParameters.privacyPolicyUrl);
9294
assertEquals(AuthUI.getDefaultTheme(), flowParameters.themeId);
9395
}
9496
}

0 commit comments

Comments
 (0)