2017-01-16 63 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