当前位置:  开发笔记 > 编程语言 > 正文

具有完全驱动器访问权限的Android应用+ Drive API ...程序是什么?

如何解决《具有完全驱动器访问权限的Android应用+DriveAPI程序是什么?》经验,为你挑选了1个好方法。

我正在寻找与使用Android应用访问Google云端硬盘相关的一些指导.

1)我需要能够读取我的应用程序之外的用户上传的文件.这是否意味着我需要全驱动访问?(如果应用程序可以创建一个文件夹,然后查看该文件夹中存在的用户上传的所有文件,那就太棒了,但我不认为这样做.)

2)如果我需要全驱动访问权限,似乎Googles"Drive API for Android"不支持此功能,我需要使用REST API.我认为这是真的.

3)我需要来自Google的Auth 2.0客户端ID.如果我使用其余的API,这是否意味着我需要使用"Web应用程序"ID?我想我需要这个,因为我想要一个"授权代码".我无法使用"Android"类型ID.

4)我目前正在使用Android的"Google登录"来处理登录并提供身份验证代码.然后我可以将它转换为令牌+刷新令牌,并保存这些,这样我可以在一小时后以某种方式获得新的令牌.是否需要手动处理刷新令牌?

它变得丑陋,但我认为,因为我需要(?)全驱动访问,所以这是程序.

谢谢你的指导.

编辑:该问题已被确定为重复.提供的链接为问题#2提供了答案,但没有解决其他问题.

我同意这个问题很混乱......



1> Ernie Thomas..:

我在回答我自己的问题.

我为此苦苦挣扎,因为A)Google的REST示例使用过时的登录过程,B)"登录"示例使用的代码不能与"完全访问"范围一起使用,而C)有太多不同的代码试图将它们放在一起的例子.

我现在看到它可以快速回答我的问题:1)是的,需要完全驱动器访问才能读取在我的应用程序之外上传的文件.2)是的,我需要使用REST api.3)是的,我需要一个"Web应用程序"客户端ID.4)Google登录似乎是目前登录的最佳方式,只要您保留刷新令牌,使用GoogleCredential对象和Drive api abject就会自动处理令牌刷新.

如果其他人正在努力使用最新的"登录"程序和REST v3从Android访问完全访问驱动器,下面是我的示例代码.

除了"Web应用程序"OAuth客户端ID之外,您还需要创建具有匹配包名称和证书指纹的"Android"类型ID,以便登录工作.另请注意,您的开发和生产版本将拥有不同的证书.来自这些Android客户端的ID /代码无需输入到应用程序中.

build.gradle:app

// 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);
    }
}

一个"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 fileList;
        public String errorMsg;
        public FileListAndErrorMsg (List 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() {
            @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() {
            @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() {
            @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 fileList = new ArrayList();
        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

    }

}

推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有