├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── appsScript └── execute │ └── app │ └── src │ └── main │ └── java │ └── com │ └── google │ └── appScript │ └── execute │ └── Main.java ├── drive ├── conflict │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── android │ │ │ │ └── gms │ │ │ │ └── drive │ │ │ │ └── sample │ │ │ │ └── conflict │ │ │ │ ├── BaseDemoActivity.java │ │ │ │ ├── ConflictResolver.java │ │ │ │ ├── ConflictUtil.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── MyDriveEventService.java │ │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── menu │ │ │ └── main.xml │ │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ │ └── values │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── images │ │ ├── conflict_first_item.png │ │ ├── conflict_home.png │ │ └── conflict_resolved.png │ └── settings.gradle ├── demos │ ├── .gitignore │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── README.md │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-project.txt │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── android │ │ │ │ └── gms │ │ │ │ └── drive │ │ │ │ └── sample │ │ │ │ └── demo │ │ │ │ ├── AppendContentsActivity.java │ │ │ │ ├── BaseDemoActivity.java │ │ │ │ ├── CreateEmptyFileActivity.java │ │ │ │ ├── CreateFileActivity.java │ │ │ │ ├── CreateFileInAppFolderActivity.java │ │ │ │ ├── CreateFileInFolderActivity.java │ │ │ │ ├── CreateFileWithCreatorActivity.java │ │ │ │ ├── CreateFolderActivity.java │ │ │ │ ├── CreateFolderInFolderActivity.java │ │ │ │ ├── DeleteCustomPropertyActivity.java │ │ │ │ ├── DeleteFileActivity.java │ │ │ │ ├── EditMetadataActivity.java │ │ │ │ ├── HomeActivity.java │ │ │ │ ├── InsertUpdateCustomPropertyActivity.java │ │ │ │ ├── PinFileActivity.java │ │ │ │ ├── QueryFilesActivity.java │ │ │ │ ├── QueryFilesInFolderActivity.java │ │ │ │ ├── QueryFilesSharedWithMeActivity.java │ │ │ │ ├── QueryFilesWithCustomPropertyActivity.java │ │ │ │ ├── QueryFilesWithTitleActivity.java │ │ │ │ ├── QueryNonTextFilesActivity.java │ │ │ │ ├── QuerySortedFilesActivity.java │ │ │ │ ├── QueryStarredTextFilesActivity.java │ │ │ │ ├── QueryTextOrHtmlFilesActivity.java │ │ │ │ ├── ResultsAdapter.java │ │ │ │ ├── RetrieveContentsActivity.java │ │ │ │ ├── RetrieveContentsWithProgressDialogActivity.java │ │ │ │ ├── RetrieveMetadataActivity.java │ │ │ │ ├── RewriteContentsActivity.java │ │ │ │ └── events │ │ │ │ ├── ListenChangeEventsForFilesActivity.java │ │ │ │ ├── MyDriveEventService.java │ │ │ │ └── SubscribeChangeEventsForFilesActivity.java │ │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── layout │ │ │ ├── activity_changeevents.xml │ │ │ ├── activity_contents.xml │ │ │ ├── activity_home.xml │ │ │ ├── activity_listfiles.xml │ │ │ └── activity_progress.xml │ │ │ ├── menu │ │ │ └── activity_home.xml │ │ │ └── values │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── titles.xml │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── images │ │ └── demos_home.png │ └── settings.gradle ├── deprecation │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── android │ │ │ │ └── gms │ │ │ │ └── drive │ │ │ │ └── sample │ │ │ │ └── driveapimigration │ │ │ │ ├── DriveServiceHelper.java │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── quickstart │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-project.txt │ │ ├── settings.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── android │ │ │ │ └── gms │ │ │ │ └── drive │ │ │ │ └── sample │ │ │ │ └── quickstart │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── ic_launcher-web.png └── trash │ ├── .gitignore │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── README.md │ ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── google │ │ │ └── samples │ │ │ └── drive │ │ │ └── trash │ │ │ ├── FileFolderAdapter.java │ │ │ └── MainActivity.java │ │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_description_black_18dp.png │ │ └── ic_folder_black_18dp.png │ │ ├── drawable-mdpi │ │ ├── ic_description_black_18dp.png │ │ └── ic_folder_black_18dp.png │ │ ├── drawable-xhdpi │ │ ├── ic_description_black_18dp.png │ │ └── ic_folder_black_18dp.png │ │ ├── drawable-xxhdpi │ │ ├── ic_description_black_18dp.png │ │ └── ic_folder_black_18dp.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_description_black_18dp.png │ │ └── ic_folder_black_18dp.png │ │ ├── layout │ │ ├── activity_main.xml │ │ └── resource_item.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── images │ └── trash_folder.png │ └── settings.gradle └── renovate.json /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | ## Actual Behavior 4 | 5 | 6 | ## Steps to Reproduce the Problem 7 | 8 | 1. 9 | 1. 10 | 1. 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | # Uncomment the following line if you do not want to check your keystore files in. 45 | #*.jks 46 | 47 | # External native build folder generated in Android Studio 2.2 and later 48 | .externalNativeBuild 49 | 50 | # Misc 51 | .DS_Store 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an 13 | [individual CLA](https://developers.google.com/open-source/cla/individual). 14 | * If you work for a company that wants to allow you to contribute your work, 15 | then you'll need to sign a 16 | [corporate CLA](https://developers.google.com/open-source/cla/corporate). 17 | 18 | Follow either of the two links above to access the appropriate CLA and 19 | instructions for how to sign and return it. Once we receive it, we'll be able to 20 | accept your pull requests. 21 | 22 | ## Contributing A Patch 23 | 24 | 1. Submit an issue describing your proposed change to the repo in question. 25 | 1. The repo owner will respond to your issue promptly. 26 | 1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above). 27 | 1. Fork the desired repo, develop and test your code changes. 28 | 1. Ensure that your code adheres to the existing style in the sample to which you are contributing. 29 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 30 | 1. Submit a pull request! 31 | 32 | ## Style 33 | 34 | Samples in this repository follow the [JavaScript Semi-Standard 35 | Style](https://github.com/Flet/semistandard). 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Workspace Android Samples [![Build Status](https://travis-ci.org/googleworkspace/android-samples.svg?branch=master)](https://travis-ci.org/googleworkspace/android-samples) 2 | 3 | A collection of samples that demonstrate how to call Google Workspace APIs from Android. 4 | 5 | ## Note: Deprecated/archived 6 | 7 | This repository is no longer actively maintained and the sample code contained here is likely outdated. 8 | 9 | ## Products 10 | 11 | ### Drive 12 | 13 | #### [Deprecation](drive/deprecation) 14 | 15 | A sample app demonstrating migrating from the Android API to the REST API. See 16 | the [migration guide](https://developers.google.com/drive/android/deprecation) 17 | for details. 18 | 19 | ## Contributing 20 | 21 | Contributions welcome! See the [Contributing Guide](CONTRIBUTING.md). 22 | -------------------------------------------------------------------------------- /appsScript/execute/app/src/main/java/com/google/appScript/execute/Main.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | // [START apps_script_api_execute] 15 | /** 16 | * Call the API to run an Apps Script function that returns a list 17 | * of folders within the user's root directory on Drive. 18 | * 19 | * @return list of String folder names and their IDs 20 | * @throws IOException 21 | */ 22 | private List getDataFromApi() 23 | throws IOException, GoogleAuthException { 24 | // ID of the script to call. Acquire this from the Apps Script editor, 25 | // under Publish > Deploy as API executable. 26 | String scriptId = "ENTER_YOUR_SCRIPT_ID_HERE"; 27 | 28 | List folderList = new ArrayList(); 29 | 30 | // Create an execution request object. 31 | ExecutionRequest request = new ExecutionRequest() 32 | .setFunction("getFoldersUnderRoot"); 33 | 34 | // Make the request. 35 | Operation op = 36 | mService.scripts().run(scriptId, request).execute(); 37 | 38 | // Print results of request. 39 | if (op.getError() != null) { 40 | throw new IOException(getScriptError(op)); 41 | } 42 | if (op.getResponse() != null && 43 | op.getResponse().get("result") != null) { 44 | // The result provided by the API needs to be cast into 45 | // the correct type, based upon what types the Apps Script 46 | // function returns. Here, the function returns an Apps 47 | // Script Object with String keys and values, so must be 48 | // cast into a Java Map (folderSet). 49 | Map folderSet = 50 | (Map)(op.getResponse().get("result")); 51 | 52 | for (String id: folderSet.keySet()) { 53 | folderList.add( 54 | String.format("%s (%s)", folderSet.get(id), id)); 55 | } 56 | } 57 | 58 | return folderList; 59 | } 60 | 61 | /** 62 | * Interpret an error response returned by the API and return a String 63 | * summary. 64 | * 65 | * @param op the Operation returning an error response 66 | * @return summary of error response, or null if Operation returned no 67 | * error 68 | */ 69 | private String getScriptError(Operation op) { 70 | if (op.getError() == null) { 71 | return null; 72 | } 73 | 74 | // Extract the first (and only) set of error details and cast as a Map. 75 | // The values of this map are the script's 'errorMessage' and 76 | // 'errorType', and an array of stack trace elements (which also need to 77 | // be cast as Maps). 78 | Map detail = op.getError().getDetails().get(0); 79 | List> stacktrace = 80 | (List>)detail.get("scriptStackTraceElements"); 81 | 82 | java.lang.StringBuilder sb = 83 | new StringBuilder("\nScript error message: "); 84 | sb.append(detail.get("errorMessage")); 85 | 86 | if (stacktrace != null) { 87 | // There may not be a stacktrace if the script didn't start 88 | // executing. 89 | sb.append("\nScript error stacktrace:"); 90 | for (Map elem : stacktrace) { 91 | sb.append("\n "); 92 | sb.append(elem.get("function")); 93 | sb.append(":"); 94 | sb.append(elem.get("lineNumber")); 95 | } 96 | } 97 | sb.append("\n"); 98 | return sb.toString(); 99 | } 100 | // [END apps_script_api_execute] 101 | -------------------------------------------------------------------------------- /drive/conflict/.gitignore: -------------------------------------------------------------------------------- 1 | # Android Studio excludes 2 | .idea/ 3 | *.iml 4 | 5 | # Gradle build excludes 6 | .gradle/ 7 | build/ 8 | local.properties 9 | 10 | # Misc 11 | .DS_Store -------------------------------------------------------------------------------- /drive/conflict/README.md: -------------------------------------------------------------------------------- 1 | # [DEPRECATED] Google Drive Android API Conflict Sample 2 | 3 | The Drive Android API used in this sample is now deprecated. Please see the 4 | [migration guide](https://developers.google.com/drive/android/deprecation) 5 | for more information. 6 | 7 | --- 8 | 9 | This application demonstrates how to resolve remote [conflicts](https://developers.google.com/drive/android/completion#conflict) using 10 | the [Google Android Drive API](https://developers.google.com/drive/android/intro). Keep in mind the default behavior is to 11 | overwrite the server content with the local content when a conflict arises. Please read the 12 | [conflict strategy documentation](https://developers.google.com/android/reference/com/google/android/gms/drive/ExecutionOptions.Builder#setConflictStrategy(int)) when 13 | choosing to override this capability. This application uses the [CONFLICT_STRATEGY_KEEP_REMOTE](https://developers.google.com/android/reference/com/google/android/gms/drive/ExecutionOptions#CONFLICT_STRATEGY_KEEP_REMOTE) 14 | option to allow the remote app (not the server) to manage the conflict. 15 | 16 | ## Set Up 17 | 1. Install the [Android SDK](https://developer.android.com/sdk/index.html). 18 | 1. Download and configure the 19 | [Google Play services SDK](https://developer.android.com/google/play-services/setup.html), 20 | which includes the Google Drive Android API. 21 | 1. Create [Google API Console](https://console.developers.google.com/projectselector/apis/dashboard) 22 | project and/or enable the Drive API for an existing project. 23 | 1. Register an OAuth 2.0 client for the package 'com.google.android.gms.drive.sample.conflict' 24 | with your own [debug keys](https://developers.google.com/drive/android/auth). 25 | See full instructions in the [Getting Started guide](https://developers.google.com/drive/android/get-started). 26 | 1. Build and install the app on 2 devices or emulators. If using Android Studio, install the app 27 | individually on each emulator then use the AVD Manager to run both emulators simultaneously. Be 28 | sure to select Android images that have the Play Store enabled. 29 | 30 | ## Run the Demo 31 | Launch the application on both devices. Both apps should like this with no items listed. 32 | ![Home Screen](images/conflict_home.png) 33 | 34 | Add an item to the first device's list and hit 'Update List'. 35 | ![Item Screen](images/conflict_first_item.png) 36 | 37 | The second device should then show the item. Now add items on the second device's list. 38 | After updating the list, both devices should show the merge lists. 39 | ![Resolved Screen](images/conflict_resolved.png) 40 | -------------------------------------------------------------------------------- /drive/conflict/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /drive/conflict/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.google.android.gms.drive.sample.conflict" 9 | minSdkVersion 21 10 | targetSdkVersion 26 11 | versionCode 2 12 | versionName "1.1" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | } 25 | 26 | dependencies { 27 | compile 'com.android.support:support-v4:26.1.0' 28 | compile 'com.google.android.gms:play-services-auth:11.6.0' 29 | compile 'com.google.android.gms:play-services-drive:11.6.0' 30 | } 31 | -------------------------------------------------------------------------------- /drive/conflict/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/android-studio/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /drive/conflict/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /drive/conflict/app/src/main/java/com/google/android/gms/drive/sample/conflict/ConflictUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.google.android.gms.drive.sample.conflict; 15 | 16 | import java.io.BufferedReader; 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.io.InputStreamReader; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | 25 | class ConflictUtil { 26 | /** 27 | * Performs a three-way merge of three sets of "grocery items" into one set. 28 | * 29 | * @param baseStr Items local modifications are based on. 30 | * @param currentStr Items currently on the server. 31 | * @param modifiedStr Locally modified items. 32 | * @return Items merged from three sets of items provided. 33 | */ 34 | static String resolveConflict(String baseStr, String currentStr, String modifiedStr) { 35 | List baseItems = Arrays.asList(baseStr.split("\n")); 36 | List currentItems = Arrays.asList(currentStr.split("\n")); 37 | List modifiedItems = Arrays.asList(modifiedStr.split("\n")); 38 | List allItems = new ArrayList<>(); 39 | 40 | // Add unique items to allItems. 41 | allItems.addAll(baseItems); 42 | for (String item : currentItems) { 43 | if (!allItems.contains(item)) { 44 | allItems.add(item); 45 | } 46 | } 47 | for (String item : modifiedItems) { 48 | if (!allItems.contains(item)) { 49 | allItems.add(item); 50 | } 51 | } 52 | 53 | // Remove items that were removed from currentItems or modifiedItems. 54 | for (Iterator iter = allItems.iterator(); iter.hasNext();) { 55 | String item = iter.next(); 56 | if (baseItems.contains(item) 57 | && (!currentItems.contains(item) || !modifiedItems.contains(item))) { 58 | iter.remove(); 59 | } 60 | } 61 | StringBuilder stringBuilder = new StringBuilder(); 62 | for (String item : allItems) { 63 | stringBuilder.append(item); 64 | stringBuilder.append("\n"); 65 | } 66 | return stringBuilder.toString(); 67 | } 68 | 69 | /** 70 | * Gets String from InputStream. 71 | * 72 | * @param is InputStream used to read into String. 73 | * @return String resulting from reading is. 74 | */ 75 | static String getStringFromInputStream(InputStream is) { 76 | StringBuilder sb = new StringBuilder(); 77 | String line; 78 | try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { 79 | while ((line = br.readLine()) != null) { 80 | sb.append(line).append("\n"); 81 | } 82 | } catch (IOException e) { 83 | throw new RuntimeException("Unable to read string content."); 84 | } 85 | return sb.toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /drive/conflict/app/src/main/java/com/google/android/gms/drive/sample/conflict/MyDriveEventService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.google.android.gms.drive.sample.conflict; 15 | 16 | import android.util.Log; 17 | 18 | import com.google.android.gms.drive.events.CompletionEvent; 19 | import com.google.android.gms.drive.events.DriveEventService; 20 | 21 | import java.util.concurrent.ExecutorService; 22 | import java.util.concurrent.Executors; 23 | 24 | // [START drive_android_on_completion] 25 | public class MyDriveEventService extends DriveEventService { 26 | private static final String TAG = "MyDriveEventService"; 27 | private ExecutorService mExecutorService; 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | // [START_EXCLUDE] 33 | mExecutorService = Executors.newSingleThreadExecutor(); 34 | // [END_EXCLUDE] 35 | } 36 | 37 | @Override 38 | public synchronized void onDestroy() { 39 | super.onDestroy(); 40 | // [START_EXCLUDE] 41 | mExecutorService.shutdown(); 42 | // [END_EXCLUDE] 43 | } 44 | 45 | @Override 46 | public void onCompletion(CompletionEvent event) { 47 | boolean eventHandled = false; 48 | switch (event.getStatus()) { 49 | case CompletionEvent.STATUS_SUCCESS: 50 | // Commit completed successfully. 51 | // Can now access the remote resource Id 52 | // [START_EXCLUDE] 53 | String resourceId = event.getDriveId().getResourceId(); 54 | Log.d(TAG, "Remote resource ID: " + resourceId); 55 | eventHandled = true; 56 | // [END_EXCLUDE] 57 | break; 58 | case CompletionEvent.STATUS_FAILURE: 59 | // Handle failure.... 60 | // Modified contents and metadata failed to be applied to the server. 61 | // They can be retrieved from the CompletionEvent to try to be applied later. 62 | // [START_EXCLUDE] 63 | // CompletionEvent is only dismissed here. In a real world application failure 64 | // should be handled before the event is dismissed. 65 | eventHandled = true; 66 | // [END_EXCLUDE] 67 | break; 68 | case CompletionEvent.STATUS_CONFLICT: 69 | // Handle completion conflict. 70 | // [START_EXCLUDE] 71 | ConflictResolver conflictResolver = 72 | new ConflictResolver(event, this, mExecutorService); 73 | conflictResolver.resolve(); 74 | eventHandled = false; // Resolver will snooze or dismiss 75 | // [END_EXCLUDE] 76 | break; 77 | } 78 | 79 | if (eventHandled) { 80 | event.dismiss(); 81 | } 82 | } 83 | } 84 | // [END drive_android_on_completion] -------------------------------------------------------------------------------- /drive/conflict/app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleworkspace/android-samples/b524adf78f72563bc19ab569008db736a86ec688/drive/conflict/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /drive/conflict/app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleworkspace/android-samples/b524adf78f72563bc19ab569008db736a86ec688/drive/conflict/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /drive/conflict/app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleworkspace/android-samples/b524adf78f72563bc19ab569008db736a86ec688/drive/conflict/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /drive/conflict/app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleworkspace/android-samples/b524adf78f72563bc19ab569008db736a86ec688/drive/conflict/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /drive/conflict/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | 31 | 37 | 45 |