The following classes could not be instantiated: - com.google.android.gms.plus.PlusOneButton

前端 未结 2 1621
无人共我
无人共我 2021-01-05 03:08

I am following this tutorial https://developers.google.com/+/mobile/android/recommend to integrate +1 button in my app.

As soon as I put this code in xml I get error

相关标签:
2条回答
  • 2021-01-05 03:12

    Android Studio solution:

    I just got a simple example working by starting with the code from the gplus-quickstart, and then adding a +1 button using the guide linked in the question.

    build.gradle dependencies:

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.1.1'
        compile 'com.google.android.gms:play-services:7.0.0'
    }
    

    AndroidManifest.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.plustest.daniel.googleplusone" >
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
    
    </manifest>
    

    I added com.google.android.gms.plus.PlusOneButton to the bottom of activity_main.xml, which is based on main_activity.xml from the quick start code:

    xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus"
                android:id="@+id/plus_one_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                plus:size="standard"
                plus:annotation="inline" />
    

    MainActivity.java, with the added +1 button.

    Note that for URL I used the Carnegie Mellon School of Computer Science Google+ page.

    public class MainActivity extends FragmentActivity implements
            ConnectionCallbacks, OnConnectionFailedListener,
            ResultCallback<LoadPeopleResult>, View.OnClickListener,
            CheckBox.OnCheckedChangeListener, GoogleApiClient.ServerAuthCodeCallbacks {
    
        private static final String TAG = "android-plus-quickstart";
    
        private static final int STATE_DEFAULT = 0;
        private static final int STATE_SIGN_IN = 1;
        private static final int STATE_IN_PROGRESS = 2;
    
        private static final int RC_SIGN_IN = 0;
    
        private static final String SAVED_PROGRESS = "sign_in_progress";
    
        // Client ID for a web server that will receive the auth code and exchange it for a
        // refresh token if offline access is requested.
        private static final String WEB_CLIENT_ID = "WEB_CLIENT_ID";
    
        // Base URL for your token exchange server, no trailing slash.
        private static final String SERVER_BASE_URL = "SERVER_BASE_URL";
    
        // URL where the client should GET the scopes that the server would like granted
        // before asking for a serverAuthCode
        private static final String EXCHANGE_TOKEN_URL = SERVER_BASE_URL + "/exchangetoken";
    
        // URL where the client should POST the serverAuthCode so that the server can exchange
        // it for a refresh token,
        private static final String SELECT_SCOPES_URL = SERVER_BASE_URL + "/selectscopes";
    
    
        private int mSignInProgress;
    
        // Used to store the PendingIntent most recently returned by Google Play
        // services until the user clicks 'sign in'.
        private PendingIntent mSignInIntent;
    
        // Used to store the error code most recently returned by Google Play services
        // until the user clicks 'sign in'.
        private int mSignInError;
    
        // Used to determine if we should ask for a server auth code when connecting the
        // GoogleApiClient.  False by default so that this sample can be used without configuring
        // a WEB_CLIENT_ID and SERVER_BASE_URL.
        private boolean mRequestServerAuthCode = false;
    
        // Used to mock the state of a server that would receive an auth code to exchange
        // for a refresh token,  If true, the client will assume that the server has the
        // permissions it wants and will not send an auth code on sign in.  If false,
        // the client will request offline access on sign in and send and new auth code
        // to the server.  True by default because this sample does not implement a server
        // so there would be nowhere to send the code.
        private boolean mServerHasToken = true;
    
        private SignInButton mSignInButton;
        private Button mSignOutButton;
        private Button mRevokeButton;
        private TextView mStatus;
        private ListView mCirclesListView;
        private ArrayAdapter<String> mCirclesAdapter;
        private ArrayList<String> mCirclesList;
    
        //added:
        private PlusOneButton mPlusOneButton;
        private String URL = "https://plus.google.com/101009371381835899795/posts";
    
        private static final int PLUS_ONE_REQUEST_CODE = 0;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mSignInButton = (SignInButton) findViewById(R.id.sign_in_button);
            mSignOutButton = (Button) findViewById(R.id.sign_out_button);
            mRevokeButton = (Button) findViewById(R.id.revoke_access_button);
            mStatus = (TextView) findViewById(R.id.sign_in_status);
            mCirclesListView = (ListView) findViewById(R.id.circles_list);
    
            //added:
            mPlusOneButton = (PlusOneButton) findViewById(R.id.plus_one_button);
    
            // Button listeners
            mSignInButton.setOnClickListener(this);
            mSignOutButton.setOnClickListener(this);
            mRevokeButton.setOnClickListener(this);
    
            // CheckBox listeners
            ((CheckBox) findViewById(R.id.request_auth_code_checkbox)).setOnCheckedChangeListener(this);
            ((CheckBox) findViewById(R.id.has_token_checkbox)).setOnCheckedChangeListener(this);
    
            mCirclesList = new ArrayList<String>();
            mCirclesAdapter = new ArrayAdapter<String>(
                    this, R.layout.circle_member, mCirclesList);
            mCirclesListView.setAdapter(mCirclesAdapter);
    
            if (savedInstanceState != null) {
                mSignInProgress = savedInstanceState
                        .getInt(SAVED_PROGRESS, STATE_DEFAULT);
            }
    
            mGoogleApiClient = buildGoogleApiClient();
        }
    
        private GoogleApiClient buildGoogleApiClient() {
            // When we build the GoogleApiClient we specify where connected and
            // connection failed callbacks should be returned, which Google APIs our
            // app uses and which OAuth 2.0 scopes our app requests.
            GoogleApiClient.Builder builder = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(Plus.API, Plus.PlusOptions.builder().build())
                    .addScope(Plus.SCOPE_PLUS_LOGIN);
    
            if (mRequestServerAuthCode) {
                checkServerAuthConfiguration();
                builder = builder.requestServerAuthCode(WEB_CLIENT_ID, this);
            }
    
            return builder.build();
        }
    
        //added:
        @Override
        protected void onResume() {
            super.onResume();
            // Refresh the state of the +1 button each time the activity receives focus.
            mPlusOneButton.initialize(URL, PLUS_ONE_REQUEST_CODE);
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            mGoogleApiClient.connect();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
    
            if (mGoogleApiClient.isConnected()) {
                mGoogleApiClient.disconnect();
            }
        }
    
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putInt(SAVED_PROGRESS, mSignInProgress);
        }
    
        @Override
        public void onClick(View v) {
            if (!mGoogleApiClient.isConnecting()) {
                // We only process button clicks when GoogleApiClient is not transitioning
                // between connected and not connected.
                switch (v.getId()) {
                    case R.id.sign_in_button:
                        mStatus.setText(R.string.status_signing_in);
                        mSignInProgress = STATE_SIGN_IN;
                        mGoogleApiClient.connect();
                        break;
                    case R.id.sign_out_button:
                        // We clear the default account on sign out so that Google Play
                        // services will not return an onConnected callback without user
                        // interaction.
                        if (mGoogleApiClient.isConnected()) {
                            Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
                            mGoogleApiClient.disconnect();
                        }
                        onSignedOut();
                        break;
                    case R.id.revoke_access_button:
                        // After we revoke permissions for the user with a GoogleApiClient
                        // instance, we must discard it and create a new one.
                        Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
                        // Our sample has caches no user data from Google+, however we
                        // would normally register a callback on revokeAccessAndDisconnect
                        // to delete user data so that we comply with Google developer
                        // policies.
                        Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient);
                        mGoogleApiClient = buildGoogleApiClient();
                        mGoogleApiClient.connect();
                        break;
                }
            }
        }
    
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            switch (buttonView.getId()) {
                case R.id.request_auth_code_checkbox:
                    mRequestServerAuthCode = isChecked;
                    buildGoogleApiClient();
                    if (isChecked) {
                        findViewById(R.id.layout_has_token).setVisibility(View.VISIBLE);
                    } else {
                        findViewById(R.id.layout_has_token).setVisibility(View.INVISIBLE);
                    }
                    break;
                case R.id.has_token_checkbox:
                    mServerHasToken = isChecked;
                    break;
            }
        }
    
        /* onConnected is called when our Activity successfully connects to Google
         * Play services.  onConnected indicates that an account was selected on the
         * device, that the selected account has granted any requested permissions to
         * our app and that we were able to establish a service connection to Google
         * Play services.
         */
        @Override
        public void onConnected(Bundle connectionHint) {
            // Reaching onConnected means we consider the user signed in.
            Log.i(TAG, "onConnected");
    
            // Update the user interface to reflect that the user is signed in.
            mSignInButton.setEnabled(false);
            mSignOutButton.setEnabled(true);
            mRevokeButton.setEnabled(true);
    
            // Hide the sign-in options, they no longer apply
            findViewById(R.id.layout_server_auth).setVisibility(View.GONE);
    
            // Retrieve some profile information to personalize our app for the user.
            Person currentUser = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);
    
            mStatus.setText(String.format(
                    getResources().getString(R.string.signed_in_as),
                    currentUser.getDisplayName()));
    
            Plus.PeopleApi.loadVisible(mGoogleApiClient, null)
                    .setResultCallback(this);
    
            // Indicate that the sign in process is complete.
            mSignInProgress = STATE_DEFAULT;
        }
    
        /* onConnectionFailed is called when our Activity could not connect to Google
         * Play services.  onConnectionFailed indicates that the user needs to select
         * an account, grant permissions or resolve an error in order to sign in.
         */
        @Override
        public void onConnectionFailed(ConnectionResult result) {
            // Refer to the javadoc for ConnectionResult to see what error codes might
            // be returned in onConnectionFailed.
            Log.i(TAG, "onConnectionFailed: ConnectionResult.getErrorCode() = "
                    + result.getErrorCode());
    
            if (result.getErrorCode() == ConnectionResult.API_UNAVAILABLE) {
                // An API requested for GoogleApiClient is not available. The device's current
                // configuration might not be supported with the requested API or a required component
                // may not be installed, such as the Android Wear application. You may need to use a
                // second GoogleApiClient to manage the application's optional APIs.
                Log.w(TAG, "API Unavailable.");
            } else if (mSignInProgress != STATE_IN_PROGRESS) {
                // We do not have an intent in progress so we should store the latest
                // error resolution intent for use when the sign in button is clicked.
                mSignInIntent = result.getResolution();
                mSignInError = result.getErrorCode();
    
                if (mSignInProgress == STATE_SIGN_IN) {
                    // STATE_SIGN_IN indicates the user already clicked the sign in button
                    // so we should continue processing errors until the user is signed in
                    // or they click cancel.
                    resolveSignInError();
                }
            }
    
            // In this sample we consider the user signed out whenever they do not have
            // a connection to Google Play services.
            onSignedOut();
        }
    
    
        private void resolveSignInError() {
            if (mSignInIntent != null) {
                // We have an intent which will allow our user to sign in or
                // resolve an error.  For example if the user needs to
                // select an account to sign in with, or if they need to consent
                // to the permissions your app is requesting.
    
                try {
                    // Send the pending intent that we stored on the most recent
                    // OnConnectionFailed callback.  This will allow the user to
                    // resolve the error currently preventing our connection to
                    // Google Play services.
                    mSignInProgress = STATE_IN_PROGRESS;
                    startIntentSenderForResult(mSignInIntent.getIntentSender(),
                            RC_SIGN_IN, null, 0, 0, 0);
                } catch (SendIntentException e) {
                    Log.i(TAG, "Sign in intent could not be sent: "
                            + e.getLocalizedMessage());
                    // The intent was canceled before it was sent.  Attempt to connect to
                    // get an updated ConnectionResult.
                    mSignInProgress = STATE_SIGN_IN;
                    mGoogleApiClient.connect();
                }
            } else {
                // Google Play services wasn't able to provide an intent for some
                // error types, so we show the default Google Play services error
                // dialog which may still start an intent on our behalf if the
                // user can resolve the issue.
                createErrorDialog().show();
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode,
                                        Intent data) {
            switch (requestCode) {
                case RC_SIGN_IN:
                    if (resultCode == RESULT_OK) {
                        // If the error resolution was successful we should continue
                        // processing errors.
                        mSignInProgress = STATE_SIGN_IN;
                    } else {
                        // If the error resolution was not successful or the user canceled,
                        // we should stop processing errors.
                        mSignInProgress = STATE_DEFAULT;
                    }
    
                    if (!mGoogleApiClient.isConnecting()) {
                        // If Google Play services resolved the issue with a dialog then
                        // onStart is not called so we need to re-attempt connection here.
                        mGoogleApiClient.connect();
                    }
                    break;
            }
        }
    
        @Override
        public void onResult(LoadPeopleResult peopleData) {
            if (peopleData.getStatus().getStatusCode() == CommonStatusCodes.SUCCESS) {
                mCirclesList.clear();
                PersonBuffer personBuffer = peopleData.getPersonBuffer();
                try {
                    int count = personBuffer.getCount();
                    for (int i = 0; i < count; i++) {
                        mCirclesList.add(personBuffer.get(i).getDisplayName());
                    }
                } finally {
                    personBuffer.close();
                }
    
                mCirclesAdapter.notifyDataSetChanged();
            } else {
                Log.e(TAG, "Error requesting visible circles: " + peopleData.getStatus());
            }
        }
    
        private void onSignedOut() {
            // Update the UI to reflect that the user is signed out.
            mSignInButton.setEnabled(true);
            mSignOutButton.setEnabled(false);
            mRevokeButton.setEnabled(false);
    
            // Show the sign-in options
            findViewById(R.id.layout_server_auth).setVisibility(View.VISIBLE);
    
            mStatus.setText(R.string.status_signed_out);
    
            mCirclesList.clear();
            mCirclesAdapter.notifyDataSetChanged();
        }
    
        @Override
        public void onConnectionSuspended(int cause) {
            // The connection to Google Play services was lost for some reason.
            // We call connect() to attempt to re-establish the connection or get a
            // ConnectionResult that we can attempt to resolve.
            mGoogleApiClient.connect();
        }
    
        private Dialog createErrorDialog() {
            if (GooglePlayServicesUtil.isUserRecoverableError(mSignInError)) {
                return GooglePlayServicesUtil.getErrorDialog(
                        mSignInError,
                        this,
                        RC_SIGN_IN,
                        new DialogInterface.OnCancelListener() {
                            @Override
                            public void onCancel(DialogInterface dialog) {
                                Log.e(TAG, "Google Play services resolution cancelled");
                                mSignInProgress = STATE_DEFAULT;
                                mStatus.setText(R.string.status_signed_out);
                            }
                        });
            } else {
                return new AlertDialog.Builder(this)
                        .setMessage(R.string.play_services_error)
                        .setPositiveButton(R.string.close,
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        Log.e(TAG, "Google Play services error could not be "
                                                + "resolved: " + mSignInError);
                                        mSignInProgress = STATE_DEFAULT;
                                        mStatus.setText(R.string.status_signed_out);
                                    }
                                }).create();
            }
        }
    
        @Override
        public CheckResult onCheckServerAuthorization(String idToken, Set<Scope> scopeSet) {
            Log.i(TAG, "Checking if server is authorized.");
            Log.i(TAG, "Mocking server has refresh token: " + String.valueOf(mServerHasToken));
    
            if (!mServerHasToken) {
                // Server does not have a valid refresh token, so request a new
                // auth code which can be exchanged for one.  This will cause the user to see the
                // consent dialog and be prompted to grant offline access. This callback occurs on a
                // background thread so it is OK to do synchronous network access.
    
                // Ask the server which scopes it would like to have for offline access.  This
                // can be distinct from the scopes granted to the client.  By getting these values
                // from the server, you can change your server's permissions without needing to
                // recompile the client application.
                HttpClient httpClient = new DefaultHttpClient();
                HttpGet httpGet = new HttpGet(SELECT_SCOPES_URL);
                HashSet<Scope> serverScopeSet = new HashSet<Scope>();
    
                try {
                    HttpResponse httpResponse = httpClient.execute(httpGet);
                    int responseCode = httpResponse.getStatusLine().getStatusCode();
                    String responseBody = EntityUtils.toString(httpResponse.getEntity());
    
                    if (responseCode == 200) {
                        String[] scopeStrings = responseBody.split(" ");
                        for (String scope : scopeStrings) {
                            Log.i(TAG, "Server Scope: " + scope);
                            serverScopeSet.add(new Scope(scope));
                        }
                    } else {
                        Log.e(TAG, "Error in getting server scopes: " + responseCode);
                    }
    
                } catch (ClientProtocolException e) {
                    Log.e(TAG, "Error in getting server scopes.", e);
                } catch (IOException e) {
                    Log.e(TAG, "Error in getting server scopes.", e);
                }
    
                // This tells GoogleApiClient that the server needs a new serverAuthCode with
                // access to the scopes in serverScopeSet.  Note that we are not asking the server
                // if it already has such a token because this is a sample application.  In reality,
                // you should only do this on the first user sign-in or if the server loses or deletes
                // the refresh token.
                return CheckResult.newAuthRequiredResult(serverScopeSet);
            } else {
                // Server already has a valid refresh token with the correct scopes, no need to
                // ask the user for offline access again.
                return CheckResult.newAuthNotRequiredResult();
            }
        }
    
        @Override
        public boolean onUploadServerAuthCode(String idToken, String serverAuthCode) {
            // Upload the serverAuthCode to the server, which will attempt to exchange it for
            // a refresh token.  This callback occurs on a background thread, so it is OK
            // to perform synchronous network access.  Returning 'false' will fail the
            // GoogleApiClient.connect() call so if you would like the client to ignore
            // server failures, always return true.
            HttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(EXCHANGE_TOKEN_URL);
    
            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
                nameValuePairs.add(new BasicNameValuePair("serverAuthCode", serverAuthCode));
                httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
    
                HttpResponse response = httpClient.execute(httpPost);
                int statusCode = response.getStatusLine().getStatusCode();
                final String responseBody = EntityUtils.toString(response.getEntity());
                Log.i(TAG, "Code: " + statusCode);
                Log.i(TAG, "Resp: " + responseBody);
    
                // Show Toast on UI Thread
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, responseBody, Toast.LENGTH_LONG).show();
                    }
                });
                return (statusCode == 200);
            } catch (ClientProtocolException e) {
                Log.e(TAG, "Error in auth code exchange.", e);
                return false;
            } catch (IOException e) {
                Log.e(TAG, "Error in auth code exchange.", e);
                return false;
            }
        }
    
        private void checkServerAuthConfiguration() {
            // Check that the server URL is configured before allowing this box to
            // be unchecked
            if ("WEB_CLIENT_ID".equals(WEB_CLIENT_ID) ||
                    "SERVER_BASE_URL".equals(SERVER_BASE_URL)) {
                Log.w(TAG, "WEB_CLIENT_ID or SERVER_BASE_URL configured incorrectly.");
                Dialog dialog = new AlertDialog.Builder(this)
                        .setMessage(getString(R.string.configuration_error))
                        .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                            }
                        })
                        .create();
    
                dialog.show();
            }
        }
    }
    

    Result after signing in and clicking the +1 button:

    Plus One Button Result

    0 讨论(0)
  • 2021-01-05 03:22

    Add compile 'com.google.android.gms:play-services-plus:10.0.1' to your app's gradle

    0 讨论(0)
提交回复
热议问题