Integrating In App Purchase in B4A Platform - Huawei Developers

Introduction
HUAWEI In-App Purchases (IAP) aggregates multiple payment methods for global payments, and can be easily integrated into your app to help revenue increase. Users can purchase a variety of products or services, including common virtual products and subscriptions and directly within your app. For details, please refer to HUAWEI IAP 5.0.
First you need to understand three types of in-app products supported by HUAWEI In-App Purchases.
1. Consumable: Consumables are used once, are depleted, and can be purchased again.
Example: Extra lives and gems in a game
2. Non-consumable: Non-consumables are purchased once and do not expire.
Example: Extra game levels in a game or permanent membership of an app
3. Auto-renewable subscriptions: Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.
Example: Non-permanent membership of an app, such as a monthly video membership
For details, refer to
https://www.b4x.com/b4a.html
Follow all the steps mentioned in Basic Setup to start HMS integration on B4A Platform.
Refer to
https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201286424114350051&fid=0101246461018590361
Enable In-App Purchases in App gallery connect.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Tips & Tricks:
Before enable In-App Purchases create marchant account. It will take two days to approve then only we can enable In-App Purchases in AppGallery Connect.
Creating Wrapper Class
1. Downloading the AAR Packages and JSON File
Sign in to HUAWEI Developer and download the AAR packages required.
AAR packages related to HMS In-App Purchases kit, as follows.
HMSSDK Agconnect-Core:
https://developer.huawei.com/repo/com/huawei/agconnect/agconnect-core/1.4.0.300/agconnect-core-1.4.0.300.aar
HMSSDK Tasks:
https://developer.huawei.com/repo/com/huawei/hmf/tasks/1.4.1.300/tasks-1.4.1.300.aar
HMSSDK Available Update:
https://developer.huawei.com/repo/com/huawei/hms/availableupdate/5.0.0.301/availableupdate-5.0.0.301.aar
HMSSDK Base:
https://developer.huawei.com/repo/com/huawei/hms/base/5.0.0.301/base-5.0.0.301.aar
HMSSDK Device:
https://developer.huawei.com/repo/com/huawei/hms/device/5.0.0.301/device-5.0.0.301.aar
HMSSDK Log:
https://developer.huawei.com/repo/com/huawei/hms/log/5.0.0.301/log-5.0.0.301.aar
HMSSDK Network-Common:
https://developer.huawei.com/repo/com/huawei/hms/network-common/4.0.2.300/network-common-4.0.2.300.aar
HMSSDK Network-Grs:
https://developer.huawei.com/repo/com/huawei/hms/network-grs/4.0.2.300/network-grs-4.0.2.300.aar
HMSSDK Stats:
https://developer.huawei.com/repo/com/huawei/hms/stats/5.0.0.301/stats-5.0.0.301.aar
HMSSDK UI:
https://developer.huawei.com/repo/com/huawei/hms/ui/5.0.0.301/ui-5.0.0.301.aar
HMSSDK Update:
https://developer.huawei.com/repo/com/huawei/hms/update/2.0.6.302/update-2.0.6.302.aar
HMSSDK IAP:
https://developer.huawei.com/repo/com/huawei/hms/iap/5.0.2.300/iap-5.0.2.300.aar
2. Open AAR packages with rar tool and rename the class.jar and AndroidManifest.xml files. And save those jar file inside libs folder (It is recommended that the two files be renamed consistently with the AAR package names.).
3. Copy required permissions in the <manifest> section in B4A IDE.
4. Copy all the configurations in <application> section.
5. Change ${applicationId} to $PACKAGE$.
6. Download the configuration file(agconnect-services.json) from App Gallery Connect and add the JSON File to the assets Folder of the AAR file as shown below.
B4A will automatically incorporate files in the assets folder of an AAR package to the assets folder of your main project.
Setting Package Signing Options
To create a signed application package perform the following steps:
1. Choose Project > Build Configurations and change the B4A project Package name to the project package name configured in App Gallery Connect.
2. Choose Tools > Private Sign Key and select the SHA256 signature file specified in AppGallery Connect as the B4A project signature file.
2. Declare MainApplication in the Manifest file.
The path of MainApplication must also be the path to src folder where MainApplication is located.
Encapsulating Java Files Using SLC
1. Create Library as parent and then create bin, libs and src as subfolder in the project directory
2. Develop java project inside the following path:
Choose Library Folder > src > b4x > iap > demo
3. Import the following two lines of code to each Java file.
Code:
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.*;
import com.huawei.hmf.tasks.Task;
import android.content.Intent;
import anywheresoftware.b4a.IOnActivityResult;
4. Add necessary annotations to the Java files.
Code:
@DependsOn(values={ "agconnect-core-1.4.0.300.aar",
"base-5.0.0.301.aar",
"network-common-4.0.2.300.aar",
"network-grs-4.0.2.300.aar",
"iap-5.0.2.300.aar",
"availableupdate-5.0.0.301.aar",
"device-5.0.0.301.aar",
"log-5.0.0.301.aar",
"stats-5.0.0.301.aar",
"tasks-1.4.1.300.aar",
"ui-5.0.0.301.aar",
"update-2.0.6.302.aar",
})
5. Modify the context.
B4A does not recognize the Context class. Therefore, when parsing a class that contains @version (1.0f) annotation, it will report an error if a method of the class has referenced Context . In this case, you need to change Context to B4A object BA.
Code:
@ShortName("IAPWork")
public class IAPWork {
public static String eventName = "listener";
public static String TAG = "IAP_Kit";
public static BA ba;
public void init(Context context) {
AGConnectServicesConfig.fromContext(context).overlayWith(new LazyInputStream(context) {
@Override
public InputStream get(Context context) {
try {
return context.getAssets().open("agconnect-services.json");
} catch (IOException e) {
e.printStackTrace();
BA.Log(e.toString());
}
return null;
}
});
}
public void loadProduct(BA ba1) {
ba = ba1;
// obtain in-app product details configured in AppGallery Connect, and then show the products
IapClient iapClient = Iap.getIapClient(ba.context);
Task<ProductInfoResult> task = iapClient.obtainProductInfo(createProductInfoReq());
task.addOnSuccessListener(new OnSuccessListener<ProductInfoResult>() {
@Override
public void onSuccess(ProductInfoResult result) {
ba.raiseEventFromUI(this, eventName + "_iaparray", result.toString());
if (result != null && !result.getProductInfoList().isEmpty()) {
List<ProductInfo> productInfoList = result.getProductInfoList();
for (ProductInfo productInfo : productInfoList) {
ba.raiseEventFromUI(this, eventName + "_iaptext", productInfo.getProductName(), productInfo.getProductId(), productInfo.getPrice());
}
//ba.raiseEventFromUI(this,eventName+ "_iaparray",new Object[] {productInfoList} );
// showProduct(result.getProductInfoList());
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, e.getMessage());
if (e instanceof IapApiException) {
IapApiException iapApiException = (IapApiException) e;
int returnCode = iapApiException.getStatusCode();
if (returnCode == OrderStatusCode.ORDER_HWID_NOT_LOGIN) {
ba.raiseEventFromUI(this, eventName + "_iaparray", "Please sign in to the app with a HUAWEI ID.");
} else {
ba.raiseEventFromUI(this, eventName + "_iaparray", e.getMessage());
// Toast.makeText(ba.context, e.getMessage(), Toast.LENGTH_SHORT).show();
}
} else {
ba.raiseEventFromUI(this, eventName + "_iaparray", "error");
// Toast.makeText(ba.context, "error", Toast.LENGTH_SHORT).show();
}
}
});
}
private ProductInfoReq createProductInfoReq() {
ProductInfoReq req = new ProductInfoReq();
// In-app product type contains:
// 0: consumable
// 1: non-consumable
// 2: auto-renewable subscription
req.setPriceType(IapClient.PriceType.IN_APP_CONSUMABLE);
ArrayList<String> productIds = new ArrayList<>();
// Pass in the item_productId list of products to be queried.
// The product ID is the same as that set by a developer when configuring product information in AppGallery Connect.
productIds.add("ProductID1");
productIds.add("ProductID2");
productIds.add("ProductID3");
productIds.add("ProductID4");
req.setProductIds(productIds);
ba.raiseEventFromUI(this, eventName + "_iaparray", productIds.get(0));
return req;
}
}
Tips & Tricks
The account for sandbox testing takes effect 30 minutes to 1 hour after being added. Check whether the current account can be used in the sandbox testing.

Related

HMS Safety Detect API integration — (MVVM RxAndroid)

This article is originally from HUAWEI Developer Forum
Forum link: https://forums.developer.huawei.com/forumPortal/en/home​
This is all about integration of HMS Safety Detect API in the Android app using MVVM RxAndroid.
What is HMS Safety Detect API?
Ø The Safety Detect provides system integrity check (SysIntegrity), app security check (AppsCheck), malicious URL check (URLCheck), and fake user detection (UserDetect), helping you prevent security threats to your app.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Let’s create a Demo Project:
HUAWEI HMS Safety Detect integration requires the following preparations
Ø Creating an AGC Application.
Ø Creating an Android Studio Project.
Ø Generating a signature certificate.
Ø Generating a signature certificate fingerprint.
Ø Configuring the signature certificate fingerprint.
Ø Adding the application package name and save the configuration file.
Ø Configure the Maven address and AGC gradle plug-in.
Ø Configure the signature file in Android Studio.
In this article, we will implement SysIntegrity API in demo project using with RxAndroid and MVVM.
Call the API and handle responses.
Verify the certificate chain, signature, and domain name on the server.
1. Open AppGallery Console:
1. We need to create an application inside console.
2. We need to enable the Safety Detect api.
Go to Console > AppGallery Connect > My apps, click your app, and go to Develop > Manage APIs.
Now enable Safety Detect Api
Download the agconnect-services.json
Move the downloaded agconnect-services.json file to the app root directory of your Android Studio project.
We need to add HMS SDK dependency in app:gradle file
Code:
implementation 'com.huawei.hms:safetydetect:4.0.0.300'
We need to add maven dependency inside project:gradle file
Code:
maven { url 'http://developer.huawei.com/repo/' }
We need to add two more dependencies in app:gradle file
Code:
// MVVM
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
// RxAndroid
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
Enable Data Binding
Code:
dataBinding {
enabled = true
}
2. Let’s implement api :
I have created following classes.
1. SysIntegrityDataSource : Which invoke the System Integrity Api with help of RxJava.
2. SysIntegrityViewModel : Which handle the response from System Integrity api and provide LiveData for view componets.
3. SysIntegrityFragment : Which observe the livedata from viewmodel class and set values in views such as textviews and button.
Note: If you are not familiar with MVVM or RxAndroid then I would like to suggest you to please go through my following articles:
· Android MyShows App — Rxandroid MVVM LiveData ViewModel DataBinding, Networking with Retrofit, Gson & Glide — Series
· Demystifying Data Binding — Android Jetpack — Series
Let’s see the implementation of SysIntegrityDataSource.java class.
Code:
public class SysIntegrityDataSource {
private static final String APP_ID = "XXXXXXXX";
private Context context;
public SysIntegrityDataSource(Context context) {
this.context = context;
}
public Single<SysIntegrityResp> executeSystemIntegrity() {
return Single.create(this::invokeSysIntegrity);
}
private void invokeSysIntegrity(SingleEmitter<SysIntegrityResp> emitter) {
byte[] nonce = ("Sample" + System.currentTimeMillis()).getBytes();
SafetyDetect.getClient(context)
.sysIntegrity(nonce, APP_ID)
.addOnSuccessListener(emitter::onSuccess)
.addOnFailureListener(emitter::onError);
}
}
invokeSysIntegrity() : This method invoke the System Integrity api and emit the data onSuccess/OnError and past it to Single<SysIntegrityResp> observable.
executeSystemIntegrity() : This method will create Single observable and return the response from invokeSysIntegrity() method.
3. Let’s implement ViewModel :
I have created SysIntegrityViewModel.java class.
Code:
public class SysIntegrityViewModel extends AndroidViewModel {
private final CompositeDisposable disposables = new CompositeDisposable();
private SysIntegrityDataSource sysIntegrityDataSource;
private MutableLiveData<SysIntegrityResp> systemIntegrityLiveData;
private MutableLiveData<String> error;
public SysIntegrityViewModel(Application app) {
super(app);
sysIntegrityDataSource = new SysIntegrityDataSource(app.getBaseContext());
systemIntegrityLiveData = new MutableLiveData<>();
error = new MutableLiveData<>();
}
public LiveData<SysIntegrityResp> observerSystemIntegrity() {
sysIntegrityDataSource.executeSystemIntegrity()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<SysIntegrityResp>() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
@Override
public void onSuccess(SysIntegrityResp response) {
systemIntegrityLiveData.setValue(response);
}
@Override
public void onError(Throwable e) {
error.setValue(e.getMessage());
}
});
return systemIntegrityLiveData;
}
public LiveData<String> getError() {
return error;
}
@Override
protected void onCleared() {
disposables.clear();
}
}
MutableLiveData<SysIntegrityResp> systemintegrityLiveData: This field which provide the live data and return the value from viewmodel to fragment class.
observerSysIntegrity() : Which observe RxAndroid’s Single(observable) on main thread and set the value in systemIntegrityLiveData. If we got error while observing it will post the error in MutableLiveData<String> error.
4. Let’s implement Fragment :
I have created SysIntegrityFragment.java class Which obaserve the System Integrity api’s reponse and set the values in views.
Code:
public class SysIntegrityFragment extends Fragment {
private SysIntegrityViewModel sysIntegrityViewModel;
private FragmentSysBinding sysBinding;
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
sysBinding=DataBindingUtil.inflate(inflater, R.layout.fragment_sys, container, false);
sysIntegrityViewModel = ViewModelProviders.of(this).get(SysIntegrityViewModel.class);
sysBinding.btnSys.setOnClickListener(v->{
processView();
sysIntegrityViewModel.observerSystemIntegrity().observe(getViewLifecycleOwner(), this::setSystemIntegrity);
sysIntegrityViewModel.getError().observe(getViewLifecycleOwner(),this::showError);
});
return sysBinding.getRoot();
}
private void setSystemIntegrity(SysIntegrityResp response){
String jwsStr = response.getResult();
String[] jwsSplit = jwsStr.split("\\.");
String jwsPayloadStr = jwsSplit[1];
String payloadDetail = new String(Base64.decode(jwsPayloadStr.getBytes(), Base64.URL_SAFE));
try {
final JSONObject jsonObject = new JSONObject(payloadDetail);
final boolean basicIntegrity = jsonObject.getBoolean("basicIntegrity");
sysBinding.btnSys.setBackgroundResource(basicIntegrity ? R.drawable.btn_round_green : R.drawable.btn_round_red);
sysBinding.btnSys.setText(R.string.rerun);
String isBasicIntegrity = String.valueOf(basicIntegrity);
String basicIntegrityResult = "Basic Integrity: " + isBasicIntegrity;
sysBinding.txtBasicIntegrityTitle.setText(basicIntegrityResult);
if (!basicIntegrity) {
String advice = "Advice: " + jsonObject.getString("advice");
sysBinding.txtPayloadAdvice.setText(advice);
}
} catch (JSONException e) {
}
}
private void showError(String error){
Toast.makeText(getActivity().getApplicationContext(), error, Toast.LENGTH_SHORT).show();
sysBinding.btnSys.setBackgroundResource(R.drawable.btn_round_yellow);
sysBinding.btnSys.setText(R.string.rerun);
}
private void processView() {
sysBinding.txtBasicIntegrityTitle.setText("");
sysBinding.txtPayloadBasicIntegrity.setText("");
sysBinding.btnSys.setText(R.string.processing);
sysBinding.btnSys.setBackgroundResource(R.drawable.btn_round_processing);
}
}
We have instantiated instance of view model using ViewModel factory method.
We will consume the response on button click’s event.
If we got success response then we will display inside textviews and button otherwise we will show the error toast.
5. Let’s see the result:
Build the app and hit run button.
Click > RunDetection Case 1: Success Case 2: SDK Error Case 3: Integrity false (Rooted)
I hope you have learnt something new today. If you have any query regarding this article, please feel free to post any comments.
Any questions about this, you can try to acquire answers from HUAWEI Developer Forum.​
Useful sharing,thanks
Thank you so much for sharing, very useful.
Thank you so much for sharing, very useful.
Thank you so much for sharing, very useful.
Does it work offline?
useful sharing,thanks!

How to use HMS IAP Plugin with Ionic?

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
While working with Ionic, it is recommended to use the Capacitor for new Ionic apps but sometimes you need to use some Cordova or Ionic Native plugins. In this guide you will learn how to use HMS IAP Cordova Plugin with an Ionic Capacitor application.
In order to use IAP Plugin with any application, you have to create an application on Huawei Developer Console and add some products.
You can read this guide for further details to create an application and add purchasable products to work with.
Huawei In-App Purchases
Huawei In-App Purchases (IAP) provides multiple payment methods for global payments and can be easily integrated into your application to help increase your revenue. Users can purchase a variety of products and services, including popular virtual products and subscriptions, from within your direct application.
Create an Application with Ionic
Install the Ionic CLI with npm if you haven’t already.
Code:
ionic start HMS-IAP-Demo blank --capacitor
cd HMS-IAP-Demo
npm run build
Add your agconnect-services.json and keystore files to project folder as explained in the guide I mentioned above.
Add the AppID you got from the Huawei Developer console to the AndroidManifest.xml file. (inside application tag)
Code:
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="appid=102461279">
</meta-data>
Add HMS IAP Cordova Plugin & Ionic Native
Code:
npm i @hmscore/cordova-plugin-hms-iap --save
npm i @ionic-native/core --save-dev
Copy the “ionic/dist/hms-in-app-purchases” folder from library in node_modules to the “node_modules/@ionic-native” folder in your ionic project.
Code:
ionic capacitor add add android
ionic capacitor sync
ionic capacitor build android
Run the project on a connected device
Run the Ionic application with the command below to try the application with a Huawei device. We’re using the --livereload and --external options to see changes on tthe device when changes in the app are detected.
Code:
ionic capacitor run android --livereload --external
Exploring IAP Library
You have to login with your Huawei ID on the device you’re going to install application. Otherwise, you will get an error. (Error Code: 60050). You can check the error details and other error codes here.
Adding Library to Providers
You have to add library to the providers section in app.module.ts before using it.
Code:
import { HMSInAppPurchases } from '@ionic-native/hms-in-app-purchases/ngx';
@NgModule({
...
providers: [
HMSInAppPurchases,
],
bootstrap: [AppComponent]
})
export class AppModule {}
Checking Environment
Before fetching the products, you should check the environment is ready.
Code:
import { Component } from '@angular/core';
import { HMSInAppPurchases } from '@ionic-native/hms-in-app-purchases/ngx';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss']
})
export class HomePage {
constructor(public iap: HMSInAppPurchases) { }
async ngOnInit() {
try {
let message = await this.iap.isEnvReady();
console.log(message);
} catch (error) {
console.error(error);
}
}
}
Fetching Products
If you have configured products in AppGallery Connect, you need to use the obtainProductInfo API to obtain details of these products.
Perform the following steps:
Construct a ProductInfoReq object and pass the product ID that has been defined and taken effect in AppGallery Connect to the ProductInfoReq object, and specify priceType for a product. “priceType” is type of the product. Check the code below for price types and their integer equivalents.
If the operation is successful, obtainProductInfo will return a ProductInfoResult object that contains productInfoList. You can use it to display the product information within your application.
Code:
import { Component } from '@angular/core';
import { HMSInAppPurchases } from '@ionic-native/hms-in-app-purchases/ngx';
import { HMSAppLinking } from '@ionic-native/hms-applinking/ngx';
const DEVELOPERPAYLOAD = "HMSCoreDeveloper"
const DEVELOPERCHALLENGE = "HMSCoreDeveloperChallenge"
const PRICETYPE = {
CONSUMABLE : 0,
NONCONSUMABLE : 1,
SUBSCRIPTION : 2,
}
const PRODUCTS = {
consumable: {
type: PRICETYPE.CONSUMABLE,
products: [{
id: 'test',
name: 'Consumable Item'
}]
},
nonconsumable: {
type: PRICETYPE.NONCONSUMABLE,
products: [{
id: 'non_consumable_item_1',
name: 'Non Consumable Item'
}, ]
},
subscription: {
type: PRICETYPE.SUBSCRIPTION,
products: [{
id: 'subscription_item_1',
name: 'Subscription Item'
}, ]
},
}
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
constructor(private iap: HMSInAppPurchases) {}
ngOnInit() {
...
this.getProductsInformation();
}
getProductsInformation = () => {
Object.keys(PRODUCTS).map(async product_type=> {
await this.obtainProductInfoFromType(product_type);
});
}
obtainProductInfoFromType = async (product_type) => {
try {
let products = await this.iap.obtainProductInfo({
priceType: PRODUCTS[product_type].type,
productList: PRODUCTS[product_type].products.map(p=>p.id)
});
console.log(products);
} catch (err) {
console.log(err);
}
}
}
Initiating a Purchase
Your app can display the purchase screen through the createPurchaseIntent API.
Call createPurchaseIntent with required parameter. Parameter is an object includes productId, priceType and developerPayload. If parameters are correct it automatically brings the checkout page. If a problem occurs the createPurchaseIntent API will return a Status object describing the error.
Code:
import { Component } from '@angular/core';
import { HMSInAppPurchases } from '@ionic-native/hms-in-app-purchases/ngx';
import { HMSAppLinking } from '@ionic-native/hms-applinking/ngx';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
constructor(private iap: HMSInAppPurchases) {}
ngOnInit() {
...
}
createPurchaseIntent = async (product) => {
try {
let message = await this.iap.createPurchaseIntent({
priceType: product.priceType,
productId: product.productId,
developerPayload: DEVELOPERPAYLOAD
});
if (message.returnCode === 0) {// if successful
console.log(message);
} else {
alert('Purchase was not successful.');
console.log(message);
}
} catch (err) {
console.log(err);
}
}
}
Auto-renewable Subscriptions
Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.
You can use the createPurchaseIntent API like the example above to display the subscription purchase page. HMS IAP Plugin provides startIapActivity API to display subscription management and subscription editing pages.
Simply call the startIapActivity API to display the subscription management page. If you want to display display the subscription editing page you just need to pass the ID of the product as a parameter.
Code:
import { Component } from '@angular/core';
import { HMSInAppPurchases } from '@ionic-native/hms-in-app-purchases/ngx';
import { HMSAppLinking } from '@ionic-native/hms-applinking/ngx';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
constructor(private iap: HMSInAppPurchases) {}
async ngOnInit() {
...
}
editSubscription = (product_id) => {
this.iap.startIapActivity(product_id).then(() => {
console.log('Subscription editing page is displayed');
}).catch(error => {
console.log(error);
})
}
manageSubscriptions = () => {
this.iap.startIapActivity().then(() => {
console.log('Subscription management page is displayed');
}).catch(error => {
console.log(error);
});
}
}
In this article, I tried to explain to you the use of the In-App Purchases Cordova Plugin through code examples.

Integrating Text to speech (TTS) in B4A Platform

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
This article talks about how to convert text to Text to speech (TTS) can convert text information into audio output. Rich timbres are provided and the volume and speed can be adjusted, thereby natural voices can be produced. This service uses the deep neural network (DNN) synthesis mode and can be quickly integrated through the on-device SDK to generate audio data in real time. In the current version, two standard male voices and six standard female voices are available.
TTS is available only on Huawei phones.
The text in a single request can contain a maximum of 500 characters and is encoded using UTF-8.
TTS in French, Spanish, German, and Italian is deployed only in Asia, Africa, Latin America, and Europe.
TTS depends on on-cloud APIs. During commissioning and usage, ensure that the device can access the Internet.
Default specifications of the real-time output audio data are as follows: PCM mono, 16-bit depth, and 16 kHz audio sampling rate.
Follow all the steps mentioned in Basic Setup to start HMS integration on B4A Platform.
Refer to previous Article
Enable ML Kit in App gallery connect.
Tips & Tricks:
In case voice is not audible please make ensure that ML-Kit is enabled or not in App Gallery Connect.
Creating Wrapper Class
1. Downloading the AAR Packages and JSON File
Sign in to HUAWEI Developer and download the AAR packages required.
AAR packages related to HMS TTS kit is displayed as below
Agconnect-core:
https://developer.huawei.com/repo/com/huawei/agconnect/agconnect-core/1.0.0.300/agconnect-core-1.0.0.300.aar
HMSSDKBase:
https://developer.huawei.com/repo/com/huawei/hms/base/4.0.1.300/base-4.0.1.300.aar
Ml-computer-agc-inner:
https://developer.huawei.com/repo/com/huawei/hms/ml-computer-agc-inner/2.0.1.300/ml-computer-agc-inner-2.0.1.300.aar
Ml-computer-commonutils-inner:
https://developer.huawei.com/repo/com/huawei/hms/ml-computer-commonutils-inner/2.0.1.300/ml-computer-commonutils-inner-2.0.1.300.aar
Ml-computer-ha-inner:
https://developer.huawei.com/repo/com/huawei/hms/ml-computer-ha-inner/2.0.0.300/ml-computer-ha-inner-2.0.0.300.aar
Tasks:
https://developer.huawei.com/repo/com/huawei/hmf/tasks/1.4.1.300/tasks-1.4.1.300.aar
Network-common:
https://developer.huawei.com/repo/com/huawei/hms/network-common/4.0.2.301/network-common-4.0.2.301.aar
Network-grs:
https://developer.huawei.com/repo/com/huawei/hms/network-grs/4.0.2.301/network-grs-4.0.2.301.aar
Ml-computer-grs-inner:
https://developer.huawei.com/repo/com/huawei/hms/ml-computer-grs-inner/2.0.0.300/ml-computer-grs-inner-2.0.0.300.aar
Ml-computer-net:
https://developer.huawei.com/repo/com/huawei/hms/ml-computer-net/1.0.4.300/ml-computer-net-1.0.4.300.aar
Okhttp:
https://jar-download.com/artifacts/com.squareup.okhttp3/okhttp/3.12.0/source-code
Okio:
https://mvnrepository.com/artifact/com.squareup.okio/okio/1.15.0
Ml-computer-voice-tts:
https://developer.huawei.com/repo/com/huawei/hms/ml-computer-voice-tts/2.0.0.401/ml-computer-voice-tts-2.0.0.401.aar
2. Open AAR packages with rar tool and rename the class.jar and AndroidManifest.xml files. And save those jar file inside libs folder (It is recommended that the two files be renamed consistently with the AAR package names.)
3. Copy required permissions in the <manifest> section in B4A IDE
4. Copy all configurations in the <application> section
5. Change ${applicationId} to $PACKAGE$.
6. Download the configuration file (agconnect-services.json) from App Gallery Connect and Add the JSON File to the assets Folder of the AAR file as shown below.
[/CENTER]
B4A will automatically incorporate files in the assets folder of an AAR package to the assets folder of your main project.
Encapsulating Java Files Using SLC
1. Create Library as parent and then create bin, libs and src as subfolder in the project directory.
2. Develop java project inside the following path, Choose Library Folder > src > b4x > tts > demo
3. Import the following two lines of code to each Java file
Code:
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.*;
import anywheresoftware.b4a.IOnActivityResult;
4. Add necessary annotations to the Java files
Code:
@DependsOn(values={
"base-4.0.1.300.aar",
"agconnect-core-1.0.1.300.aar",
"tasks-1.3.1.302.aar",
"network-common-4.0.2.301.aar",
"network-grs-4.0.2.301.aar",
"ml-computer-net-2.0.1.300.aar",
"ml-computer-grs-inner-2.0.1.300.aar",
"okhttp-3.12.0.jar",
"okio-1.15.0.jar",
"ml-computer-voice-tts-2.0.1.300.aar",
"ml-computer-agc-inner-2.0.1.300.aar",
"ml-computer-commonutils-inner-2.0.1.300.aar",
"ml-computer-ha-inner-2.0.1.300.aar"
})
5. Modify the context.
B4A does not recognize the Context class. Therefore, when parsing a class that contains the @vercion(1.0f) annotation, it will report an error if a method of the class has referenced Context. In this case, you need to change Context to the B4A object BA
Code:
@ShortName("ttsWork")
@Events(values = {"TTSStop (token As String)"})
public class ttsWork {
public static String eventName = "listener";
public static final String TAG = "tts_Kit";
public static void init(Context context) {
AGConnectServicesConfig.fromContext(context).overlayWith(new LazyInputStream(context) {
@Override
public InputStream get(Context context) {
try {
return context.getAssets().open("agconnect-services.json");
} catch (IOException e) {
e.printStackTrace();
BA.Log(e.toString());
}
return null;
}
});
}
private static IOnActivityResult ion;
public static void ListenFortts(final Context context, String text) {
MLApplication.getInstance().setApiKey("API_KEY");
MLTtsConfig mlConfigs = new MLTtsConfig().setLanguage(MLTtsConstants.TTS_EN_US).setPerson(MLTtsConstants.TTS_SPEAKER_FEMALE_EN).setSpeed(1.0f).setVolume(1.0f);
MLTtsEngine mlTtsEngine = new MLTtsEngine(mlConfigs);
Log.d(" text from ui ", text);
mlTtsEngine.updateConfig(mlConfigs);
mlTtsEngine.setTtsCallback(callback);
String sourceText = "Welcome";
String id = mlTtsEngine.speak(text, MLTtsEngine.QUEUE_APPEND);
}
static MLTtsCallback callback = new MLTtsCallback() {
@Override
public void onError(String taskId, MLTtsError err) {
}
@Override
public void onWarn(String taskId, MLTtsWarn warn) {
}
@Override
public void onRangeStart(String taskId, int start, int end) {
}
@Override
public void onAudioAvailable(String taskId, MLTtsAudioFragment audioFragment, int offset, Pair<Integer, Integer> range, Bundle bundle)
{
}
@Override
public void onEvent(String taskId, int eventId, Bundle bundle) {
// Log.v(" stop service ","text1111");
switch (eventId) {
case MLTtsConstants.EVENT_PLAY_START:
Log.v(" stop service ", "textplaystart");
ba.raiseEventFromUI(this, eventName + "_TTSText", "text");
Log.v(" stop service ", "text11");
break;
case MLTtsConstants.EVENT_PLAY_STOP:
boolean isInterrupted = bundle.getBoolean(MLTtsConstants.EVENT_PLAY_STOP_INTERRUPTED);
Log.v(" stop service ", "textplaystop");
ba.raiseEventFromUI(this, eventName + "_TTSStop", "Service Stopped");
Log.v(" stop service ", "text11");
break;
case MLTtsConstants.EVENT_PLAY_RESUME:
break;
case MLTtsConstants.EVENT_PLAY_PAUSE:
break;
case MLTtsConstants.EVENT_SYNTHESIS_START:
break;
case MLTtsConstants.EVENT_SYNTHESIS_END:
break;
case MLTtsConstants.EVENT_SYNTHESIS_COMPLETE:
boolean isInterrupted1 = bundle.getBoolean(MLTtsConstants.EVENT_SYNTHESIS_INTERRUPTED);
break;
default:
break;
}
}
};
}
Can we customize languages ?

How Can I Quickly Integrate AppGallery Connect Cloud Storage into a Unity App?

HUAWEI AppGallery Connect Cloud Storage provides maintenance-free cloud storage functions.
In this post, we’ll walk you through the steps required for integrating this service in Unity. You can access the Cloud Storage sample code on GitHub.
1. Test Environment
SDK Version: agconnect-storage:1.3.1.100
Platform:Unity 2019.4.17f1c1
Test Device:HONOR Magic 2
AppGallery Connect:
https://developer.huawei.com/consumer/en/service/josp/agc/index.html
2. Applying for Cloud Storage
Currently, Cloud Storage is still in beta status. To use the service, you need to send an application by email. For details, check:
https://developer.huawei.com/consum...Gallery-connect-Guides/agc-cloudstorage-apply
3. Importing the Unity Package
Unity materials:
https://docs.unity.cn/cn/Packages-cn/[email protected]/manual/cloudstorage.html
1. Download Unity Hub here and install Unity Editor.
2. Configure the Android environment.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
3. Import the HuaweiServices package. Download the package from Unity Assets Store. In Unity Editor, choose Assets > Import package.
Select the required package and click Import.
4. Completing Configurations in AppGallery Connect
1. Sign in to AppGallery Connect, and click the created app.
2. Go to My projects > Build > Cloud Storage and click Enable now. Set the default storage instance as required.
To permit Cloud Storage to read and write data without authorization, add the following code:
XML:
agc.cloud.storage[
match: /{bucket}/{path=**} {
allow read, write: if true;
}
]
3. Go to Project settings > General information and download the latest agconnect-services.json file.
4. Save the file to the Assets\Plugins\Android directory of your Unity project.
If the downloaded JSON file does not contain the default_storage parameter under cloudstorage, you need to add it manually. Set its value to the storage instance you just configured.
5. Completing Project Information in Unity Editor
1. Choose Edit > Project Settings > Player > Publish Settings. In the Build area, select the items for Android according to your requirements.
2. In Unity Editor, choose Edit > Project Settings > Player > Other Settings, and set the package name to match the package name you set in AppGallery Connect.
3. Add the following code to the project-level baseProjectTmeplate.gradle file in the Assets\Plugins\Android directory:
XML:
allprojects {
buildscript {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.huawei.agconnect:agcp:1.4.2.301'
**BUILD_SCRIPT_DEPS**
}
}
repositories {
maven { url 'https://developer.huawei.com/repo/' }
}
}
4. Add the following code to the app-level LauncherTmeplate.gradle file in the Assets\Plugins\Android directory:
XML:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation project(':unityLibrary')
implementation "com.huawei.agconnect:agconnect-storage:1.3.1.100"
implementation 'com.huawei.agconnect:agconnect-auth:1.4.2.301'
5. Configure the manifest file to add the corresponding permissions.
XML:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="false"
android:requestLegacyExternalStorage="true" >
6. Using Cloud Storage
1. Configure the UI layout.
In Unity Editor, choose GameObject > UI > Button and create a button. Click the button, click Add Component on the right to create a script file, and then create the corresponding method.
2. Initialize the storage instance and apply for the read and write permissions.
C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiService;
using HuaweiService.CloudStorage;
using System;
public delegate void SuccessCallBack<T>(T o);
public class HmsSuccessListener<T>:OnSuccessListener{
public SuccessCallBack<T> CallBack;
public HmsSuccessListener(SuccessCallBack<T> c){
CallBack = c;
}
public void onSuccess(T arg0)
{
Debug.Log("OnSuccessListener onSuccess");
if(CallBack != null)
{
CallBack.Invoke(arg0);
}
}
public override void onSuccess(AndroidJavaObject arg0){
Debug.Log("OnSuccessListener onSuccess");
if(CallBack !=null)
{
Type type = typeof(T);
IHmsBase ret = (IHmsBase)Activator.CreateInstance(type);
ret.obj = arg0;
CallBack.Invoke((T)ret);
}
}
}
public class testStorageDemo : MonoBehaviour
{
private AGCStorageManagement mAGCStorageManagement;
private string[] permissions =
{
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_EXTERNAL_STORAGE",
};
// Start is called before the first frame update
void Start()
{
AndroidJavaClass javaUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
Activity aaa = HmsUtil.GetHmsBase<Activity>(currentActivity);
ActivityCompat.requestPermissions(aaa, permissions, 1);
}
// Update is called once per frame
void Update()
{
}
public void initAGCStorageManagement() {
mAGCStorageManagement = AGCStorageManagement.getInstance("9105385871708601205-ffeon");
Debug.Log("Instance is: "+ mAGCStorageManagement);
}
public class MySuccessListener : OnSuccessListener
{
private string m_name;
public MySuccessListener(string name)
{
m_name = name;
}
public MySuccessListener()
{
m_name = "default";
}
public override void onSuccess(AndroidJavaObject ex)
{
Debug.Log("download success: " + m_name);
}
}
}
3. Upload a file.
C#:
public void uploadFile() {
if (mAGCStorageManagement == null){
initAGCStorageManagement();
}
string fileName = "testUnity.jpg";
StorageReference reference = mAGCStorageManagement.getStorageReference(fileName);
string FileFolder = "/storage/emulated/0/AGCSdk/";
string FilePath = FileFolder + fileName;
Debug.Log("FilePath = " + FilePath);
File file = new File(FilePath);
Debug.Log("UploadFile = " + file);
UploadTask task = reference.putFile(file);
task.addOnSuccessListener(new MySuccessListener());
Debug.Log("UploadFile done:");
}
4. Download a file.
C#:
public void downloadFile() {
if (mAGCStorageManagement == null){
initAGCStorageManagement();
}
StorageReference reference = mAGCStorageManagement.getStorageReference("test.jpg");
string FileFolder = "/storage/emulated/0/AGCSdk/";
string FilePath = FileFolder + "test.jpg";
Debug.Log("FilePath = " + FilePath);
File file = new File(FilePath);
Debug.Log("File = " + file);
DownloadTask task = reference.getFile(file);
task.addOnSuccessListener(new MySuccessListener("NormalListener"));
Debug.Log("DownloadTask Result:");
}
5. Delete a file.
C#:
public void deleteFile() {
if (mAGCStorageManagement == null){
initAGCStorageManagement();
}
StorageReference reference = mAGCStorageManagement.getStorageReference("testUnity.jpg");
reference.delete();
Debug.Log("DeleteFileTest success.");
}
6. Package and test the app.
After you have packaged and installed the test app, you can tap each button, and view related logs in the Android Studio Logcat.
You can view the files you upload to or download from AppGallery Connect.
7. Summary
You can use Cloud Storage to store your Unity game data on the cloud without the hassle of server building and O&M. With AppGallery Connect, a web console, you can easily manage your files on the cloud side.
In addition to file upload, download, and deletion, Cloud Storage also offers a metadata setting function.
For reference:
Cloud Storage development guide:
https://developer.huawei.com/consum...-connect-Guides/agc-cloudstorage-introduction
Unity materials:
https://docs.unity.cn/cn/Packages-cn/[email protected]/manual/cloudstorage.html
Cloud Storage codelab:
https://github.com/AppGalleryConnect/agc-demos/tree/main/Android/agc-cloudstorage-demo-java

Extract table data from Image using Huawei Table Recognition by Huawei HiAI in Android

Introduction
The API can perfectly retrieve the information of tables as well as text from cells, besides, merged cells can also be recognized. It supports recognition of tables with clear and unbroken lines, but not supportive of tables with crooked lines or cells divided by color background. Currently the API supports recognition from printed materials and snapshots of slide meetings, but it is not functioning for the screenshots or photos of excel sheets and any other table editing software.
Here, the image resolution should be higher than 720p (1280×720 px), and the aspect ratio (length-to-width ratio) should be lower than 2:1.
In this article, we will learn how to implement Huawei HiAI kit using Table Recognition service into android application, this service helps us to extract the table content from images.
Software requirements
1. Any operating system (MacOS, Linux and Windows).
2. Any IDE with Android SDK installed (IntelliJ, Android Studio).
3. HiAI SDK.
4. Minimum API Level 23 is required.
5. Required EMUI 9.0.0 and later version devices.
6. Required processors kirin 990/985/980/970/ 825Full/820Full/810Full/ 720Full/710Full
How to integrate Table recognition.
1. Configure the application on the AGC.
2. Apply for HiAI Engine Library.
3. Client application development process.
Configure application on the AGC
Follow the steps.
Step 1: We need to register as a developer account in AppGallery Connect. If you are already a developer ignore this step.
Step 2: Create an app by referring to Creating a Project and Creating an App in the Project
Step 3: Set the data storage location based on the current location.
Step 4: Generating a Signing Certificate Fingerprint.
Step 5: Configuring the Signing Certificate Fingerprint.
Step 6: Download your agconnect-services.json file, paste it into the app root directory.
Apply for HiAI Engine Library
What is Huawei HiAI?
HiAI is Huawei's AI computing platform. HUAWEI HiAI is a mobile terminal–oriented artificial intelligence (AI) computing platform that constructs three layers of ecology: service capability openness, application capability openness, and chip capability openness. The three-layer open platform that integrates terminals, chips, and the cloud brings more extraordinary experience for users and developers.
How to apply for HiAI Engine?
Follow the steps
Step 1: Navigate to this URL, choose App Service > Development and click HUAWEI HiAI.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Step 2: Click Apply for HUAWEI HiAI kit.
Step 3: Enter required information like Product name and Package name, click Next button.
Step 4: Verify the application details and click Submit button.
Step 5: Click the Download SDK button to open the SDK list.
Step 6: Unzip downloaded SDK and add into your android project under libs folder.
Step 7: Add jar files dependences into app build.gradle file.
implementation fileTree(include: ['*.aar', '*.jar'], dir: 'libs')
implementation 'com.google.code.gson:gson:2.8.6'
repositories {
flatDir {
dirs 'libs'
}
}Copy code
Client application development process
Follow the steps.
Step 1: Create an Android application in the Android studio (Any IDE which is your favorite).
Step 2: Add the App level Gradle dependencies. Choose inside project Android > app > build.gradle.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Root level gradle dependencies.
Code:
maven { url 'https://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Step 3: Add permission in AndroidManifest.xml
XML:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
Step 4: Build application.
Java:
import android.Manifest;
import android.content.Intent;
import android.graphics.Bitmap;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.huawei.hiai.vision.common.ConnectionCallback;
import com.huawei.hiai.vision.common.VisionBase;
import com.huawei.hiai.vision.common.VisionImage;
import com.huawei.hiai.vision.image.sr.ImageSuperResolution;
import com.huawei.hiai.vision.text.TableDetector;
import com.huawei.hiai.vision.visionkit.text.config.VisionTableConfiguration;
import com.huawei.hiai.vision.visionkit.text.table.Table;
import com.huawei.hiai.vision.visionkit.text.table.TableCell;
import com.huawei.hiai.vision.visionkit.text.table.TableContent;
import java.util.List;
public class TableRecognition extends AppCompatActivity {
private boolean isConnection = false;
private int REQUEST_CODE = 101;
private int REQUEST_PHOTO = 100;
private Bitmap bitmap;
private Button btnImage;
private ImageView originalImage;
private ImageView conversionImage;
private TextView textView;
private TextView tableContentText;
private final String[] permission = {
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE};
private ImageSuperResolution resolution;
private TableLayout tableLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_table_recognition);
requestPermissions(permission, REQUEST_CODE);
initializeVisionBase();
originalImage = findViewById(R.id.super_origin);
conversionImage = findViewById(R.id.super_image);
textView = findViewById(R.id.text);
tableContentText = findViewById(R.id.content_text);
btnImage = findViewById(R.id.btn_album);
tableLayout = findViewById(R.id.tableLayout);
btnImage.setOnClickListener(v -> {
selectImage();
tableLayout.removeAllViews();
});
}
private void initializeVisionBase() {
VisionBase.init(this, new ConnectionCallback() {
@Override
public void onServiceConnect() {
isConnection = true;
DoesDeviceSupportTableRecognition();
}
@Override
public void onServiceDisconnect() {
}
});
}
private void DoesDeviceSupportTableRecognition() {
resolution = new ImageSuperResolution(this);
int support = resolution.getAvailability();
if (support == 0) {
Toast.makeText(this, "Device supports HiAI Image super resolution service", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Device doesn't supports HiAI Image super resolution service", Toast.LENGTH_SHORT).show();
}
}
public void selectImage() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_PHOTO);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (data != null && requestCode == REQUEST_PHOTO) {
try {
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
originalImage.setImageBitmap(bitmap);
if (isConnection) {
extractTableFromTheImage();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void extractTableFromTheImage() {
tableContentText.setVisibility(View.VISIBLE);
TableDetector mTableDetector = new TableDetector(this);
VisionImage image = VisionImage.fromBitmap(bitmap);
VisionTableConfiguration mTableConfig = new VisionTableConfiguration.Builder()
.setAppType(VisionTableConfiguration.APP_NORMAL)
.setProcessMode(VisionTableConfiguration.MODE_OUT)
.build();
mTableDetector.setVisionConfiguration(mTableConfig);
mTableDetector.prepare();
Table table = new Table();
int mResult_code = mTableDetector.detect(image, table, null);
if (mResult_code == 0) {
int count = table.getTableCount();
List<TableContent> tc = table.getTableContent();
StringBuilder sbTableCell = new StringBuilder();
List<TableCell> tableCell = tc.get(0).getBody();
for (TableCell c : tableCell) {
List<String> words = c.getWord();
StringBuilder sb = new StringBuilder();
for (String s : words) {
sb.append(s).append(",");
}
String cell = c.getStartRow() + ":" + c.getEndRow() + ": " + c.getStartColumn() + ":" +
c.getEndColumn() + "; " + sb.toString();
sbTableCell.append(cell).append("\n");
tableContentText.setText("Count = " + count + "\n\n" + sbTableCell.toString());
}
}
}
}Copy code
Result
Tips and Tricks
Recommended image should be larger than 720px.
Multiple table recognition currently not supported.
If you are taking Video from a camera or gallery make sure your app has camera and storage permission.
Add the downloaded huawei-hiai-vision-ove-10.0.4.307.aar, huawei-hiai-pdk-1.0.0.aar file to libs folder.
Check dependencies added properly.
Latest HMS Core APK is required.
Min SDK is 21. Otherwise you will get Manifest merge issue.
Conclusion
In this article, we have done table content extraction from image, for further analysis with statistics or just for editing it. This works for tables with clear and simple structure information. We have learnt the following concepts.
1. Introduction of Table recognition?
2. How to integrate Table using Huawei HiAI
3. How to Apply Huawei HiAI
4. How to build the application
Reference
Table Recognition
Apply for Huawei HiAI
Happy coding
useful sharing,thanks!

Categories

Resources