2017-01-16 61 views
2

我正在尋找與使用Android應用訪問Google雲端硬盤相關的一些指導。Android應用程序+全驅動器訪問的Drive API ...程序是什麼?

1)我需要能夠讀取我的應用以外的用戶上傳的文件。這是否意味着我需要全驅動器訪問? (如果應用程序可以創建一個文件夾,然後查看該文件夾中存在的用戶上傳的所有文件,那將很棒,但我認爲它不是這樣工作的。)

2)如果我需要滿驅動器訪問,似乎谷歌「Android的驅動器API」不支持這一點,我需要使用REST api。我認爲這是真的。

3)我需要Google提供的Auth 2.0客戶端ID。如果我使用其他API,這是否意味着我需要使用「Web應用程序」ID?我想我需要這個,因爲我想要一個「授權碼」。我無法使用「Android」類型的ID進行操作。

4)我目前正在使用Android的「Google登錄」來處理登錄並提供認證碼。然後,我可以將其轉換爲令牌+刷新令牌,並保存這些令我可以在一小時後以某種方式獲得新的令牌。這是否需要手動處理刷新令牌?

它越來越醜,但我認爲,因爲我需要(?)全驅動器訪問,那麼這是程序。

感謝您的任何指導。

編輯:該問題已被確定爲重複。所提供的鏈接給出了問題2的答案,但沒有解決其他問題。

我同意這個問題很混亂...

+1

[列出驅動器中的所有文件]的可能的重複(http://stackoverflow.com/questions/34253889/list-all-files-in-drive) – Vyacheslav

+0

[This tool](https://cloudrail.com/cn/zh/ -cloud-storage-api /)應該可以幫助您處理Android上的Rest API,並解決4)中提到的刷新令牌問題。 – Tmm

回答

3

我回答我自己的問題。

我一直在努力,因爲A)谷歌的REST示例使用過時的登錄過程,B)「登錄」示例使用的代碼不適用於「完全訪問」範圍,以及C)當試圖將它們放在一起時,有很多不同的代碼示例。

要快速回答我現在看到的問題: 1)是的,需要全驅動器訪問才能讀取在我的應用程序之外上傳的文件。 2)是的,我需要使用REST api。 3)是的,我需要一個「Web應用程序」客戶端ID。 4)Google登錄似乎是當前登錄的最佳方式,只要您保留刷新令牌,使用GoogleCredential對象和Drive api abject就會自動處理令牌刷新。

如果其他人在使用最新的「登錄」過程和REST v3訪問使用Android完全訪問的Drive時遇到困難,下面是我的示例代碼。

除了「Web應用程序」OAuth客戶端ID之外,您還需要創建一個「Android」類型ID以及匹配的包名稱和證書指紋,以便登錄工作。另請注意,您將爲您的開發版和生產版擁有不同的證書。這些Android客戶端的ID /代碼不需要輸入到應用程序中。

構建。gradle這個:應用

// Google Sign In 
compile 'com.google.android.gms:play-services-auth:10.0.1' 

// Drive REST API 
compile('com.google.apis:google-api-services-drive:v3-rev54-1.22.0') { 
    exclude group: 'org.apache.httpcomponents' 
} 

活動

@Override 
public void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 

    // Callback from Signin (Auth.GoogleSignInApi.getSignInIntent) 
    if (requestCode == 1) { 
     GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); 
     _googleApi.handleSignInResult(result); 
    } 
} 

A 「GoogleApi」 課上做的工作

import android.app.Activity; 
import android.content.Context; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.content.pm.ApplicationInfo; 
import android.os.Bundle; 
import android.os.Handler; 
import android.util.Log; 

import com.google.android.gms.auth.api.Auth; 
import com.google.android.gms.auth.api.signin.GoogleSignInAccount; 
import com.google.android.gms.auth.api.signin.GoogleSignInOptions; 
import com.google.android.gms.auth.api.signin.GoogleSignInResult; 
import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.api.GoogleApiClient; 
import com.google.android.gms.common.api.ResultCallback; 
import com.google.android.gms.common.api.Scope; 
import com.google.android.gms.common.api.Status; 
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; 
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; 
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; 
import com.google.api.client.http.HttpTransport; 
import com.google.api.client.http.javanet.NetHttpTransport; 
import com.google.api.client.json.JsonFactory; 
import com.google.api.client.json.jackson2.JacksonFactory; 
import com.google.api.services.drive.Drive; 
import com.google.api.services.drive.model.File; 
import com.google.api.services.drive.model.FileList; 

import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Locale; 


public class GoogleApi implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { 

    private Context   _context; 
    private Handler   _handler; 
    private GoogleCredential _credential; 
    private Drive   _drive; 

    private GoogleApiClient _googleApiClient;  // only set during login process 
    private Activity  _activity;    // launch intent for login (UI) 

    // Saved to data store 
    private boolean   _loggedIn; 
    private String   _refreshToken;   // store, even if user is logged out as we may need to reuse 


    private static final String ClientID = "xxxxxx.apps.googleusercontent.com"; // web client 
    private static final String ClientSecret = "xxxxx"; // web client 

    private class FileAndErrorMsg { 
     public File file; 
     public String errorMsg; 
     public FileAndErrorMsg (File file_, String errorMsg_) { file = file_; errorMsg = errorMsg_; } 
    } 
    private class FileListAndErrorMsg { 
     public List<File> fileList; 
     public String errorMsg; 
     public FileListAndErrorMsg (List<File> fileList_, String errorMsg_) { fileList = fileList_; errorMsg = errorMsg_; } 
    } 

    // ------------------- 
    // Constructor 
    // ------------------- 


    public GoogleApi (Context context) { 

     _context = context; 
     _handler = new Handler(); 
     loadFromPrefs();  // loggedIn, refreshToken 

     // create credential; will refresh itself automatically (in Drive calls) as long as valid refresh token exists 
     HttpTransport transport = new NetHttpTransport(); 
     JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); 
     _credential = new GoogleCredential.Builder() 
       .setTransport(transport) 
       .setJsonFactory(jsonFactory) 
       .setClientSecrets(ClientID, ClientSecret)  // .addRefreshListener 
       .build(); 
     _credential.setRefreshToken(_refreshToken); 

     // Get app name from Manifest (for Drive builder) 
     ApplicationInfo appInfo = context.getApplicationInfo(); 
     String appName = appInfo.labelRes == 0 ? appInfo.nonLocalizedLabel.toString() : context.getString(appInfo.labelRes); 

     _drive = new Drive.Builder(transport, jsonFactory, _credential).setApplicationName(appName).build(); 
    } 

    // ------------------- 
    // Auth 
    // ------------------- 

    // https://developers.google.com/identity/sign-in/android/offline-access#before_you_begin 
    // https://developers.google.com/identity/sign-in/android/offline-access#enable_server-side_api_access_for_your_app 
    // https://android-developers.googleblog.com/2016/02/using-credentials-between-your-server.html 
    // https://android-developers.googleblog.com/2016/05/improving-security-and-user-experience.html 


    public boolean isLoggedIn() { 
     return _loggedIn; 
    } 

    public void startAuth(Activity activity) { 
     startAuth(activity, false); 
    } 

    public void startAuth(Activity activity, boolean forceRefreshToken) { 

     _activity = activity; 
     _loggedIn = false; 
     saveToPrefs(); 

     GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) 
       .requestScopes(new Scope("https://www.googleapis.com/auth/drive")) 
       .requestServerAuthCode(ClientID, forceRefreshToken)  // if force, guaranteed to get back refresh token, but will show "offline access?" if Google already issued refresh token 
       .build(); 

     _googleApiClient = new GoogleApiClient.Builder(activity) 
       .addConnectionCallbacks(this) 
       .addOnConnectionFailedListener(this) 
       .addApi(Auth.GOOGLE_SIGN_IN_API, gso) 
       .build(); 

     _googleApiClient.connect(); 
    } 

    @Override 
    public void onConnected(Bundle connectionHint) { 
     // Called soon after .connect() 
     // This is only called when starting our Login process. Sign Out first so select-account screen shown. (OK if not already signed in) 
     Auth.GoogleSignInApi.signOut(_googleApiClient).setResultCallback(new ResultCallback<Status>() { 
      @Override 
      public void onResult(Status status) { 
       // Start sign in 
       Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(_googleApiClient); 
       _activity.startActivityForResult(signInIntent, 1); // Activity's onActivityResult will use the same code: 1 
      } 
     }); 
    } 

    @Override 
    public void onConnectionSuspended(int cause) { 
     authDone("Connection suspended."); 
    } 
    @Override 
    public void onConnectionFailed(ConnectionResult connectionResult) { authDone("Connection failed."); } 

    public void handleSignInResult(GoogleSignInResult result) { 

     // Callback from Activity > onActivityResult 
     if (result.isSuccess()) { 
      GoogleSignInAccount acct = result.getSignInAccount(); 
      String authCode = acct.getServerAuthCode(); 
      new Thread(new ContinueAuthWithAuthCode_Background(authCode)).start(); 
     } 
     else authDone("Login canceled or unable to connect to Google."); // can we get better error message? 
    } 

    private class ContinueAuthWithAuthCode_Background implements Runnable { 

     String _authCode; 
     public ContinueAuthWithAuthCode_Background (String authCode) { 
      _authCode = authCode; 
     } 
     public void run() { 

      // Convert authCode to tokens 
      GoogleTokenResponse tokenResponse = null; 
      String errorMsg = null; 
      try { 
       tokenResponse = new GoogleAuthorizationCodeTokenRequest(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), "https://www.googleapis.com/oauth2/v4/token", ClientID, ClientSecret, _authCode, "").execute(); 
      } 
      catch (IOException e) { errorMsg = e.getLocalizedMessage(); } 
      final GoogleTokenResponse tokenResponseFinal = tokenResponse; 
      final String errorMsgFinal = errorMsg; 

      _handler.post(new Runnable() { public void run() { 
       // Main thread 
       GoogleTokenResponse tokenResponse = tokenResponseFinal; 
       String errorMsg = errorMsgFinal; 
       if (tokenResponse != null && errorMsg == null) { 
        _credential.setFromTokenResponse(tokenResponse); // this will keep old refresh token if no new one sent 
        _refreshToken = _credential.getRefreshToken(); 
        _loggedIn = true; 
        saveToPrefs(); 
        // FIXME: if our refresh token is bad and we're not getting a new one, how do we deal with this? 
        Log("New refresh token: " + tokenResponse.getRefreshToken()); 
       } 
       else if (errorMsg == null) errorMsg = "Get token error."; // shouldn't get here 
       authDone(errorMsg); 
      } }); 
     } 
    } 

    private void authDone(String errorMsg) { 
     // Disconnect (we only need googleApiClient for login process) 
     if (_googleApiClient != null && _googleApiClient.isConnected()) _googleApiClient.disconnect(); 
     _googleApiClient = null; 
    } 

    /* 
    public void signOut() { 
     Auth.GoogleSignInApi.signOut(_googleApiClient).setResultCallback(new ResultCallback<Status>() { 
      @Override 
      public void onResult(Status status) { 
      } 
     }); 
    } 

    public void revokeAccess() { 
     // FIXME: I don't know yet, but this may revoke access for all android devices 
     Auth.GoogleSignInApi.revokeAccess(_googleApiClient).setResultCallback(new ResultCallback<Status>() { 
      @Override 
      public void onResult(Status status) { 
      } 
     }); 
    } 
    */ 

    public void LogOut() { 
     _loggedIn = false; 
     saveToPrefs();  // don't clear refresh token as we may need again 
    } 


    // ------------------- 
    // API Calls 
    // ------------------- 


    public void makeApiCall() { 
     new Thread(new TestApiCall_Background()).start(); 
    } 

    private class TestApiCall_Background implements Runnable { 
     public void run() { 

      FileAndErrorMsg fileAndErr = getFolderFromName_b("Many Files", null); 
      if (fileAndErr.errorMsg != null) Log("getFolderFromName_b error: " + fileAndErr.errorMsg); 
      else { 
       FileListAndErrorMsg fileListAndErr = getFileListInFolder_b(fileAndErr.file); 
       if (fileListAndErr.errorMsg != null) 
        Log("getFileListInFolder_b error: " + fileListAndErr.errorMsg); 
       else { 
        Log("file count: " + fileListAndErr.fileList.size()); 
        for (File file : fileListAndErr.fileList) { 
         //Log(file.getName()); 
        } 
       } 
      } 

      _handler.post(new Runnable() { public void run() { 
       // Main thread 
      } }); 
     } 
    } 

    private FileAndErrorMsg getFolderFromName_b (String folderName, File parent) { 

     // parent can be null for top level 
     // Working with folders: https://developers.google.com/drive/v3/web/folder 

     File folder = null; 
     folderName = folderName.replace("'", "\\'"); // escape ' 
     String q = String.format(Locale.US, "mimeType='application/vnd.google-apps.folder' and '%s' in parents and name='%s' and trashed=false", parent == null ? "root" : parent.getId(), folderName); 
     String errorMsg = null; 
     try { 
      FileList result = _drive.files().list().setQ(q).setPageSize(1000).execute(); 
      int foundCount = 0; 
      for (File file : result.getFiles()) { 
       foundCount++; 
       folder = file; 
      } 
      if (foundCount == 0) errorMsg = "Folder not found: " + folderName; 
      else if (foundCount > 1) errorMsg = "More than one folder found with name (" + foundCount + "): " + folderName; 
     } 
     catch (IOException e) { errorMsg = e.getLocalizedMessage(); } 
     if (errorMsg != null) folder = null; 
     return new FileAndErrorMsg(folder, errorMsg); 
    } 

    private FileListAndErrorMsg getFileListInFolder_b (File folder) { 

     // folder can be null for top level; does not return subfolder names 
     List<File> fileList = new ArrayList<File>(); 
     String q = String.format(Locale.US, "mimeType != 'application/vnd.google-apps.folder' and '%s' in parents and trashed=false", folder == null ? "root" : folder.getId()); 
     String errorMsg = null; 
     try { 
      String pageToken = null; 
      do { 
       FileList result = _drive.files().list().setQ(q).setPageSize(1000).setPageToken(pageToken).execute(); 
       fileList.addAll(result.getFiles()); 
       pageToken = result.getNextPageToken(); 
      } while (pageToken != null); 
     } 
     catch (IOException e) { errorMsg = e.getLocalizedMessage(); } 
     if (errorMsg != null) fileList = null; 
     return new FileListAndErrorMsg(fileList, errorMsg); 
    } 


    // ------------------- 
    // Misc 
    // ------------------- 

    private void Log(String msg) { 
     Log.v("ept", msg); 
    } 


    // ------------------- 
    // Load/Save Tokens 
    // ------------------- 


    private void loadFromPrefs() { 
     SharedPreferences pref = _context.getSharedPreferences("prefs", Context.MODE_PRIVATE); 
     _loggedIn = pref.getBoolean("GoogleLoggedIn", false); 
     _refreshToken = pref.getString("GoogleRefreshToken", null); 
    } 
    private void saveToPrefs() { 
     SharedPreferences.Editor editor = _context.getSharedPreferences("prefs", Context.MODE_PRIVATE).edit(); 
     editor.putBoolean("GoogleLoggedIn", _loggedIn); 
     editor.putString("GoogleRefreshToken", _refreshToken); 
     editor.apply();  // async 

    } 

} 
+0

此解決方案真的有效!你拯救了我的一天! – mariotaku

+0

只是將任何人都包含在Android教程(https://developers.google.com/drive/v3/web/quickstart/android)中,以使客戶端API將其添加到您的Gradle應用中。 configurations.all { resolutionStrategy.force'com.google.code.findbugs:jsr305:1.3.9' } – Sprout

0

最新的例子在https://developers.google.com/drive/v3/web/quickstart/android作品開箱。

只需做到以下幾點:

1 - 轉到谷歌API控制檯並使用你的包名和您的簽名證書的指紋調試/釋放鍵建立OAuth2用戶端ID。

2 - 啓用谷歌雲端硬盤API

3 - 應用下面的代碼

的build.gradle:應用

compile 'com.google.android.gms:play-services-auth:10.0.1' 
compile 'pub.devrel:easypermissions:0.2.1' 
compile('com.google.api-client:google-api-client-android:1.22.0') { 
     exclude group: 'org.apache.httpcomponents' 
} 
compile('com.google.apis:google-api-services-drive:v3-rev57-1.22.0') { 
     exclude group: 'org.apache.httpcomponents' 
} 

活動

在這段代碼只是改變範圍DriveScopes .DRIVE全驅驅動器訪問

import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.GoogleApiAvailability; 
import com.google.api.client.extensions.android.http.AndroidHttp; 
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential; 
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException; 
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException; 

import com.google.api.client.http.HttpTransport; 
import com.google.api.client.json.JsonFactory; 
import com.google.api.client.json.jackson2.JacksonFactory; 
import com.google.api.client.util.ExponentialBackOff; 

import com.google.api.services.drive.DriveScopes; 

import com.google.api.services.drive.model.*; 

import android.Manifest; 
import android.accounts.AccountManager; 
import android.app.Activity; 
import android.app.Dialog; 
import android.app.ProgressDialog; 
import android.content.Context; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.net.ConnectivityManager; 
import android.net.NetworkInfo; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.support.annotation.NonNull; 
import android.text.TextUtils; 
import android.text.method.ScrollingMovementMethod; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.Button; 
import android.widget.LinearLayout; 
import android.widget.TextView; 

import java.io.IOException; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

import pub.devrel.easypermissions.AfterPermissionGranted; 
import pub.devrel.easypermissions.EasyPermissions; 

public class MainActivity extends Activity 
    implements EasyPermissions.PermissionCallbacks { 
    GoogleAccountCredential mCredential; 
    private TextView mOutputText; 
    private Button mCallApiButton; 
    ProgressDialog mProgress; 

    static final int REQUEST_ACCOUNT_PICKER = 1000; 
    static final int REQUEST_AUTHORIZATION = 1001; 
    static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002; 
    static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003; 

    private static final String BUTTON_TEXT = "Call Drive API"; 
    private static final String PREF_ACCOUNT_NAME = "accountName"; 
    private static final String[] SCOPES = { DriveScopes.DRIVE }; 

    /** 
    * Create the main activity. 
    * @param savedInstanceState previously saved instance data. 
    */ 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     LinearLayout activityLayout = new LinearLayout(this); 
     LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
       LinearLayout.LayoutParams.MATCH_PARENT, 
       LinearLayout.LayoutParams.MATCH_PARENT); 
     activityLayout.setLayoutParams(lp); 
     activityLayout.setOrientation(LinearLayout.VERTICAL); 
     activityLayout.setPadding(16, 16, 16, 16); 

     ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams(
       ViewGroup.LayoutParams.WRAP_CONTENT, 
       ViewGroup.LayoutParams.WRAP_CONTENT); 

     mCallApiButton = new Button(this); 
     mCallApiButton.setText(BUTTON_TEXT); 
     mCallApiButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       mCallApiButton.setEnabled(false); 
       mOutputText.setText(""); 
       getResultsFromApi(); 
       mCallApiButton.setEnabled(true); 
      } 
     }); 
     activityLayout.addView(mCallApiButton); 

     mOutputText = new TextView(this); 
     mOutputText.setLayoutParams(tlp); 
     mOutputText.setPadding(16, 16, 16, 16); 
     mOutputText.setVerticalScrollBarEnabled(true); 
     mOutputText.setMovementMethod(new ScrollingMovementMethod()); 
     mOutputText.setText(
       "Click the \'" + BUTTON_TEXT +"\' button to test the API."); 
     activityLayout.addView(mOutputText); 

     mProgress = new ProgressDialog(this); 
     mProgress.setMessage("Calling Drive API ..."); 

     setContentView(activityLayout); 

     // Initialize credentials and service object. 
     mCredential = GoogleAccountCredential.usingOAuth2(
       getApplicationContext(), Arrays.asList(SCOPES)) 
       .setBackOff(new ExponentialBackOff()); 
    } 



    /** 
    * Attempt to call the API, after verifying that all the preconditions are 
    * satisfied. The preconditions are: Google Play Services installed, an 
    * account was selected and the device currently has online access. If any 
    * of the preconditions are not satisfied, the app will prompt the user as 
    * appropriate. 
    */ 
    private void getResultsFromApi() { 
     if (! isGooglePlayServicesAvailable()) { 
      acquireGooglePlayServices(); 
     } else if (mCredential.getSelectedAccountName() == null) { 
      chooseAccount(); 
     } else if (! isDeviceOnline()) { 
      mOutputText.setText("No network connection available."); 
     } else { 
      new MakeRequestTask(mCredential).execute(); 
     } 
    } 

    /** 
    * Attempts to set the account used with the API credentials. If an account 
    * name was previously saved it will use that one; otherwise an account 
    * picker dialog will be shown to the user. Note that the setting the 
    * account to use with the credentials object requires the app to have the 
    * GET_ACCOUNTS permission, which is requested here if it is not already 
    * present. The AfterPermissionGranted annotation indicates that this 
    * function will be rerun automatically whenever the GET_ACCOUNTS permission 
    * is granted. 
    */ 
    @AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS) 
    private void chooseAccount() { 
     if (EasyPermissions.hasPermissions(
       this, Manifest.permission.GET_ACCOUNTS)) { 
      String accountName = getPreferences(Context.MODE_PRIVATE) 
        .getString(PREF_ACCOUNT_NAME, null); 
      if (accountName != null) { 
       mCredential.setSelectedAccountName(accountName); 
       getResultsFromApi(); 
      } else { 
       // Start a dialog from which the user can choose an account 
       startActivityForResult(
         mCredential.newChooseAccountIntent(), 
         REQUEST_ACCOUNT_PICKER); 
      } 
     } else { 
      // Request the GET_ACCOUNTS permission via a user dialog 
      EasyPermissions.requestPermissions(
        this, 
        "This app needs to access your Google account (via Contacts).", 
        REQUEST_PERMISSION_GET_ACCOUNTS, 
        Manifest.permission.GET_ACCOUNTS); 
     } 
    } 

    /** 
    * Called when an activity launched here (specifically, AccountPicker 
    * and authorization) exits, giving you the requestCode you started it with, 
    * the resultCode it returned, and any additional data from it. 
    * @param requestCode code indicating which activity result is incoming. 
    * @param resultCode code indicating the result of the incoming 
    *  activity result. 
    * @param data Intent (containing result data) returned by incoming 
    *  activity result. 
    */ 
    @Override 
    protected void onActivityResult(
      int requestCode, int resultCode, Intent data) { 
     super.onActivityResult(requestCode, resultCode, data); 
     switch(requestCode) { 
      case REQUEST_GOOGLE_PLAY_SERVICES: 
       if (resultCode != RESULT_OK) { 
        mOutputText.setText(
          "This app requires Google Play Services. Please install " + 
          "Google Play Services on your device and relaunch this app."); 
       } else { 
        getResultsFromApi(); 
       } 
       break; 
      case REQUEST_ACCOUNT_PICKER: 
       if (resultCode == RESULT_OK && data != null && 
         data.getExtras() != null) { 
        String accountName = 
          data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); 
        if (accountName != null) { 
         SharedPreferences settings = 
           getPreferences(Context.MODE_PRIVATE); 
         SharedPreferences.Editor editor = settings.edit(); 
         editor.putString(PREF_ACCOUNT_NAME, accountName); 
         editor.apply(); 
         mCredential.setSelectedAccountName(accountName); 
         getResultsFromApi(); 
        } 
       } 
       break; 
      case REQUEST_AUTHORIZATION: 
       if (resultCode == RESULT_OK) { 
        getResultsFromApi(); 
       } 
       break; 
     } 
    } 

    /** 
    * Respond to requests for permissions at runtime for API 23 and above. 
    * @param requestCode The request code passed in 
    *  requestPermissions(android.app.Activity, String, int, String[]) 
    * @param permissions The requested permissions. Never null. 
    * @param grantResults The grant results for the corresponding permissions 
    *  which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null. 
    */ 
    @Override 
    public void onRequestPermissionsResult(int requestCode, 
              @NonNull String[] permissions, 
              @NonNull int[] grantResults) { 
     super.onRequestPermissionsResult(requestCode, permissions, grantResults); 
     EasyPermissions.onRequestPermissionsResult(
       requestCode, permissions, grantResults, this); 
    } 

    /** 
    * Callback for when a permission is granted using the EasyPermissions 
    * library. 
    * @param requestCode The request code associated with the requested 
    *   permission 
    * @param list The requested permission list. Never null. 
    */ 
    @Override 
    public void onPermissionsGranted(int requestCode, List<String> list) { 
     // Do nothing. 
    } 

    /** 
    * Callback for when a permission is denied using the EasyPermissions 
    * library. 
    * @param requestCode The request code associated with the requested 
    *   permission 
    * @param list The requested permission list. Never null. 
    */ 
    @Override 
    public void onPermissionsDenied(int requestCode, List<String> list) { 
     // Do nothing. 
    } 

    /** 
    * Checks whether the device currently has a network connection. 
    * @return true if the device has a network connection, false otherwise. 
    */ 
    private boolean isDeviceOnline() { 
     ConnectivityManager connMgr = 
       (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 
     NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); 
     return (networkInfo != null && networkInfo.isConnected()); 
    } 

    /** 
    * Check that Google Play services APK is installed and up to date. 
    * @return true if Google Play Services is available and up to 
    *  date on this device; false otherwise. 
    */ 
    private boolean isGooglePlayServicesAvailable() { 
     GoogleApiAvailability apiAvailability = 
       GoogleApiAvailability.getInstance(); 
     final int connectionStatusCode = 
       apiAvailability.isGooglePlayServicesAvailable(this); 
     return connectionStatusCode == ConnectionResult.SUCCESS; 
    } 

    /** 
    * Attempt to resolve a missing, out-of-date, invalid or disabled Google 
    * Play Services installation via a user dialog, if possible. 
    */ 
    private void acquireGooglePlayServices() { 
     GoogleApiAvailability apiAvailability = 
       GoogleApiAvailability.getInstance(); 
     final int connectionStatusCode = 
       apiAvailability.isGooglePlayServicesAvailable(this); 
     if (apiAvailability.isUserResolvableError(connectionStatusCode)) { 
      showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode); 
     } 
    } 


    /** 
    * Display an error dialog showing that Google Play Services is missing 
    * or out of date. 
    * @param connectionStatusCode code describing the presence (or lack of) 
    *  Google Play Services on this device. 
    */ 
    void showGooglePlayServicesAvailabilityErrorDialog(
      final int connectionStatusCode) { 
     GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); 
     Dialog dialog = apiAvailability.getErrorDialog(
       MainActivity.this, 
       connectionStatusCode, 
       REQUEST_GOOGLE_PLAY_SERVICES); 
     dialog.show(); 
    } 

    /** 
    * An asynchronous task that handles the Drive API call. 
    * Placing the API calls in their own task ensures the UI stays responsive. 
    */ 
    private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> { 
     private com.google.api.services.drive.Drive mService = null; 
     private Exception mLastError = null; 

     MakeRequestTask(GoogleAccountCredential credential) { 
      HttpTransport transport = AndroidHttp.newCompatibleTransport(); 
      JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); 
      mService = new com.google.api.services.drive.Drive.Builder(
        transport, jsonFactory, credential) 
        .setApplicationName("Drive API Android Quickstart") 
        .build(); 
     } 

     /** 
     * Background task to call Drive API. 
     * @param params no parameters needed for this task. 
     */ 
     @Override 
     protected List<String> doInBackground(Void... params) { 
      try { 
       return getDataFromApi(); 
      } catch (Exception e) { 
       mLastError = e; 
       cancel(true); 
       return null; 
      } 
     } 

     /** 
     * Fetch a list of up to 10 file names and IDs. 
     * @return List of Strings describing files, or an empty list if no files 
     *   found. 
     * @throws IOException 
     */ 
     private List<String> getDataFromApi() throws IOException { 
      // Get a list of up to 10 files. 
      List<String> fileInfo = new ArrayList<String>(); 
      FileList result = mService.files().list() 
       .setPageSize(10) 
       .setFields("nextPageToken, files(id, name)") 
       .execute(); 
      List<File> files = result.getFiles(); 
      if (files != null) { 
       for (File file : files) { 
        fileInfo.add(String.format("%s (%s)\n", 
          file.getName(), file.getId())); 
       } 
      } 
      return fileInfo; 
     } 


     @Override 
     protected void onPreExecute() { 
      mOutputText.setText(""); 
      mProgress.show(); 
     } 

     @Override 
     protected void onPostExecute(List<String> output) { 
      mProgress.hide(); 
      if (output == null || output.size() == 0) { 
       mOutputText.setText("No results returned."); 
      } else { 
       output.add(0, "Data retrieved using the Drive API:"); 
       mOutputText.setText(TextUtils.join("\n", output)); 
      } 
     } 

     @Override 
     protected void onCancelled() { 
      mProgress.hide(); 
      if (mLastError != null) { 
       if (mLastError instanceof GooglePlayServicesAvailabilityIOException) { 
        showGooglePlayServicesAvailabilityErrorDialog(
          ((GooglePlayServicesAvailabilityIOException) mLastError) 
            .getConnectionStatusCode()); 
       } else if (mLastError instanceof UserRecoverableAuthIOException) { 
        startActivityForResult(
          ((UserRecoverableAuthIOException) mLastError).getIntent(), 
          MainActivity.REQUEST_AUTHORIZATION); 
       } else { 
        mOutputText.setText("The following error occurred:\n" 
          + mLastError.getMessage()); 
       } 
      } else { 
       mOutputText.setText("Request cancelled."); 
      } 
     } 
    } 
} 
+1

我對這種方法的擔心是,它似乎需要GET_ACCOUNTS權限,它包含在示例代碼中頁。從下面的網頁,谷歌似乎現在勸阻。 https://android-developers.googleblog.com/2016/05/improving-security-and-user-experience.html 「最糟糕的是GET_ACCOUNTS權限。在棉花糖及以上版本中,此權限會顯示給用戶作爲「聯繫人」許多用戶不願意授予對此運行時權限的訪問權限解決方案:切換到我們的新Auth.GOOGLE_SIGN_IN_API。「 –

+0

@ErnieThomason我沒有意識到這一點,無論如何,我會等待他們的文檔被GOOGLE_SIGN_IN_API更新,因爲我無法讓您的實現在我的設備上正常工作 – Steve