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-starter
branch, 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.keystore
Note: Use
android
as 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.properties
in your project level directory, and then add the following line:CLIENT_ID = "YOUR_CLIENT_ID"
Note: Replace
YOUR_CLIENT_ID
with 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-sample
module-levelbuild.gradle
under theplugins
element add the plugin id. If you're configuring this step by step on thecode-starter
branch, look for the comment// Paste below this line the snippet of core plugin
and 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
, andreleaseNonGms
debugSingleBuild
,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_PATH
andBuildConfig.STORAGE_NON_GMS_PATH
variables. -
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
providesOmhAuthClient
function in theSingletonModule
file. 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
providesOmhAuthClient
for a fully functional sample. -
Go to the
providesOmhStorageClient
function in theSingletonMOdule
file. 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
providesOmhStorageClient
for 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
refreshFileListEvent
function in theFileViewerViewModel
file. 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
refreshFileListEvent
for 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
createFileEvent
function in theFileViewerViewModel
file. 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
createFileEvent
for 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
deleteFileEvent
function in theFileViewerViewModel
file. 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
deleteFileEvent
for 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
uploadFile
function in theFileViewerViewModel
file. 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
uploadFile
for 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
updateFileEvent
function in theFileViewerViewModel
file. 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
updateFileEvent
for 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
downloadFileEvent
function in theFileViewerViewModel
file. 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
downloadFileEvent
for 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-sample
on 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.
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.