/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.samples.apps.iosched.sync.userdata.gms; import android.util.Log; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.drive.*; import com.google.api.client.util.Charsets; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashSet; import java.util.Set; import static com.google.samples.apps.iosched.sync.userdata.util.UserDataHelper.*; import static com.google.samples.apps.iosched.util.LogUtils.LOGD; import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag; public class DriveHelper { private static final String TAG = makeLogTag(DriveHelper.class); // Constants related to the JSON serialization: private static final String MIMETYPE_JSON = "application/json"; public static final String DRIVE_FILENAME = "starred_sessions.json"; static void saveToDrive(DriveFile file, Set<String> contents, GoogleApiClient apiClient) throws IOException { DriveApi.ContentsResult contentsResult = file.openContents(apiClient, DriveFile.MODE_WRITE_ONLY, null).await(); checkStatus("Open file for writing", contentsResult.getStatus()); FileOutputStream os = new FileOutputStream(contentsResult.getContents() .getParcelFileDescriptor().getFileDescriptor()); byte[] serializedContents = toByteArray(contents); Log.d(TAG, "Saving contents to drive file: "+new String(serializedContents)); os.write(serializedContents); com.google.android.gms.common.api.Status status = file.commitAndCloseContents(apiClient, contentsResult.getContents()).await(); checkStatus("Commit file contents", status); } static public DriveFile lookupDriveFile(DriveId driveId, GoogleApiClient apiClient) { DriveFile result = null; // First, check if ID is valid if (driveId != null) { Log.d(TAG, "DriveID passed is not null, trying to get the corresponding file"); try { result = Drive.DriveApi.getFile(apiClient, driveId); if (result != null) { // check if metadata is ok. For example, if the file has been directly removed from // the server, the getFile can return a file that is actually not valid. Hopefully // the metadata will get the correct info try { DriveResource.MetadataResult metadataResult = result.getMetadata(apiClient).await(); if (!metadataResult.getStatus().isSuccess()) { result = null; } } catch (Exception ex) { result = null; } } } catch (Exception e) { Log.d(TAG, "Saved drive ID "+driveId+" seems to be invalid (message: " + e.getMessage()+"). Ignoring it"); result = null; } } if (result == null) { // search for a file with the expected name (and get the most recent one, if many) Log.d(TAG, "DriveID passed is null, looking up for a file named "+DRIVE_FILENAME); Metadata metaOfMostRecent = null; MetadataBuffer buffer = Drive.DriveApi.getAppFolder(apiClient) .listChildren(apiClient).await().getMetadataBuffer(); Log.d(TAG, "Found "+buffer.getCount()+" files"); for (Metadata metadata: buffer) { if (metaOfMostRecent != null) { Log.w(TAG, "Warning, found more than one file named "+DRIVE_FILENAME+ " in AppData folder. Using the most recently modified."); } if (metaOfMostRecent == null || metaOfMostRecent .getModifiedDate().compareTo(metadata.getModifiedDate())<0) { metaOfMostRecent = metadata; } } if (metaOfMostRecent != null) { driveId = metaOfMostRecent.getDriveId(); result = Drive.DriveApi.getFile(apiClient, driveId); } buffer.close(); } return result; } static void createNewDriveFile(Set<String> contents, GoogleApiClient apiClient) throws IOException { DriveApi.ContentsResult contentsResult = Drive.DriveApi.newContents(apiClient).await(); checkStatus("creating new file", contentsResult.getStatus()); // query Drive for an AppFolder reference (might be slow: ~4s in my tests) DriveFolder appDataFolder = Drive.DriveApi.getAppFolder(apiClient); // create a new file in AppFolder MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder() .setMimeType(MIMETYPE_JSON) .setTitle(DRIVE_FILENAME) .build(); Contents contentsObj = contentsResult.getContents(); FileOutputStream os = new FileOutputStream(contentsObj.getParcelFileDescriptor().getFileDescriptor()); os.write(toByteArray(contents)); DriveFolder.DriveFileResult fileResult = appDataFolder.createFile( apiClient, metadataChangeSet, contentsResult.getContents()).await(); Log.d(TAG, "Content saved to new Drive file: "+new String(toByteArray(contents), Charsets.UTF_8)); checkStatus("saving contents to new file", fileResult.getStatus()); // DON'T DO THIS: It seems that a bug makes this driveID being unusable later: // params.setDriveId(fileResult.getDriveFile().getDriveId()); } static public void checkStatus(String message, com.google.android.gms.common.api.Status status) { if (!status.isSuccess()) { throw new RuntimeException("Error "+status.getStatusCode()+" on "+message); } } static public Set<String> loadFromCloud(DriveFile file, GoogleApiClient apiClient) throws IOException { DriveApi.ContentsResult contentsResult = file.openContents(apiClient, DriveFile.MODE_READ_ONLY, null).await(); checkStatus("Open file for reading", contentsResult.getStatus()); HashSet<String> result = new HashSet<String>(); try { FileInputStream is = new FileInputStream(contentsResult.getContents() .getParcelFileDescriptor().getFileDescriptor()); String contents = fromStreamToString(is); file.discardContents(apiClient, contentsResult.getContents()); LOGD(TAG, "Contents in the cloud file: [" + contents + "]"); return fromString(contents); } catch (Exception ex) { Log.w(TAG, "Ignoring invalid remote content.", ex); return null; } } }