OMH Storage is an Android client library that makes it easy to integrate storage on both Google Mobile Services (GMS) and non-GMS devices. It eliminates the need for separate codebases for different Android builds.
With the OMH Storage Client Library, you can easily add Google Drive and other third-party storage to your applications, regardless of whether the device has GMS or not. The library takes care of the technical details, providing a unified interface and components for a consistent storage experience.
For instance, the following screenshots showcase multiple devices with Android, both with GMS and Non-GMS. The same app works without changing a single line of code, supporting multiple storage provider implementations.
This section describes how to setup an Android Studio project to use the OMH Storage SDK for Android. For greater ease, a base code will be used within the repository.
Note: To quickly run a full-featured app with all OMH Storage functionality, refer to the Sample App section and follow the provided steps.
- Android Studio is required. If you haven't already done so, download and install it.
- Ensure that you are using the Android Gradle plugin version 7.0 or later in Android Studio.
To clone the repository and checkout the code-starter branch, use the following command in your Terminal:
git clone --branch code-starter https://github.com/openmobilehub/omh-storage.git omh-storage-starter-code
To access Google APIs, generate a unique client_id for your app in the Google API Console, for additional information see. Add the client_id to your app's code and complete the required Cloud Console setup steps:
-
Go to the Google Cloud Console and open the project selector page.
-
Click on "Create Project" to start creating a new Cloud project.
-
On the Credentials page, click on "Create credentials" and choose "OAuth Client ID".
-
In the "Application Type" option, select "Android".
-
Set your application package name.
Note: If you are running the sample app or following the steps in the
code-starterbranch, usecom.omh.android.storage.sample. -
Update the Android Client ID's debug/release SHA-1 certificate fingerprint by running the following command in your terminal:
keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystoreNote: Use
androidas the default password. For instructions on other operating systems, refer to these guidelines. -
In the OAuth consent screen add the test users that you will be using for QA and development. Without this step you won't be able to access the application while it's in testing mode.
-
Go to the API library section on the project you created
-
Once there, make sure enable Google Drive Api
-
You're all set!
You should not check your Client ID into your version control system, so it is recommended storing it in the local.properties file, which is located in the root directory of your project.
For more information about the local.properties file, see Gradle properties and files.
-
Open the
local.propertiesin your project level directory, and then add the following line:CLIENT_ID = "YOUR_CLIENT_ID"Note: Replace
YOUR_CLIENT_IDwith your API key client id. -
Save the file and sync your project with Gradle.
To incorporate OMH Storage into your project, you have two options: utilize the OMH Core Plugin or directly include the OMH Client libraries dependencies. This plugin simplifies the addition of Gradle dependencies, allowing you to effortlessly manage and include the necessary dependencies for seamless integration.
The subsequent instructions will outline the necessary steps for including the OMH Core Plugin as a Gradle dependency.
-
In your
storage-starter-samplemodule-levelbuild.gradleunder thepluginselement add the plugin id. If you're configuring this step by step on thecode-starterbranch, look for the comment// Paste below this line the snippet of core pluginand paste there the plugin id as shown on the next snippet:plugins { ... id("com.openmobilehub.android.omh-core") } -
Save the file and sync Project with Gradle Files.
In your storage-starter-sample module-level build.gradle.kts search the comment // replace this block with README omhConfig snippet and replace the block with this snippet:
omhConfig {
bundle("singleBuild") {
storage() {
gmsService {
dependency = "com.openmobilehub.android:storage-api-drive-gms:1.0.7-beta"
}
nonGmsService {
dependency = "com.openmobilehub.android:storage-api-drive-nongms:1.0.8-beta"
}
}
auth {
gmsService {
dependency = "com.openmobilehub.android:auth-api-gms:1.0.1-beta"
}
nonGmsService {
dependency = "com.openmobilehub.android:auth-api-non-gms:1.0.1-beta"
}
}
}
bundle("gms") {
storage {
gmsService {
dependency = "com.openmobilehub.android:storage-api-drive-gms:1.0.7-beta"
}
}
auth {
gmsService {
dependency = "com.openmobilehub.android:auth-api-gms:1.0.1-beta"
}
}
}
bundle("nongms") {
storage {
nonGmsService {
dependency = "com.openmobilehub.android:storage-api-drive-nongms:1.0.8-beta"
}
}
auth {
nonGmsService {
dependency = "com.openmobilehub.android:auth-api-non-gms:1.0.1-beta"
}
}
}
}
NOTE: This section covers concepts about the core plugin
In your "storage-starter-sample" module-level build.gradle file is required to configure the omhConfig. The omhConfig definition is used to extend the existing Android Studio variants in the core plugin. For more details about omhConfig see OMH Core.
In this step, you will define the OMH Core Plugin bundles to generate multiple build variants with specific suffixes as their names. For example, if your project has release and debug variants with singleBuild, gms, and nonGms OMH bundles, the following build variants will be generated:
releaseSingleBuild,releaseGms, andreleaseNonGmsdebugSingleBuild,debugGms, anddebugNonGms
- Define the `Service`. In this example is storage.
- Define the `ServiceDetails`. In this example are `gmsService` and `nonGmsService`.
- Define the dependency and the path. In this example
are `com.openmobilehub.android:storage-api-drive-gms:1.0-rc"`
and `com.openmobilehub.android:storage-api-drive-nongms:1.0-rc`.
Note: It's important to observe how a single build encompasses both GMS (Google MobileServices) and Non-GMS configurations.
- Define the `Service`. In this example is storage.
- Define the `ServiceDetails` . In this example is `gmsService`.
- Define the dependency and the path. In this example
is `com.openmobilehub.android:storage-api-drive-gms:1.0-rc"`.
Note: gms build covers only GMS (Google Mobile Services).
- Define the `Service`. In this example is storage.
- Define the `ServiceDetails` . In this example is `nonGmsService`.
- Define the dependency and the path. In this example
is `com.openmobilehub.android:storage-api-drive-non-gms:1.0-rc`.
Note: nongms build covers only Non-GMS configurations.
-
Save and sync Project with Gradle Files.
-
Rebuild the project to ensure the availability of
BuildConfig.AUTH_GMS_PATH,BuildConfig.AUTH_NON_GMS_PATH,BuildConfig.STORAGE_GMS_PATHandBuildConfig.STORAGE_NON_GMS_PATHvariables. -
Now you can select a build variant. To change the build variant Android Studio uses, do one of the following:
- Select "Build" > "Select Build Variant..." in the menu.
- Select "View" > "Tool Windows" > "Build Variants" in the menu.
- Click the "Build Variants" tab on the tool window bar.
-
You can select any of the 3 variants for the
:storage-starter-sample:- "singleBuild" variant builds for GMS (Google Mobile Services) and Non-GMS devices without changes to the code.(Recommended)
- "gms" variant builds for devices that has GMS (Google Mobile Services).
- "nongms" variant builds for devices that doesn't have GMS (Google Mobile Services).
-
Go to the
providesOmhAuthClientfunction in theSingletonModulefile. Look for the comment// Add here snippet for provide auth client, and replace the existing code below this comment with the following snippet:return OmhAuthProvider.Builder() .addNonGmsPath(BuildConfig.AUTH_NON_GMS_PATH) .addGmsPath(BuildConfig.AUTH_GMS_PATH) .build() .provideAuthClient( context = context, scopes = listOf( "openid", "email", "profile", "https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/drive.file" ), clientId = BuildConfig.CLIENT_ID )
Note: Make sure to use the full implementation of
providesOmhAuthClientfor a fully functional sample. -
Go to the
providesOmhStorageClientfunction in theSingletonMOdulefile. Look for the comment// Add here snippet for provide storage client, and replace the existing code below this comment with the following snippet:return OmhStorageProvider.Builder() .addGmsPath(BuildConfig.STORAGE_GMS_PATH) .addNonGmsPath(BuildConfig.STORAGE_NON_GMS_PATH) .build() .provideStorageClient(omhAuthClient, context)
Note: Make sure to use the full implementation of
providesOmhStorageClientfor a fully functional sample.
First and foremost, the main interface that you'll be interacting with is called OmhStorageClient. In contains all your basic storage functionalities: list, create, delete, download, update and upload files.
You can checkout the branch code-starter and copy/paste the following snippets in the right place or if you want to see the full implementation you can just see the main branch
For list files, just use the instance you created of the omhStorageClient and call method listFiles sending as parameter the desired parent id.
If you are configuring this step by step on the code-starter branch:
-
Go to the
refreshFileListEventfunction in theFileViewerViewModelfile. Look for the comment// Add here snippet for list files, and replace the existing code below this comment with the following snippet:val cancellable = omhStorageClient.listFiles(parentId) .addOnSuccess { result: GetFilesListUseCaseResult -> // Get the files list val filesList: List<OmhFile> = result.files // TODO - Developer: Manage success } .addOnFailure { exception: Exception -> // TODO - Developer: Manage error } .execute() cancellableCollector.addCancellable(cancellable)
Note: Make sure to use the full implementation of
refreshFileListEventfor a fully functional sample. -
Run the sample app to see the list of files.
For create files, just use the instance you created of the omhStorageClient and call method createFile sending as parameter the desired name, mime type and parent id.
If you are configuring this step by step on the code-starter branch:
-
Go to the
createFileEventfunction in theFileViewerViewModelfile. Look for the comment// Add here snippet for create files, and replace the existing code below this comment with the following snippet:val cancellable = omhStorageClient.createFile(name, mimeType, parentId) .addOnSuccess { result: CreateFileUseCaseResult -> // An instance of OmhFile with the information of the created file. In case the file was not created, will be null val file: OmhFile? = result.file // TODO - Developer: Manage success } .addOnFailure { exception: Exception -> // TODO - Developer: Manage error } .execute() cancellableCollector.addCancellable(cancellable)
Note: Make sure to use the full implementation of
createFileEventfor a fully functional sample. -
Run the sample app to create files.
For delete files, just use the instance you created of the omhStorageClient and call method deleteFile sending as parameter the id of the file you want to delete.
If you are configuring this step by step on the code-starter branch:
-
Go to the
deleteFileEventfunction in theFileViewerViewModelfile. Look for the comment// Add here snippet for delete files, and replace the existing code below this comment with the following snippet:val cancellable = omhStorageClient.deleteFile(fileId) .addOnSuccess { result: DeleteFileUseCaseResult -> // The success variable indicates if the file was deleted or not val success: Boolean = result.isSuccess // TODO - Developer: Manage success } .addOnFailure { exception: Exception -> // TODO - Developer: Manage error } .execute() cancellableCollector.addCancellable(cancellable)
Note: Make sure to use the full implementation of
deleteFileEventfor a fully functional sample. -
Run the sample app to delete files.
For upload files, just use the instance you created of the omhStorageClient and call method uploadFile sending as parameter the local path of the file you want to upload and the id of the remote folder where you want to place it (parent id).
If you are configuring this step by step on the code-starter branch:
-
Go to the
uploadFilefunction in theFileViewerViewModelfile. Look for the comment// Add here snippet for upload files, and replace the existing code below this comment with the following snippet:val cancellable = omhStorageClient.uploadFile(filePath, parentId) .addOnSuccess { result: UploadFileUseCaseResult -> // An instance of OmhFile with the information of the uploaded file. In case the file was not uploaded, will be null val file: OmhFile? = result.file // TODO - Developer: Manage success } .addOnFailure { exception: Exception -> // TODO - Developer: Manage error } .execute() cancellableCollector.addCancellable(cancellable)
Note: Make sure to use the full implementation of
uploadFilefor a fully functional sample. -
Run the sample app to upload files.
For update files, just use the instance you created of the omhStorageClient and call method updateFile sending as parameter the local path of the file you want to update and the id of the remote file you want to replace (file id).
If you are configuring this step by step on the code-starter branch:
-
Go to the
updateFileEventfunction in theFileViewerViewModelfile. Look for the comment// Add here snippet for update files, and replace the existing code below this comment with the following snippet:val cancellable = omhStorageClient.updateFile(filePath, fileId) .addOnSuccess { result: UpdateFileUseCaseResult -> // An instance of OmhFile with the information of the updated file. In case the file was not updated, will be null val file: OmhFile? = result.file // TODO - Developer: Manage success } .addOnFailure { exception: Exception -> // TODO - Developer: Manage error } .execute() cancellableCollector.addCancellable(cancellable)
Note: Make sure to use the full implementation of
updateFileEventfor a fully functional sample. -
Run the sample app to update files.
For download files, just use the instance you created of the omhStorageClient and call method createFile sending as parameter the id of the file you want to download and the mime type you desire to have locally (once downloaded)
If you are configuring this step by step on the code-starter branch:
-
Go to the
downloadFileEventfunction in theFileViewerViewModelfile. Look for the comment// Add here snippet for download files, and replace the existing code below this comment with the following snippet:val cancellable = omhStorageClient.downloadFile(id, mimeTypeToSave) .addOnSuccess { result: DownloadFileUseCaseResult -> // An instance of ByteArrayOutputStream with the downloaded file val outputStream: ByteArrayOutputStream = result.outputStream // TODO - Developer: Manage success } .addOnFailure { exception: Exception -> // TODO - Developer: Manage error } .execute() cancellableCollector.addCancellable(cancellable)
Note: Make sure to use the full implementation of
downloadFileEventfor a fully functional sample. -
Run the sample app to download files.
This repository also contains a Storage-sample, which demonstrates all the functionalities of the OMH Storage Client Library. To get started, follow these steps:
- Clone the repository to your local machine.
- Set up your Google Cloud project. You can refer to the instructions for guidance.
- Add the client id to your app using the instructions provided here.
- Set the variant to
debugSingleBuild - Run the
storage-sampleon your device
However, if you prefer a more structured approach to learn the client library from scratch, it's recommended to begin with the Getting Started section.
See example and check the full documentation and add custom implementation at our Wiki.
Additionally for more information about the OMH Storage functions, Docs.
OMH Storage SDK is open-source, promoting community collaboration and plugin support from other storage providers to enhance capabilities and expand supported storage services. More details can be found at the wiki.
Please contribute! We will gladly review any pull requests. Make sure to read the CONTRIBUTING page first though.
For details on our project's governance model and how decisions are made, please see our Governance Policy.
Copyright 2023 Open Mobile Hub
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
https://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.


