1. Overview
Among all HMS services, ML Kit, Cloud DB, and Cloud Storage are the most commonly applied services in educational apps.
This codelab provides an overview for:
- How to use Cloud Storage to save files on the cloud and access them from any device
- How to use Cloud DB to store data and access it from any device in real time
- How to use ML Kit to implement common e-book reader app features, such as translation and text-to-speech (TTS).
2. Introduction
This codelab walks you through how to integrate Cloud Storage, Cloud DB, ML Kit, and In-App Purchases (IAP).
Cloud Storage is used to save PDF files to the cloud, as well as provide links to access them.
Cloud DB is used to store user information in the database, and allows for data to be created, read, updated, and deleted from any device.
ML Kit is used for developing the translation and TTS function.
Application Scenario
This codelab illustrates the process for developing an e-book reader app, showing how HMS services can be integrated into your app with ease, and how they can help boost your revenue.
Users of E-Book Reader can use it to register for an account, read books, translate books into other languages, and convert text into speech, which can be listened to. In addition, users can receive notifications about topics they've subscribed to, and enjoy ad-free usage with IAP.
In this codelab, you will learn how to develop an e-book reader app which can:
- Store and retrieve files via Cloud Storage.
- Store user information and PDF file information in the cloud database via Cloud DB.
- Translate text in PDF files.
- Convert text in PDF files into speech.
- Authenticate users via Auth Service.
- Provide an ad-free UI and premium books via IAP.
Please note that Cloud Storage, Cloud DB, ML Kit, and IAP are closely related to each other:
A PDF file is firstly saved via Cloud Storage. Its path is then saved by Cloud DB. Cloud DB is used to obtain details about books and display books within the app, where users are free to read them or have the translated, with the help of ML Kit. IAP then allows the app to offer membership plans for users who would prefer a premium, ad-free reading experience.
What You Will Create
In this codelab, you will use the demo project that we provided to call the mentioned services. With the demo project, you will:
- Save user interests and obtain the books list from Cloud DB.
- Convert text to speech and translate books via ML Kit.
- Initiate a purchase and complete payment via IAP.
Activity Description
3. What You Will Need
Hardware Requirements
- A computer (desktop or laptop) that runs Windows 10
- A Huawei phone with HMS Core (APK) 5.0.0.300 or later
Software Requirements
- Android Studio 3.X or above
- JDK 1.8 or later
- SDK Platform 23 or later
- Gradle 4.6 or later
4. Integration Preparations
To integrate HMS services, you'll need to complete the following tasks:
- Create an app in AppGallery Connect.
- Create a project in Android Studio.
- Generate a signing certificate.
- Generate a signing certificate fingerprint.
- Configure the signing certificate fingerprint.
- Save the configuration file.
- Add the AppGallery Connect plugin and Maven repository in the project-level build.gradle file.
- Configure the signing certificate in Android Studio.
Enabling HMS Services
Sign in to AppGallery Connect, click My projects, and select a project. On the page displayed, go to Project settings > Manage APIs. Toggle on the switches of the following services to enable them:
- ML Kit
- Cloud DB
- Cloud Storage
- In-App Purchases
Now, the services of HMS required for developing E-book Reader have been enabled.
5. Integrating HMS Services
Integrating Cloud Storage
After a PDF file is uploaded to Cloud Storage, its path can be accessed from any device.
To store PDF files in Cloud Storage, follow the steps below:
Step 1. Upload files to Cloud Storage in AppGallery Connect.
Step 2. Click View details. Then, click Copy to copy the sharing token. Save this sharing token to Cloud DB.
—End
Integrating Cloud DB
To store data in Cloud DB, first create an object type in AppGallery Connect. You can create and modify object types in a Cloud DB zone. In addition, you can also export object types from or import object types into a Cloud DB zone.
Note: First integrate the anonymous account authentication mode of Auth Service into the app to authenticate users who attempt to access Cloud DB.
To find Cloud DB, sign in to AppGallery Connect, click My projects, and select a project. The service is displayed in the left navigation pane under Build.
Adding a Cloud DB Zone
A Cloud DB zone is used to store data on the cloud, which can be created in AppGallery Connect. To create one, follow the steps below:
Step 1. Click the Cloud DB Zones tab.
Step 2. Click Add.
Step 3. On the page displayed, enter the name for the Cloud DB zone, and then click OK.
Step 4. Find the Cloud DB zone you've created from the Cloud DB zone list.
—End
Adding and Exporting Object Types
Step 1. Sign in to AppGallery Connect, click My projects, and select a project. On the page displayed, go to Build > Cloud DB.
Step 2. Under the ObjectTypes tab, click Add.
Step 3. On the page displayed, enter the name for the object type, and click Next.
Step 4. Click to add fields for an object type. Click OK.
Step 5. Find the created object type in the object type list. If you need to export the object type, click Export.
Step 6. Select JAVA as the format of the exported file. Enter its package name, and click Export.
—End
Adding the Object Type File to Your Project and Initializing the Cloud DB Zone
Add the exported files of the object type to your project:
Opening a Cloud DB Zone
Step 1. Initialize the Cloud DB zone in your app.
Java:
public class EBookApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initAGConnectCloudDB(this);
}
/**
* Initialize AGConnectCloudDB in the app.
*
* @param context app context.
*/
public static void initAGConnectCloudDB(Context context) {
AGConnectCloudDB.initialize(context);
}
}
Kotlin:
class EBookApplication : Application() {
override fun onCreate() {
super.onCreate()
initAGConnectCloudDB(this)
}
companion object {
/**
* Initialize AGConnectCloudDB in the app.
*
* @param context app context.
*/
fun initAGConnectCloudDB(context: Context?) {
AGConnectCloudDB.initialize(context!!)
}
}
Step 2. Obtain the AGConnectCloudDB instance and create object types.
Java:
public static synchronized CloudDb getInstance() {
if (sCloudDb == null) {
sCloudDb = new CloudDb();
sAgconnectCloudDb = AGConnectCloudDB.getInstance();
}
return sCloudDb;
}
/**
* Call AGConnectCloudDB.createObjectType to initialize the schema.
*/
public void createObjectType() {
try {
sAgconnectCloudDb.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo());
} catch (AGConnectCloudDBException exception) {
Log.e(TAG, "createObjectType: " + exception.getMessage());
}
}
Kotlin:
fun createObjectType() {
fun createObjectType() {
try {
if (sAgconnectCloudDb != null) sAgconnectCloudDb!!.createObjectType(
ObjectTypeInfoHelper.getObjectTypeInfo()
)
} catch (exception: AGConnectCloudDBException) {
Log.e(TAG, "AGConnectCloudDBException")
}
}
Step 3. Open the Cloud DB zone.
Java:
/**
* Call AGConnectCloudDB.openCloudDBZone to open a Cloud DB zone.
* It is set in cloud cache mode. Data can also be stored in local storage.
*
* @param dialogInterface to obtain the callback from the Cloud DB zone
*/
public void openCloudDBZone(DBOpenInterface dialogInterface) {
CloudDBZoneConfig mConfig =
new CloudDBZoneConfig(
CloudDbConstant.DB_NAME, CloudDbConstant.SYNC_PROPERTY, CloudDbConstant.ACCESS_ZONE);
mConfig.setPersistenceEnabled(true);
Task openDBZoneTask = sAgconnectCloudDb.openCloudDBZone2(mConfig, true);
openDBZoneTask
.addOnSuccessListener(
cloudDbZone -> {
mCloudDBZone = cloudDbZone;
dialogInterface.success(cloudDbZone);
})
.addOnFailureListener(
exception -> {
dialogInterface.failure();
});
}
Kotlin:
fun openCloudDBZone(dialogInterface: DBOpenInterface) {
val mConfig = CloudDBZoneConfig(
CloudDbConstant.DB_NAME, CloudDbConstant.SYNC_PROPERTY, CloudDbConstant.ACCESS_ZONE
)
mConfig.persistenceEnabled = true
if (sAgconnectCloudDb != null) {
val openDBZoneTask =
sAgconnectCloudDb!!.openCloudDBZone2(mConfig, true)
openDBZoneTask
.addOnSuccessListener { cloudDbZone: CloudDBZone? ->
cloudDBZone = cloudDbZone
dialogInterface.success(cloudDbZone)
}
.addOnFailureListener { exception: Exception? -> dialogInterface.failure() }
}
}
—End
Saving Data in Cloud DB
Save the bookmark in Cloud DB to store the page as a user favorite.
Java:
/**
* Save the bookmark in Cloud DB.
*
* @param pageNo which is marked as a bookmark.
*/
private void saveBookmark(int pageNo) {
Bookmark mBookmark = new Bookmark();
mBookmark.setEmailid(sp.fetchEmailId());
mBookmark.setPageno(pageNo);
if (bookmarkMaxId > 0) {
bookmarkMaxId++;
mBookmark.setBookmarkid(bookmarkMaxId);
} else {
mBookmark.setBookmarkid(1);
}
mBookmark.setBookId(mCurrentBookId);
mBookmark.setBookname(mBookName);
if (mCloudDBZone == null) {
return;
}
Task upsert = mCloudDBZone.executeUpsert(mBookmark);
upsert
.addOnSuccessListener(
snapshot -> {
btnBookmarkPage.setBackgroundResource(R.drawable.bookmark1);
mCurrentBookDetails.add(currentPage.getIndex());
showToast("Page " + (currentPage.getIndex() + 1) + " added as Favourite.", getContext());
})
.addOnFailureListener(
exception -> {
showToast(getString(R.string.bookmark_error), getContext());
exception.printStackTrace();
});
}
Kotlin:
private fun saveBookmark(pageNo: Int) {
val mBookmark =
Bookmark()
mBookmark.emailid = sp!!.fetchEmailId()
mBookmark.pageno = pageNo
if (bookmarkMaxId > 0) {
bookmarkMaxId++
mBookmark.bookmarkid = bookmarkMaxId
} else {
mBookmark.bookmarkid = 1
}
mBookmark.bookId = mCurrentBookId
mBookmark.bookname = mBookName
if (mCloudDBZone == null) {
return
}
val upsert = mCloudDBZone!!.executeUpsert(mBookmark)
upsert
.addOnSuccessListener { snapshot: Int? ->
Log.d(TAG, "Bookmark added successfully")
btnBookmarkPage!!.setBackgroundResource(R.drawable.bookmark1)
mCurrentBookDetails!!.add(currentPage!!.index)
showToast(
"Page " + (currentPage!!.index + 1) + " added as Favourite.",
context
)
}
.addOnFailureListener { exception: Exception? ->
showToast(
getString(R.string.bookmark_error),
context
)
}
}
Deleting Data from Cloud DB
Delete a bookmark.
Java:
Kotlin:
private fun deleteBookmark(bookmarkDetail: List) {
val delete = mCloudDBZone!!.executeDelete(bookmarkDetail)
delete.addOnSuccessListener { snapshot: Int? ->
mCurrentBookDetails!!.removeAt(currentPage!!.index)
btnBookmarkPage!!.setBackgroundResource(R.drawable.nobookmark1)
}
.addOnFailureListener { e: Exception? ->
Log.d(
TAG,
"Exception failure deleteBookmark"
)
}
}
Obtaining Data from Cloud DB
Obtain the bookmark of a specific book.
Java:
private void deleteBookMark(List bookmrkInfoList, int pageNo) {
int bukmarkid = Integer.parseInt(Integer.toString(mCurrentBookId) + Integer.toString(pageNo));
for (int i = 0; i < bookmrkInfoList.size(); i++) {
if (!bookmrkInfoList.get(i).getBookmarkid().equals(bukmarkid)) {
bookmrkInfoList.remove(i);
}
}
if (mCloudDBZone == null) {
return;
}
Task delete = mCloudDBZone.executeDelete(bookmrkInfoList);
delete
.addOnSuccessListener(
new OnSuccessListener() {
@Override
public void onSuccess(Integer cloudDBZoneResult) {
mCurrentBookDetails.remove(pageNumber);
btnBookmarkPage.setBackgroundResource(R.drawable.nobookmark);
}
})
.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(Exception e) {
showToast(getString(R.string.bookmark_delete_error), getContext());
}
});
}
public void queryBookmarkList() {
if (mCloudDBZone == null) {
return;
}
Task> queryTask =
mCloudDBZone.executeQuery(
CloudDBZoneQuery.where(Bookmark.class),
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
queryTask
.addOnSuccessListener(
new OnSuccessListener>() {
@Override
public void onSuccess(CloudDBZoneSnapshot snapshot) {
Log.d(TAG, "queryBookmarkList success");
processQueryResult(snapshot);
}
})
.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(Exception exp) {
Log.d(TAG, exp.getMessage());
showToast(exp.getMessage(), getContext());
}
});
}
Kotlin:
fun queryBookmarkList() {
if (mCloudDBZone == null) {
return
}
val queryTask =
mCloudDBZone!!.executeQuery(
CloudDBZoneQuery.where(Bookmark::class.java)
.equalTo("emailid", sp!!.fetchEmailId()),
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY
)
queryTask
.addOnSuccessListener { snapshot ->
Log.d(
TAG,
"Get Bookmark list successfully"
)
processQueryResult(snapshot)
}
.addOnFailureListener { showToast("on failure", context) }
}
Integrating IAP
Configure the product information in AppGallery Connect.
- Sign in to AppGallery Connect and click My apps.
- Select the app for which you wish to add a product.
- Click the Operate tab. Go to Products and click Product Management in the left navigation pane. Go to Products > Add Product.
- Configure the product information and click Save.
- In the dialog box that is displayed, click OK. After the configuration is completed, the product will be in a valid state in the product list.
Displaying Product Information
Call the obtainProductInfo API to query the product configured in AppGallery Connect. After a successful call, obtain the product details by calling ProductInfoResult and display the details in RecyclerView.
Java:
private void queryProducts(List productIds) {
IapRequestHelper.obtainProductInfo(
Iap.getIapClient(view.getActivity()),
productIds,
IapClient.PriceType.IN_APP_SUBSCRIPTION,
new IapApiCallback() {
@Override
public void onSuccess(final ProductInfoResult result) {
if (result == null) {
return;
}
List productInfos = result.getProductInfoList();
view.showProducts(productInfos);
}
@Override
public void onFail(Exception exception) {
int error = ExceptionHandle.handle(view.getActivity(), exception);
if (errorcode != ExceptionHandle.SOLVED) {
Log.e(TAG, "unknown error");
}
view.showProducts(null);
}
});
}
Kotlin:
private fun queryProducts(productIds: List) {
IapRequestHelper.obtainProductInfo(
Iap.getIapClient(view.getActivity()),
productIds,
IapClient.PriceType.IN_APP_SUBSCRIPTION,
object : IapApiCallback {
fun onSuccess(result: ProductInfoResult) {
if (result == null) {
return
}
val productInfos =
result.productInfoList
view.showProducts(productInfos)
}
override fun onFail(exception: Exception) {
val error = ExceptionHandle.handle(view.getActivity(), exception)
if (errorcode !== ExceptionHandle.SOLVED) {
Log.e(TAG, "unknown error")
}
view.showProducts(null)
}
})
}
Purchasing the Product
Step 1. Create a task for calling the createPurchaseIntent API.
Step 2. Set OnSuccessListener and OnFailureListener for the task to receive the API call result.
Java:
public void buy(final String productId) {
cacheOwnedPurchasesResult = null;
IapClient iapClient = Iap.getIapClient(view.getActivity());
IapRequestHelper.createPurchaseIntent(
iapClient,
productId,
IapClient.PriceType.IN_APP_SUBSCRIPTION,
new IapApiCallback() {
@Override
public void onSuccess(PurchaseIntentResult result) {
if (result == null) {
return;
}
// Pull up the page to complete the payment process.
IapRequestHelper.startResolutionForResult(
view.getActivity(), result.getStatus(), Constants.REQ_CODE_BUY);
}
@Override
public void onFail(Exception exception) {
int errorCode = ExceptionHandle.handle(view.getActivity(), exception);
if ( errorCode != ExceptionHandle.SOLVED) {
if (errorCode == OrderStatusCode.ORDER_PRODUCT_OWNED) {
showSubscription(productId);
} else {
Log.e(TAG, "unknown error");
}
}
}
});
}
Kotlin:
fun buy(productId: String?) {
cacheOwnedPurchasesResult = null
val iapClient = Iap.getIapClient(view.getActivity())
IapRequestHelper.createPurchaseIntent(
iapClient,
productId,
IapClient.PriceType.IN_APP_SUBSCRIPTION,
object : IapApiCallback {
fun onSuccess(result: PurchaseIntentResult) {
if (result == null) {
return
}
// Pull up the page to complete the payment process.
IapRequestHelper.startResolutionForResult(
view.getActivity(), result.status, Constants.REQ_CODE_BUY
)
}
override fun onFail(exception: Exception) {
val errorCode = ExceptionHandle.handle(view.getActivity(), exception)
if (errorCode != ExceptionHandle.SOLVED) {
if (errorCode == OrderStatusCode.ORDER_PRODUCT_OWNED) {
showSubscription(productId)
} else {
Log.e(TAG, "unknown error")
}
}
}
})
}
Step 3. The callback indicating the payment result is indicated by onActivityResult. Use the parsePurchaseResultInfoFromIntent method of IapClient to obtain the PurchaseResultInfo object, which contains the payment result.
Java:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult requestCode is:" + requestCode);
if (requestCode == Constants.REQ_CODE_BUY) {
if (resultCode == Activity.RESULT_OK) {
Log.d(TAG, "onActivityResult RESULT_OK");
int purchaseResult = SubscriptionUtils.getPurchaseResult(getActivity(), data);
if (purchaseResult == OrderStatusCode.ORDER_STATE_SUCCESS) {
Log.d(TAG, "onActivityResult ORDER_STATE_SUCCESS");
Toast.makeText(getActivity(), R.string.pay_success, Toast.LENGTH_SHORT).show();
presenter.refreshSubscription();
return;
}
if (purchaseResult == OrderStatusCode.ORDER_STATE_CANCEL) {
Log.d(TAG, "onActivityResult ORDER_STATE_CANCEL");
Toast.makeText(getActivity(), R.string.cancel, Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(getActivity(), R.string.pay_fail, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), R.string.cancel, Toast.LENGTH_SHORT).show();
}
}
}
Kotlin:
fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.d(TAG, "onActivityResult requestCode is:$requestCode")
if (requestCode == Constants.REQ_CODE_BUY) {
if (resultCode == Activity.RESULT_OK) {
Log.d(TAG, "onActivityResult RESULT_OK")
val purchaseResult = SubscriptionUtils.getPurchaseResult(getActivity(), data)
if (purchaseResult == OrderStatusCode.ORDER_STATE_SUCCESS) {
Log.d(TAG, "onActivityResult ORDER_STATE_SUCCESS")
Toast.makeText(getActivity(), R.string.pay_success, Toast.LENGTH_SHORT).show()
presenter.refreshSubscription()
return
}
if (purchaseResult == OrderStatusCode.ORDER_STATE_CANCEL) {
Log.d(TAG, "onActivityResult ORDER_STATE_CANCEL")
Toast.makeText(getActivity(), R.string.cancel, Toast.LENGTH_SHORT).show()
return
}
Toast.makeText(getActivity(), R.string.pay_fail, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(getActivity(), R.string.cancel, Toast.LENGTH_SHORT).show()
}
}
}
—End
Integrating ML Kit
ML Kit helps with implementing TTS and translation into the app.
Implementing TTS
TTS can convert text information into audio output in real time. Follow the steps below to implement it:
Step 1. Initialize the API key when the app is started.
Java:
/**
* Set the key, which is needed to use ML Kit.
*/
private void setApiKey() {
AGConnectServicesConfig config = AGConnectServicesConfig.fromContext(getActivity());
MLApplication.getInstance().setApiKey(config.getString(API_KEY));
}
Kotlin:
/**
* Set the key, which is needed to use ML Kit.
*/
private fun setApiKey() {
val config = AGConnectServicesConfig.fromContext(getActivity())
MLApplication.getInstance().apiKey = config.getString(API_KEY)
}
Step 2. Create the object of the MLTtsConfig class and pass the object to MLTtsEngine.
Java:
mlConfigs = new MLTtsConfig();
mlTtsEngine = new MLTtsEngine(mlConfigs);
Kotlin:
mlConfigs = MLTtsConfig()
mlTtsEngine = MLTtsEngine(mlConfigs)
Step 3. Add the callback method of MLTtsEngine.
Java:
mlTtsEngine.setTtsCallback(new MLTtsCallback() {
String str = "";
@Override
public void onError(String taskId, MLTtsError err) {
str = "TaskID: " + taskId + ", error:" + err;
displayResult(str);
}
@Override
public void onWarn(String taskId, MLTtsWarn mlTtsWarn) {
str = "TaskID: " + taskId + ", warn:" + mlTtsWarn;
displayResult(str);
}
@Override
public void onRangeStart(String taskId, int start, int end) {
str = "TaskID: " + taskId + ", onRangeStart [" + start + "," + end + "]";
displayResult(str);
}
@Override
public void onAudioAvailable(
String s,
MLTtsAudioFragment mlTtsAudioFragment,
int i,
Pair pair,
Bundle bundle) {
}
@Override
public void onEvent(String taskId, int eventName, Bundle bundle) {
str = "TaskID: " + taskId + ", eventName:" + eventName;
if (eventName == MLTtsConstants.EVENT_PLAY_STOP) {
str += " " + bundle.getBoolean(MLTtsConstants.EVENT_PLAY_STOP_INTERRUPTED);
}
displayResult(str);
}
});
Kotlin:
mlTtsEngine = MLTtsEngine(mlConfigs)
mlTtsEngine!!.setTtsCallback(object : MLTtsCallback {
var str = ""
override fun onError(taskId: String, err: MLTtsError) {
str = "TaskID: $taskId, error:$err"
displayResult(str)
}
override fun onWarn(taskId: String, mlTtsWarn: MLTtsWarn) {
str = "TaskID: $taskId, warn:$mlTtsWarn"
displayResult(str)
}
override fun onRangeStart(
taskId: String,
start: Int,
end: Int
) {
str = "TaskID: $taskId, onRangeStart [$start,$end]"
displayResult(str)
}
override fun onAudioAvailable(
s: String,
mlTtsAudioFragment: MLTtsAudioFragment,
i: Int,
pair: Pair,
bundle: Bundle
) {
}
override fun onEvent(
taskId: String,
eventName: Int,
bundle: Bundle
) {
str = "TaskID: $taskId, eventName:$eventName"
if (eventName == MLTtsConstants.EVENT_PLAY_STOP) {
str += " " + bundle.getBoolean(MLTtsConstants.EVENT_PLAY_STOP_INTERRUPTED)
}
displayResult(str)
}
})
Step 4. Call the speak API of MLTtsEngine to start TTS.
Java:
mlTtsEngine.speak(text, MLTtsEngine.QUEUE_APPEND);
Kotlin:
val id = mlTtsEngine!!.speak(text, MLTtsEngine.QUEUE_APPEND)
Step 5. Call the stop API of MLTtsEngine to stop TTS.
mlTtsEngine.stop();
Step 6. Call the pause API of MLTtsEngine to pause TTS.
mlTtsEngine.pause();
Step 7. Call the resume API of MLTtsEngine to resume TTS.
mlTtsEngine.resume();
—End
Implementing Translation
Call the getRemoteTranslator API of MLTranslatorFactory. By using the code below, we can translate the text into Hindi, Chinese, and Russian.
Java:
private void remoteTranslator(String lang) {
// Create an analyzer. You can customize the analyzer by creating an MLRemoteTranslateSetting object.
MLRemoteTranslateSetting setting = new MLRemoteTranslateSetting.Factory().setTargetLangCode(lang).create();
this.translator = MLTranslatorFactory.getInstance().getRemoteTranslator(setting);
String sourceText = readPdf(downloadedFilePath.getPath());
Task task = this.translator.asyncTranslate(sourceText);
task.addOnSuccessListener(
new OnSuccessListener() {
@Override
public void onSuccess(String text) {
DisplayBook.this.remoteDisplaySuccess(text, true);
})
.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(Exception exception) {
// Recognition failure.
Log.d(TAG, exception.getLocalizedMessage());
DisplayBook.this.displayFailure(exception);
}
});
}
}
Kotlin:
private fun remoteTranslator(lang: String) {
setProgressBar(context)
// Create an analyzer. You can customize the analyzer by creating an MLRemoteTranslateSetting object.
val setting =
MLRemoteTranslateSetting.Factory().setTargetLangCode(lang).create()
translator = MLTranslatorFactory.getInstance().getRemoteTranslator(setting)
val sourceText = readPdf(downloadedFilePath!!.path)
if (sourceText != null) {
val task = translator?.asyncTranslate(sourceText)
task?.addOnSuccessListener { text ->
stopProgressBar()
remoteDisplaySuccess(text, true)
}
}?.addOnFailureListener { exception -> // Recognition failure.
Log.d(TAG, "Exception : onfailure")
displayFailure(exception)
}
} else {
showToast("Nothing to translate", context)
}
}
6. Congratulations
Well done. You have successfully completed this codelab, built an e-book reader app, and learned how to:
- Configure product information in AppGallery Connect.
- Integrate Cloud Storage.
- Integrate Cloud DB.
- Integrate ML Kit.
- Integrate IAP.
7. References
For more information, click the following links:
ML Kit
Cloud DB
Cloud Storage
In-App Purchases
To download the sample code, click the button below:
Download