После отключения приложения Google Drive Android API по-прежнему возвращает успешные результаты, но не загружает файл

Я использую Google Drive Android API (как часть сервисов Google Play) для загрузки файлов в облако.

Для подключения клиента я использую следующий код (упрощенный):

apiClient = new GoogleApiClient.Builder(context)
            .addApi(Drive.API)
            .setAccountName(preferences.getString("GOOGLE_DRIVE_ACCOUNT", null))
            .build();

ConnectionResult connectionResult = apiClient.blockingConnect(SERVICES_CONNECTION_TIMEOUT_SEC, TimeUnit.SECONDS);
if (!connectionResult.isSuccess()) {
    throw new ApiConnectionException(); //our own exception
}

Чтобы загрузить файл, я использую следующий код (упрощенный):

DriveApi.ContentsResult result = Drive.DriveApi.newContents(apiClient).await();
if (!result.getStatus().isSuccess()) {
    /* ... code for error handling ... */
    return;
}

OutputStream output = result.getContents().getOutputStream();
/* ... writing to output ... */

//create actual file on Google Drive
DriveFolder.DriveFileResult driveFileResult = Drive.DriveApi
            .getFolder(apiClient, folderId)
            .createFile(apiClient, metadataChangeSet, result.getContents())
            .await();

Все работает так, как ожидалось, за исключением одного конкретного пользовательского случая. Когда пользователь удаляет наше приложение из «Подключенных приложений» (с помощью приложения «Настройки Google»), этот код по-прежнему возвращает успешные результаты для всех вызовов. Хотя файл никогда не загружается на Google Диск.

Подключение к сервисам Google Play также проходит успешно.

Это баг API или можно как-то обнаружить, что пользователь отключил приложение?


person Dmitry Zaytsev    schedule 03.06.2014    source источник
comment
Можете ли вы опубликовать простой, но полный пример? (Разумеется, без вашего ключа API)   -  person Cheok Yan Cheng    schedule 06.06.2014
comment
@CheokYanCheng Я думаю, что приведенного выше кода должно быть достаточно.   -  person Dmitry Zaytsev    schedule 06.06.2014
comment
this code still returns successful results for all invocations означает, что apiClient.blockingConnect(...) по-прежнему возвращает успешные результаты? Или только newContents и getContents().getOutputStream()?   -  person ben75    schedule 09.06.2014
comment
@ ben75 да, apiClient.blockingConnect(...) также возвращает успешный результат.   -  person Dmitry Zaytsev    schedule 09.06.2014
comment
Вы пытались вызвать listChildren() или queryChildren() и проверить, присутствует ли файл в результатах?   -  person koral    schedule 09.06.2014
comment
@koral Я попробую, но боюсь, что Google Диск не всегда мгновенно добавляет файл в список.   -  person Dmitry Zaytsev    schedule 09.06.2014
comment
Вероятно, тривиальный вопрос, но разве onDisconnected() не вызывается в объекте обратного вызова?   -  person matiash    schedule 11.06.2014
comment
Любое решение для этого? Прошло 4 года, а я все еще сталкиваюсь с той же проблемой. Ссылка на мой вопрос: stackoverflow.com/q/50319379/3273962   -  person Mangesh    schedule 18.05.2018


Ответы (3)


Я не знаю API внутри и снаружи, однако эта страница может помочь https://support.google.com/drive/answer/2523073?hl=ru. Я бы дважды проверил страницу account.google.com и убедился, что все разрешения удалены. Это не решит поведение API, но, по крайней мере, вы можете проверить разрешения.

person SoundsDangerous    schedule 12.06.2014

Разве вы не получаете исключение UserRecoverableAuthIOException? Потому что вы должны. Любая попытка чтения/загрузки на диск, где пользователь отключил приложение, должна возвращать это исключение. Вероятно, вы ловите общие исключения и пропускаете это. Попробуйте выполнить отладку, чтобы убедиться, что вы не получаете это исключение.

Если да, все, что вам нужно сделать, это повторно запросить

        catch (UserRecoverableAuthIOException e) {
            startActivityForResult(e.getIntent(), COMPLETE_AUTHORIZATION_REQUEST_CODE);
        }

И затем обработайте ответ следующим образом:

case COMPLETE_AUTHORIZATION_REQUEST_CODE:
        if (resultCode == RESULT_OK) {
            // App is authorized, you can go back to sending the API request
        } else {
            // User denied access, show him the account chooser again
        }
        break;
    }
person Dpedrinha    schedule 21.06.2014
comment
Как видно из моего кода, я не ловлю Exception. Хотя в этом случае был бы смысл, и я бы ожидал такого поведения, как вы описали. К сожалению, в настоящее время это не работает так. - person Dmitry Zaytsev; 21.06.2014
comment
Разве вы не подавляете исключения или не поднимаете их в иерархии? Проверьте, не выдает ли ваш класс исключения (и не перехватывает ли их где-то еще) и не подавляете ли вы их. Потому что этот код должен требовать блоков try/catch. - person Dpedrinha; 25.06.2014
comment
Как я уже сказал, я проверил поведение (и с журналом, и с отладчиком). Результаты есть и они почему-то успешны. Исключения не выбрасываются. - person Dmitry Zaytsev; 25.06.2014

Для создания файла попробуйте отправить IntentSender в соответствии с этим.

Предоставляя IntentSender другому приложению, вы предоставляете ему право выполнять указанную вами операцию, как если бы это другое приложение было вами (с теми же разрешениями и идентификатором). Похоже на ожидание намерения. Вы можете создать файл, используя

ResultCallback<ContentsResult> onContentsCallback =
                    new ResultCallback<ContentsResult>() {
                @Override
                public void onResult(ContentsResult result) {
                    // TODO: error handling in case of failure
                    MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
                            .setMimeType(MIME_TYPE_TEXT).build();
                    IntentSender createIntentSender = Drive.DriveApi
                            .newCreateFileActivityBuilder()
                            .setInitialMetadata(metadataChangeSet)
                            .setInitialContents(result.getContents())
                            .build(mGoogleApiClient);
                    try {
                        startIntentSenderForResult(createIntentSender, REQUEST_CODE_CREATOR, null,
                                0, 0, 0);
                    } catch (SendIntentException e) {
                        Log.w(TAG, "Unable to send intent", e);
                    }
                }
            };

Здесь

`startIntentSenderForResult (IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)`

Если requestCode >= 0, этот код будет возвращен в onActivityResult() при завершении действия. в вашем onActivityResult() вы можете

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        //REQUEST_CODE_CREATOR == 1
        case REQUEST_CODE_CREATOR:
            if (resultCode == RESULT_OK) {
                DriveId driveId = (DriveId) data.getParcelableExtra(
                        OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);
                showMessage("File created with ID: " + driveId);
            }
            finish();
            break;
        default:
            super.onActivityResult(requestCode, resultCode, data);
            break;
        }
    }

Попробуйте получить apiClient вот так

mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(Drive.API).addScope(Drive.SCOPE_FILE)
                    .setAccountName(mAccountName).addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this).build();



  /**
     * Called when {@code mGoogleApiClient} is connected.
     */
    @Override
    public void onConnected(Bundle connectionHint) {
        Log.i(TAG, "GoogleApiClient connected");
    }

     /**
     * Called when {@code mGoogleApiClient} is disconnected.
     */
    @Override
    public void onConnectionSuspended(int cause) {
        Log.i(TAG, "GoogleApiClient connection suspended");
    }

    /**
     * Called when {@code mGoogleApiClient} is trying to connect but failed.
     * Handle {@code result.getResolution()} if there is a resolution is
     * available.
     */
    @Override
    public void onConnectionFailed(ConnectionResult result) {
        Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
        if (!result.hasResolution()) {
            GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
            return;
        }
        try {
            result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
        } catch (SendIntentException e) {
            Log.e(TAG, "Exception while starting resolution activity", e);
        }
    }

Вы можете получить mAccountName следующим образом:

Account[] accounts = AccountManager.get(this).getAccountsByType("com.google");
            if (accounts.length == 0) {
                Log.d(TAG, "Must have a Google account installed");
                return;
            }
            mAccountName = accounts[0].name;

Надеюсь это поможет.

person android_Muncher    schedule 12.06.2014
comment
Я не ищу, как обучать. Более того, мне не нужно создавать файл через IntentSender - мне нужно сделать это внутри моего процесса - person Dmitry Zaytsev; 12.06.2014