package com.kaorisan.activity;
import java.util.Arrays;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.plus.PlusClient;
import com.google.android.gms.plus.PlusClient.OnAccessRevokedListener;
import com.kaorisan.R;
/**
* A fragment that you can add to your layout to automate connecting a {@link PlusClient}.
* The key integration points are to attach the fragment with {@link #getPlusClientFragment}
* in your Activity's {@code onCreate} method, then call {@link #signIn} when the user clicks your
* sign-in button and forward calls to {@code onActivityResult} to {@link #handleOnActivityResult}.
*/
public final class PlusClientFragment extends Fragment
implements ConnectionCallbacks, OnConnectionFailedListener, OnAccessRevokedListener{
// static final String[] SCOPES = new String[] { Scopes.PLUS_PROFILE };
/**
* Tag to refer to this fragment.
*/
private static final String TAG_PLUS_CLIENT = "plusClientFragment";
/**
* Tag to refer to an error (resolution) dialog.
*/
private static final String TAG_ERROR_DIALOG = "plusClientFragmentErrorDialog";
/**
* Tag to refer to a progress dialog when sign in is requested.
*/
private static final String TAG_PROGRESS_DIALOG = "plusClientFragmentProgressDialog";
/**
* Array of strings representing visible activities to request for {@link #getArguments()}.
*/
private static final String ARG_VISIBLE_ACTIVITIES = "visible_activities";
/**
* Integer request code to apply to requests, as set by {@link #signIn(int)}.
*/
private static final String STATE_REQUEST_CODE = "request_code";
/**
* Signed in successfully connection state.
*/
private static final ConnectionResult CONNECTION_RESULT_SUCCESS =
new ConnectionResult(ConnectionResult.SUCCESS, null);
/**
* An invalid request code to use to indicate that {@link #signIn(int)} hasn't been called.
*/
private static final int INVALID_REQUEST_CODE = -1;
// The PlusClient to connect.
private PlusClient mPlusClient;
// The last result from onConnectionFailed.
private ConnectionResult mLastConnectionResult;
// The request specified in signIn or INVALID_REQUEST_CODE if not signing in.
private int mRequestCode;
// A handler to post callbacks (rather than call them in a potentially reentrant way.)
private Handler mHandler;
/**
* Local handler to send callbacks on sign in.
*/
private final class PlusClientFragmentHandler extends Handler {
public static final int WHAT_SIGNED_IN = 1;
public PlusClientFragmentHandler() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message msg) {
if (msg.what != WHAT_SIGNED_IN) {
return;
}
Activity activity = getActivity();
if (mPlusClient.isConnected() && activity instanceof OnSignedInListener) {
((OnSignedInListener) activity).onSignedIn(mPlusClient);
}
}
}
/**
* Listener interface for sign in events. Activities hosting a {@link PlusClientFragment}
* must implement this.
*/
public static interface OnSignedInListener {
/**
* Called when the {@link PlusClient} has been connected successfully.
*
* @param plusClient The connected {@link PlusClient} to make requests on.
*/
void onSignedIn(PlusClient plusClient);
}
/**
* Attach a {@link PlusClient} managing fragment to you activity.
*
* @param activity The activity to attach the fragment to.
* @param visibleActivities An array of visible activities to request.
* @return The fragment managing a {@link PlusClient}.
*/
public static PlusClientFragment getPlusClientFragment(
FragmentActivity activity, String[] visibleActivities) {
if (!(activity instanceof OnSignedInListener)) {
throw new IllegalArgumentException(
"The activity must implement OnSignedInListener to receive callbacks.");
}
// Check if the fragment is already attached.
FragmentManager fragmentManager = activity.getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_PLUS_CLIENT);
if (fragment instanceof PlusClientFragment) {
// The fragment is attached. If it has the right visible activities, return it.
if (Arrays.equals(visibleActivities,
fragment.getArguments().getStringArray(ARG_VISIBLE_ACTIVITIES))) {
return (PlusClientFragment) fragment;
}
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// If a fragment was already attached, remove it to clean up.
if (fragment != null) {
fragmentTransaction.remove(fragment);
}
// Create a new fragment and attach it to the fragment manager.
Bundle arguments = new Bundle();
arguments.putStringArray(ARG_VISIBLE_ACTIVITIES, visibleActivities);
PlusClientFragment signInFragment = new PlusClientFragment();
signInFragment.setArguments(arguments);
fragmentTransaction.add(signInFragment, TAG_PLUS_CLIENT);
fragmentTransaction.commit();
return signInFragment;
}
/**
* Creates a {@link PlusClient} and kicks-off the connection flow.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain instance to avoid reconnecting on rotate. This means that onDestroy and onCreate
// will not be called on configuration changes.
setRetainInstance(true);
mHandler = new PlusClientFragmentHandler();
// Create the PlusClient.
PlusClient.Builder plusClientBuilder =
new PlusClient.Builder(getActivity().getApplicationContext(), this, this);
String[] visibleActivities = getArguments().getStringArray(ARG_VISIBLE_ACTIVITIES);
if (visibleActivities != null && visibleActivities.length > 0) {
plusClientBuilder.setVisibleActivities(visibleActivities);
}
mPlusClient = plusClientBuilder.build();
if (savedInstanceState == null) {
mRequestCode = INVALID_REQUEST_CODE;
} else {
mRequestCode = savedInstanceState.getInt(STATE_REQUEST_CODE, INVALID_REQUEST_CODE);
}
}
/**
* Disconnects the {@link PlusClient} to avoid leaks.
*/
@Override
public void onDestroy() {
super.onDestroy();
if (mPlusClient.isConnecting() || mPlusClient.isConnected()) {
mPlusClient.disconnect();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_REQUEST_CODE, mRequestCode);
}
@Override
public void onResume() {
super.onResume();
if (mRequestCode == INVALID_REQUEST_CODE) {
// No user interaction, hide the progress dialog.
hideProgressDialog();
hideErrorDialog();
} else if (mLastConnectionResult != null && !mLastConnectionResult.isSuccess()
&& !isShowingErrorDialog()) {
showProgressDialog();
}
}
@Override
public void onStart() {
super.onStart();
if (mRequestCode == INVALID_REQUEST_CODE) {
mLastConnectionResult = null;
connectPlusClient();
}
}
@Override
public void onConnected(Bundle connectionHint) {
// Successful connection!
mLastConnectionResult = CONNECTION_RESULT_SUCCESS;
mRequestCode = INVALID_REQUEST_CODE;
if (isResumed()) {
hideProgressDialog();
}
Activity activity = getActivity();
if (activity instanceof OnSignedInListener) {
((OnSignedInListener) activity).onSignedIn(mPlusClient);
}
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
mLastConnectionResult = connectionResult;
// On a failed connection try again.
if (isResumed() && mRequestCode != INVALID_REQUEST_CODE) {
resolveLastResult();
}
}
@Override
public void onAccessRevoked(ConnectionResult status) {
// Reconnect to get a new mPlusClient.
mLastConnectionResult = null;
// Cancel sign in.
mRequestCode = INVALID_REQUEST_CODE;
// Reconnect to fetch the sign-in (account chooser) intent from the plus
// client.
connectPlusClient();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Let new activities know the signed-in state.
if (mPlusClient.isConnected()) {
mHandler.sendEmptyMessage(PlusClientFragmentHandler.WHAT_SIGNED_IN);
}
}
@Override
public void onDisconnected() {
// Do nothing.
}
/**
* Shows any UI required to resolve the error connecting.
*/
public void signIn(int requestCode) {
if (requestCode < 0) {
throw new IllegalArgumentException(
"A non-negative request code is required.");
}
if (mPlusClient.isConnected()) {
// Already connected! Schedule callback.
mHandler.sendEmptyMessage(PlusClientFragmentHandler.WHAT_SIGNED_IN);
return;
}
if (mRequestCode != INVALID_REQUEST_CODE) {
// We're already signing in.
connectPlusClient();
return;
}
mRequestCode = requestCode;
if (mLastConnectionResult == null) {
// We're starting up, show progress.
showProgressDialog();
return;
}
resolveLastResult();
}
/**
* Perform resolution given a non-null result.
*/
private void resolveLastResult() {
if (GooglePlayServicesUtil.isUserRecoverableError(mLastConnectionResult
.getErrorCode())) {
// Show a dialog to install or enable Google Play services.
showErrorDialog(ErrorDialogFragment.create(
mLastConnectionResult.getErrorCode(), mRequestCode));
return;
}
if (mLastConnectionResult.hasResolution()) {
startResolution();
}
}
public static final class ErrorDialogFragment extends
GooglePlayServicesErrorDialogFragment {
public static ErrorDialogFragment create(int errorCode, int requestCode) {
ErrorDialogFragment fragment = new ErrorDialogFragment();
fragment.setArguments(createArguments(errorCode, requestCode));
return fragment;
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
FragmentActivity activity = getActivity();
if (activity == null) {
return;
}
Fragment fragment = activity.getSupportFragmentManager()
.findFragmentByTag(TAG_PLUS_CLIENT);
if (fragment instanceof PlusClientFragment) {
((PlusClientFragment) fragment).onDialogCanceled(getTag());
}
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
FragmentActivity activity = getActivity();
if (activity == null) {
return;
}
Fragment fragment = activity.getSupportFragmentManager()
.findFragmentByTag(TAG_PLUS_CLIENT);
if (fragment instanceof PlusClientFragment) {
((PlusClientFragment) fragment).onDialogDismissed(getTag());
}
}
}
private void onDialogCanceled(String tag) {
mRequestCode = INVALID_REQUEST_CODE;
hideProgressDialog();
}
private void onDialogDismissed(String tag) {
if (TAG_PROGRESS_DIALOG.equals(tag)) {
mRequestCode = INVALID_REQUEST_CODE;
hideProgressDialog();
}
}
private void showProgressDialog() {
DialogFragment progressDialog = (DialogFragment) getFragmentManager()
.findFragmentByTag(TAG_PROGRESS_DIALOG);
if (progressDialog == null) {
progressDialog = ProgressDialogFragment.create();
progressDialog.show(getFragmentManager(), TAG_PROGRESS_DIALOG);
}
}
public static final class ProgressDialogFragment extends DialogFragment {
private static final String ARG_MESSAGE = "message";
public static ProgressDialogFragment create(int message) {
ProgressDialogFragment progressDialogFragment = new ProgressDialogFragment();
Bundle args = new Bundle();
args.putInt(ARG_MESSAGE, message);
progressDialogFragment.setArguments(args);
return progressDialogFragment;
}
public static ProgressDialogFragment create() {
return create(R.string.progress_message);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
ProgressDialog progressDialog = new ProgressDialog(getActivity());
progressDialog.setIndeterminate(true);
progressDialog.setMessage(getString(getArguments().getInt(
ARG_MESSAGE)));
return progressDialog;
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
FragmentActivity activity = getActivity();
if (activity == null) {
return;
}
Fragment fragment = activity.getSupportFragmentManager()
.findFragmentByTag(TAG_PLUS_CLIENT);
if (fragment instanceof PlusClientFragment) {
((PlusClientFragment) fragment).onDialogCanceled(getTag());
}
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
FragmentActivity activity = getActivity();
if (activity == null) {
return;
}
Fragment fragment = activity.getSupportFragmentManager()
.findFragmentByTag(TAG_PLUS_CLIENT);
if (fragment instanceof PlusClientFragment) {
((PlusClientFragment) fragment).onDialogDismissed(getTag());
}
}
}
protected void hideProgressDialog() {
FragmentManager manager = getFragmentManager();
if (manager != null) {
DialogFragment progressDialog = (DialogFragment) manager
.findFragmentByTag(TAG_PROGRESS_DIALOG);
if (progressDialog != null) {
progressDialog.dismiss();
}
}
}
private void showErrorDialog(DialogFragment errorDialog) {
DialogFragment oldErrorDialog = (DialogFragment) getFragmentManager()
.findFragmentByTag(TAG_ERROR_DIALOG);
if (oldErrorDialog != null) {
oldErrorDialog.dismiss();
}
errorDialog.show(getFragmentManager(), TAG_ERROR_DIALOG);
}
private boolean isShowingErrorDialog() {
DialogFragment errorDialog = (DialogFragment) getFragmentManager()
.findFragmentByTag(TAG_ERROR_DIALOG);
return errorDialog != null && !errorDialog.isHidden();
}
private void hideErrorDialog() {
DialogFragment errorDialog = (DialogFragment) getFragmentManager()
.findFragmentByTag(TAG_ERROR_DIALOG);
if (errorDialog != null) {
errorDialog.dismiss();
}
}
private void startResolution() {
try {
mLastConnectionResult.startResolutionForResult(getActivity(),
mRequestCode);
hideProgressDialog();
} catch (SendIntentException e) {
// The intent we had is not valid right now, perhaps the remote
// process died.
// Try to reconnect to get a new resolution intent.
mLastConnectionResult = null;
showProgressDialog();
connectPlusClient();
}
}
public boolean handleOnActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode != mRequestCode) {
return false;
}
switch (resultCode) {
case Activity.RESULT_OK:
mLastConnectionResult = null;
connectPlusClient();
break;
case Activity.RESULT_CANCELED:
// User canceled sign in, clear the request code.
mRequestCode = INVALID_REQUEST_CODE;
// Attempt to connect again.
connectPlusClient();
break;
}
return true;
}
/**
* Sign out of the app.
*/
public void signOut() {
if (mPlusClient.isConnected()) {
mPlusClient.clearDefaultAccount();
}
if (mPlusClient.isConnecting() || mPlusClient.isConnected()) {
mPlusClient.disconnect();
// Reconnect to get a new mPlusClient.
mLastConnectionResult = null;
// Cancel sign in.
mRequestCode = INVALID_REQUEST_CODE;
// Reconnect to fetch the sign-in (account chooser) intent from the
// plus client.
connectPlusClient();
}
}
/**
* Revoke access to the current app.
*/
public void revokeAccessAndDisconnect() {
if (mPlusClient.isConnected()) {
mPlusClient.revokeAccessAndDisconnect(this);
}
}
/**
* Attempts to connect the client to Google Play services if the client
* isn't already connected, and isn't in the process of being connected.
*/
private void connectPlusClient() {
if (!mPlusClient.isConnecting() && !mPlusClient.isConnected()) {
mPlusClient.connect();
}
}
}