aboutsummaryrefslogtreecommitdiff
path: root/wearable/wear/WearMessagingApp
diff options
context:
space:
mode:
authorBenjamin Baxter <benbaxter@google.com>2017-03-22 15:58:49 -0700
committerBenjamin Baxter <benbaxter@google.com>2017-04-18 10:00:19 -0700
commitf6a7013b7b9a9ca8d05713f5c16c95abf3af71cb (patch)
treef8ea3878d0c3c0296df9546dc17134951230b12b /wearable/wear/WearMessagingApp
parentc8a79136f775bf9098779d62777dc69f1762d546 (diff)
downloadandroid-f6a7013b7b9a9ca8d05713f5c16c95abf3af71cb.tar.gz
Adding sign in with google activity and a base activity for activities that required a signed in user
Bug: 34841755 Change-Id: I8c4630d1fd49acb735cd39650addd8ed8ef600b9
Diffstat (limited to 'wearable/wear/WearMessagingApp')
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java171
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java208
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java4
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_signin.xml42
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/values/strings.xml3
5 files changed, 426 insertions, 2 deletions
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java
new file mode 100644
index 00000000..494c9c7b
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.wearable.wear.messaging;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.wearable.activity.WearableActivity;
+import android.util.Log;
+import com.example.android.wearable.wear.messaging.model.Profile;
+import com.example.android.wearable.wear.messaging.util.SharedPreferencesHelper;
+import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
+import com.google.android.gms.auth.api.signin.GoogleSignInResult;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.OptionalPendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import java.io.IOException;
+
+/**
+ * This activity should be extended for any activity that requires an authenticated user. This
+ * activity handles the signin flow with Google signin.
+ *
+ * <p>When the activity starts, it will silently try to verify that the user is valid. If a user is
+ * not signed in, it will redirect to a SignInActivity.
+ *
+ * <p>It also provides a hook for any sub class to get a reference to the user object {@link
+ * #getUser()}
+ */
+public abstract class GoogleSignedInActivity extends WearableActivity
+ implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+ private static final String TAG = "GoogleSignedInActivity";
+
+ protected GoogleApiClient mGoogleApiClient;
+ protected GoogleSignInAccount mGoogleSignInAccount;
+ private String mUserIdToken;
+ private Profile mUser;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setAmbientEnabled();
+
+ // Try to get the user if they don't exist, return to sign in.
+ try {
+ mUser = SharedPreferencesHelper.readUserFromJsonPref(this);
+ } catch (IOException e) {
+ Log.e(TAG, "User is not stored locally");
+ }
+ if (mUser == null) {
+ onGoogleSignInFailure();
+ }
+
+ setupGoogleApiClient();
+ }
+
+ /* gives a handle to the user object for the sub-activities */
+ protected Profile getUser() {
+ return mUser;
+ }
+
+ /** Configures the GoogleApiClient used for sign in. Requests scopes profile and email. */
+ protected void setupGoogleApiClient() {
+ GoogleSignInOptions gso =
+ new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestProfile()
+ .requestEmail()
+ .requestIdToken(getString(R.string.default_web_client_id))
+ .build();
+
+ mGoogleApiClient =
+ new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
+ .build();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (mGoogleApiClient != null && !mGoogleApiClient.isConnected()) {
+ mGoogleApiClient.connect();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
+ mGoogleApiClient.disconnect();
+ }
+ }
+
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+ Log.d(TAG, "onConnected(): refreshing sign in");
+ refreshSignIn();
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+ Log.d(TAG, "onConnectionSuspended(): connection to location client suspended: " + i);
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+ Log.d(TAG, "Connection Failed.");
+ }
+
+ /**
+ * Handles sign in result and gets the UserIdToken.
+ *
+ * @param result sign in result
+ */
+ protected void handleSignInResult(GoogleSignInResult result) {
+ if (result != null && result.isSuccess()) {
+ mGoogleSignInAccount = result.getSignInAccount();
+ if (mGoogleSignInAccount != null) {
+ mUserIdToken = mGoogleSignInAccount.getIdToken();
+ Log.d(TAG, "Google sign in success " + mUserIdToken);
+ }
+ } else if (result != null && !result.isSuccess()) {
+ Log.d(TAG, "Google sign in failure: " + result.getStatus());
+ onGoogleSignInFailure();
+ } else {
+ Log.d(TAG, "Google sign in result is null");
+ onGoogleSignInFailure();
+ }
+ }
+
+ protected void onGoogleSignInFailure() {
+ // If sign in fails, ask them to sign in
+ Intent signinIntent = new Intent(this, SignInActivity.class);
+ startActivity(signinIntent);
+ }
+
+ /** Silently signs in. */
+ private void refreshSignIn() {
+ OptionalPendingResult<GoogleSignInResult> pendingResult =
+ Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
+ if (pendingResult.isDone()) {
+ handleSignInResult(pendingResult.get());
+ } else {
+ pendingResult.setResultCallback(
+ new ResultCallback<GoogleSignInResult>() {
+ @Override
+ public void onResult(@NonNull GoogleSignInResult googleSignInResult) {
+ handleSignInResult(googleSignInResult);
+ }
+ });
+ }
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java
new file mode 100644
index 00000000..af3d74be
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.wearable.wear.messaging;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.wearable.activity.WearableActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+import com.example.android.wearable.wear.messaging.mock.MockDatabase;
+import com.example.android.wearable.wear.messaging.model.Profile;
+import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
+import com.google.android.gms.auth.api.signin.GoogleSignInResult;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.SignInButton;
+import com.google.android.gms.common.api.GoogleApiClient;
+
+/**
+ * Activity authenticates via Google and mocks backend model with shared preferences.
+ *
+ * <p>The login flow:
+ *
+ * <p>On sign in: Create mProfile from the user details from their google account Check if the user
+ * exists in our 'backend'
+ *
+ * <p>If the user does not exist: Add them to our 'backend' Once a user has been established, then
+ * reset the UI's state and launch the 'Main' Activity. In this case, we will go to the ChatActivity
+ * and view the list of chats for the user.
+ */
+public class SignInActivity extends WearableActivity
+ implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+ private static final String TAG = "SignInActivity";
+
+ /* request code for signing in with a google account */
+ private static final int RC_SIGN_IN = 9001;
+
+ private GoogleApiClient mGoogleApiClient;
+
+ private SignInButton mSignInButton;
+
+ private Profile mProfile;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_signin);
+ setAmbientEnabled();
+ MockDatabase.init(this);
+
+ // Configure Google Sign In
+ GoogleSignInOptions.Builder builder =
+ new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestIdToken(getString(R.string.default_web_client_id))
+ .requestEmail();
+ GoogleSignInOptions gso = builder.build();
+ mGoogleApiClient =
+ new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
+ .build();
+
+ mSignInButton = (SignInButton) findViewById(R.id.sign_in_button);
+ mSignInButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent signInIntent =
+ Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
+ startActivityForResult(signInIntent, RC_SIGN_IN);
+ }
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (mGoogleApiClient != null && !mGoogleApiClient.isConnected()) {
+ mGoogleApiClient.connect();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
+ mGoogleApiClient.disconnect();
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
+ if (requestCode == RC_SIGN_IN) {
+ GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
+ handleGoogleSigninResult(result);
+ }
+ }
+
+ private void handleGoogleSigninResult(GoogleSignInResult result) {
+ if (result.isSuccess()) {
+ GoogleSignInAccount account = result.getSignInAccount();
+ syncUserWithBackend(account);
+ } else {
+ // Google Sign-In failed
+ Log.e(TAG, "Google Sign-In failed.");
+ Toast.makeText(SignInActivity.this, R.string.google_signin_failed, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+
+ /*
+ * Syncs with mock backend (shared preferences). You will want to put in your
+ * backend logic here.
+ */
+ private void syncUserWithBackend(GoogleSignInAccount acct) {
+ Log.d(TAG, "syncUserWithBackend:" + acct.getId());
+
+ mProfile = new Profile(acct);
+
+ MockDatabase.getUser(
+ mProfile.getId(),
+ new MockDatabase.RetrieveUserCallback() {
+ @Override
+ public void retrieved(Profile user) {
+ if (user == null) {
+ // User did not exists so create the user.
+ // Using mProfile since user is null
+ MockDatabase.createUser(
+ mProfile,
+ new MockDatabase.CreateUserCallback() {
+ @Override
+ public void onSuccess() {
+ finishSuccessfulSignin();
+ }
+
+ @Override
+ public void onError(Exception e) {
+ setAuthFailedState();
+ }
+ });
+ } else {
+ finishSuccessfulSignin();
+ }
+ }
+
+ @Override
+ public void error(Exception e) {
+ setAuthFailedState();
+ }
+ });
+ }
+
+ private void setAuthFailedState() {
+ if (mSignInButton != null) {
+ mSignInButton.setEnabled(false);
+ }
+ Toast.makeText(SignInActivity.this, R.string.authentication_failed, Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ private void finishSuccessfulSignin() {
+ if (mSignInButton != null) {
+ mSignInButton.setEnabled(false);
+ }
+
+// Intent chatActivityIntent = new Intent(this, ChatListActivity.class);
+// startActivity(chatActivityIntent);
+ finish();
+ }
+
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+ Log.d(TAG, "onConnected()");
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+ Log.d(TAG, "onConnectionSuspended(): connection to location client suspended: " + i);
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+ Log.d(TAG, "onConnectionFailed:" + connectionResult);
+ Toast.makeText(this, R.string.connection_failed, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java
index 1b0d91a8..034baa1b 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java
@@ -43,14 +43,14 @@ public class MockDatabase {
private static Context mContext;
/** A callback for events when retrieving a user asynchronously */
- interface RetrieveUserCallback {
+ public interface RetrieveUserCallback {
void retrieved(Profile user);
void error(Exception e);
}
/** A callback for getting events during the creation of a user */
- interface CreateUserCallback {
+ public interface CreateUserCallback {
void onSuccess();
void onError(Exception e);
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_signin.xml b/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_signin.xml
new file mode 100644
index 00000000..716cd78f
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_signin.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2017 Google Inc.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/blue_500"
+ android:gravity="center"
+ android:orientation="vertical"
+ tools:context=".SignInActivity">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/LogoText"
+ android:text="@string/wear_messaging_app" />
+
+ <com.google.android.gms.common.SignInButton
+ android:id="@+id/sign_in_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:buttonSize="standard"
+ app:colorScheme="auto"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/values/strings.xml b/wearable/wear/WearMessagingApp/Wearable/src/main/res/values/strings.xml
index e52f0300..7a024fed 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/res/values/strings.xml
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/values/strings.xml
@@ -38,4 +38,7 @@
<string name="open_keyboard_input">Open keyboard input</string>
<string name="wear_messaging_app">Wear Messaging App</string>
<string name="you_have_no_messages">You have no messages.\nStart a message with the actions below!</string>
+ <string name="google_signin_failed">Google Sign-In Failed.</string>
+ <string name="authentication_failed">Authentication Failed.</string>
+ <string name="connection_failed">Connection failed.</string>
</resources> \ No newline at end of file