在一个片段中完成安卓Google+登录

6

目前我正在尝试使用片段实现Google+登录,以便可以从不同的活动中使用它。我已经创建了以下这个片段:

public class GoogleSignUpFragment extends Fragment implements
    ConnectionCallbacks, OnConnectionFailedListener, OnClickListener {

// PlusClient Variables
private static final int REQUEST_CODE_RESOLVE_ERR = 9000;
private ProgressDialog mConnectionProgressDialog;
private PlusClient mPlusClient;
private ConnectionResult mConnectionResult;

@Override
public void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);

    mPlusClient = new PlusClient.Builder(getActivity()
            .getApplicationContext(), this, this)
            .setActions("http://schemas.google.com/AddActivity",
                    "http://schemas.google.com/BuyActivity")
            .setScopes(Scopes.PLUS_LOGIN, Scopes.PLUS_PROFILE).build();

    getActivity().findViewById(R.id.sign_in_button)
            .setOnClickListener(this);

    // Progress bar to be displayed if the connection failure is not
    // resolved.
    mConnectionProgressDialog = new ProgressDialog(getActivity());
    mConnectionProgressDialog.setMessage("Signing in...");

}

@Override
public void onStart() {
    // TODO Auto-generated method stub
    super.onStart();
    mPlusClient.connect();
}

@Override
public void onStop() {
    // TODO Auto-generated method stub
    super.onStop();
    mPlusClient.disconnect();
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (requestCode == REQUEST_CODE_RESOLVE_ERR
            && resultCode == Activity.RESULT_OK) {
        mConnectionResult = null;
        mPlusClient.connect();
    }
}

@Override
public void onClick(View view) {
    if (view.getId() == R.id.sign_in_button && !mPlusClient.isConnected()) {
        if (mConnectionResult == null) {
            mConnectionProgressDialog.show();

        } else {
            try {
                mConnectionResult.startResolutionForResult(getActivity(),
                        REQUEST_CODE_RESOLVE_ERR);
            } catch (SendIntentException e) {
                // Try connecting again.
                mConnectionResult = null;
                mPlusClient.connect();
            }
        }
    }
}

@Override
public void onConnectionFailed(ConnectionResult result) {
    if (mConnectionProgressDialog.isShowing()) {
        // The user clicked the sign-in button already. Start to resolve
        // connection errors. Wait until onConnected() to dismiss the
        // connection dialog.
        if (result.hasResolution()) {
            try {
                result.startResolutionForResult(getActivity(),
                        REQUEST_CODE_RESOLVE_ERR);
            } catch (SendIntentException e) {
                mPlusClient.connect();
            }
        }
    }

}

@Override
public void onConnected(Bundle connectionHint) {
    mConnectionProgressDialog.dismiss();
    Toast.makeText(getActivity().getApplicationContext(),
            "User is connected!", Toast.LENGTH_LONG).show();
    Intent intent = new Intent(getActivity().getApplicationContext(),
            LogoutActivity.class);
    startActivity(intent);

}

@Override
public void onDisconnected() {
    // TODO Auto-generated method stub

}

}

我正在按照官方教程操作,并稍作修改以在片段中使用。

在主活动中,我有:

FragmentManager fragmentManager = getSupportFragmentManager();
    android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    googleSignUpFragment = new GoogleSignUpFragment();
    fragmentTransaction.add(R.id.detailFragment, googleSignUpFragment);
    fragmentTransaction.commit();

然而,每当我点击登录按钮时,只有一个显示“正在登录…”的对话框,没有其他反应。有什么建议如何解决这个问题吗?在正常活动中它表现得很好。
谢谢任何帮助。
@编辑。 我已经发现onConnectionFailed方法没有启动,所以似乎没有onActivityResult,但是当我直接在我的单击内放置mPlusClient.connect()时,它可以工作,但是我觉得这不是正确的方式。也许问题与onConnectionFailed未启动有关。
@编辑2。 现在我已经发现问题似乎与dialog.isShowing()有关,因为当我删除该行后,账户选择器会立即显示,尝试了使用bool值,在单击后更改,但这也不起作用…
2个回答

5
为了在官方教程中使用Fragment,您需要知道onActivityResult方法将在拥有该Fragment的Activity中调用。
然后在Fragment中实现公共的connect()方法,在Activity的onActivityResult中调用该方法而不是直接调用plus.connect()。这样,教程将与使用Activity时预期的完全相同地使用Fragment。如果您需要详细的示例,我可以将其添加到此答案中。
我建议使用Activity而不是Fragment,因为代码模块化和易于实现,并处理按键返回按钮以防止进入应用程序等问题。

3

对于那些想在Android Studio中看到示例的人,只需创建一个LoginActivity,勾选“包括Google+ sig in”。

如果您正在Fragment中工作,只需在Activity中添加一个调用onActivityResult到Fragment的方法,如下所示:

FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.findFragmentById(R.id.login_fragment_leftPanel).onActivityResult(requestCode, resultCode, data);

在AS创建的示例中,您不应在活动中实现抽象函数...只需在片段中使用。 对我有用的是com.google.android.gms:play-services:6.1.11,如果您需要代码,请提出要求。
针对用户@SHADOW的代码
当然Shadow,我所做的是使以下代码工作。之后,我为我的项目进行了修改。 他们使用2个类(PlusBaseActivity和LoginActivity)。
/ **
*登录屏幕,通过电子邮件/密码和通过Google+登录提供登录。
*
* ************重要设置说明:************
*为了使Google+登录与您的应用程序一起工作,您必须首先转到:
*https://developers.google.com/+/mobile/android/getting-started#step_1_enable_the_google_api
*并遵循“第1步”中的步骤为您的软件包创建OAuth 2.0客户端。
*/
public class LoginActivity extends PlusBaseActivity implements LoaderCallbacks<Cursor>{

/**
 * A dummy authentication store containing known user names and passwords.
 * TODO: remove after connecting to a real authentication system.
 */
private static final String[] DUMMY_CREDENTIALS = new String[]{
        "foo@example.com:hello", "bar@example.com:world"
};
/**
 * Keep track of the login task to ensure we can cancel it if requested.
 */
private UserLoginTask mAuthTask = null;

// UI references.
private AutoCompleteTextView mEmailView;
private EditText mPasswordView;
private View mProgressView;
private View mEmailLoginFormView;
private SignInButton mPlusSignInButton;
private View mSignOutButtons;
private View mLoginFormView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);

    // Find the Google+ sign in button.
    mPlusSignInButton = (SignInButton) findViewById(R.id.plus_sign_in_button);
    if (supportsGooglePlayServices()) {
        // Set a listener to connect the user when the G+ button is clicked.
        mPlusSignInButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                signIn();
            }
        });
    } else {
        // Don't offer G+ sign in if the app's version is too low to support Google Play
        // Services.
        mPlusSignInButton.setVisibility(View.GONE);
        return;
    }

    // Set up the login form.
    mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
    populateAutoComplete();

    mPasswordView = (EditText) findViewById(R.id.password);
    mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
            if (id == R.id.login || id == EditorInfo.IME_NULL) {
                attemptLogin();
                return true;
            }
            return false;
        }
    });

    Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
    mEmailSignInButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            attemptLogin();
        }
    });

    mLoginFormView = findViewById(R.id.login_form);
    mProgressView = findViewById(R.id.login_progress);
    mEmailLoginFormView = findViewById(R.id.email_login_form);
    mSignOutButtons = findViewById(R.id.plus_sign_out_buttons);
}

private void populateAutoComplete() {
    getLoaderManager().initLoader(0, null, this);
}


/**
 * Attempts to sign in or register the account specified by the login form.
 * If there are form errors (invalid email, missing fields, etc.), the
 * errors are presented and no actual login attempt is made.
 */
public void attemptLogin() {
    if (mAuthTask != null) {
        return;
    }

    // Reset errors.
    mEmailView.setError(null);
    mPasswordView.setError(null);

    // Store values at the time of the login attempt.
    String email = mEmailView.getText().toString();
    String password = mPasswordView.getText().toString();

    boolean cancel = false;
    View focusView = null;


    // Check for a valid password, if the user entered one.
    if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
        mPasswordView.setError(getString(R.string.error_invalid_password));
        focusView = mPasswordView;
        cancel = true;
    }

    // Check for a valid email address.
    if (TextUtils.isEmpty(email)) {
        mEmailView.setError(getString(R.string.error_field_required));
        focusView = mEmailView;
        cancel = true;
    } else if (!isEmailValid(email)) {
        mEmailView.setError(getString(R.string.error_invalid_email));
        focusView = mEmailView;
        cancel = true;
    }

    if (cancel) {
        // There was an error; don't attempt login and focus the first
        // form field with an error.
        focusView.requestFocus();
    } else {
        // Show a progress spinner, and kick off a background task to
        // perform the user login attempt.
        showProgress(true);
        mAuthTask = new UserLoginTask(email, password);
        mAuthTask.execute((Void) null);
    }
}
private boolean isEmailValid(String email) {
    //TODO: Replace this with your own logic
    return email.contains("@");
}

private boolean isPasswordValid(String password) {
    //TODO: Replace this with your own logic
    return password.length() > 4;
}

/**
 * Shows the progress UI and hides the login form.
 */
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public void showProgress(final boolean show) {
    // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
    // for very easy animations. If available, use these APIs to fade-in
    // the progress spinner.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
        int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);

        mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
        mLoginFormView.animate().setDuration(shortAnimTime).alpha(
                show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
            }
        });

        mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
        mProgressView.animate().setDuration(shortAnimTime).alpha(
                show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
            }
        });
    } else {
        // The ViewPropertyAnimator APIs are not available, so simply show
        // and hide the relevant UI components.
        mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
        mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
    }
}

@Override
protected void onPlusClientSignIn() {
    //Set up sign out and disconnect buttons.
    Button signOutButton = (Button) findViewById(R.id.plus_sign_out_button);
    signOutButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            signOut();
        }
    });
    Button disconnectButton = (Button) findViewById(R.id.plus_disconnect_button);
    disconnectButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            revokeAccess();
        }
    });
}

@Override
protected void onPlusClientBlockingUI(boolean show) {
    showProgress(show);
}

@Override
protected void updateConnectButtonState() {
    //TODO: Update this logic to also handle the user logged in by email.
    boolean connected = getPlusClient().isConnected();

    mSignOutButtons.setVisibility(connected ? View.VISIBLE : View.GONE);
    mPlusSignInButton.setVisibility(connected ? View.GONE : View.VISIBLE);
    mEmailLoginFormView.setVisibility(connected ? View.GONE : View.VISIBLE);
}

@Override
protected void onPlusClientRevokeAccess() {
    // TODO: Access to the user's G+ account has been revoked.  Per the developer terms, delete
    // any stored user data here.
}

@Override
protected void onPlusClientSignOut() {

}

/**
 * Check if the device supports Google Play Services.  It's best
 * practice to check first rather than handling this as an error case.
 *
 * @return whether the device supports Google Play Services
 */
private boolean supportsGooglePlayServices() {
    return GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) ==
            ConnectionResult.SUCCESS;
}

@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
    return new CursorLoader(this,
            // Retrieve data rows for the device user's 'profile' contact.
            Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
                    ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION,

            // Select only email addresses.
            ContactsContract.Contacts.Data.MIMETYPE +
                    " = ?", new String[]{ContactsContract.CommonDataKinds.Email
                                                                 .CONTENT_ITEM_TYPE},

            // Show primary email addresses first. Note that there won't be
            // a primary email address if the user hasn't specified one.
            ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
}

@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
    List<String> emails = new ArrayList<String>();
    cursor.moveToFirst();
    while (!cursor.isAfterLast()) {
        emails.add(cursor.getString(ProfileQuery.ADDRESS));
        cursor.moveToNext();
    }

    addEmailsToAutoComplete(emails);
}

@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {

}

private interface ProfileQuery {
    String[] PROJECTION = {
            ContactsContract.CommonDataKinds.Email.ADDRESS,
            ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
    };

    int ADDRESS = 0;
    int IS_PRIMARY = 1;
}


private void addEmailsToAutoComplete(List<String> emailAddressCollection) {
    //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
    ArrayAdapter<String> adapter =
            new ArrayAdapter<String>(LoginActivity.this,
                    android.R.layout.simple_dropdown_item_1line, emailAddressCollection);

    mEmailView.setAdapter(adapter);
}

/**
 * Represents an asynchronous login/registration task used to authenticate
 * the user.
 */
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {

    private final String mEmail;
    private final String mPassword;

    UserLoginTask(String email, String password) {
        mEmail = email;
        mPassword = password;
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        // TODO: attempt authentication against a network service.

        try {
            // Simulate network access.
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            return false;
        }

        for (String credential : DUMMY_CREDENTIALS) {
            String[] pieces = credential.split(":");
            if (pieces[0].equals(mEmail)) {
                // Account exists, return true if the password matches.
                return pieces[1].equals(mPassword);
            }
        }

        // TODO: register the new account here.
        return true;
    }

    @Override
    protected void onPostExecute(final Boolean success) {
        mAuthTask = null;
        showProgress(false);

        if (success) {
            finish();
        } else {
            mPasswordView.setError(getString(R.string.error_incorrect_password));
            mPasswordView.requestFocus();
        }
    }

    @Override
    protected void onCancelled() {
        mAuthTask = null;
        showProgress(false);
    }
}

}

public abstract class PlusBaseActivity extends Activity
    implements GooglePlayServicesClient.ConnectionCallbacks,
    GooglePlayServicesClient.OnConnectionFailedListener {

private static final String TAG = PlusBaseActivity.class.getSimpleName();

// A magic number we will use to know that our sign-in error resolution activity has completed
private static final int OUR_REQUEST_CODE = 49404;

// A flag to stop multiple dialogues appearing for the user
private boolean mAutoResolveOnFail;

// A flag to track when a connection is already in progress
public boolean mPlusClientIsConnecting = false;

// This is the helper object that connects to Google Play Services.
private PlusClient mPlusClient;

// The saved result from {@link #onConnectionFailed(ConnectionResult)}.  If a connection
// attempt has been made, this is non-null.
// If this IS null, then the connect method is still running.
private ConnectionResult mConnectionResult;


/**
 * Called when the {@link PlusClient} revokes access to this app.
 */
protected abstract void onPlusClientRevokeAccess();

/**
 * Called when the PlusClient is successfully connected.
 */
protected abstract void onPlusClientSignIn();

/**
 * Called when the {@link PlusClient} is disconnected.
 */
protected abstract void onPlusClientSignOut();

/**
 * Called when the {@link PlusClient} is blocking the UI.  If you have a progress bar widget,
 * this tells you when to show or hide it.
 */
protected abstract void onPlusClientBlockingUI(boolean show);

/**
 * Called when there is a change in connection state.  If you have "Sign in"/ "Connect",
 * "Sign out"/ "Disconnect", or "Revoke access" buttons, this lets you know when their states
 * need to be updated.
 */
protected abstract void updateConnectButtonState();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Initialize the PlusClient connection.
    // Scopes indicate the information about the user your application will be able to access.
    mPlusClient =
            new PlusClient.Builder(this, this, this).setScopes(Scopes.PLUS_LOGIN,
                    Scopes.PLUS_ME).build();
}

/**
 * Try to sign in the user.
 */
public void signIn() {
    if (!mPlusClient.isConnected()) {
        // Show the dialog as we are now signing in.
        setProgressBarVisible(true);
        // Make sure that we will start the resolution (e.g. fire the intent and pop up a
        // dialog for the user) for any errors that come in.
        mAutoResolveOnFail = true;
        // We should always have a connection result ready to resolve,
        // so we can start that process.
        if (mConnectionResult != null) {
            startResolution();
        } else {
            // If we don't have one though, we can start connect in
            // order to retrieve one.
            initiatePlusClientConnect();
        }
    }

    updateConnectButtonState();
}

/**
 * Connect the {@link PlusClient} only if a connection isn't already in progress.  This will
 * call back to {@link #onConnected(android.os.Bundle)} or
 * {@link #onConnectionFailed(com.google.android.gms.common.ConnectionResult)}.
 */
private void initiatePlusClientConnect() {
    if (!mPlusClient.isConnected() && !mPlusClient.isConnecting()) {
        mPlusClient.connect();
    }
}

/**
 * Disconnect the {@link PlusClient} only if it is connected (otherwise, it can throw an error.)
 * This will call back to {@link #onDisconnected()}.
 */
private void initiatePlusClientDisconnect() {
    if (mPlusClient.isConnected()) {
        mPlusClient.disconnect();
    }
}

/**
 * Sign out the user (so they can switch to another account).
 */
public void signOut() {

    // We only want to sign out if we're connected.
    if (mPlusClient.isConnected()) {
        // Clear the default account in order to allow the user to potentially choose a
        // different account from the account chooser.
        mPlusClient.clearDefaultAccount();

        // Disconnect from Google Play Services, then reconnect in order to restart the
        // process from scratch.
        initiatePlusClientDisconnect();

        Log.v(TAG, "Sign out successful!");
    }

    updateConnectButtonState();
}

/**
 * Revoke Google+ authorization completely.
 */
public void revokeAccess() {

    if (mPlusClient.isConnected()) {
        // Clear the default account as in the Sign Out.
        mPlusClient.clearDefaultAccount();

        // Revoke access to this entire application. This will call back to
        // onAccessRevoked when it is complete, as it needs to reach the Google
        // authentication servers to revoke all tokens.
        mPlusClient.revokeAccessAndDisconnect(new PlusClient.OnAccessRevokedListener() {
            public void onAccessRevoked(ConnectionResult result) {
                updateConnectButtonState();
                onPlusClientRevokeAccess();
            }
        });
    }

}

@Override
protected void onStart() {
    super.onStart();
    initiatePlusClientConnect();
}

@Override
protected void onStop() {
    super.onStop();
    initiatePlusClientDisconnect();
}

public boolean isPlusClientConnecting() {
    return mPlusClientIsConnecting;
}

private void setProgressBarVisible(boolean flag) {
    mPlusClientIsConnecting = flag;
    onPlusClientBlockingUI(flag);
}

/**
 * A helper method to flip the mResolveOnFail flag and start the resolution
 * of the ConnectionResult from the failed connect() call.
 */
private void startResolution() {
    try {
        // Don't start another resolution now until we have a result from the activity we're
        // about to start.
        mAutoResolveOnFail = false;
        // If we can resolve the error, then call start resolution and pass it an integer tag
        // we can use to track.
        // This means that when we get the onActivityResult callback we'll know it's from
        // being started here.
        mConnectionResult.startResolutionForResult(this, OUR_REQUEST_CODE);
    } catch (IntentSender.SendIntentException e) {
        // Any problems, just try to connect() again so we get a new ConnectionResult.
        mConnectionResult = null;
        initiatePlusClientConnect();
    }
}

/**
 * An earlier connection failed, and we're now receiving the result of the resolution attempt
 * by PlusClient.
 *
 * @see #onConnectionFailed(ConnectionResult)
 */
@Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
    updateConnectButtonState();
    if (requestCode == OUR_REQUEST_CODE && responseCode == RESULT_OK) {
        // If we have a successful result, we will want to be able to resolve any further
        // errors, so turn on resolution with our flag.
        mAutoResolveOnFail = true;
        // If we have a successful result, let's call connect() again. If there are any more
        // errors to resolve we'll get our onConnectionFailed, but if not,
        // we'll get onConnected.
        initiatePlusClientConnect();
    } else if (requestCode == OUR_REQUEST_CODE && responseCode != RESULT_OK) {
        // If we've got an error we can't resolve, we're no longer in the midst of signing
        // in, so we can stop the progress spinner.
        setProgressBarVisible(false);
    }
}

/**
 * Successfully connected (called by PlusClient)
 */
@Override
public void onConnected(Bundle connectionHint) {
    updateConnectButtonState();
    setProgressBarVisible(false);
    onPlusClientSignIn();
}

/**
 * Successfully disconnected (called by PlusClient)
 */
@Override
public void onDisconnected() {
    updateConnectButtonState();
    onPlusClientSignOut();
}

/**
 * Connection failed for some reason (called by PlusClient)
 * Try and resolve the result.  Failure here is usually not an indication of a serious error,
 * just that the user's input is needed.
 *
 * @see #onActivityResult(int, int, Intent)
 */
@Override
public void onConnectionFailed(ConnectionResult result) {
    updateConnectButtonState();

    // Most of the time, the connection will fail with a user resolvable result. We can store
    // that in our mConnectionResult property ready to be used when the user clicks the
    // sign-in button.
    if (result.hasResolution()) {
        mConnectionResult = result;
        if (mAutoResolveOnFail) {
            // This is a local helper function that starts the resolution of the problem,
            // which may be showing the user an account chooser or similar.
            startResolution();
        }
    }
}

public PlusClient getPlusClient() {
    return mPlusClient;
}

}


你好,我正在使用Fragment,能否分享一段代码给我?我有些问题需要解决。 - Shadow
1
嗨@Shadow,我刚刚添加了由AS生成的代码,希望它能帮到你。 - MiguelHincapieC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接