/** * Helper just to avoid copy&paste'ing code. * * <p>Note: since this contains blocking Drive API calls, this will crash if you try to call it * from the main thread, it can only be called from other threads. * * @return null if the folder can not be found. */ private DriveFolder checkOrCreateRemoteFolder( GoogleApiClient googleApiClient, DriveFolder rootFolder, String folderName) { // check for STORAGE permission if (!canWriteToStorage()) return null; Query query = new Query.Builder() .addFilter( Filters.and( Filters.eq(SearchableField.TITLE, folderName), Filters.contains(SearchableField.MIME_TYPE, "folder"))) .build(); DriveApi.MetadataBufferResult result = rootFolder.queryChildren(mGoogleApiClient, query).await(); if (!result.getStatus().isSuccess()) { Log.e( mActivity.getResources().getString(R.string.app_name), "Cannot query folders in the root of Google Drive."); return null; } else { for (Metadata m : result.getMetadataBuffer()) { if (m.getTitle().equals(folderName)) { // Folder exists - we found it! DriveFolder folder = m.getDriveId().asDriveFolder(); result.getMetadataBuffer().release(); return folder; } } } result.getMetadataBuffer().release(); // Folder not found; let's create it. MetadataChangeSet changeSet = new MetadataChangeSet.Builder().setTitle(folderName).build(); DriveFolder.DriveFolderResult result1 = rootFolder.createFolder(googleApiClient, changeSet).await(); if (!result1.getStatus().isSuccess()) { Log.e( mActivity.getResources().getString(R.string.app_name), "Error while trying to create the folder \"" + folderName + "\""); return null; } return result1.getDriveFolder(); }
private void syncPhotosForTeam(GoogleApiClient googleApiClient, int teamNumber) { if (mTeamNumber >= 0) { Log.i( mActivity.getResources().getString(R.string.app_name), "Beginning photo sync for team " + teamNumber); // get the list of local files File photosDir = new File(mLocalTeamPhotosFilePath + "/" + teamNumber); /** **** get the list of local files ***** */ // check if that folder exists if (!photosDir.isDirectory()) { // we have no photos for this team if (mRequester != null) mRequester.updatePhotos(new Bitmap[0]); googleApiClient.disconnect(); return; } File[] listOfFiles = photosDir.listFiles(); ArrayList<String> arrLocalFiles = new ArrayList<String>(); for (File file : listOfFiles) { // make sure it's an image file Bitmap bitmap = BitmapFactory.decodeFile(file.getPath()); if (bitmap != null) { arrLocalFiles.add(file.getPath()); } // else: if it's not a file, then what is it???? .... skip I guess } // Navigate to the correct folder - there has to be a more efficient way to do this DriveFolder rootFolder = Drive.DriveApi.getFolder(googleApiClient, mDriveIdTeamPhotosFolder); Log.i( mActivity.getResources().getString(R.string.app_name), "Local Files: " + arrLocalFiles); Query query = new Query.Builder() .addFilter(Filters.eq(SearchableField.TITLE, "" + teamNumber)) .build(); DriveApi.MetadataBufferResult result = rootFolder.queryChildren(googleApiClient, query).await(); DriveFolder teamPhotosFolder = null; for (Metadata m : result.getMetadataBuffer()) { teamPhotosFolder = m.getDriveId().asDriveFolder(); } result.getMetadataBuffer().close(); ArrayList<PathAndDriveId> arrRemoteFiles = new ArrayList<PathAndDriveId>(); if (teamPhotosFolder != null) { query = new Query.Builder() .addFilter( Filters.eq(SearchableField.MIME_TYPE, "application/vnd.google-apps.photo")) .build(); result = teamPhotosFolder.queryChildren(googleApiClient, query).await(); for (Metadata m : result.getMetadataBuffer()) { arrRemoteFiles.add( new PathAndDriveId( mRemoteToplevelFolderName + "/" + mRemoteTeamFolderName + "/" + mRemoteTeamPhotosFolderName + "/" + mTeamNumber + "/" + m.getTitle(), m.getDriveId(), m.getTitle())); } result.getMetadataBuffer().close(); } Log.i( mActivity.getResources().getString(R.string.app_name), "Remote Files: " + arrRemoteFiles); // Remove files that are in both lists - these don't need to be synced. for (PathAndDriveId remoteFile : arrRemoteFiles) { String remotePath = remoteFile.path; if (arrLocalFiles.contains(remotePath)) { arrRemoteFiles.remove(remoteFile); arrLocalFiles.remove(remotePath); } } /** **** Download any Files we're missing locally ***** */ for (PathAndDriveId remoteFile : arrRemoteFiles) { // the Drive API actually makes a local cache of the file, so let's copy the contents into // our app's file structure DriveApi.DriveContentsResult fileResult = remoteFile .driveId .asDriveFile() .open(googleApiClient, DriveFile.MODE_READ_ONLY, null) .await(); if (!fileResult.getStatus().isSuccess()) { // file can't be opened continue; } String localFileToCreate = mLocalTeamPhotosFilePath + "/" + teamNumber + "/" + remoteFile.title; DriveContents contents = null; InputStream in = null; FileOutputStream fout = null; try { // DriveContents object contains pointers to the actual byte stream, which we will // manually copy contents = fileResult.getDriveContents(); in = contents.getInputStream(); fout = new FileOutputStream(localFileToCreate); // read bytes from source file and write to destination file byte[] b = new byte[1024]; int noOfBytes = 0; while ((noOfBytes = in.read(b)) != -1) fout.write(b, 0, noOfBytes); in.close(); fout.close(); contents.discard(googleApiClient); } catch (FileNotFoundException e) { // something went wrong, delete the file we were trying to create (new File(localFileToCreate)).delete(); } catch (IOException e) { // something went wrong, delete the file we were trying to create (new File(localFileToCreate)).delete(); } } /** **** Upload any files that are missing remotely ***** */ for (String localFile : arrLocalFiles) { // if the remote folder does not exist, create it. This should only need to be called // once. if (teamPhotosFolder == null) { MetadataChangeSet changeSet = new MetadataChangeSet.Builder().setTitle("" + teamNumber).build(); DriveFolder.DriveFolderResult folderResult = rootFolder.createFolder(googleApiClient, changeSet).await(); teamPhotosFolder = folderResult.getDriveFolder(); } DriveContents contents = null; try { FileInputStream in = new FileInputStream(localFile); // create a new file in Drive. This works a little different than normal file IO it that // you create the file first, // then tell it at the end which folder it's part of. Think of "folders" in drive more // like "tags" or "labels". DriveApi.DriveContentsResult contentResult = Drive.DriveApi.newDriveContents(googleApiClient).await(); contents = contentResult.getDriveContents(); OutputStream out = contents.getOutputStream(); // read bytes from source file and write to destination file byte[] b = new byte[1024]; int noOfBytes = 0; while ((noOfBytes = in.read(b)) != -1) out.write(b, 0, noOfBytes); in.close(); out.close(); } catch (FileNotFoundException e) { // something went wrong, discard the changes to the Drive file if (contents != null) contents.discard(googleApiClient); } catch (IOException e) { // something went wrong, discard the changes to the Drive file if (contents != null) contents.discard(googleApiClient); } if (contents != null) { String[] splitFilename = localFile.split("/"); String filename = splitFilename[splitFilename.length - 1]; MetadataChangeSet changeSet = new MetadataChangeSet.Builder() .setTitle(filename) .setMimeType(URLConnection.guessContentTypeFromName(filename)) .build(); teamPhotosFolder.createFile(googleApiClient, changeSet, contents).await(); } } Log.i( mActivity.getResources().getString(R.string.app_name), "Finished photo sync for team " + teamNumber); googleApiClient.disconnect(); // hand the photos back to the PhotoRequester if (mRequester != null) { // first, get the list of image files listOfFiles = photosDir.listFiles(); ArrayList<Bitmap> arrLocalBitmaps = new ArrayList<Bitmap>(); for (File file : listOfFiles) { // make sure it's an image file Bitmap bitmap = BitmapFactory.decodeFile(file.getPath()); if (bitmap != null) { arrLocalBitmaps.add(bitmap); } // else: if it's not a file, then what is it???? .... skip I guess } Bitmap[] bitmaps = new Bitmap[arrLocalBitmaps.size()]; // TODO: commented out because it crashes the app with the error: // "Only the original thread that created a view hierarchy can touch its views." // This will need a re-jigging - maybe a loop in the PhotoRequester that checks for // updates every // few seconds and re-draws if there has been one. // mRequester.updatePhotos(arrLocalBitmaps.toArray(bitmaps)); } } }