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.
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, use com.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-level build.gradle
under the plugins
element add the plugin id. If you're configuring this step by step on the code-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
, and releaseNonGms
debugSingleBuild
, debugGms
, and debugNonGms
- 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
and BuildConfig.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:
You can select any of the 3 variants for the :storage-starter-sample
:
Go to the providesOmhAuthClient
function in the SingletonModule
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 the SingletonMOdule
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 the FileViewerViewModel
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 the FileViewerViewModel
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 the FileViewerViewModel
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 the FileViewerViewModel
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 the FileViewerViewModel
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 the FileViewerViewModel
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:
debugSingleBuild
storage-sample
on your deviceHowever, 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.