2014-04-24 32 views
16

Vì Android 4.2.2 có thể chạy các Dịch vụ của Google trên Trình giả lập Android. Tôi hiện đang tạo một ứng dụng Android và đã thực hiện một dự án thử nghiệm để xem liệu tôi có thể đăng nhập và đăng xuất Google+ để hoạt động không.Trình giả lập Android: Ứng dụng này sẽ không chạy nếu không có các dịch vụ của Google Play

Tôi đã theo các hướng dẫn sau: http://www.androidhive.info/2014/02/android-login-with-google-plus-account-1/

Với thông tin thêm được sử dụng từ các hướng dẫn/trang web sau:

này tạo ra đoạn mã sau:

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.example.testproject_gmaillogin" 
    android:versionCode="1" 
    android:versionName="1.0" > 

    <uses-sdk 
     android:minSdkVersion="9" 
     android:targetSdkVersion="19" /> 

    <uses-permission android:name="android.permission.INTERNET" /> 
    <uses-permission android:name="android.permission.GET_ACCOUNTS" /> 
    <uses-permission android:name="android.permission.USE_CREDENTIALS" /> 

    <application 
     android:allowBackup="true" 
     android:icon="@drawable/ic_launcher" 
     android:label="@string/app_name" 
     android:theme="@style/AppTheme" > 

     <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> 

     <activity 
      android:name="com.example.testproject_gmaillogin.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> 

</manifest> 

strings.xml:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 

    <string name="app_name">TestProject_GmailLogin</string> 
    <string name="action_settings">Settings</string> 

    <string name="profile_pic_description">Google Profile Picture</string> 
    <string name="btn_logout_from_google">Logout from Google</string> 
    <string name="btn_revoke_access">Revoke Access</string> 

</resources> 

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" 
    android:padding="16dp" 
    tools:context=".MainActivity" > 

    <LinearLayout 
     android:id="@+id/profile_layout" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:layout_marginBottom="20dp" 
     android:orientation="horizontal" 
     android:weightSum="3" 
     android:visibility="gone"> 

     <ImageView 
      android:id="@+id/img_profile_pic" 
      android:contentDescription="@string/profile_pic_description" 
      android:layout_width="80dp" 
      android:layout_height="wrap_content" 
      android:layout_weight="1"/> 

     <LinearLayout 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_marginLeft="10dp" 
      android:orientation="vertical" 
      android:layout_weight="2" > 

      <TextView 
       android:id="@+id/txt_name" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:padding="5dp" 
       android:textSize="20sp" /> 

      <TextView 
       android:id="@+id/txt_email" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:padding="5dp" 
       android:textSize="18sp" /> 
     </LinearLayout> 
    </LinearLayout> 

    <com.google.android.gms.common.SignInButton 
     android:id="@+id/btn_sign_in" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:layout_marginBottom="20dp"/> 

    <Button 
     android:id="@+id/btn_sign_out" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:text="@string/btn_logout_from_google" 
     android:visibility="gone" 
     android:layout_marginBottom="10dp"/> 

    <Button 
     android:id="@+id/btn_revoke_access" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:text="@string/btn_revoke_access" 
     android:visibility="gone" /> 

</LinearLayout> 

MainActivity.java:

package com.example.testproject_gmaillogin; 

import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.GooglePlayServicesUtil; 
import com.google.android.gms.common.SignInButton; 
import com.google.android.gms.common.api.GoogleApiClient; 
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; 
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; 
import com.google.android.gms.common.api.ResultCallback; 
import com.google.android.gms.common.api.Status; 
import com.google.android.gms.plus.Plus; 
import com.google.android.gms.plus.model.people.Person; 

import android.support.v7.app.ActionBarActivity; 
import android.content.Intent; 
import android.content.IntentSender.SendIntentException; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.ImageView; 
import android.widget.LinearLayout; 
import android.widget.TextView; 
import android.widget.Toast; 

public class MainActivity extends ActionBarActivity implements ConnectionCallbacks, OnConnectionFailedListener, OnClickListener 
{ 
    // Logcat tag 
    private static final String TAG = "MainActivity"; 

    // Profile pix image size in pixels 
    private static final int PROFILE_PIC_SIZE = 400; 

    // Request code used to invoke sign in user interactions 
    private static final int RC_SIGN_IN = 0; 

    // Client used to interact with Google APIs 
    private GoogleApiClient mGoogleApiClient; 

    // A flag indicating that a PendingIntent is in progress and prevents 
    // us from starting further intents 
    private boolean mIntentInProgress; 

    // Track whether the sign-in button has been clicked so that we know to resolve 
    // all issues preventing sign-in without waiting 
    private boolean mSignInClicked; 

    // Store the connection result from onConnectionFailed callbacks so that we can 
    // resolve them when the user clicks sign-in 
    private ConnectionResult mConnectionResult; 

    // The used UI-elements 
    private SignInButton btnSignIn; 
    private Button btnSignOut, btnRevokeAccess; 
    private ImageView imgProfilePic; 
    private TextView txtName, txtEmail; 
    private LinearLayout profileLayout; 

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

     // Get the UI-elements 
     btnSignIn = (SignInButton) findViewById(R.id.btn_sign_in); 
     btnSignOut = (Button) findViewById(R.id.btn_sign_out); 
     btnRevokeAccess = (Button) findViewById(R.id.btn_revoke_access); 
     imgProfilePic = (ImageView) findViewById(R.id.img_profile_pic); 
     txtName = (TextView) findViewById(R.id.txt_name); 
     txtEmail = (TextView) findViewById(R.id.txt_email); 
     profileLayout = (LinearLayout) findViewById(R.id.profile_layout); 

     // Set the Button onClick-listeners 
     btnSignIn.setOnClickListener(this); 
     btnSignOut.setOnClickListener(this); 
     btnRevokeAccess.setOnClickListener(this); 

     mGoogleApiClient = new GoogleApiClient.Builder(this) 
      .addConnectionCallbacks(this) 
      .addOnConnectionFailedListener(this) 
      .addApi(Plus.API, null) 
      .addScope(Plus.SCOPE_PLUS_LOGIN) 
      .build(); 
    } 

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

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

     if(mGoogleApiClient.isConnected()) 
      mGoogleApiClient.disconnect(); 
    } 

    @Override 
    public void onClick(View view){ 
     switch(view.getId()){ 
      case R.id.btn_sign_in: 
       signInWithGPlus(); 
       break; 
      case R.id.btn_sign_out: 
       signOutFromGPlus(); 
       break; 
      case R.id.btn_revoke_access: 
       revokeGPlusAccess(); 
       break; 
     } 
    } 

    @Override 
    public void onConnectionFailed(ConnectionResult result) { 
     if(!result.hasResolution()){ 
      GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show(); 
      return; 
     } 

     if(!mIntentInProgress){ 
      // Store the ConnectionResult so that we can use it later when the user clicks 'sign-in' 
      mConnectionResult = result; 

      if(mSignInClicked) 
       // The user has already clicked 'sign-in' so we attempt to resolve all 
       // errors until the user is signed in, or they cancel 
       resolveSignInErrors(); 
     } 
    } 

    @Override 
    protected void onActivityResult(int requestCode, int responseCode, Intent intent){ 
     if(requestCode == RC_SIGN_IN && responseCode == RESULT_OK) 
      SignInClicked = true; 

      mIntentInProgress = false; 

      if(!mGoogleApiClient.isConnecting()) 
       mGoogleApiClient.connect(); 
     } 
    } 

    @Override 
    public void onConnected(Bundle connectionHint) { 
     mSignInClicked = false; 
     Toast.makeText(this, "User is connected!", Toast.LENGTH_LONG).show(); 

     // Get all the user's information 
     getProfileInformation(); 

     // Update the UI after sign-in 
     updateUI(true); 
    } 

    @Override 
    public void onConnectionSuspended(int cause){ 
     mGoogleApiClient.connect(); 
     updateUI(false); 
    } 

    // Updating the UI, showing/hiding buttons and profile layout 
    private void updateUI(boolean isSignedIn){ 
     if(isSignedIn){ 
      btnSignIn.setVisibility(View.GONE); 
      btnSignOut.setVisibility(View.VISIBLE); 
      btnRevokeAccess.setVisibility(View.VISIBLE); 
      profileLayout.setVisibility(View.VISIBLE); 
     } 
     else{ 
      btnSignIn.setVisibility(View.VISIBLE); 
      btnSignOut.setVisibility(View.GONE); 
      btnRevokeAccess.setVisibility(View.GONE); 
      profileLayout.setVisibility(View.GONE); 
     } 
    } 

    // Sign-in into Google 
    private void signInWithGPlus(){ 
     if(!mGoogleApiClient.isConnecting()){ 
      mSignInClicked = true; 
      resolveSignInErrors(); 
     } 
    } 

    // Method to resolve any sign-in errors 
    private void resolveSignInErrors(){ 
     if(mConnectionResult.hasResolution()){ 
      try{ 
       mIntentInProgress = true; 

       //Toast.makeText(this, "Resolving Sign-in Errors", Toast.LENGTH_SHORT).show(); 

       mConnectionResult.startResolutionForResult(this, RC_SIGN_IN); 
      } 
      catch(SendIntentException e){ 
       // The intent was cancelled before it was sent. Return to the default 
       // state and attempt to connect to get an updated ConnectionResult 
       mIntentInProgress = false; 
       mGoogleApiClient.connect(); 
      } 
     } 
    } 

    // Fetching the user's infromation name, email, profile pic 
    private void getProfileInformation(){ 
     try{ 
      if(Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null){ 
       Person currentPerson = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient); 
       String personName = currentPerson.getDisplayName(); 
       String personPhotoUrl = currentPerson.getImage().getUrl(); 
       String personGooglePlusProfile = currentPerson.getUrl(); 
       String personEmail = Plus.AccountApi.getAccountName(mGoogleApiClient); 

       Log.e(TAG, "Name: " + personName + ", " 
         + "plusProfile: " + personGooglePlusProfile + ", " 
         + "email: " + personEmail + ", " 
         + "image: " + personPhotoUrl); 

       txtName.setText(personName); 
       txtEmail.setText(personEmail); 

       // by default the profile url gives 50x50 px image, 
       // but we can replace the value with whatever dimension we 
       // want by replacing sz=X 
       personPhotoUrl = personPhotoUrl.substring(0, personPhotoUrl.length() - 2) 
         + PROFILE_PIC_SIZE; 

       new LoadProfileImage(imgProfilePic).execute(personPhotoUrl); 
      } 
      else{ 
       Toast.makeText(getApplicationContext(), "Person information is null", Toast.LENGTH_LONG).show(); 
      } 
     } 
     catch(Exception ex){ 
      ex.printStackTrace(); 
     } 
    } 

    // Sign-out from Google 
    private void signOutFromGPlus(){ 
     if(mGoogleApiClient.isConnected()){ 
      Plus.AccountApi.clearDefaultAccount(mGoogleApiClient); 
      mGoogleApiClient.disconnect(); 
      mGoogleApiClient.connect(); 
      updateUI(false); 
     } 
    } 

    // Revoking access from Google 
    private void revokeGPlusAccess(){ 
     if(mGoogleApiClient.isConnected()){ 
      Plus.AccountApi.clearDefaultAccount(mGoogleApiClient); 
      Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient) 
       .setResultCallback(new ResultCallback<Status>(){ 
        @Override 
        public void onResult(Status s){ 
         Log.e(TAG, "User access revoked!"); 
         mGoogleApiClient.connect(); 
         updateUI(false); 
        } 
       }); 
     } 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     getMenuInflater().inflate(R.menu.main, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     // Handle action bar item clicks here. The action bar will 
     // automatically handle clicks on the Home/Up button, so long 
     // as you specify a parent activity in AndroidManifest.xml. 
     int id = item.getItemId(); 
     if (id == R.id.action_settings) 
      return true; 

     return super.onOptionsItemSelected(item); 
    } 
} 

LoadProfileImage.java:

package com.example.testproject_gmaillogin; 

import java.io.InputStream; 

import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.os.AsyncTask; 
import android.util.Log; 
import android.widget.ImageView; 

/** 
* Background async task to load user profile picture from url 
**/ 
public class LoadProfileImage extends AsyncTask<String, Void, Bitmap> { 
    private ImageView bmImage; 

    public LoadProfileImage(ImageView bmImage){ 
     this.bmImage = bmImage; 
    } 

    @Override 
    protected Bitmap doInBackground(String... urls){ 
     String urlDisplay = urls[0]; 
     Bitmap mIcon11 = null; 
     try{ 
      InputStream in = new java.net.URL(urlDisplay).openStream(); 
      mIcon11 = BitmapFactory.decodeStream(in); 
     } 
     catch(Exception ex){ 
      Log.e("Error", ex.getMessage()); 
      ex.printStackTrace(); 
     } 
     return mIcon11; 
    } 

    @Override 
    protected void onPostExecute(Bitmap result){ 
     bmImage.setImageBitmap(result); 
    } 
} 

Các bước khác mà tôi đã làm là:

Tại https://console.developers.google.com/project tôi đã tạo ra một dự án với:

Google+ API trên:

Google+ API on

Và ID khách hàng được tạo bằng đúng SHA1 và không gian tên chính xác giống như dự án:

And a Client ID created with the correct SHA1

Tại Eclipse:

Tôi đã cài đặt google-play-dịch vụ thư viện:

Google-play services installed

Và thêm nó vào dự án:

Google-play services library added (2) Google-play services library added (2)

Tôi cũng đã tạo Trình giả lập với phiên bản 4.4.2:

I've also created an Emulator with version 4.4.2

Nhưng khi tôi chạy ứng dụng tôi nhận được lỗi sau, và lỗi này giữ nảy lên khi tôi nhấp vào nút:

Get Google Play services Error

Bất cứ ai có bất kỳ ý tưởng nơi nó đi sai? Cảm ơn trước cho các phản ứng.

+0

đẹp câu hỏi :) – shkschneider

Trả lời

14

Ok, sau khi thử một số thứ nó bật ra tôi có một lựa chọn cuối cùng không được chọn một cách chính xác, mà đã không được đề cập bất cứ nơi nào trong (các) hướng dẫn ..

Thay vì Android 4.4.2 làm Mục tiêu xây dựng dự án & Mục tiêu giả lập, tôi đã chọn API Google 4.4.2. Bây giờ tôi không gặp lỗi dịch vụ Google Play nữa.

tôi làm được một NullPointerException Mặc dù vậy, nhưng ít nhất tôi có thể tiếp tục ..;)

EDIT: Cố định các NullPointerException và sửa đổi mã trong bài viết ban đầu của tôi. Tất cả mọi thứ hoạt động như hiện tại và tôi hy vọng bài đăng này sẽ giúp những người khác có cùng lỗi (hoặc các lỗi khác) sử dụng dịch vụ đăng nhập google play bằng Trình giả lập Android.

Solution Error Solution Error Emulator

+1

Bạn có thể giải thích cách truy cập màn hình Mục tiêu xây dựng dự án hay không, vì vậy tôi có thể xác minh rằng dự án của tôi đang sử dụng lib dịch vụ của google play? –

+0

@TonyWickham Tôi không có Eclipse trên máy tính xách tay mới của mình, vì vậy tôi không thể xác minh, nhưng tôi tin rằng đó là chuột phải trong dự án -> Thuộc tính -> Android-tab ở bên trái. –

+0

Ah bạn đang sử dụng Eclipse - Tôi đang sử dụng Android Studio, giải thích tại sao nó trông khác. –

8

Hãy thử sử dụng Goole API API mức 19 như mục tiêu giả lập thay vì mức API bình thường 19.

+0

Khi tôi đang làm cho ảnh chụp màn hình của câu hỏi tôi đã cố gắng để thay đổi Android 4.4.2 đến Google API 4.4.2 trong mục tiêu xây dựng, và nó thực sự hoạt động. (Tôi vừa hoàn thành câu hỏi của mình để giúp đỡ những người khác với cùng một vấn đề, vì tôi đã nhìn thấy một số bài viết với lỗi này.) –

+1

Tôi đã có vài lần này, do đó thậm chí không đọc toàn bộ câu hỏi và trả lời đúng sau khi nhìn thấy tên mục tiêu của ảnh chụp màn hình trình mô phỏng. ' –

+1

Bạn đã lưu lại ngày của tôi, Tự hỏi tại sao những thông tin này không được ghi chép đầy đủ. – user2812866

Các vấn đề liên quan