+ * 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
+ *
+ * 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.google.firebase.quickstart.auth.java;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.navigation.fragment.NavHostFragment;
+
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseAuthMultiFactorException;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.auth.MultiFactorResolver;
+import com.google.firebase.quickstart.auth.R;
+import com.google.firebase.quickstart.auth.databinding.FragmentEmailpasswordBinding;
+
+public class EmailPasswordFragment extends BaseFragment {
+
+ private static final String TAG = "EmailPassword";
+
+ private FragmentEmailpasswordBinding mBinding;
+
+ private FirebaseAuth mAuth;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ mBinding = FragmentEmailpasswordBinding.inflate(inflater, container, false);
+ return mBinding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setProgressBar(mBinding.progressBar);
+
+ // Buttons
+ mBinding.emailSignInButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String email = mBinding.fieldEmail.getText().toString();
+ String password = mBinding.fieldPassword.getText().toString();
+ signIn(email, password);
+ }
+ });
+ mBinding.emailCreateAccountButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String email = mBinding.fieldEmail.getText().toString();
+ String password = mBinding.fieldPassword.getText().toString();
+ createAccount(email, password);
+ }
+ });
+ mBinding.signOutButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ signOut();
+ }
+ });
+ mBinding.verifyEmailButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ sendEmailVerification();
+ }
+ });
+ mBinding.reloadButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ reload();
+ }
+ });
+
+ // Initialize Firebase Auth
+ mAuth = FirebaseAuth.getInstance();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Check if user is signed in (non-null) and update UI accordingly.
+ FirebaseUser currentUser = mAuth.getCurrentUser();
+ if(currentUser != null){
+ reload();
+ }
+ }
+
+ private void createAccount(String email, String password) {
+ Log.d(TAG, "createAccount:" + email);
+ if (!validateForm()) {
+ return;
+ }
+
+ showProgressBar();
+
+ mAuth.createUserWithEmailAndPassword(email, password)
+ .addOnCompleteListener(requireActivity(), new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful()) {
+ // Sign in success, update UI with the signed-in user's information
+ Log.d(TAG, "createUserWithEmail:success");
+ FirebaseUser user = mAuth.getCurrentUser();
+ updateUI(user);
+ } else {
+ // If sign in fails, display a message to the user.
+ Log.w(TAG, "createUserWithEmail:failure", task.getException());
+ Toast.makeText(getContext(), "Authentication failed.",
+ Toast.LENGTH_SHORT).show();
+ updateUI(null);
+ }
+
+ hideProgressBar();
+ }
+ });
+ }
+
+ private void signIn(String email, String password) {
+ Log.d(TAG, "signIn:" + email);
+ if (!validateForm()) {
+ return;
+ }
+
+ showProgressBar();
+
+ mAuth.signInWithEmailAndPassword(email, password)
+ .addOnCompleteListener(requireActivity(), new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful()) {
+ // Sign in success, update UI with the signed-in user's information
+ Log.d(TAG, "signInWithEmail:success");
+ FirebaseUser user = mAuth.getCurrentUser();
+ updateUI(user);
+ } else {
+ // If sign in fails, display a message to the user.
+ Log.w(TAG, "signInWithEmail:failure", task.getException());
+ Toast.makeText(getContext(), "Authentication failed.",
+ Toast.LENGTH_SHORT).show();
+ updateUI(null);
+ checkForMultiFactorFailure(task.getException());
+ }
+
+ if (!task.isSuccessful()) {
+ mBinding.status.setText(R.string.auth_failed);
+ }
+ hideProgressBar();
+ }
+ });
+ }
+
+ private void signOut() {
+ mAuth.signOut();
+ updateUI(null);
+ }
+
+ private void sendEmailVerification() {
+ // Disable button
+ mBinding.verifyEmailButton.setEnabled(false);
+
+ // Send verification email
+ final FirebaseUser user = mAuth.getCurrentUser();
+ user.sendEmailVerification()
+ .addOnCompleteListener(requireActivity(), new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ // Re-enable button
+ mBinding.verifyEmailButton.setEnabled(true);
+
+ if (task.isSuccessful()) {
+ Toast.makeText(getContext(),
+ "Verification email sent to " + user.getEmail(),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Log.e(TAG, "sendEmailVerification", task.getException());
+ Toast.makeText(getContext(),
+ "Failed to send verification email.",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+
+ private void reload() {
+ mAuth.getCurrentUser().reload().addOnCompleteListener(new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful()) {
+ updateUI(mAuth.getCurrentUser());
+ Toast.makeText(getContext(),
+ "Reload successful!",
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Log.e(TAG, "reload", task.getException());
+ Toast.makeText(getContext(),
+ "Failed to reload user.",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+
+ private boolean validateForm() {
+ boolean valid = true;
+
+ String email = mBinding.fieldEmail.getText().toString();
+ if (TextUtils.isEmpty(email)) {
+ mBinding.fieldEmail.setError("Required.");
+ valid = false;
+ } else {
+ mBinding.fieldEmail.setError(null);
+ }
+
+ String password = mBinding.fieldPassword.getText().toString();
+ if (TextUtils.isEmpty(password)) {
+ mBinding.fieldPassword.setError("Required.");
+ valid = false;
+ } else {
+ mBinding.fieldPassword.setError(null);
+ }
+
+ return valid;
+ }
+
+ private void updateUI(FirebaseUser user) {
+ hideProgressBar();
+ if (user != null) {
+ mBinding.status.setText(getString(R.string.emailpassword_status_fmt,
+ user.getEmail(), user.isEmailVerified()));
+ mBinding.detail.setText(getString(R.string.firebase_status_fmt, user.getUid()));
+
+ mBinding.emailPasswordButtons.setVisibility(View.GONE);
+ mBinding.emailPasswordFields.setVisibility(View.GONE);
+ mBinding.signedInButtons.setVisibility(View.VISIBLE);
+
+ if (user.isEmailVerified()) {
+ mBinding.verifyEmailButton.setVisibility(View.GONE);
+ } else {
+ mBinding.verifyEmailButton.setVisibility(View.VISIBLE);
+ }
+ } else {
+ mBinding.status.setText(R.string.signed_out);
+ mBinding.detail.setText(null);
+
+ mBinding.emailPasswordButtons.setVisibility(View.VISIBLE);
+ mBinding.emailPasswordFields.setVisibility(View.VISIBLE);
+ mBinding.signedInButtons.setVisibility(View.GONE);
+ }
+ }
+
+ private void checkForMultiFactorFailure(Exception e) {
+ // Multi-factor authentication with SMS is currently only available for
+ // Google Cloud Identity Platform projects. For more information:
+ // https://cloud.google.com/identity-platform/docs/android/mfa
+ if (e instanceof FirebaseAuthMultiFactorException) {
+ Log.w(TAG, "multiFactorFailure", e);
+ MultiFactorResolver resolver = ((FirebaseAuthMultiFactorException) e).getResolver();
+ Bundle args = new Bundle();
+ args.putParcelable(MultiFactorSignInFragment.EXTRA_MFA_RESOLVER, resolver);
+ NavHostFragment.findNavController(this)
+ .navigate(R.id.action_emailpassword_to_mfasignin, args);
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mBinding = null;
+ }
+}
diff --git a/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/FacebookLoginFragment.java b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/FacebookLoginFragment.java
new file mode 100644
index 0000000000..1db613001a
--- /dev/null
+++ b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/FacebookLoginFragment.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.google.firebase.quickstart.auth.java;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.facebook.AccessToken;
+import com.facebook.CallbackManager;
+import com.facebook.FacebookCallback;
+import com.facebook.FacebookException;
+import com.facebook.login.LoginManager;
+import com.facebook.login.LoginResult;
+import com.facebook.login.widget.LoginButton;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.auth.AuthCredential;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.FacebookAuthProvider;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.quickstart.auth.R;
+import com.google.firebase.quickstart.auth.databinding.FragmentFacebookBinding;
+
+/**
+ * Demonstrate Firebase Authentication using a Facebook access token.
+ */
+public class FacebookLoginFragment extends BaseFragment {
+
+ private static final String TAG = "FacebookLogin";
+
+ private FragmentFacebookBinding mBinding;
+
+ private FirebaseAuth mAuth;
+
+ private CallbackManager mCallbackManager;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ mBinding = FragmentFacebookBinding.inflate(inflater, container, false);
+ return mBinding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setProgressBar(mBinding.progressBar);
+
+ // Views
+ mBinding.buttonFacebookSignout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ signOut();
+ }
+ });
+
+ // Initialize Firebase Auth
+ mAuth = FirebaseAuth.getInstance();
+
+ // Initialize Facebook Login button
+ mCallbackManager = CallbackManager.Factory.create();
+ LoginButton loginButton = mBinding.buttonFacebookLogin;
+ loginButton.setPermissions("email", "public_profile");
+ loginButton.registerCallback(mCallbackManager, new FacebookCallback() {
+ @Override
+ public void onSuccess(LoginResult loginResult) {
+ Log.d(TAG, "facebook:onSuccess:" + loginResult);
+ handleFacebookAccessToken(loginResult.getAccessToken());
+ }
+
+ @Override
+ public void onCancel() {
+ Log.d(TAG, "facebook:onCancel");
+ updateUI(null);
+ }
+
+ @Override
+ public void onError(FacebookException error) {
+ Log.d(TAG, "facebook:onError", error);
+ updateUI(null);
+ }
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Check if user is signed in (non-null) and update UI accordingly.
+ FirebaseUser currentUser = mAuth.getCurrentUser();
+ updateUI(currentUser);
+ }
+
+ private void handleFacebookAccessToken(AccessToken token) {
+ Log.d(TAG, "handleFacebookAccessToken:" + token);
+ showProgressBar();
+
+ AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
+ mAuth.signInWithCredential(credential)
+ .addOnCompleteListener(requireActivity(), new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful()) {
+ // Sign in success, update UI with the signed-in user's information
+ Log.d(TAG, "signInWithCredential:success");
+ FirebaseUser user = mAuth.getCurrentUser();
+ updateUI(user);
+ } else {
+ // If sign in fails, display a message to the user.
+ Log.w(TAG, "signInWithCredential:failure", task.getException());
+ Toast.makeText(getContext(), "Authentication failed.",
+ Toast.LENGTH_SHORT).show();
+ updateUI(null);
+ }
+
+ hideProgressBar();
+ }
+ });
+ }
+
+ public void signOut() {
+ mAuth.signOut();
+ LoginManager.getInstance().logOut();
+
+ updateUI(null);
+ }
+
+ private void updateUI(FirebaseUser user) {
+ hideProgressBar();
+ if (user != null) {
+ mBinding.status.setText(getString(R.string.facebook_status_fmt, user.getDisplayName()));
+ mBinding.detail.setText(getString(R.string.firebase_status_fmt, user.getUid()));
+
+ mBinding.buttonFacebookLogin.setVisibility(View.GONE);
+ mBinding.buttonFacebookSignout.setVisibility(View.VISIBLE);
+ } else {
+ mBinding.status.setText(R.string.signed_out);
+ mBinding.detail.setText(null);
+
+ mBinding.buttonFacebookLogin.setVisibility(View.VISIBLE);
+ mBinding.buttonFacebookSignout.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/FirebaseUIFragment.java b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/FirebaseUIFragment.java
new file mode 100644
index 0000000000..6f87e77eba
--- /dev/null
+++ b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/FirebaseUIFragment.java
@@ -0,0 +1,130 @@
+package com.google.firebase.quickstart.auth.java;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.firebase.ui.auth.AuthUI;
+import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
+import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.quickstart.auth.BuildConfig;
+import com.google.firebase.quickstart.auth.R;
+import com.google.firebase.quickstart.auth.databinding.FragmentFirebaseUiBinding;
+
+import java.util.Collections;
+
+/**
+ * Demonstrate authentication using the FirebaseUI-Android library. This fragment demonstrates
+ * using FirebaseUI for basic email/password sign in.
+ *
+ * For more information, visit https://github.com/firebase/firebaseui-android
+ */
+public class FirebaseUIFragment extends Fragment {
+
+ private FirebaseAuth mAuth;
+
+ private FragmentFirebaseUiBinding mBinding;
+
+ // Build FirebaseUI sign in intent. For documentation on this operation and all
+ // possible customization see: https://github.com/firebase/firebaseui-android
+ private final ActivityResultLauncher signInLauncher = registerForActivityResult(
+ new FirebaseAuthUIActivityResultContract(),
+ this::onSignInResult
+ );
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ mBinding = FragmentFirebaseUiBinding.inflate(inflater, container, false);
+ return mBinding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ // Initialize Firebase Auth
+ mAuth = FirebaseAuth.getInstance();
+
+ mBinding.signInButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startSignIn();
+ }
+ });
+
+ mBinding.signOutButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ signOut();
+ }
+ });
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mBinding = null;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ updateUI(mAuth.getCurrentUser());
+ }
+
+ private void onSignInResult(FirebaseAuthUIAuthenticationResult result) {
+ if (result.getResultCode() == Activity.RESULT_OK) {
+ // Sign in succeeded
+ updateUI(mAuth.getCurrentUser());
+ } else {
+ // Sign in failed
+ Toast.makeText(getContext(), "Sign In Failed", Toast.LENGTH_SHORT).show();
+ updateUI(null);
+ }
+ }
+
+ private void startSignIn() {
+ Intent intent = AuthUI.getInstance().createSignInIntentBuilder()
+ .setCredentialManagerEnabled(!BuildConfig.DEBUG)
+ .setAvailableProviders(Collections.singletonList(
+ new AuthUI.IdpConfig.EmailBuilder().build()))
+ .setLogo(R.mipmap.ic_launcher)
+ .build();
+
+ signInLauncher.launch(intent);
+ }
+
+ private void updateUI(FirebaseUser user) {
+ if (user != null) {
+ // Signed in
+ mBinding.status.setText(getString(R.string.firebaseui_status_fmt, user.getEmail()));
+ mBinding.detail.setText(getString(R.string.id_fmt, user.getUid()));
+
+ mBinding.signInButton.setVisibility(View.GONE);
+ mBinding.signOutButton.setVisibility(View.VISIBLE);
+ } else {
+ // Signed out
+ mBinding.status.setText(R.string.signed_out);
+ mBinding.detail.setText(null);
+
+ mBinding.signInButton.setVisibility(View.VISIBLE);
+ mBinding.signOutButton.setVisibility(View.GONE);
+ }
+ }
+
+ private void signOut() {
+ AuthUI.getInstance().signOut(getContext());
+ updateUI(null);
+ }
+}
\ No newline at end of file
diff --git a/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/GenericIdpFragment.java b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/GenericIdpFragment.java
new file mode 100644
index 0000000000..ab24dbee78
--- /dev/null
+++ b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/GenericIdpFragment.java
@@ -0,0 +1,202 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.google.firebase.quickstart.auth.java;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.gms.tasks.OnFailureListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.auth.OAuthProvider;
+import com.google.firebase.quickstart.auth.R;
+import com.google.firebase.quickstart.auth.databinding.FragmentGenericIdpBinding;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Demonstrate Firebase Authentication using a Generic Identity Provider (IDP).
+ */
+@SuppressWarnings("Convert2Lambda")
+public class GenericIdpFragment extends BaseFragment {
+
+ private static final String TAG = "GenericIdp";
+
+ private static final Map PROVIDER_MAP = new HashMap() {
+ {
+ put("Apple", "apple.com");
+ put("Microsoft", "microsoft.com");
+ put("Yahoo", "yahoo.com");
+ put("Twitter", "twitter.com");
+ }
+ };
+
+ private FragmentGenericIdpBinding mBinding;
+ private ArrayAdapter mSpinnerAdapter;
+
+ private FirebaseAuth mAuth;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ mBinding = FragmentGenericIdpBinding.inflate(inflater, container, false);
+ return mBinding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // Initialize Firebase Auth
+ mAuth = FirebaseAuth.getInstance();
+
+ // Set up button click listeners
+ mBinding.genericSignInButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ signIn();
+ }
+ });
+ mBinding.signOutButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAuth.signOut();
+ updateUI(null);
+ }
+ });
+
+ // Spinner
+ List providers = new ArrayList<>(PROVIDER_MAP.keySet());
+ mSpinnerAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner_list, providers);
+ mBinding.providerSpinner.setAdapter(mSpinnerAdapter);
+ mBinding.providerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ mBinding.genericSignInButton.setText(getString(R.string.generic_signin_fmt, mSpinnerAdapter.getItem(position)));
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {}
+ });
+ mBinding.providerSpinner.setSelection(0);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Check if user is signed in (non-null) and update UI accordingly.
+ FirebaseUser currentUser = mAuth.getCurrentUser();
+ updateUI(currentUser);
+
+ // Look for a pending auth result
+ Task pending = mAuth.getPendingAuthResult();
+ if (pending != null) {
+ pending.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(AuthResult authResult) {
+ Log.d(TAG, "checkPending:onSuccess:" + authResult);
+ updateUI(authResult.getUser());
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ Log.w(TAG, "checkPending:onFailure", e);
+ }
+ });
+ } else {
+ Log.d(TAG, "checkPending: null");
+ }
+ }
+
+ private void signIn() {
+ // Could add custom scopes here
+ ArrayList scopes = new ArrayList<>();
+
+ // Examples of provider ID: apple.com (Apple), microsoft.com (Microsoft), yahoo.com (Yahoo)
+ String providerId = getProviderId();
+
+ mAuth.startActivityForSignInWithProvider(requireActivity(),
+ OAuthProvider.newBuilder(providerId, mAuth)
+ .setScopes(scopes)
+ .build())
+ .addOnSuccessListener(
+ new OnSuccessListener() {
+ @Override
+ public void onSuccess(AuthResult authResult) {
+ Log.d(TAG, "activitySignIn:onSuccess:" + authResult.getUser());
+ updateUI(authResult.getUser());
+ }
+ })
+ .addOnFailureListener(
+ new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ Log.w(TAG, "activitySignIn:onFailure", e);
+ showToast(getString(R.string.error_sign_in_failed));
+ }
+ });
+ }
+
+ private String getProviderId() {
+ String providerName = mSpinnerAdapter.getItem(mBinding.providerSpinner.getSelectedItemPosition());
+ return PROVIDER_MAP.get(providerName);
+ }
+
+ private void updateUI(FirebaseUser user) {
+ hideProgressBar();
+ if (user != null) {
+ mBinding.status.setText(getString(R.string.generic_status_fmt, user.getDisplayName(), user.getEmail()));
+ mBinding.detail.setText(getString(R.string.firebase_status_fmt, user.getUid()));
+
+ mBinding.spinnerLayout.setVisibility(View.GONE);
+ mBinding.genericSignInButton.setVisibility(View.GONE);
+ mBinding.signOutButton.setVisibility(View.VISIBLE);
+ } else {
+ mBinding.status.setText(R.string.signed_out);
+ mBinding.detail.setText(null);
+
+ mBinding.spinnerLayout.setVisibility(View.VISIBLE);
+ mBinding.genericSignInButton.setVisibility(View.VISIBLE);
+ mBinding.signOutButton.setVisibility(View.GONE);
+ }
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mBinding = null;
+ }
+}
diff --git a/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/GoogleSignInFragment.java b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/GoogleSignInFragment.java
new file mode 100644
index 0000000000..0f2447a452
--- /dev/null
+++ b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/GoogleSignInFragment.java
@@ -0,0 +1,234 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.google.firebase.quickstart.auth.java;
+
+import static com.google.android.libraries.identity.googleid.GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL;
+
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.credentials.ClearCredentialStateRequest;
+import androidx.credentials.Credential;
+import androidx.credentials.CredentialManager;
+import androidx.credentials.CredentialManagerCallback;
+import androidx.credentials.CustomCredential;
+import androidx.credentials.GetCredentialRequest;
+import androidx.credentials.GetCredentialResponse;
+import androidx.credentials.exceptions.ClearCredentialException;
+import androidx.credentials.exceptions.GetCredentialException;
+import com.google.android.libraries.identity.googleid.GetGoogleIdOption;
+import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption;
+import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential;
+import com.google.android.material.snackbar.Snackbar;
+import com.google.firebase.auth.AuthCredential;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.auth.GoogleAuthProvider;
+import com.google.firebase.quickstart.auth.R;
+import com.google.firebase.quickstart.auth.databinding.FragmentGoogleBinding;
+import java.util.concurrent.Executors;
+
+/**
+ * Demonstrate Firebase Authentication using a Google ID Token.
+ */
+public class GoogleSignInFragment extends BaseFragment {
+
+ private static final String TAG = "GoogleFragment";
+
+ private FirebaseAuth mAuth;
+
+ private CredentialManager credentialManager;
+ private FragmentGoogleBinding mBinding;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ mBinding = FragmentGoogleBinding.inflate(inflater, container, false);
+ return mBinding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setProgressBar(mBinding.progressBar);
+
+ // Initialize Credential Manager
+ credentialManager = CredentialManager.create(requireContext());
+
+ // Initialize Firebase Auth
+ mAuth = FirebaseAuth.getInstance();
+
+ // Button listeners
+ mBinding.signInButton.setOnClickListener(v -> signIn());
+ mBinding.signOutButton.setOnClickListener(v -> signOut());
+
+ // Display Credential Manager Bottom Sheet if user isn't logged in
+ if (mAuth.getCurrentUser() == null) {
+ showBottomSheet();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Check if user is signed in (non-null) and update UI accordingly.
+ FirebaseUser currentUser = mAuth.getCurrentUser();
+ updateUI(currentUser);
+ }
+
+ private void signIn() {
+ // Create the dialog configuration for the Credential Manager request
+ GetSignInWithGoogleOption signInWithGoogleOption = new GetSignInWithGoogleOption
+ .Builder(requireContext().getString(R.string.default_web_client_id))
+ .build();
+
+ // Create the Credential Manager request using the configuration created above
+ GetCredentialRequest request = new GetCredentialRequest.Builder()
+ .addCredentialOption(signInWithGoogleOption)
+ .build();
+
+ launchCredentialManager(request);
+ }
+
+ private void showBottomSheet() {
+ // Create the bottom sheet configuration for the Credential Manager request
+ GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
+ .setFilterByAuthorizedAccounts(true)
+ .setServerClientId(requireContext().getString(R.string.default_web_client_id))
+ .build();
+
+ // Create the Credential Manager request using the configuration created above
+ GetCredentialRequest request = new GetCredentialRequest.Builder()
+ .addCredentialOption(googleIdOption)
+ .build();
+
+ launchCredentialManager(request);
+ }
+
+ private void launchCredentialManager(GetCredentialRequest request) {
+ credentialManager.getCredentialAsync(
+ requireContext(),
+ request,
+ new CancellationSignal(),
+ Executors.newSingleThreadExecutor(),
+ new CredentialManagerCallback<>() {
+ @Override
+ public void onResult(GetCredentialResponse result) {
+ // Extract credential from the result returned by Credential Manager
+ createGoogleIdToken(result.getCredential());
+ }
+
+ @Override
+ public void onError(GetCredentialException e) {
+ Log.e(TAG, "Couldn't retrieve user's credentials: " + e.getLocalizedMessage());
+ }
+ }
+ );
+ }
+
+ private void createGoogleIdToken(Credential credential) {
+ // Update UI to show progress bar while response is being processed
+ requireActivity().runOnUiThread(this::showProgressBar);
+
+ // Check if credential is of type Google ID
+ if (credential instanceof CustomCredential customCredential
+ && credential.getType().equals(TYPE_GOOGLE_ID_TOKEN_CREDENTIAL)) {
+ // Create Google ID Token
+ Bundle credentialData = customCredential.getData();
+ GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credentialData);
+
+ // Sign in to Firebase with using the token
+ firebaseAuthWithGoogle(googleIdTokenCredential.getIdToken());
+ } else {
+ Log.w(TAG, "Credential is not of type Google ID!");
+ }
+ }
+
+ private void firebaseAuthWithGoogle(String idToken) {
+ AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
+ mAuth.signInWithCredential(credential)
+ .addOnCompleteListener(requireActivity(), task -> {
+ if (task.isSuccessful()) {
+ // Sign in success, update UI with the signed-in user's information
+ Log.d(TAG, "signInWithCredential:success");
+ FirebaseUser user = mAuth.getCurrentUser();
+ updateUI(user);
+ } else {
+ // If sign in fails, display a message to the user.
+ Log.w(TAG, "signInWithCredential:failure", task.getException());
+ Snackbar.make(mBinding.mainLayout, "Authentication Failed.", Snackbar.LENGTH_SHORT).show();
+ updateUI(null);
+ }
+
+ hideProgressBar();
+ });
+ }
+
+ private void signOut() {
+ // Firebase sign out
+ mAuth.signOut();
+
+ // When a user signs out, clear the current user credential state from all credential providers.
+ // This will notify all providers that any stored credential session for the given app should be cleared.
+ ClearCredentialStateRequest clearRequest = new ClearCredentialStateRequest();
+ credentialManager.clearCredentialStateAsync(
+ clearRequest,
+ new CancellationSignal(),
+ Executors.newSingleThreadExecutor(),
+ new CredentialManagerCallback<>() {
+ @Override
+ public void onResult(@NonNull Void result) {
+ updateUI(null);
+ }
+
+ @Override
+ public void onError(@NonNull ClearCredentialException e) {
+ Log.e(TAG, "Couldn't clear user credentials: " + e.getLocalizedMessage());
+ }
+ });
+ }
+
+ private void updateUI(FirebaseUser user) {
+ requireActivity().runOnUiThread(() -> {
+ hideProgressBar();
+ if (user != null) {
+ mBinding.status.setText(getString(R.string.google_status_fmt, user.getEmail()));
+ mBinding.detail.setText(getString(R.string.firebase_status_fmt, user.getUid()));
+
+ mBinding.signInButton.setVisibility(View.GONE);
+ mBinding.signOutButton.setVisibility(View.VISIBLE);
+ } else {
+ mBinding.status.setText(R.string.signed_out);
+ mBinding.detail.setText(null);
+
+ mBinding.signInButton.setVisibility(View.VISIBLE);
+ mBinding.signOutButton.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mBinding = null;
+ }
+}
diff --git a/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MainActivity.java b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MainActivity.java
new file mode 100644
index 0000000000..1770d2b2ce
--- /dev/null
+++ b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MainActivity.java
@@ -0,0 +1,21 @@
+package com.google.firebase.quickstart.auth.java;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.navigation.Navigation;
+
+import com.google.firebase.quickstart.auth.R;
+
+public class MainActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Navigation.findNavController(this, R.id.nav_host_fragment)
+ .setGraph(R.navigation.nav_graph_java);
+ }
+}
diff --git a/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MultiFactorEnrollFragment.java b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MultiFactorEnrollFragment.java
new file mode 100644
index 0000000000..40f755ccf1
--- /dev/null
+++ b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MultiFactorEnrollFragment.java
@@ -0,0 +1,169 @@
+package com.google.firebase.quickstart.auth.java;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.navigation.fragment.NavHostFragment;
+
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.OnFailureListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.FirebaseException;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.MultiFactorSession;
+import com.google.firebase.auth.PhoneAuthCredential;
+import com.google.firebase.auth.PhoneAuthOptions;
+import com.google.firebase.auth.PhoneAuthProvider;
+import com.google.firebase.auth.PhoneAuthProvider.OnVerificationStateChangedCallbacks;
+import com.google.firebase.auth.PhoneMultiFactorGenerator;
+import com.google.firebase.quickstart.auth.databinding.FragmentPhoneAuthBinding;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Fragment that allows the user to enroll second factors.
+ */
+public class MultiFactorEnrollFragment extends BaseFragment {
+
+ private static final String TAG = "MfaEnrollFragment";
+
+ private FragmentPhoneAuthBinding mBinding;
+
+ private String mCodeVerificationId;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ mBinding = FragmentPhoneAuthBinding.inflate(inflater, container, false);
+ return mBinding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ mBinding.titleText.setText("SMS as a Second Factor");
+ mBinding.status.setVisibility(View.GONE);
+ mBinding.detail.setVisibility(View.GONE);
+ mBinding.buttonStartVerification.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onClickVerifyPhoneNumber();
+ }
+ });
+ mBinding.buttonVerifyPhone.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onClickSignInWithPhoneNumber();
+ }
+ });
+ }
+
+ private void onClickVerifyPhoneNumber() {
+ String phoneNumber = mBinding.fieldPhoneNumber.getText().toString();
+
+ OnVerificationStateChangedCallbacks callbacks =
+ new OnVerificationStateChangedCallbacks() {
+ @Override
+ public void onVerificationCompleted(PhoneAuthCredential credential) {
+ // Instant-validation has been disabled (see requireSmsValidation below).
+ // Auto-retrieval has also been disabled (timeout is set to 0).
+ // This should never be triggered.
+ throw new RuntimeException(
+ "onVerificationCompleted() triggered with instant-validation and auto-retrieval disabled.");
+ }
+
+ @Override
+ public void onCodeSent(
+ final String verificationId, PhoneAuthProvider.ForceResendingToken token) {
+ Log.d(TAG, "onCodeSent:" + verificationId);
+ Toast.makeText( getContext(), "SMS code has been sent", Toast.LENGTH_SHORT)
+ .show();
+
+ mCodeVerificationId = verificationId;
+ }
+
+ @Override
+ public void onVerificationFailed(FirebaseException e) {
+ Log.w(TAG, "onVerificationFailed ", e);
+ Toast.makeText(getContext(), "Verification failed: " + e.getMessage(), Toast.LENGTH_SHORT)
+ .show();
+ }
+ };
+
+ FirebaseAuth.getInstance()
+ .getCurrentUser()
+ .getMultiFactor()
+ .getSession()
+ .addOnCompleteListener(
+ new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful()) {
+ PhoneAuthOptions phoneAuthOptions =
+ PhoneAuthOptions.newBuilder()
+ .setActivity(requireActivity())
+ .setPhoneNumber(phoneNumber)
+ // A timeout of 0 disables SMS-auto-retrieval.
+ .setTimeout(0L, TimeUnit.SECONDS)
+ .setMultiFactorSession(task.getResult())
+ .setCallbacks(callbacks)
+ // Disable instant-validation.
+ .requireSmsValidation(true)
+ .build();
+
+ PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
+ } else {
+ Toast.makeText(getContext(),
+ "Failed to get session: " + task.getException(), Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ });
+ }
+
+ private void onClickSignInWithPhoneNumber() {
+ String smsCode = mBinding.fieldVerificationCode.getText().toString();
+ if (TextUtils.isEmpty(smsCode)) {
+ return;
+ }
+ PhoneAuthCredential credential = PhoneAuthProvider.getCredential(mCodeVerificationId, smsCode);
+ enrollWithPhoneAuthCredential(credential);
+ }
+
+ private void enrollWithPhoneAuthCredential(PhoneAuthCredential credential) {
+ FirebaseAuth.getInstance()
+ .getCurrentUser()
+ .getMultiFactor()
+ .enroll(PhoneMultiFactorGenerator.getAssertion(credential), /* displayName= */ null)
+ .addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ Toast.makeText(getContext(), "MFA enrollment was successful",
+ Toast.LENGTH_LONG)
+ .show();
+
+ NavHostFragment.findNavController(MultiFactorEnrollFragment.this)
+ .popBackStack();
+ }
+ })
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ Log.d(TAG, "MFA failure", e);
+ Toast.makeText(getContext(),
+ "MFA enrollment was unsuccessful. " + e,
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ });
+ }
+}
diff --git a/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MultiFactorFragment.java b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MultiFactorFragment.java
new file mode 100644
index 0000000000..399a57661e
--- /dev/null
+++ b/auth/app/src/main/java/com/google/firebase/quickstart/auth/java/MultiFactorFragment.java
@@ -0,0 +1,229 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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.google.firebase.quickstart.fcm.java;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Build;
+import androidx.core.app.NotificationCompat;
+import android.util.Log;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+import com.google.firebase.quickstart.fcm.R;
+
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkManager;
+
+/**
+ * NOTE: There can only be one service in each app that receives FCM messages. If multiple
+ * are declared in the Manifest then the first one will be chosen.
+ *
+ * In order to make this Java sample functional, you must remove the following from the Kotlin messaging
+ * service in the AndroidManifest.xml:
+ *
+ *
+ *
+ *
+ */
+public class MyFirebaseMessagingService extends FirebaseMessagingService {
+
+ private static final String TAG = "MyFirebaseMsgService";
+
+ /**
+ * Called when message is received.
+ *
+ * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
+ */
+ // [START receive_message]
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ // [START_EXCLUDE]
+ // There are two types of messages data messages and notification messages. Data messages
+ // are handled
+ // here in onMessageReceived whether the app is in the foreground or background. Data
+ // messages are the type
+ // traditionally used with GCM. Notification messages are only received here in
+ // onMessageReceived when the app
+ // is in the foreground. When the app is in the background an automatically generated
+ // notification is displayed.
+ // When the user taps on the notification they are returned to the app. Messages
+ // containing both notification
+ // and data payloads are treated as notification messages. The Firebase console always
+ // sends notification
+ // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options
+ // [END_EXCLUDE]
+
+ // TODO(developer): Handle FCM messages here.
+ // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
+ Log.d(TAG, "From: " + remoteMessage.getFrom());
+
+ // Check if message contains a data payload.
+ if (remoteMessage.getData().size() > 0) {
+ Log.d(TAG, "Message data payload: " + remoteMessage.getData());
+
+ if (/* Check if data needs to be processed by long running job */ true) {
+ // For long-running tasks (10 seconds or more) use WorkManager.
+ scheduleJob();
+ } else {
+ // Handle message within 10 seconds
+ handleNow();
+ }
+
+ }
+
+ // Check if message contains a notification payload.
+ if (remoteMessage.getNotification() != null) {
+ Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
+ String notificationBody = remoteMessage.getNotification().getBody();
+ if (remoteMessage.getNotification().getBody() != null) {
+ sendNotification(notificationBody);
+ }
+ }
+
+ // Also if you intend on generating your own notifications as a result of a received FCM
+ // message, here is where that should be initiated. See sendNotification method below.
+ }
+ // [END receive_message]
+
+
+ // [START on_new_token]
+ /**
+ * There are two scenarios when onNewToken is called:
+ * 1) When a new token is generated on initial app startup
+ * 2) Whenever an existing token is changed
+ * Under #2, there are three scenarios when the existing token is changed:
+ * A) App is restored to a new device
+ * B) User uninstalls/reinstalls the app
+ * C) User clears app data
+ */
+ @Override
+ public void onNewToken(String token) {
+ Log.d(TAG, "Refreshed token: " + token);
+
+ // If you want to send messages to this application instance or
+ // manage this apps subscriptions on the server side, send the
+ // FCM registration token to your app server.
+ sendRegistrationToServer(token);
+ }
+ // [END on_new_token]
+
+ /**
+ * Schedule async work using WorkManager.
+ */
+ private void scheduleJob() {
+ // [START dispatch_job]
+ OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(MyWorker.class)
+ .build();
+ WorkManager.getInstance(this).beginWith(work).enqueue();
+ // [END dispatch_job]
+ }
+
+ /**
+ * Handle time allotted to BroadcastReceivers.
+ */
+ private void handleNow() {
+ Log.d(TAG, "Short lived task is done.");
+ }
+
+ /**
+ * Persist token to third-party servers.
+ *
+ * Modify this method to associate the user's FCM registration token with any
+ * server-side account maintained by your application.
+ *
+ * @param token The new token.
+ */
+ private void sendRegistrationToServer(String token) {
+ // TODO: Implement this method to send token to your app server.
+ }
+
+ /**
+ * Create and show a simple notification containing the received FCM message.
+ *
+ * @param messageBody FCM message body received.
+ */
+ private void sendNotification(String messageBody) {
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ String channelId = getString(R.string.default_notification_channel_id);
+ Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+ NotificationCompat.Builder notificationBuilder =
+ new NotificationCompat.Builder(this, channelId)
+ .setSmallIcon(R.drawable.ic_stat_ic_notification)
+ .setContentTitle(getString(R.string.fcm_message))
+ .setContentText(messageBody)
+ .setAutoCancel(true)
+ .setSound(defaultSoundUri)
+ .setContentIntent(pendingIntent);
+
+ NotificationManager notificationManager =
+ (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // Since android Oreo notification channel is needed.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(channelId,
+ "Channel human readable title",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ notificationManager.createNotificationChannel(channel);
+ }
+
+ notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
+ }
+}
diff --git a/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/java/MyWorker.java b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/java/MyWorker.java
new file mode 100644
index 0000000000..e714a845a9
--- /dev/null
+++ b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/java/MyWorker.java
@@ -0,0 +1,25 @@
+package com.google.firebase.quickstart.fcm.java;
+
+import android.content.Context;
+import androidx.annotation.NonNull;
+import android.util.Log;
+
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+
+public class MyWorker extends Worker {
+
+ private static final String TAG = "MyWorker";
+
+ public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
+ super(appContext, workerParams);
+ }
+
+ @NonNull
+ @Override
+ public Result doWork() {
+ Log.d(TAG, "Performing long running task in scheduled job");
+ // TODO(developer): add long running task here.
+ return Result.success();
+ }
+}
diff --git a/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MainActivity.kt b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MainActivity.kt
new file mode 100644
index 0000000000..ff5a0bc598
--- /dev/null
+++ b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MainActivity.kt
@@ -0,0 +1,132 @@
+package com.google.firebase.quickstart.fcm.kotlin
+
+import android.Manifest
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import com.google.android.gms.tasks.OnCompleteListener
+import com.google.firebase.Firebase
+import com.google.firebase.messaging.messaging
+import com.google.firebase.quickstart.fcm.R
+import com.google.firebase.quickstart.fcm.databinding.ActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ private val requestPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission(),
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ Toast.makeText(this, "Notifications permission granted", Toast.LENGTH_SHORT)
+ .show()
+ } else {
+ Toast.makeText(
+ this,
+ "FCM can't post notifications without POST_NOTIFICATIONS permission",
+ Toast.LENGTH_LONG,
+ ).show()
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ // Create channel to show notifications.
+ val channelId = getString(R.string.default_notification_channel_id)
+ val channelName = getString(R.string.default_notification_channel_name)
+ val notificationManager = getSystemService(NotificationManager::class.java)
+ notificationManager?.createNotificationChannel(
+ NotificationChannel(
+ channelId,
+ channelName,
+ NotificationManager.IMPORTANCE_LOW,
+ ),
+ )
+ }
+
+ // If a notification message is tapped, any data accompanying the notification
+ // message is available in the intent extras. In this sample the launcher
+ // intent is fired when the notification is tapped, so any accompanying data would
+ // be handled here. If you want a different intent fired, set the click_action
+ // field of the notification message to the desired intent. The launcher intent
+ // is used when no click_action is specified.
+ //
+ // Handle possible data accompanying notification message.
+ // [START handle_data_extras]
+ intent.extras?.let {
+ for (key in it.keySet()) {
+ val value = intent.extras?.getString(key)
+ Log.d(TAG, "Key: $key Value: $value")
+ }
+ }
+ // [END handle_data_extras]
+
+ binding.subscribeButton.setOnClickListener {
+ Log.d(TAG, "Subscribing to weather topic")
+ // [START subscribe_topics]
+ Firebase.messaging.subscribeToTopic("weather")
+ .addOnCompleteListener { task ->
+ var msg = getString(R.string.msg_subscribed)
+ if (!task.isSuccessful) {
+ msg = getString(R.string.msg_subscribe_failed)
+ }
+ Log.d(TAG, msg)
+ Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
+ }
+ // [END subscribe_topics]
+ }
+
+ binding.logTokenButton.setOnClickListener {
+ // Get token
+ // [START log_reg_token]
+ Firebase.messaging.token.addOnCompleteListener(
+ OnCompleteListener { task ->
+ if (!task.isSuccessful) {
+ Log.w(TAG, "Fetching FCM registration token failed", task.exception)
+ return@OnCompleteListener
+ }
+
+ // Get new FCM registration token
+ val token = task.result
+
+ // Log and toast
+ val msg = getString(R.string.msg_token_fmt, token)
+ Log.d(TAG, msg)
+ Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
+ },
+ )
+ // [END log_reg_token]
+ }
+
+ Toast.makeText(this, "See README for setup instructions", Toast.LENGTH_SHORT).show()
+ askNotificationPermission()
+ }
+
+ private fun askNotificationPermission() {
+ // This is only necessary for API Level > 33 (TIRAMISU)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
+ PackageManager.PERMISSION_GRANTED
+ ) {
+ // FCM SDK (and your app) can post notifications.
+ } else {
+ // Directly ask for the permission
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }
+ }
+
+ companion object {
+
+ private const val TAG = "MainActivity"
+ }
+}
diff --git a/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MyFirebaseMessagingService.kt b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MyFirebaseMessagingService.kt
new file mode 100644
index 0000000000..a8b43265cf
--- /dev/null
+++ b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MyFirebaseMessagingService.kt
@@ -0,0 +1,160 @@
+package com.google.firebase.quickstart.fcm.kotlin
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.media.RingtoneManager
+import android.os.Build
+import android.util.Log
+import androidx.core.app.NotificationCompat
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkManager
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+import com.google.firebase.quickstart.fcm.R
+
+class MyFirebaseMessagingService : FirebaseMessagingService() {
+
+ /**
+ * Called when message is received.
+ *
+ * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
+ */
+ // [START receive_message]
+ override fun onMessageReceived(remoteMessage: RemoteMessage) {
+ // [START_EXCLUDE]
+ // There are two types of messages data messages and notification messages. Data messages are handled
+ // here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
+ // traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
+ // is in the foreground. When the app is in the background an automatically generated notification is displayed.
+ // When the user taps on the notification they are returned to the app. Messages containing both notification
+ // and data payloads are treated as notification messages. The Firebase console always sends notification
+ // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options
+ // [END_EXCLUDE]
+
+ // TODO(developer): Handle FCM messages here.
+ // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
+ Log.d(TAG, "From: ${remoteMessage.from}")
+
+ // Check if message contains a data payload.
+ if (remoteMessage.data.isNotEmpty()) {
+ Log.d(TAG, "Message data payload: ${remoteMessage.data}")
+
+ // Check if data needs to be processed by long running job
+ if (isLongRunningJob()) {
+ // For long-running tasks (10 seconds or more) use WorkManager.
+ scheduleJob()
+ } else {
+ // Handle message within 10 seconds
+ handleNow()
+ }
+ }
+
+ // Check if message contains a notification payload.
+ remoteMessage.notification?.let {
+ Log.d(TAG, "Message Notification Body: ${it.body}")
+ it.body?.let { body -> sendNotification(body) }
+ }
+
+ // Also if you intend on generating your own notifications as a result of a received FCM
+ // message, here is where that should be initiated. See sendNotification method below.
+ }
+ // [END receive_message]
+
+ private fun isLongRunningJob() = true
+
+ // [START on_new_token]
+ /**
+ * Called if the FCM registration token is updated. This may occur if the security of
+ * the previous token had been compromised. Note that this is called when the
+ * FCM registration token is initially generated so this is where you would retrieve the token.
+ */
+ override fun onNewToken(token: String) {
+ Log.d(TAG, "Refreshed token: $token")
+
+ // If you want to send messages to this application instance or
+ // manage this apps subscriptions on the server side, send the
+ // FCM registration token to your app server.
+ sendRegistrationToServer(token)
+ }
+ // [END on_new_token]
+
+ /**
+ * Schedule async work using WorkManager.
+ */
+ private fun scheduleJob() {
+ // [START dispatch_job]
+ val work = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
+ WorkManager.getInstance(this).beginWith(work).enqueue()
+ // [END dispatch_job]
+ }
+
+ /**
+ * Handle time allotted to BroadcastReceivers.
+ */
+ private fun handleNow() {
+ Log.d(TAG, "Short lived task is done.")
+ }
+
+ /**
+ * Persist token to third-party servers.
+ *
+ * Modify this method to associate the user's FCM registration token with any server-side account
+ * maintained by your application.
+ *
+ * @param token The new token.
+ */
+ private fun sendRegistrationToServer(token: String?) {
+ // TODO: Implement this method to send token to your app server.
+ Log.d(TAG, "sendRegistrationTokenToServer($token)")
+ }
+
+ /**
+ * Create and show a simple notification containing the received FCM message.
+ *
+ * @param messageBody FCM message body received.
+ */
+ private fun sendNotification(messageBody: String) {
+ val requestCode = 0
+ val intent = Intent(this, MainActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ val pendingIntent = PendingIntent.getActivity(
+ this,
+ requestCode,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE,
+ )
+
+ val channelId = getString(R.string.default_notification_channel_id)
+ val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
+ val notificationBuilder = NotificationCompat.Builder(this, channelId)
+ .setSmallIcon(R.drawable.ic_stat_ic_notification)
+ .setContentTitle(getString(R.string.fcm_message))
+ .setContentText(messageBody)
+ .setAutoCancel(true)
+ .setSound(defaultSoundUri)
+ .setContentIntent(pendingIntent)
+
+ val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ // Since android Oreo notification channel is needed.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(
+ channelId,
+ "Channel human readable title",
+ NotificationManager.IMPORTANCE_DEFAULT,
+ )
+ notificationManager.createNotificationChannel(channel)
+ }
+
+ val notificationId = 0
+ notificationManager.notify(notificationId, notificationBuilder.build())
+ }
+
+ companion object {
+
+ private const val TAG = "MyFirebaseMsgService"
+ }
+}
diff --git a/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MyWorker.kt b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MyWorker.kt
new file mode 100644
index 0000000000..45e235ed7e
--- /dev/null
+++ b/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MyWorker.kt
@@ -0,0 +1,19 @@
+package com.google.firebase.quickstart.fcm.kotlin
+
+import android.content.Context
+import android.util.Log
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+
+class MyWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
+
+ override fun doWork(): Result {
+ Log.d(TAG, "Performing long running task in scheduled job")
+ // TODO(developer): add long running task here.
+ return Result.success()
+ }
+
+ companion object {
+ private val TAG = "MyWorker"
+ }
+}
diff --git a/messaging/app/src/main/res/layout/activity_main.xml b/messaging/app/src/main/res/layout/activity_main.xml
index a26ca74026..3217bb5e3c 100644
--- a/messaging/app/src/main/res/layout/activity_main.xml
+++ b/messaging/app/src/main/res/layout/activity_main.xml
@@ -7,7 +7,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.google.firebase.quickstart.fcm.MainActivity">
+ tools:context="com.google.firebase.quickstart.fcm.java.MainActivity">
+ android:text="@string/subscribe_to_weather" />
Firebase Cloud Messaging
- Click the SUBSCRIBE TO NEWS button below to subscribe to the
- news topic. Messages sent to the news topic will be received. The LOG TOKEN button logs the
- InstanceID token to logcat.
- Subscribe To News
+ Click the SUBSCRIBE TO WEATHER button below to subscribe to the
+ weather topic. Messages sent to the weather topic will be received. The LOG TOKEN button logs the
+ FCM registration token to logcat.
+ Subscribe To WeatherLog Token
- Subscribed to news topic
- InstanceID Token: %s
+ Subscribed to weather topic
+ FCM registration Token: %s
+
+ fcm_default_channel
+
+
+ Weather
+
+ Failed to subscribe to weather topic
+ FCM Message
diff --git a/messaging/app/src/main/res/values/styles.xml b/messaging/app/src/main/res/values/styles.xml
index 09c8bdf79b..5009ab9c76 100644
--- a/messaging/app/src/main/res/values/styles.xml
+++ b/messaging/app/src/main/res/values/styles.xml
@@ -1,7 +1,7 @@
-