/* * Copyright (c) 2011 Denis Solonenko. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ package ru.orangesoftware.financisto2.export.drive; import android.content.Context; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.Status; import com.google.android.gms.drive.Drive; import com.google.android.gms.drive.DriveApi; import com.google.android.gms.drive.DriveContents; import com.google.android.gms.drive.DriveFile; import com.google.android.gms.drive.DriveFolder; import com.google.android.gms.drive.DriveId; import com.google.android.gms.drive.Metadata; import com.google.android.gms.drive.MetadataBuffer; import com.google.android.gms.drive.MetadataChangeSet; import com.google.android.gms.drive.query.Filters; import com.google.android.gms.drive.query.Query; import com.google.android.gms.drive.query.SearchableField; import org.androidannotations.annotations.AfterInject; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EBean; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import ru.orangesoftware.financisto2.R; import ru.orangesoftware.financisto2.backup.DatabaseExport; import ru.orangesoftware.financisto2.backup.DatabaseImport; import ru.orangesoftware.financisto2.bus.GreenRobotBus; import ru.orangesoftware.financisto2.db.CategoryRepository; import ru.orangesoftware.financisto2.db.DatabaseAdapter; import ru.orangesoftware.financisto2.export.Export; import ru.orangesoftware.financisto2.export.ImportExportException; import ru.orangesoftware.financisto2.utils.MyPreferences; @EBean(scope = EBean.Scope.Singleton) public class GoogleDriveClient { private final Context context; @Bean GreenRobotBus bus; @Bean DatabaseAdapter db; @Bean CategoryRepository categoryRepository; private GoogleApiClient googleApiClient; public GoogleDriveClient(Context context) { this.context = context.getApplicationContext(); } @AfterInject public void init() { bus.register(this); } public ConnectionResult connect() throws ImportExportException { if (googleApiClient == null) { String googleDriveAccount = MyPreferences.getGoogleDriveAccount(context); if (googleDriveAccount == null) { throw new ImportExportException(context.getString(R.string.google_drive_account_required)); } googleApiClient = new GoogleApiClient.Builder(context) .addApi(Drive.API) .addScope(Drive.SCOPE_FILE) .setAccountName(googleDriveAccount) .build(); } return googleApiClient.blockingConnect(1, TimeUnit.MINUTES); } public void disconnect() { if (googleApiClient != null) { googleApiClient.disconnect(); } } public void onEventBackgroundThread(DoDriveBackup event) { DatabaseExport export = new DatabaseExport(context, db.db(), true); try { String targetFolder = getDriveFolderName(); ConnectionResult connectionResult = connect(); if (connectionResult.isSuccess()) { DriveFolder folder = getDriveFolder(targetFolder); String fileName = export.generateFilename(); byte[] bytes = export.generateBackupBytes(); Status status = createFile(folder, fileName, bytes); if (status.isSuccess()) { handleSuccess(fileName); } else { handleFailure(status); } } else { handleConnectionResult(connectionResult); } } catch (Exception e) { handleError(e); } } public void onEventBackgroundThread(DoDriveListFiles event) { try { String targetFolder = getDriveFolderName(); ConnectionResult connectionResult = connect(); if (connectionResult.isSuccess()) { DriveFolder folder = getDriveFolder(targetFolder); Query query = new Query.Builder() .addFilter(Filters.and( Filters.eq(SearchableField.MIME_TYPE, Export.BACKUP_MIME_TYPE), Filters.eq(SearchableField.TRASHED, false) )) .build(); DriveApi.MetadataBufferResult metadataBufferResult = folder.queryChildren(googleApiClient, query).await(); if (metadataBufferResult.getStatus().isSuccess()) { List<DriveFileInfo> driveFiles = fetchFiles(metadataBufferResult); handleSuccess(driveFiles); } else { handleFailure(metadataBufferResult.getStatus()); } } else { handleConnectionResult(connectionResult); } } catch (Exception e) { handleError(e); } } public void onEventBackgroundThread(DoDriveRestore event) { try { String targetFolder = getDriveFolderName(); ConnectionResult connectionResult = connect(); if (connectionResult.isSuccess()) { DriveFolder folder = getDriveFolder(targetFolder); DriveFile file = Drive.DriveApi.getFile(googleApiClient, event.selectedDriveFile.driveId); DriveApi.DriveContentsResult contentsResult = file.open(googleApiClient, DriveFile.MODE_READ_ONLY, null).await(); if (contentsResult.getStatus().isSuccess()) { DriveContents contents = contentsResult.getDriveContents(); try { DatabaseImport.createFromGoogleDriveBackup(context, db, categoryRepository, contents).importDatabase(); bus.post(new DriveRestoreSuccess()); } finally { contents.discard(googleApiClient); } } else { handleFailure(contentsResult.getStatus()); } } else { handleConnectionResult(connectionResult); } } catch (Exception e) { handleError(e); } } protected List<DriveFileInfo> fetchFiles(DriveApi.MetadataBufferResult metadataBufferResult) { List<DriveFileInfo> files = new ArrayList<DriveFileInfo>(); MetadataBuffer metadataBuffer = metadataBufferResult.getMetadataBuffer(); if (metadataBuffer == null) return files; try { for (Metadata metadata : metadataBuffer) { if (metadata == null) continue; String title = metadata.getTitle(); if (!title.endsWith(".backup")) continue; files.add(new DriveFileInfo(metadata.getDriveId(), title, metadata.getCreatedDate())); } } finally { metadataBuffer.close(); } Collections.sort(files); return files; } protected String getDriveFolderName() throws ImportExportException { String folder = MyPreferences.getBackupFolder(context); // check the backup folder registered on preferences if (folder == null || folder.equals("")) { throw new ImportExportException(context.getString(R.string.gdocs_folder_not_configured)); } return folder; } protected DriveFolder getDriveFolder(String targetFolder) throws IOException, ImportExportException { DriveFolder folder = getOrCreateDriveFolder(targetFolder); if (folder == null) { throw new ImportExportException(context.getString(R.string.gdocs_folder_not_found)); } return folder; } public DriveFolder getOrCreateDriveFolder(String targetFolder) throws IOException { Query query = new Query.Builder().addFilter(Filters.and( Filters.eq(SearchableField.TRASHED, false), Filters.eq(SearchableField.TITLE, targetFolder), Filters.eq(SearchableField.MIME_TYPE, "application/vnd.google-apps.folder") )).build(); DriveApi.MetadataBufferResult result = Drive.DriveApi.query(googleApiClient, query).await(); if (result.getStatus().isSuccess()) { DriveId driveId = fetchDriveId(result); if (driveId != null) { return Drive.DriveApi.getFolder(googleApiClient, driveId); } } return createDriveFolder(targetFolder); } private DriveFolder createDriveFolder(String targetFolder) { MetadataChangeSet changeSet = new MetadataChangeSet.Builder() .setTitle(targetFolder).build(); DriveFolder.DriveFolderResult result = Drive.DriveApi.getRootFolder(googleApiClient).createFolder(googleApiClient, changeSet).await(); if (result.getStatus().isSuccess()) { return result.getDriveFolder(); } else { return null; } } private DriveId fetchDriveId(DriveApi.MetadataBufferResult result) { MetadataBuffer buffer = result.getMetadataBuffer(); try { for (Metadata metadata : buffer) { if (metadata == null) continue; return metadata.getDriveId(); } } finally { buffer.close(); } return null; } public Status createFile(DriveFolder folder, String fileName, byte[] bytes) throws IOException { MetadataChangeSet changeSet = new MetadataChangeSet.Builder() .setTitle(fileName) .setMimeType(Export.BACKUP_MIME_TYPE).build(); // Create a file in the root folder DriveApi.DriveContentsResult contentsResult = Drive.DriveApi.newDriveContents(googleApiClient).await(); Status contentsResultStatus = contentsResult.getStatus(); if (contentsResultStatus.isSuccess()) { DriveContents contents = contentsResult.getDriveContents(); contents.getOutputStream().write(bytes); DriveFolder.DriveFileResult fileResult = folder.createFile(googleApiClient, changeSet, contents).await(); return fileResult.getStatus(); } else { return contentsResultStatus; } } private void handleConnectionResult(ConnectionResult connectionResult) { bus.post(new DriveConnectionFailed(connectionResult)); } private void handleError(Exception e) { bus.post(new DriveBackupError(e)); } private void handleFailure(Status status) { bus.post(new DriveBackupFailure(status)); } private void handleSuccess(String fileName) { bus.post(new DriveBackupSuccess(fileName)); } private void handleSuccess(List<DriveFileInfo> files) { bus.post(new DriveFileList(files)); } }