Related
Bank Card Recognition
The bank card recognition service can quickly recognize information such as the bank card number, covering mainstream bank cards such as China UnionPay, American Express, Mastercard, Visa, and JCB around the world. It is widely used in finance and payment scenarios requiring bank card binding to quickly extract bank card information, realizing quick input of bank card information.
{
"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"
}
Configuring App Information in AppGallery Connect
Before you start developing an app, configure app information in AppGallery Connect.
Registering as a Developer
Before you get started, you must register as a Huawei developer and complete identity verification on HUAWEI Developers. For details refer to Registration and Verification.
Creating an App
Follow the instructions to create an app in Creating an AppGallery Connect Project and Adding an App to the Project.
Enabling the Service
1. Sign in to AppGallery Connect and select My projects.
2. Find your project from the project list and click the app for which you need to enable a service on the project card.
3. Click the Manage APIs tab and toggle the switch for the service to be enabled.
Adding the AppGallery Connect Configuration File
For adding configuration file refer this below link
https://developer.huawei.com/consum...MSCore-Guides/add-appgallery-0000001050038080
Configuring the Maven Repository Address
For adding Maven files refer this below link
https://developer.huawei.com/consum.../HMSCore-Guides/config-maven-0000001050040031
Integrating the Bank Card Recognition Plug-in
The bank card recognition service supports two SDK integration modes: full SDK and base SDK. You can select either one based on your needs.
Mode 1: Full SDK Integration (Recommended)
Combine the bank card recognition model and bank card recognition plug-in into a package.
The following is the sample code for integration in full SDK mode:
Code:
dependencies{
// Import the combined package of the bank card recognition plug-in and recognition capability.
implementation 'com.huawei.hms:ml-computer-card-bcr:2.0.3.301'
}
Mode 2: Base SDK Integration
The sample code is as follows:
Code:
dependencies{
// Import the bank card recognition plug-in package.
implementation 'com.huawei.hms:ml-computer-card-bcr-plugin:2.0.3.301'
}
Adding the Configuration to the File Header
After integrating the SDK in either mode, add the following information under apply plugin: 'com.android.application' in the file header:
Code:
apply plugin: 'com.huawei.agconnect'
Development Process
1. Create the recognition result callback function and reload the onSuccess, onCanceled, onFailure, and onDenied methods.
onSuccess indicates that the recognition is successful.
MLBcrCaptureResult indicates the recognition result.
onCanceled indicates that the user cancels the recognition.
onFailure indicates that the recognition fails.
onDenied indicates that the recognition request is denied, For example the camera is unavailable.
Code:
private void initCallBack() {
callback = new MLBcrCapture.Callback() {
@Override
public void onSuccess(MLBcrCaptureResult bankCardResult) {
if (bankCardResult != null) {
String cardNumber = bankCardResult.getNumber();
String cardExpire = bankCardResult.getExpire();
String cardIssuer = bankCardResult.getIssuer();
String cardType = bankCardResult.getType();
String cardOrganization = bankCardResult.getOrganization();
CardModel cardModel = new CardModel(cardNumber, cardExpire, cardIssuer, cardType, cardOrganization);
Intent intent = new Intent(ScanCardActivity.this, PaymentActivity.class);
intent.putExtra("CardData", cardModel);
startActivity(intent);
}
// Processing for successful recognition.
}
@Override
public void onCanceled() {
// Processing for recognition request cancelation.
}
// Callback method used when no text is recognized or a system exception occurs during recognition.
// retCode: result code.
// bitmap: bank card image that fails to be recognized.
@Override
public void onFailure(int retCode, Bitmap bitmap) {
// Processing logic for recognition failure.
}
@Override
public void onDenied() {
// Processing for recognition request deny scenarios, for example, the camera is unavailable.
}
};
}
2. Set the recognition parameters for calling the captureFrame API of the recognizer. The recognition result is returned through the callback function created in initCallBack method.
Code:
private void startCaptureActivity(MLBcrCapture.Callback callback) {
MLBcrCaptureConfig config = new MLBcrCaptureConfig.Factory()
// Set the expected result type of bank card recognition.
// MLBcrCaptureConfig.RESULT_NUM_ONLY: Recognize only the bank card number.
// MLBcrCaptureConfig.RESULT_SIMPLE: Recognize only the bank card number and validity period.
// MLBcrCaptureConfig.ALL_RESULT: Recognize information such as the bank card number, validity period, issuing bank, card organization, and card type.
.setResultType(MLBcrCaptureConfig.RESULT_ALL)
// Set the recognition screen display orientation.
// MLBcrCaptureConfig.ORIENTATION_AUTO: adaptive mode. The display orientation is determined by the physical sensor.
// MLBcrCaptureConfig.ORIENTATION_LANDSCAPE: landscape mode.
// MLBcrCaptureConfig.ORIENTATION_PORTRAIT: portrait mode.
.setOrientation(MLBcrCaptureConfig.ORIENTATION_AUTO)
.create();
MLBcrCapture bankCapture = MLBcrCaptureFactory.getInstance().getBcrCapture(config);
bankCapture.captureFrame(this, callback);
}
3. In the callback of the recognition button, call the method defined in startCaptureActivity method to implement bank card recognition
Code:
@Override
public void onClick(View view) {
switch (view.getId()) {
// Detection button.
case R.id.btn_scan_bank_card:
if (checkPermissions())
startCaptureActivity(callback);
break;
default:
break;
}
}
Adding Permissions
CAMERA: To use the camera on the device for recognition or detection, your app needs to apply for the camera permission.
READ_EXTERNAL_STORAGE: To use general card recognition plug-in, your app needs to apply for the file read permission.
The procedure is as follows:
1. Specify permissions in the AndroidManifest.xml file.
Code:
<!--Camera permission-->
<uses-permission android:name="android.permission.CAMERA" />
<!--Read permission-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2. After specifying the permissions in the AndroidManifest.xml file, dynamically apply for the permissions in the code for Android 6.0 and later versions.
Code:
private int PERMISSION_REQUEST_CODE = 10;
private boolean checkPermissions() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// The app has the camera permission.
return true;
} else {
// Apply for the camera permission.
requestCameraPermission();
}
return false;
}
private void requestCameraPermission() {
final String[] permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0) {
boolean cameraAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean storageAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (cameraAccepted && storageAccepted ) {
Toast.makeText(ScanCardActivity.this, "Permission Granted, Now you can access camera", Toast.LENGTH_SHORT).show();
startCaptureActivity(callback);
} else {
requestCameraPermission();
}
}
}
}
Find the activity_scan_card.xml as follows
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_scan_bank_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="50dp"
android:background="@drawable/btn_background"
android:text="Scan Bank Card"
android:textAllCaps="false"
android:textColor="#fff" />
</RelativeLayout>
In the onSuccess method we used one CardModel class implemented with Parcelable interface. Here CardModel is used for passing data through intent to PaymentActivity. Check the CardModel class below
Code:
public class CardModel implements Parcelable {
private String cardNumber;
private String cardExpire;
private String cardIssuer;
private String cardType;
private String cardOrganization;
public CardModel(String cardNumber, String cardExpire, String cardIssuer, String cardType,String cardOrganization) {
this.cardNumber = cardNumber;
this.cardExpire = cardExpire;
this.cardIssuer = cardIssuer;
this.cardType = cardType;
this.cardOrganization = cardOrganization;
}
protected CardModel(Parcel in) {
cardNumber = in.readString();
cardExpire = in.readString();
cardIssuer = in.readString();
cardType = in.readString();
cardOrganization = in.readString();
}
public static final Creator<CardModel> CREATOR = new Creator<CardModel>() {
@Override
public CardModel createFromParcel(Parcel in) {
return new CardModel(in);
}
@Override
public CardModel[] newArray(int size) {
return new CardModel[size];
}
};
public String getCardNumber() {
return cardNumber;
}
public String getCardExpire() {
return cardExpire;
}
public String getCardIssuer() {
return cardIssuer;
}
public String getCardType() {
return cardType;
}
public String getCardOrganization() {
return cardOrganization;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(cardNumber);
parcel.writeString(cardExpire);
parcel.writeString(cardIssuer);
parcel.writeString(cardType);
parcel.writeString(cardOrganization);
}
}
After reconizing card details we are passing all the details to PaymentActivity for further steps. Check the activity_payment.xml below
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000">
<RelativeLayout
android:id="@+id/rl_tool"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Payment"
android:textColor="#fff"
android:textSize="16sp"
android:textStyle="bold" />
</RelativeLayout>
<androidx.cardview.widget.CardView
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_below="@+id/rl_tool"
android:layout_margin="10dp"
app:cardCornerRadius="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/chip" />
<TextView
android:id="@+id/txt_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="YOUR NAME"
android:textColor="#fff"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_card_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/txt_valid"
android:layout_centerHorizontal="true"
android:shadowColor="#7F000000"
android:shadowDx="1"
android:shadowDy="2"
android:shadowRadius="5"
android:textColor="#FBFBFB"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_valid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/img_organization"
android:layout_centerHorizontal="true"
android:text="VALID\nTHRU"
android:textColor="#fff"
android:textSize="10sp" />
<TextView
android:id="@+id/txt_expiry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txt_card_number"
android:layout_centerHorizontal="true"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@id/txt_valid"
android:textColor="#fff"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_card_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textAllCaps="true"
android:textColor="#fff"
android:textSize="18sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/img_organization"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/card"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Verify your Card information"
android:textColor="#fff"
android:textSize="14sp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColorHint="#fff"
app:hintTextColor="#fff">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_card_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Card Number"
android:textColor="#fff" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColorHint="#fff"
app:hintTextColor="#fff">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_valid_thru"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Valid Thru"
android:textColor="#fff" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColorHint="#fff"
app:hintTextColor="#fff">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_cvv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="CVV"
android:inputType="textPassword"
android:text="***"
android:textColor="#fff" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<androidx.appcompat.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_margin="20dp"
android:background="@drawable/btn_background"
android:paddingStart="20dp"
android:paddingTop="5dp"
android:paddingEnd="20dp"
android:paddingBottom="5dp"
android:text="Pay Now"
android:textColor="#fff"
android:textSize="12sp" />
</RelativeLayout>
In the PaymentActivity, We differentiated cards based on getCardOrganization method. Added different background colors and related data. Check the PaymentActivity code below
Code:
public class PaymentActivity extends AppCompatActivity {
private TextView txtCardNumber;
private TextView txtCardType;
private TextView txtExpire;
private TextView txtUserName;
private ImageView imgOrganization;
private CardView cardBackground;
private TextInputEditText edtCardNumber;
private TextInputEditText edtValid;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_payment);
init();
Intent intent = getIntent();
if (intent != null) {
CardModel cardModel = intent.getParcelableExtra("CardData");
if (cardModel != null) {
setCardData(cardModel);
}
}
}
private void setCardData(CardModel cardModel) {
String cardNumber = cardModel.getCardNumber().replaceAll("....", "$0 ");
txtCardNumber.setText(cardNumber);
edtCardNumber.setText(cardNumber);
txtExpire.setText(cardModel.getCardExpire());
edtValid.setText(cardModel.getCardExpire());
if (cardModel.getCardType() == null) {
txtCardType.setText("CARD TYPE");
} else {
txtCardType.setText(cardModel.getCardType());
}
String cardOrganization = cardModel.getCardOrganization();
if (cardOrganization != null) {
if (cardOrganization.equalsIgnoreCase("MASTERCARD")) {
setCardBackgroundAndOrganization(R.drawable.master_card, "#c22e67");
} else if (cardOrganization.equalsIgnoreCase("VISA")) {
setCardBackgroundAndOrganization(R.drawable.visa, "#4812e8");
} else if (cardOrganization.equalsIgnoreCase("UnionPay")) {
imgOrganization.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.union));
cardBackground.setCardBackgroundColor(Color.parseColor("#918B8B"));
Shader shader = new LinearGradient(70, 50, 100, 100, Color.RED, Color.BLACK, Shader.TileMode.CLAMP);
txtCardType.getPaint().setShader(shader);
}
} else {
txtCardNumber.setTextColor(Color.BLACK);
txtExpire.setTextColor(Color.BLACK);
txtCardType.setTextColor(Color.BLACK);
txtUserName.setTextColor(Color.BLACK);
}
Toast.makeText(this, cardModel.getCardOrganization() + " " + cardModel.getCardIssuer() + " " + cardModel.getCardType(), Toast.LENGTH_LONG).show();
}
private void init() {
txtCardNumber = findViewById(R.id.txt_card_number);
txtCardType = findViewById(R.id.txt_card_type);
txtExpire = findViewById(R.id.txt_expiry);
imgOrganization = findViewById(R.id.img_organization);
cardBackground = findViewById(R.id.card);
edtCardNumber = findViewById(R.id.edt_card_number);
edtValid = findViewById(R.id.edt_valid_thru);
txtUserName = findViewById(R.id.txt_user_name);
}
private void setCardBackgroundAndOrganization(int cardOrganization, String backgroundColor) {
imgOrganization.setImageDrawable(ContextCompat.getDrawable(this, cardOrganization));
cardBackground.setCardBackgroundColor(Color.parseColor(backgroundColor));
Shader shader = new LinearGradient(0, 0, 0, 100, Color.WHITE, Color.DKGRAY, Shader.TileMode.CLAMP);
txtCardType.getPaint().setShader(shader);
}
}
Find the output in below images
Tips and Tricks
We need to use CAMERA and READ_EXTERNAL_STORAGE permissions. It is recognizing cardNumber, cardType, cardIssuer and cardOrganization but it is not recognizing cardHolderName. For some of the cards it is returning same value and some cards null values for cardType, cardIssuer and cardOrganization
Conclusion
In this article we can learn about how to scan and get bank card details by using ML Kit Bank card recognition and also populating the recognized data to payment screen with good User Interface.
Reference links
https://developer.huawei.com/consum...Guides/bank-card-recognition-0000001050038118
Xamarin is a popular cross platform framework to build mobile applications using .net
A number of AppGallery Connect services support many cross platform frameworks including Xamarin. Today we are going to take a look at how you can use one of these services, App Linking within your Xamarin project.
Enabling App Linking in AppGallery Connect
Create an app or use an existing app in AppGallery Connect. Click My projects, go to Grow > App Linking, and click Use now on the displayed page.
On the displayed App Linking page, click the URL prefixes tab and then click New URL prefix to create a unique URL prefix.
{
"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"
}
Preparing the Xamarin Android Development EnvironmentCompleting Android SetupDownload the JSON file from AppGallery Connect and copy the file to your project’s Assets directory.
Set a package name. Right-click your project and choose Properties. Click Android Manifest on the displayed page and set the same package name to that in the JSON file.
Implement LazyInputStream to read the agconnect-services.json file.
Right-click your project, and choose Add > New Item. In the displayed window, select Class and name the new class HmsLazyInputStream.cs.
Implement LazyInputStream to read the agconnect-services.json file.
Right-click your project, and choose Add > New Item. In the displayed window, select Class and name the new class HmsLazyInputStream.cs.
C-like:
using System;
using System.IO;
using Android.Content;
using Android.Util;
using Huawei.Agconnect.Config;
namespace AppLinking1
{
public class HmsLazyInputStream : LazyInputStream
{
public HmsLazyInputStream(Context context)
: base(context)
{
}
public override Stream Get(Context context)
{
try
{
return context.Assets.Open("agconnect-services.json");
}
catch (Exception e)
{
Log.Error("Hms", $"Failed to get input stream" + e.Message);
return null;
}
}
}
}
Create another new class as described in the preceding steps and read the agconnect-services.json file before your app is launched. You can name the new class CustomContentProvider.cs, which extends the ContentProvider class, and set the authorities and InitOrder attributes for the new class.
C-like:
using System;
using Android.Content;
using Android.Database;
using Huawei.Agconnect.Config;
namespace AppLinking1
{
[ContentProvider(new string[] { "com.huawei.applinkingdemo.CustomContentProvider" }, InitOrder = 99)]
class CustomContentProvider : ContentProvider
{
public override int Delete(Android.Net.Uri uri, string selection, string[] selectionArgs)
{
throw new NotImplementedException();
}
public override string GetType(Android.Net.Uri uri)
{
throw new NotImplementedException();
}
public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
{
throw new NotImplementedException();
}
public override bool OnCreate()
{
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(Context);
config.OverlayWith(new HmsLazyInputStream(Context));
return false; throw new NotImplementedException();
}
public override ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
{
throw new NotImplementedException();
}
public override int Update(Android.Net.Uri uri, ContentValues values, string selection, string[] selectionArgs)
{
throw new NotImplementedException();
}
}
}
Installing the Service SDK for AndroidRight-click your project and choose Manage NuGet Packages.
Search for AppLinking on the Browse tab. Click Xamarin.Android bindings for AGC — Applinking in the search results and install it.
Agree to the service agreement as prompted.
Developing Your AppCreating an App Linking LinkTo specify the layout of your app, open the activity_main file under Resources > layout. Sample code:
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textDeepLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="DeepLink:"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/deepLink"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create Link" />
<TextView
android:id="@+id/ShortLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Short Link:"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/textShortLink"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/LongLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Long Link:"
android:textSize="18sp"
android:textStyle="bold"
/>
<TextView
android:id="@+id/textLongLink"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/shareShort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Share Short Link" />
<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="80dp" />
</LinearLayout>
Open the MainActivity.cs file and import the following packages
C-like:
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Widget;
using AndroidX.AppCompat.App;
using System;
using Huawei.Agconnect.Applinking;
using Uri = Android.Net.Uri;
using Debug = System.Diagnostics.Debug;
using Android.Content;
Configure button actions in the onCreate method
C-like:
FindViewById<Button>(Resource.Id.create).Click += CreateAppLink;
FindViewById<Button>(Resource.Id.shareShort).Click += ShareShortAppLink;
Create an App Linking link and implement the method for sharing the link.
C-like:
private AppLinking.Builder builder;
public static string longLink = null;
public static string shortLink = null;
public static string UriPrefix = "https://applinkingtest.drcn.agconnect.link";
public static string OpenApp_Link = "https://open.androiddemoapp.com";
public static string OpenDetail_Link = "https://open.androiddemoapp.com/detail?id=358";
private async void CreateAppLink(object sender, EventArgs e)
{
builder = new AppLinking.Builder();
// Set a URL prefix.
builder.SetUriPrefix(UriPrefix);
// Set a deep link.
builder.SetDeepLink(Uri.Parse(OpenApp_Link));
//Set the link preview type. If this method is not called, the preview page with app information is displayed by default.
builder.SetPreviewType(AppLinking.LinkingPreviewType.AppInfo);
// (Optional) Set Android link behavior.
var behaviorBuilder = new AppLinking.AndroidLinkInfo.Builder();
// Set an earliest version. If a user's app version is earlier than the earliest version, your app will redirect the user to update the app on AppGallery.
behaviorBuilder.SetMinimumVersion(1);
builder.SetAndroidLinkInfo(behaviorBuilder.Build());
longLink = builder.BuildAppLinking().Uri.ToString();
FindViewById<TextView>(Resource.Id.textLongLink).Text = longLink;
}
private void ShareShortAppLink(object sender, EventArgs e)
{
string agcLink = FindViewById<TextView>(Resource.Id.textShortLink).Text;
Intent intent = new Intent(Intent.ActionSend);
intent.SetType("text/plain");
intent.PutExtra(Intent.ExtraText, agcLink);
intent.AddFlags(ActivityFlags.NewTask);
StartActivity(intent);
}
Receiving an App Linking LinkConfigure the code of the activity for receiving an App Linking link.
Right-click the project, choose add > New Item, select Activity, and name it DetailActivity.
The sample code is as follows:
C-like:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Huawei.Agconnect.Applinking;
namespace AppLinking1
{
//[Activity(Label = "DetailActivity")]
[Activity(Name = "com.company.app.DetailActivity", Label = "DetailActivity", Theme = "@style/AppTheme")]
public class DetailActivity : Activity
{
protected async override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_detail);
// Create your app here.
try
{
//To receive links, initialize the AGConnectAppLinking instance.
AGConnectAppLinking appLinkInstance = AGConnectAppLinking.Instance;
//Call GetAppLinkingAsync() to check links of App Linking to be processed
ResolvedLinkData resolvedLinkData = await appLinkInstance.GetAppLinkingAsync(this);
String deepLink = null;
if (resolvedLinkData != null)
{
deepLink = resolvedLinkData.DeepLink.ToString();
FindViewById<TextView>(Resource.Id.deepLink).Text = deepLink;
}
}
catch (System.Exception ex)
{
FindViewById<TextView>(Resource.Id.message).Text = ex.Message;
}
}
}
}
Configure the layout for the page of receiving an App Linking link.
Right-click the project, choose add > New Item, select Android Layout, and name it activity_detail. The sample code is as follows:
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:orientation="vertical"
tools:context=".DetailActivity">
<TextView
android:id="@+id/textDeepLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="DeepLink:"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/deepLink"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="message:"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="80dp" />
</LinearLayout>
Configure the Manifest file. Find the Properties directory and open the AndroidManifest file in the directory. Configure the following content in the element.
XML:
<activity android:name="com.company.app.DetailActivity" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="open.androiddemoapp.com" android:scheme="https" />
<data android:host="open.androiddemoapp.com" android:scheme="http" />
</intent-filter>
<!--App Linking SDK reads content on the clipboard each time the app is launched.-->
<meta-data android:name="com.huawei.agconnect.applinking.READ_CLIPBOARD_PERMISSION" android:value="Available" />
</activity>
Testing Your AppClick Run to test your app.
References
Getting started with Xamarin
App Linking (Android)
{
"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
In this article, we can learn how to manage your money using this Money Management app. User can add Income and Expenses in this app, so the data will be saved in room database, it can access in offline also. User can easily track their daily spending's and can take the preventive actions on the unnecessary expenses, so that they can save money and can invest in profitable return ways. In this app, user can add, update, delete and fetch operations.
So, I will provide the series of articles on this Money Management App, in upcoming articles I will integrate other Huawei Kits.
If you are new to this application, follow my previous articles.
Beginner: Find the introduction Sliders and Huawei Account Kit Integration in Money Management Android app (Kotlin) - Part 1
Beginner: Integration of Huawei Ads Kit and Analytics Kit in Money Management Android app (Kotlin) – Part 2
Components of Room DB
1. Entity
2. Dao
3. Database
1. Entity
Represents a table within the database. Room creates a table for each class that has @entity annotation, the fields in the class correspond to columns in the table. Therefore, the entity classes tend to be small model classes that does not contain any logic.
2. Dao
DAOs(Data Access Objects) are responsible for defining the methods that access the database. In the initial SQLite, we use the Cursor objects. With Room, we do not need all the Cursor related code and can simply define our queries using annotations in the Dao class.
3. Database
Contains the database holder and serves as the main access point for the underlying connection to your app's persisted, relational data.
To create a database, we need to define an abstract class that extends RoomDatabase. This class is annotated with @database, lists the entities contained in the database, and the DAOs which access them.
Requirements
1. Any operating system (MacOS, Linux and Windows).
2. Must have a Huawei phone with HMS 4.0.0.300 or later.
3. Must have a laptop or desktop with Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 and above installed.
4. Minimum API Level 24 is required.
5. Required EMUI 9.0.0 and later version devices.
How to integrate HMS Dependencies
1. First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a Huawei ID.
2. Create a project in android studio, refer Creating an Android Studio Project.
3. Generate a SHA-256 certificate fingerprint.
4. To generate SHA-256 certificate fingerprint. On right-upper corner of android project click Gradle, choose Project Name > Tasks > android, and then click signingReport, as follows.
Note: Project Name depends on the user created name.
5. Create an App in AppGallery Connect.
6. Download the agconnect-services.json file from App information, copy and paste in android Project under app directory, as follows.
7. Enter SHA-256 certificate fingerprint and click Save button, as follows.
Note: Above steps from Step 1 to 7 is common for all Huawei Kits.
8. Add the below maven URL in build.gradle(Project) file under the repositories of buildscript, dependencies and allprojects, refer Add Configuration.
Java:
maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
9. Add the below plugin and dependencies in build.gradle(Module) file.
Java:
apply plugin: id 'com.huawei.agconnect'
apply plugin: id 'kotlin-kapt'
// Huawei AGC
implementation 'com.huawei.agconnect:agconnect-core:1.6.0.300'
// Room Database
implementation "androidx.room:room-runtime:2.4.2"
kapt "androidx.room:room-compiler:2.4.2"
implementation "androidx.room:room-ktx:2.4.2"
androidTestImplementation "androidx.room:room-testing:2.4.2"
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
// Recyclerview
implementation 'androidx.recyclerview:recyclerview:1.2.1'
10. Now Sync the gradle.
Let us move to development
I have created a project on Android studio with empty activity let us start coding.
Create a Transaction.kt class annotated with @entity to create a table for each class.
Java:
@Entity(tableName = "transactions")
data class Transaction(
@PrimaryKey(autoGenerate = true)
val id: Int,
val label: String,
val amount: Double,
val description: String): Serializable{}
Create a TransactionDao o.kt interface class annotated with @dao and responsible for defining the methods that access the database.
Java:
@Dao
interface TransactionDao {
@Query("SELECT * from transactions")
fun getAll(): List<Transaction>
@Insert
fun insertAll(vararg transaction: Transaction)
@Delete
fun delete(vararg transaction: Transaction)
@Update
fun update(vararg transaction: Transaction)
}
Create a AppDatabase.kt abstract class that extends RoomDatabase annotated with @database to lists the entities contained in the database, and the DAOs which access them.
Java:
@Database(entities = [Transaction::class], version = 2)
abstract class AppDatabase : RoomDatabase() {
abstract fun transactionDao(): TransactionDao
}
In the TransactionActivity.kt activity to find the business logic for entire dashboard.
Java:
class TransactionActivity : AppCompatActivity() {
private lateinit var deletedTransaction: Transaction
private lateinit var oldtransactions: List<Transaction>
private lateinit var transactions: List<Transaction>
private lateinit var transactionAdapter: TransactionAdapter
private lateinit var linearLayoutManager: LinearLayoutManager
private lateinit var db: AppDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_transcation)
transactions = arrayListOf()
transactionAdapter = TransactionAdapter(transactions)
linearLayoutManager = LinearLayoutManager(this)
trans_recycler_view.apply {
adapter = transactionAdapter
layoutManager = linearLayoutManager
}
// Swipe to remove
val itemTouchHelper = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT){
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
deleteTransaction(transactions[viewHolder.adapterPosition])
}
}
val swipeHelper = ItemTouchHelper(itemTouchHelper)
swipeHelper.attachToRecyclerView(trans_recycler_view)
btn_float.setOnClickListener {
val intent = Intent(this, AddTransactionActivity::class.java)
startActivity(intent)
}
// Room database
db = Room.databaseBuilder(this,AppDatabase::class.java,"transactions").build()
}
@SuppressLint("SetTextI18n")
private fun updateAmount(){
val totalAmount: Double = transactions.map {it.amount}.sum()
val budgetAmount: Double = transactions.filter {it.amount > 0}.map {it.amount}.sum()
val expenseAmount: Double = totalAmount - budgetAmount
balance.text = "RS %.2f".format(totalAmount)
budget.text = "RS %.2f".format(budgetAmount)
expense.text = "RS %.2f".format(expenseAmount)
}
// Fetch Transactions form Room Database
private fun fetchAll(){
GlobalScope.launch {
transactions = db.transactionDao().getAll()
runOnUiThread {
updateAmount()
transactionAdapter.setData(transactions)
}
}
}
private fun deleteTransaction(transaction: Transaction){
deletedTransaction = transaction
oldtransactions = transactions
GlobalScope.launch {
db.transactionDao().delete(transaction)
transactions = transactions.filter {it.id != transaction.id}
runOnUiThread {
updateAmount()
transactionAdapter.setData(transactions)
}
}
Toast.makeText(this, "Item Deleted", Toast.LENGTH_SHORT).show()
}
override fun onResume() {
super.onResume()
fetchAll()
}
}
Create a TransactionAdapter.kt adapter class to hold the list.
Java:
class TransactionAdapter(private var transactions: List<Transaction>):
RecyclerView.Adapter<TransactionAdapter.TransactionViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.transcation_list, parent, false)
return TransactionViewHolder(itemView)
}
override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) {
val transaction: Transaction = transactions[position]
val context: Context = holder.amount.context
if(transaction.amount >= 0){
holder.amount.text = " RS %.2f".format(transaction.amount)
holder.amount.setTextColor(ContextCompat.getColor(context,R.color.Green))
} else {
holder.amount.text = " RS %.2f".format(transaction.amount)
holder.amount.setTextColor(ContextCompat.getColor(context,R.color.Red))
}
holder.label.text = transaction.label
holder.itemView.setOnClickListener {
val intent = Intent(context, DetailedActivity::class.java)
intent.putExtra("transaction", transaction)
context.startActivity(intent)
}
}
override fun getItemCount(): Int {
return transactions.size
}
inner class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val label: TextView = itemView.findViewById(R.id.txt_label)
val amount: TextView = itemView.findViewById(R.id.txt_amount)
}
fun setData(transactions: List<Transaction>){
this.transactions = transactions
notifyDataSetChanged()
}
}
In the AddTransactionActivity.kt activity to find the business logic to add items.
Java:
class AddTransactionActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_transaction)
btn_add.setOnClickListener {
val label = edt_label.text.toString()
val amount = edt_amount.text.toString().toDoubleOrNull()
val description = edt_desc.text.toString()
if(label.isBlank()) {
Toast.makeText(this, "Enter the label", Toast.LENGTH_SHORT).show()
}
else if(amount == null) {
Toast.makeText(this, "Enter the valid amount", Toast.LENGTH_SHORT).show()
}
else {
val transaction = Transaction(0, label, amount, description)
insert(transaction)
Toast.makeText(this, "Saved Content", Toast.LENGTH_SHORT).show()
}
}
}
private fun insert(transaction: Transaction) {
val db: AppDatabase = Room.databaseBuilder(this,AppDatabase::class.java,"transactions").build()
GlobalScope.launch {
db.transactionDao().insertAll(transaction)
finish()
}
}
}
In the DetailedActivity.kt activity to find the business logic for updating the items.
Java:
class DetailedActivity : AppCompatActivity() {
private lateinit var transaction: Transaction
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detailed)
transaction = intent.getSerializableExtra("transaction") as Transaction
update_label.setText(transaction.label)
update_amount.setText(transaction.amount.toString())
btn_update.setOnClickListener {
val label = update_label.text.toString()
val amount = update_amount.text.toString().toDoubleOrNull()
val description = update_desc.text.toString()
if(label.isBlank()) {
Toast.makeText(this, "Enter the label", Toast.LENGTH_SHORT).show()
}
else if(amount == null) {
Toast.makeText(this, "Enter the valid amount", Toast.LENGTH_SHORT).show()
}
else {
val transaction = Transaction(transaction.id, label, amount, description)
update(transaction)
Toast.makeText(this, "Saved Content", Toast.LENGTH_SHORT).show()
}
}
}
private fun update(transaction: Transaction) {
val db: AppDatabase = Room.databaseBuilder(this,AppDatabase::class.java,"transactions").build()
GlobalScope.launch {
db.transactionDao().update(transaction)
finish()
}
}
}
In the activity_transcation.xml we can create the UI screen for Dashboard.
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".room.TransactionActivity">
<LinearLayout
android:id="@+id/balance_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginStart="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total Balance:"
android:textAllCaps="false"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RS.xx"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
app:cardCornerRadius="12dp"
android:layout_below="@+id/balance_layout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="0.5"
android:layout_gravity="center">
<TextView
android:id="@+id/budget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RS.xx"
android:textAllCaps="false"
android:textSize="24sp"
android:textColor="@color/Green"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Budget "
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:layout_weight="0.5">
<TextView
android:id="@+id/expense"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RS.xx"
android:textColor="@color/Red"
android:textAllCaps="false"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Expense "
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Transcations"
android:textAllCaps="false"
android:textSize="18sp"
android:layout_marginTop="14dp"
android:layout_marginStart="10dp"
android:layout_below="@+id/cardview"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/trans_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/title"
android:layout_marginTop="10dp"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_float"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
android:backgroundTint="@color/Red"
android:clickable="true"
android:contentDescription="TODO"
app:borderWidth="0dp"
app:srcCompat="@android:drawable/ic_input_add" />
</RelativeLayout>
In the activity_add_transaction.xml we can create the UI screen for adding items.
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".room.AddTransactionActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="MissingConstraints">
<EditText
android:id="@+id/edt_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:hint="Label "
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/edt_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Amount: "
android:inputType="numberSigned"
app:layout_constraintTop_toBottomOf="@id/edt_label" />
<EditText
android:id="@+id/edt_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Description: "
app:layout_constraintTop_toBottomOf="@id/edt_amount" />
<Button
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:textSize="18sp"
android:layout_gravity="center"
android:text="Add"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/edt_desc" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
In the activity_detailed.xml we can create the UI screen for updating items.
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".room.DetailedActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="MissingConstraints">
<EditText
android:id="@+id/update_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:hint="Label "
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/update_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Amount: "
android:inputType="numberSigned"
app:layout_constraintTop_toBottomOf="@id/edt_label" />
<EditText
android:id="@+id/update_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Description: "
app:layout_constraintTop_toBottomOf="@id/edt_amount" />
<Button
android:id="@+id/btn_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:textSize="18sp"
android:layout_gravity="center"
android:text="Update"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/edt_desc" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
In the transcation_list.xml we can create the UI screen for customized items.
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center_vertical">
<TextView
android:id="@+id/txt_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Food"
android:textAllCaps="false"
android:layout_marginStart="6dp"
android:textSize="20sp"
android:layout_weight="1"/>
<TextView
android:id="@+id/txt_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RS.12"
android:layout_marginEnd="12dp"
android:textSize="20sp"/>
</LinearLayout>
Demo
Tips and Tricks
1. Make sure you are already registered as Huawei developer.
2. Set minSDK version to 24 or later, otherwise you will get AndriodManifest merge issue.
3. Make sure you have added the agconnect-services.json file to app folder.
4. Make sure you have added SHA-256 fingerprint without fail.
5. Make sure all the dependencies are added properly.
Conclusion
In this article, we have learned the integration of Room database.
We, have learnt about the room database and its components such as DAO, Entity and Database. How to create, read, update and delete the content in room database and which helps the user to access the data when they are in offline.
I hope you have read this article. If you found it is helpful, please provide likes and comments.
Reference
Room Database
Find the Original document - https://forums.developer.huawei.com/forumPortal/en/topic/0202860048259200218?fid=0101187876626530001
Overview
In this article, I will create a Courier android application using Kotlin in which I will integrate HMS Core kits such as HMS Account, Push, CloudDB, AuthService, Push Kit Uplink Message, Location and Map Kit.
We have integrated HMS Account and AuthService Kit in part-, Push Notification Using HMS Push Kit in Part-2, Cloud DB Kit in Part-3 and Huawei Client Push integration in Part4 of this series. Kindly go through the link below-
Part-1 https://forums.developer.huawei.com/forumPortal/en/topic/0202841957497640128
Part-2 https://forums.developer.huawei.com/forumPortal/en/topic/0201847982965230092
part-3 https://forums.developer.huawei.com/forumPortal/en/topic/0201854022878900124?fid=0101187876626530001
App will make use of android MVVM clean architecture using Jetpack components such as DataBinding, AndroidViewModel, Observer, LiveData and much more.
In this article, we are going to implement DataBinding using Observable pattern.
Map Kit Introduction
Map Kit covers map data of more than 200 countries and regions, and supports over 70 languages. User can easily integrate map-based functions into your apps using SDK. It optimizes and enriches the map detail display capability. Map Kit supports gestures including zoom, rotation, moving and tilt gestures to ensure smooth interaction experience.
Location Kit introduction
Location Kit combines the GPS, Wi-Fi and base station location functionalities in your app to build up global positioning capabilities, allows to provide flexible location-based services targeted at users around globally. Currently, it provides three main capabilities: fused location, activity identification and geo-fence. You can call one or more of these capabilities as required.
Prerequisite
Huawei Phone EMUI 3.0 or later.
Non-Huawei phones Android 4.4 or later (API level 19 or higher).
HMS Core APK 4.0.0.300 or later
Android Studio
AppGallery Account
App Gallery Integration process
Sign In and Create or Choose a project on AppGallery Connect portal.
{
"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"
}
Navigate to Project settings and download the configuration file.
Navigate to General Information, and then provide Data Storage location.
App Development
Add Required Dependencies:
Launch Android studio and create a new project. Once the project is ready.
Navigate to the Gradle scripts folder and open build.gradle (project: app).
Code:
ext.kotlin_version = "1.4.21"
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.huawei.agconnect:agcp:1.4.2.300'
Add following dependency for HMS Loaction and Map Kits
// Huawei Map
implementation 'com.huawei.hms:maps:6.2.0.301'
// Huawei Location Kit
implementation 'com.huawei.hms:location:6.2.0.300'
Navigate to the Gradle scripts folder and open build.gradle (module: app).
Code:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.huawei.agconnect'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 31
buildToolsVersion "29.0.3" buildFeatures {
dataBinding = true
} compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
} defaultConfig {
applicationId "com.hms.corrierapp"
minSdkVersion 27
targetSdkVersion 31
versionCode 1
versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "android.arch.lifecycle:extensions:1.1.1" implementation 'com.google.android.material:material:1.2.0' implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
implementation(
[group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.4.1'],
[group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.4.1'],
[group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.4.1'],
) //HMS Kits
implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300'
implementation 'com.huawei.hms:hwid:5.3.0.302' implementation 'com.huawei.hms:push:4.0.3.301' implementation 'com.huawei.agconnect:agconnect-cloud-database:1.5.0.300'
implementation "com.huawei.agconnect:agconnect-auth-huawei:1.6.0.300"
implementation 'com.huawei.agconnect:agconnect-auth:1.5.0.300' // Huawei Map
implementation 'com.huawei.hms:maps:6.2.0.301'
// Huawei Location Kit
implementation 'com.huawei.hms:location:6.2.0.300'}
Code Implementation
Created following package model, push, viewmodel.
Model: In your primary folder, create a new package and name it model.
MapActivity.kt:
Code:
package com.hms.corrierapp.map
import android.Manifest
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.Location
import android.os.*
import android.util.Log
import android.widget.Chronometer
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.hms.corrierapp.R
import com.huawei.hmf.tasks.Task
import com.huawei.hms.common.ApiException
import com.huawei.hms.common.ResolvableApiException
import com.huawei.hms.location.*
import com.huawei.hms.maps.*
import com.huawei.hms.maps.model.*
import java.io.File
import java.text.DecimalFormat
class MapActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMapView: MapView
private var mHwMap: HuaweiMap? = null
private var mPolylineOptions: PolylineOptions? = null
private var mListener: LocationSource.OnLocationChangedListener? = null
private var mMarkerStart: Marker? = null
private var mMarkerEnd: Marker? = null
private var mLocationRequest: LocationRequest? = null
private var mTvStart: TextView? = null
private var mTvDistance: TextView? = null
private var mTime: Chronometer? = null
private var fusedLocationProviderClient: FusedLocationProviderClient? = null
private val mPath: PathBean = PathBean()
private var mSeconds: Long = 0
private val mHandler = Handler(Looper.getMainLooper())
private val mDecimalFormat = DecimalFormat("0.00")
private var mIsRunning = false
private val mTimeRunnable: Runnable = object : Runnable {
override fun run() {
mTime!!.text = formatSeconds()
mHandler.postDelayed(this, 1000)
}
}
private var mLocationCallback: LocationCallback? = null
// GPS Data
private var mGpsDataThread: HandlerThread? = null
private var mGpsDataHandler: Handler? = null
private var mGpsDataFile: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
checkPermission()
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
// check location settings
checkLocationSettings()
// init MapView
mMapView = findViewById(R.id.hw_mapview)
var mapViewBundle: Bundle? = null
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY)
}
mMapView.onCreate(mapViewBundle)
mMapView.getMapAsync(this)
mTvDistance = findViewById(R.id.tv_distance)
mTime = findViewById(R.id.cm_time)
mTvStart = findViewById(R.id.tv_start)
mTvStart!!.setOnClickListener({ processStartClick() })
// Initializing Map Kit Polyline
mPolylineOptions = PolylineOptions()
mPolylineOptions!!.color(resources.getColor(R.color.colorAccent))
mPolylineOptions!!.width(5f)
// Recording GPS Data
mGpsDataFile = File(getExternalFilesDir(null), "GpsData.txt")
}
private fun checkPermission() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "sdk <= 28 Q")
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
val strings = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
ActivityCompat.requestPermissions(this, strings, 1)
}
} else {
// Dynamically apply for permissions required for SDK > 28. Add the android.permission.ACCESS_BACKGROUND_LOCATION permission.
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission
(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission
(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
val strings = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
ActivityCompat.requestPermissions(this, strings, 2)
}
}
}
override fun onMapReady(huaweiMap: HuaweiMap?) {
Log.d(TAG, "onMapReady: ")
mHwMap = huaweiMap
mHwMap?.isMyLocationEnabled = true
mHwMap?.uiSettings?.isZoomControlsEnabled = false
// Add Location Source
mHwMap?.setLocationSource(object : LocationSource {
override fun activate(onLocationChangedListener: LocationSource.OnLocationChangedListener?) {
mListener = onLocationChangedListener
}
override fun deactivate() {}
})
// Obtains the current position and updates the map camera.
try {
val lastLocation: Task<Location> = fusedLocationProviderClient!!.lastLocation
lastLocation.addOnSuccessListener { location ->
if (location ==null) [email protected]
mListener!!.onLocationChanged(location)
if (mListener != null) {
mListener!!.onLocationChanged(location)
val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
LatLng(location.latitude, location.longitude), 15f
)
mHwMap!!.animateCamera(cameraUpdate)
}
}.addOnFailureListener {
Log.d(TAG, "onMapReady: Obtains the current position failure")
}
} catch (e: Exception) {
}
}
companion object {
private const val MAPVIEW_BUNDLE_KEY = "MapViewBundleKey"
private const val TAG = "MapActivity"
}
override fun onStart() {
super.onStart()
mMapView.onStart()
}
override fun onStop() {
super.onStop()
mMapView.onStop()
}
override fun onDestroy() {
removeLocationUpdatesWithCallback()
super.onDestroy()
mHandler.removeCallbacksAndMessages(null)
mMapView.onDestroy()
}
override fun onPause() {
mMapView.onPause()
super.onPause()
mGpsDataThread!!.quitSafely()
try {
mGpsDataThread!!.join()
mGpsDataThread = null
mGpsDataHandler = null
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
override fun onResume() {
super.onResume()
mMapView.onResume()
mGpsDataThread = HandlerThread("DotThread")
mGpsDataThread!!.start()
mGpsDataHandler = Handler(mGpsDataThread!!.looper)
}
override fun onLowMemory() {
super.onLowMemory()
mMapView.onLowMemory()
}
private fun checkLocationSettings() {
val builder: LocationSettingsRequest.Builder = LocationSettingsRequest.Builder()
val locationSettingsRequest: LocationSettingsRequest = builder.build()
val settingsClient: SettingsClient = LocationServices.getSettingsClient(this)
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener { requestLocationUpdate() }.addOnFailureListener { e ->
when ((e as ApiException).statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
val rae: ResolvableApiException = e as ResolvableApiException
rae.startResolutionForResult([email protected], 0)
} catch (sie: IntentSender.SendIntentException) {
}
}
}
}
private fun requestLocationUpdate() {
mLocationRequest = LocationRequest()
mLocationRequest!!.interval = 5000
mLocationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
writeGpsData2Sdcard(locationResult.lastLocation)
if (mIsRunning) {
processLocationChange(locationResult.lastLocation)
}
}
override fun onLocationAvailability(locationAvailability: LocationAvailability?) {
super.onLocationAvailability(locationAvailability)
}
}
fusedLocationProviderClient
?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
?.addOnSuccessListener { Log.i(TAG, "request location updates success") }
?.addOnFailureListener { e ->
Log.e(TAG, "request location updates failed, error: " + e.message)
}
}
// Removed when the location update is no longer required.
private fun removeLocationUpdatesWithCallback() {
try {
val voidTask: Task<Void> =
fusedLocationProviderClient!!.removeLocationUpdates(mLocationCallback)
voidTask.addOnSuccessListener { }.addOnFailureListener { }
} catch (e: Exception) {
Log.e(TAG, "removeLocationUpdatesWithCallback Exception : $e")
}
}
private fun processStartClick() {
if (mIsRunning) {
mIsRunning = false
mPath.endTime = (System.currentTimeMillis())
mTvStart!!.text = "Start"
mHandler.removeCallbacks(mTimeRunnable)
if (mPath.mPathLinePoints!!.size > 0) {
mPath.endPoint = (mPath.mPathLinePoints!!.get(mPath.mPathLinePoints!!.size - 1))
if (null != mMarkerStart && null != mMarkerEnd) {
mMarkerStart!!.remove()
mMarkerEnd!!.remove()
}
val startPointOptions: MarkerOptions = MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location_marker))
.position(mPath.mStartPoint)
startPointOptions.title("Start Point")
startPointOptions.snippet("Start Point")
mMarkerStart = mHwMap!!.addMarker(startPointOptions)
val endPointOptions: MarkerOptions = MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location_marker))
.position(mPath.mEndPoint)
endPointOptions.title("End Point")
endPointOptions.snippet("End Point")
mMarkerEnd = mHwMap!!.addMarker(endPointOptions)
}
} else {
mIsRunning = true
mPath.reset()
mPath.startTime = System.currentTimeMillis()
mHandler.post(mTimeRunnable)
mTvStart!!.text = "Stop"
}
}
private fun processLocationChange(location: android.location.Location) {
val latLng = LatLng(location.latitude, location.longitude)
if (mPath.mStartPoint == null) {
mPath.mStartPoint = latLng
}
mPath.addPoint(latLng)
val distance: Float = mPath.updateDistance()
val sportMile = distance / 1000.0
if (mSeconds > 0) {
val distribution = mSeconds.toDouble() / 60.0 / sportMile
mPath.setDistribution(distribution)
mTvDistance!!.text = mDecimalFormat.format(sportMile)
} else {
mPath.setDistribution(0.0)
mTvDistance!!.text = "0.00"
}
mPolylineOptions!!.add(latLng)
mHwMap!!.addPolyline(mPolylineOptions)
if (mListener != null) {
mListener!!.onLocationChanged(location)
val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
LatLng(location.latitude, location.longitude), 15f
)
mHwMap!!.animateCamera(cameraUpdate)
}
}
fun formatSeconds(): String {
val hh = if (mSeconds / 3600 > 9) mSeconds / 3600 else mSeconds / 3600
val mm =
if (mSeconds % 3600 / 60 > 9) mSeconds % 3600 / 60 else mSeconds % 3600 / 60
mSeconds++
return "$hh:$mm"
}
private fun writeGpsData2Sdcard(location: Location) {
Log.d(
TAG,
"write latitude and longitude, latitude: " + location.latitude + ", longitude: " + location.longitude
)
mGpsDataHandler!!.post(
GpsDataSaver(
mGpsDataFile, """
${location.latitude}, ${location.longitude}
""".trimIndent()
)
)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
}
}
}
activity_map.xml:
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/sport_content"
android:layout_width="match_parent"
android:layout_height="match_parent"> <com.huawei.hms.maps.MapView
android:id="@+id/hw_mapview"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:mapType="normal"
map:uiCompass="true"
map:uiZoomControls="true" /> <LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginStart="15dp"
android:layout_marginTop="60dp"
android:layout_marginEnd="15dp"
android:background="@color/white"
android:orientation="horizontal"> <LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_weight="1"
android:orientation="vertical"> <TextView
android:id="@+id/tv_distance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_horizontal"
android:maxLength="8"
android:text="0.00"
android:textColor="#000000"
android:textSize="25sp"
android:textStyle="bold" /> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginBottom="2.5dp"
android:gravity="center_horizontal"
android:text="km"
android:textColor="#88000000"
android:textSize="18sp" />
</LinearLayout> <LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_weight="2"
android:orientation="vertical"> <Chronometer
android:id="@+id/cm_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:ellipsize="end"
android:format="00:00"
android:gravity="center"
android:textColor="#000000"
android:textSize="22sp"
android:textStyle="bold" /> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginBottom="2.5dp"
android:gravity="center_horizontal"
android:text="Total time"
android:textColor="#88000000"
android:textSize="18sp" />
</LinearLayout> </LinearLayout> <TextView
android:id="@+id/tv_start"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp"
android:gravity="center"
android:text="Track Courier Location"
android:textColor="@color/colorPrimary"
android:textSize="21sp"
android:textStyle="bold" /></RelativeLayout>
activity_delivery_status.xml:
Code:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"> <ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="@string/delivery"
android:textAlignment="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="34sp"
android:textStyle="bold" /> <LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="horizontal"> <LinearLayout
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="0.1"
android:gravity="center"
android:orientation="vertical"> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_location_marker" /> <View
android:layout_width="5dp"
android:layout_height="match_parent"
android:background="@android:color/black" /> </LinearLayout> <LinearLayout
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="0.9"
android:gravity="center_vertical"
android:orientation="vertical"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Booking"
android:textAlignment="center"
android:textColor="@color/gray"
android:textSize="20sp"
android:textStyle="bold" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="Done"
android:textAlignment="center"
android:textColor="@color/green"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="Payment Pending"
android:textAlignment="center"
android:textColor="@color/red"
android:textSize="18sp"
android:textStyle="bold" /> </LinearLayout> </LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="horizontal"> <LinearLayout
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="0.1"
android:gravity="center"
android:orientation="vertical"> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_location_marker" /> <View
android:layout_width="5dp"
android:layout_height="match_parent"
android:background="@android:color/black" /> </LinearLayout> <LinearLayout
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="0.9"
android:gravity="center_vertical"
android:orientation="vertical"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PickUp"
android:textAlignment="center"
android:textColor="@color/gray"
android:textSize="20sp"
android:textStyle="bold" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="Done"
android:textAlignment="center"
android:textColor="@color/green"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="Shipping"
android:textAlignment="center"
android:textColor="@color/gray"
android:textSize="18sp"
android:textStyle="bold" /> </LinearLayout> </LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="horizontal"> <LinearLayout
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="0.1"
android:gravity="center"
android:orientation="vertical"> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_location_marker" /> <View
android:layout_width="5dp"
android:layout_height="match_parent"
android:background="@android:color/black" /> </LinearLayout> <LinearLayout
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="0.9"
android:gravity="center_vertical"
android:orientation="vertical"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delivery"
android:textAlignment="center"
android:textColor="@color/gray"
android:textSize="20sp"
android:textStyle="bold" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="On It's Way"
android:textAlignment="center"
android:textColor="@color/gray"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="Tracking ID: 12231223"
android:textAlignment="center"
android:textColor="@color/gray"
android:textSize="18sp"
android:textStyle="bold" /> </LinearLayout> </LinearLayout>
<Button
android:id="@+id/btn_done"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="5dp"
android:background="@color/colorPrimaryDark"
android:text="See On Map"
android:textColor="@color/white"
android:textStyle="bold" />
</LinearLayout> </ScrollView> </RelativeLayout>
</layout>
App Build Result
Tips and Tricks
Set minSDK version to 24 or later, otherwise you will get AndriodManifest merge issue.
Make sure you have added the agconnect-services.json file to app folder.
Make sure you have added SHA-256 fingerprint without fail.
Make sure all the dependencies are added properly.
Conclusion
In this article, we have learned how to integrate HMS Account, Push, CloudDB, AuthService, Push Kit Uplink Message, Location and Map Kit in Android application. After completely read this article user can easily implement Location and Map Kit in the Courier android application using Kotlin.
Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.
References
HMS Docs:
https://developer.huawei.com/consum.../HMSCore-Guides/introduction-0000001050048870
https://developer.huawei.com/consum...droid-sdk-brief-introduction-0000001061991343
https://developer.huawei.com/consum...-Guides/android-introduction-0000001121930588
Location and Map Kit Training Video:
https://developer.huawei.com/consumer/en/training/course/video/201575277450653242
{
"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
In this article, we can learn about the basic questions on Android and HMS of Quiz application. The purpose of conducting quizzes within the business is more fun and educative to understand how the business is running straight away, test the knowledge of the user and helps to form a businessman or woman a far better person in terms of the business process. So, I will provide the series of articles on this Quiz App, in upcoming articles.
If you are new to this application, follow my previous article.
https://forums.developer.huawei.com/forumPortal/en/topic/0202877278014350004
Requirements
1. Any operating system (MacOS, Linux and Windows).
2. Must have a Huawei phone with HMS 4.0.0.300 or later.
3. Must have a laptop or desktop with Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 and above installed.
4. Minimum API Level 24 is required.
5. Required EMUI 9.0.0 and later version devices.
How to integrate HMS Dependencies
1. First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a Huawei ID.
2. Create a project in android studio, refer Creating an Android Studio Project.
3. Generate a SHA-256 certificate fingerprint.
4. To generate SHA-256 certificate fingerprint. On right-upper corner of android project click Gradle, choose Project Name > Tasks > android, and then click signingReport, as follows.
Note: Project Name depends on the user created name.
5. Create an App in AppGallery Connect.
6. Download the agconnect-services.json file from App information, copy and paste in android Project under app directory, as follows.
7. Enter SHA-256 certificate fingerprint and click Save button, as follows.
Note: Above steps from Step 1 to 7 is common for all Huawei Kits.
8. Add the below maven URL in build.gradle(Project) file under the repositories of buildscript, dependencies and allprojects, refer Add Configuration.
Java:
maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
9. Add the below plugin and dependencies in build.gradle(Module) file.
Java:
apply plugin: id 'com.huawei.agconnect'
// Huawei AGC
implementation 'com.huawei.agconnect:agconnect-core:1.6.0.300'
10. Now Sync the gradle.
Let us move to development
I have created a project on Android studio with empty activity let us start coding.
In the AndroidActivity.kt we can find the business logic for android questions.
Java:
class AndroidActivity : AppCompatActivity(), View.OnClickListener {
private var mCurrentPosition: Int = 1
private var mQuestionsList: ArrayList<AndroidQuestions>? = null
private var mSelectedOptionPosition: Int = 0
private var mCorrectAnswers: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_android)
mQuestionsList = AndroidConstants.getQuestions()
setQuestion()
txt_option1.setOnClickListener(this)
txt_option2.setOnClickListener(this)
txt_option3.setOnClickListener(this)
txt_option4.setOnClickListener(this)
btn_ardsubmit.setOnClickListener(this)
}
private fun setQuestion() {
val question = mQuestionsList!![mCurrentPosition -1]
defaultOptionsView()
if(mCurrentPosition == mQuestionsList!!.size){
btn_ardsubmit.text = "FINISH"
} else {
btn_ardsubmit.text = "SUBMIT"
}
progressBar_ard.progress = mCurrentPosition
txt_progress_ard.text = "$mCurrentPosition" + "/" + progressBar_ard.max
txt_ardquestion.text = question!!.questions
// iv_image.setImageResource(question.image)
txt_option1.text = question.option1
txt_option2.text = question.option2
txt_option3.text = question.option3
txt_option4.text = question.option4
}
private fun defaultOptionsView() {
val options = ArrayList<TextView>()
options.add(0, txt_option1)
options.add(1, txt_option2)
options.add(2, txt_option3)
options.add(3, txt_option4)
for(option in options){
option.setTextColor(Color.parseColor("#7A8089"))
option.typeface = Typeface.DEFAULT
option.background = ContextCompat.getDrawable(this, R.drawable.border_bg)
}
}
override fun onClick(v: View?) {
when(v?.id){
R.id.txt_option1 -> {
selectedOptionView(txt_option1, 1)
}
R.id.txt_option2 -> {
selectedOptionView(txt_option2, 2)
}
R.id.txt_option3 -> {
selectedOptionView(txt_option3, 3)
}
R.id.txt_option4 -> {
selectedOptionView(txt_option4, 4)
}
R.id.btn_ardsubmit -> {
if(mSelectedOptionPosition == 0){
mCurrentPosition++
when{
mCurrentPosition <= mQuestionsList!!.size -> {
setQuestion()
} else -> {
val intent = Intent(this, AndroidResultActivity::class.java)
intent.putExtra(AndroidConstants.TOTAL_ARD_QUESTIONS, mCorrectAnswers)
intent.putExtra(AndroidConstants.CORRECT_ARD_ANSWERS, mQuestionsList!!.size)
startActivity(intent)
}
}
} else {
val question = mQuestionsList?.get(mCurrentPosition -1)
if(question!!.correctAnswer != mSelectedOptionPosition){
answerView(mSelectedOptionPosition, R.drawable.wrong_bg)
} else {
mCorrectAnswers ++
}
answerView(question!!.correctAnswer, R.drawable.correct_bg)
if(mCurrentPosition == mQuestionsList!!.size){
btn_ardsubmit.text = "FINISH"
} else {
btn_ardsubmit.text = "Next Question"
}
mSelectedOptionPosition = 0
}
}
}
}
private fun answerView(answer:Int, drawableView:Int){
when(answer) {
1 -> {
txt_option1.background = ContextCompat.getDrawable(this, drawableView)
}
2 -> {
txt_option2.background = ContextCompat.getDrawable(this, drawableView)
}
3 -> {
txt_option3.background = ContextCompat.getDrawable(this, drawableView)
}
4 -> {
txt_option4.background = ContextCompat.getDrawable(this, drawableView)
}
}
}
private fun selectedOptionView(tv: TextView, selectedOptionNum: Int){
defaultOptionsView()
mSelectedOptionPosition = selectedOptionNum
tv.setTextColor(Color.parseColor("#363A43"))
tv.setTypeface(tv.typeface, Typeface.BOLD)
tv.background = ContextCompat.getDrawable(this, R.drawable.select_bg)
}
}
In the AndroidResultActivity.kt we can find the business logic for android result view.
Java:
class AndroidResultActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_android_result)
val totalQuestions = intent.getIntExtra(AndroidConstants.TOTAL_ARD_QUESTIONS, 0)
val correctAnswers = intent.getIntExtra(AndroidConstants.CORRECT_ARD_ANSWERS, 0)
// txt_ardscore.text = "Your Score is $correctAnswers out of $totalQuestions"
txt_ardscore.text = "Your Score is $totalQuestions out of $correctAnswers"
btn_ardfinish.setOnClickListener {
startActivity(Intent(this, Home::class.java))
}
}
}
Create a data class AndroidQuestions.kt to access variables.
Code:
data class AndroidQuestions(
val id:Int,
val questions:String,
val option1:String,
val option2:String,
val option3:String,
val option4:String,
val correctAnswer:Int
)
Create object class AndroidConstants.kt to show questions.
Code:
object AndroidConstants {
const val TOTAL_ARD_QUESTIONS: String = "total_questions"
const val CORRECT_ARD_ANSWERS: String = "correct_answers"
fun getQuestions():ArrayList<AndroidQuestions>{
val questionsList = ArrayList<AndroidQuestions>()
// 1
val ques1 = AndroidQuestions(1, "On which thread broadcast receivers will work in android?",
"Worker Thread","Main Thread", "Activity Thread", "None of the above", 2)
questionsList.add(ques1)
// 2
val ques2 = AndroidQuestions(2, "What was the first phone released that ran the Android OS?",
"Google gPhone","HTC Hero", "Motorola Droid", "T-Mobile G1", 4)
questionsList.add(ques2)
// 3
val ques3 = AndroidQuestions(3, "During an Activity life-cycle, what is the first callback method invoked by the system?",
"onCreate()", "onStart()", "onStop()", "onDestroy()", 1)
questionsList.add(ques3)
// 4
val ques4 = AndroidQuestions(4, "What is mean by ADB?",
"Application Debug Bridge", "Android Debug Bridge", "Application data bridge", "Android data bridge", 2)
questionsList.add(ques4)
// 5
val ques5 = AndroidQuestions(5, "If you want to increase the whitespace between widgets, you will need to use the ___ property",
"Android:digits", "Android:capitalize", "Android:padding", "Android:autoText", 3)
questionsList.add(ques5)
// 6
val ques6 = AndroidQuestions(6, "What is ANR responding time in android?",
"10 seconds", "5 seconds", "1 minute", "45 seconds", 2)
questionsList.add(ques6)
// 7
val ques7 = AndroidQuestions(7, "Choose the layout, that is deprecated",
"Absolute Layout", "Frame Layout", "Relative Layout", "Linear Layout", 2)
questionsList.add(ques7)
// 8
val ques8 = AndroidQuestions(8, "Which method can access a view element of a layout resource in activity",
"onCreate()", "findViewById()", "setContentView()", "None", 2)
questionsList.add(ques8)
// 9
val ques9 = AndroidQuestions(9, "In Which Directory XML Layout files are stored",
"/assets", "/src", "/res/values", "/res/layout", 4)
questionsList.add(ques9)
// 10
val ques10 = AndroidQuestions(10, "What are the functionalities in AsyncTask in Android?",
"onPreExecution()", "onPostExecution()", "DoInBackground()", "onProgressUpdate()", 2)
questionsList.add(ques10)
return questionsList
}
}
In the HMSActivity.kt we can find the business logic for android questions.
Java:
class HMSActivity : AppCompatActivity(),View.OnClickListener {
private var kCurrentPosition: Int = 1
private var kQuestionsList: ArrayList<HMSQuestions>? = null
private var kSelectedOptionPosition: Int = 0
private var kCorrectAnswers: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hmsactivity)
kQuestionsList = HMSConstants.getQuestions()
setQuestion()
txt_option1_hms.setOnClickListener(this)
txt_option2_hms.setOnClickListener(this)
txt_option3_hms.setOnClickListener(this)
txt_option4_hms.setOnClickListener(this)
btn_hms_submit.setOnClickListener(this)
}
private fun setQuestion() {
val question = kQuestionsList!![kCurrentPosition -1]
defaultOptionsView()
if(kCurrentPosition == kQuestionsList!!.size){
btn_hms_submit.text = "FINISH"
} else {
btn_hms_submit.text = "SUBMIT"
}
progressBar_hms.progress = kCurrentPosition
txt_progress_hms.text = "$kCurrentPosition" + "/" + progressBar_hms.max
txt_hmsquestion.text = question!!.hms_questions
// iv_image.setImageResource(question.image)
txt_option1_hms.text = question.hms_option1
txt_option2_hms.text = question.hms_option2
txt_option3_hms.text = question.hms_option3
txt_option4_hms.text = question.hms_option4
}
private fun defaultOptionsView() {
val options = ArrayList<TextView>()
options.add(0, txt_option1_hms)
options.add(1, txt_option2_hms)
options.add(2, txt_option3_hms)
options.add(3, txt_option4_hms)
for(option in options){
option.setTextColor(Color.parseColor("#7A8089"))
option.typeface = Typeface.DEFAULT
option.background = ContextCompat.getDrawable(this, R.drawable.border_bg)
}
}
override fun onClick(v: View?) {
when(v?.id){
R.id.txt_option1_hms -> {
selectedOptionView(txt_option1_hms, 1)
}
R.id.txt_option2_hms -> {
selectedOptionView(txt_option2_hms, 2)
}
R.id.txt_option3_hms -> {
selectedOptionView(txt_option3_hms, 3)
}
R.id.txt_option4_hms -> {
selectedOptionView(txt_option4_hms, 4)
}
R.id.btn_hms_submit -> {
if(kSelectedOptionPosition == 0){
kCurrentPosition++
when{
kCurrentPosition <= kQuestionsList!!.size -> {
setQuestion()
} else -> {
val intent = Intent(this, HMSResultActivity::class.java)
intent.putExtra(HMSConstants.TOTAL_HMS_QUESTIONS, kCorrectAnswers)
intent.putExtra(HMSConstants.CORRECT_HMS_ANSWERS, kQuestionsList!!.size)
startActivity(intent)
}
}
} else {
val question = kQuestionsList?.get(kCurrentPosition -1)
if(question!!.hms_correctAnswer != kSelectedOptionPosition){
answerView(kSelectedOptionPosition, R.drawable.wrong_bg)
} else {
kCorrectAnswers ++
}
answerView(question!!.hms_correctAnswer, R.drawable.correct_bg)
if(kCurrentPosition == kQuestionsList!!.size){
btn_hms_submit.text = "FINISH"
} else {
btn_hms_submit.text = "Next Question"
}
kSelectedOptionPosition = 0
}
}
}
}
private fun answerView(answer:Int, drawableView:Int){
when(answer) {
1 -> {
txt_option1_hms.background = ContextCompat.getDrawable(this, drawableView)
}
2 -> {
txt_option2_hms.background = ContextCompat.getDrawable(this, drawableView)
}
3 -> {
txt_option3_hms.background = ContextCompat.getDrawable(this, drawableView)
}
4 -> {
txt_option4_hms.background = ContextCompat.getDrawable(this, drawableView)
}
}
}
private fun selectedOptionView(tv: TextView, selectedOptionNum: Int){
defaultOptionsView()
kSelectedOptionPosition = selectedOptionNum
tv.setTextColor(Color.parseColor("#363A43"))
tv.setTypeface(tv.typeface, Typeface.BOLD)
tv.background = ContextCompat.getDrawable(this, R.drawable.select_bg)
}
}
In the HMSResultActivity.kt we can find the business logic for android result view.
Code:
class HMSResultActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hmsresult)
val totalQuestions = intent.getIntExtra(HMSConstants.TOTAL_HMS_QUESTIONS, 0)
val correctAnswers = intent.getIntExtra(HMSConstants.CORRECT_HMS_ANSWERS, 0)
txt_hmsscore.text = "Your Score is $totalQuestions out of $correctAnswers"
btn_hmsfinish.setOnClickListener {
startActivity(Intent(this, Home::class.java))
}
}
}
Create a data class HMSQuestions.kt to access variables.
Code:
data class HMSQuestions(
val id:Int,
val hms_questions:String,
val hms_option1:String,
val hms_option2:String,
val hms_option3:String,
val hms_option4:String,
val hms_correctAnswer:Int
)
Create object class HMSConstants.kt to show questions.
Code:
object HMSConstants {
const val TOTAL_HMS_QUESTIONS: String = "total_questions"
const val CORRECT_HMS_ANSWERS: String = "correct_answers"
fun getQuestions():ArrayList<HMSQuestions>{
val hmsquestionsList = ArrayList<HMSQuestions>()
// 1
val hques1 = HMSQuestions(1, "What is HMS?",
"Huawei Medical Services","Huawei Mobile Services", "Huawei Mission Services", "Huawei Military Services", 2)
hmsquestionsList.add(hques1)
// 2
val hques2 = HMSQuestions(2, "Which Kit provides a secure login authorization function of users?",
"Map Kit","Account Kit", "Location Kit", "Scan Kit", 2)
hmsquestionsList.add(hques2)
// 3
val hques3 = HMSQuestions(3, "Scan Kit supports -- formats for most business scenarios.",
"12", "14", "15", "13", 4)
hmsquestionsList.add(hques3)
// 4
val hques4 = HMSQuestions(4, "How many landmark names can be recognized by ML Kit?",
"300", "3000+", "500", "5000+", 4)
hmsquestionsList.add(hques4)
// 5
val hques5 = HMSQuestions(5, "What is the full form of OAID?",
"Open Advertising Identifier", "Open Adverse Identity", "Open Amendment Information", "Open All Identity", 1)
hmsquestionsList.add(hques5)
// 6
val hques6 = HMSQuestions(6, "How many user addresses are allowed by Huawei Kit for user?",
"5", "8", "15", "10", 4)
hmsquestionsList.add(hques6)
// 7
val hques7 = HMSQuestions(7, "Which type of format supports Panorama Kit?",
"JPG", "JPEG", "PNG", "All of the above", 4)
hmsquestionsList.add(hques7)
// 8
val hques8 = HMSQuestions(8, "Which method is used to obtain the last requested available location.",
"getLastLocation()", "onLocationAvailability()", "onLocationResult", "None", 1)
hmsquestionsList.add(hques8)
// 9
val hques9 = HMSQuestions(9, "How many languages does the Video Editor Kit supports?",
"100", "48", "55", "78", 4)
hmsquestionsList.add(hques9)
// 10
val hques10 = HMSQuestions(10, "What are the formats supported by Audio Editor Kit?",
"MP3", "WAV and AAC", "M4A", "All of the above", 4)
hmsquestionsList.add(hques10)
return hmsquestionsList
}
}
In the activity_android.xml we can create the UI screen for questions.
XML:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context=".android.AndroidActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center" >
<TextView
android:id="@+id/txt_ardquestion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:text="What is Android?"
android:textColor="#363A43"
android:textSize="18sp">
</TextView>
<LinearLayout
android:id="@+id/progress_details_android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="16dp" >
<ProgressBar
android:id="@+id/progressBar_ard"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="50dp"
android:progress="0"
android:indeterminate="false"
android:max="10">
</ProgressBar>
<TextView
android:id="@+id/txt_progress_ard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:gravity="center"
android:textColorHint="#7A8089"
android:textSize="14sp"
tools:text="0/10">
</TextView>
</LinearLayout>
<TextView
android:id="@+id/txt_option1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="Apple">
</TextView>
<TextView
android:id="@+id/txt_option2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="Google">
</TextView>
<TextView
android:id="@+id/txt_option3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="iOS">
</TextView>
<TextView
android:id="@+id/txt_option4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="Huawei">
</TextView>
<Button
android:id="@+id/btn_ardsubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@color/design_default_color_primary"
android:text="Submit"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</ScrollView>
In the activity_android_result.xml we can create the UI screen for result.
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/ardresult_bg"
android:padding="20dp"
tools:context=".android.AndroidResultActivity">
<TextView
android:id="@+id/txt_ardresult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="25dp"
android:text="Result"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="22sp">
</TextView>
<ImageView
android:id="@+id/img_ardtrophy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:src="@drawable/trophy"/>
<TextView
android:id="@+id/txt_ardcongratulations"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Hi, Congratulations!!"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="25sp">
</TextView>
<TextView
android:id="@+id/txt_ardscore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="Your Score is 5 out of 10"
android:textColor="@color/hwid_auth_button_color_red"
android:textSize="20sp">
</TextView>
<Button
android:id="@+id/btn_ardfinish"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:background="@android:color/white"
android:text="Finish"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
In the activity_hmsactivity.xml we can create the UI screen for questions.
XML:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context=".hms.HMSActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center" >
<TextView
android:id="@+id/txt_hmsquestion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:text="What is hms?"
android:textColor="#363A43"
android:textSize="18sp">
</TextView>
<LinearLayout
android:id="@+id/progress_details_hms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="16dp" >
<ProgressBar
android:id="@+id/progressBar_hms"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="50dp"
android:progress="0"
android:indeterminate="false"
android:max="10">
</ProgressBar>
<TextView
android:id="@+id/txt_progress_hms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:gravity="center"
android:textColorHint="#7A8089"
android:textSize="14sp"
tools:text="0/10">
</TextView>
</LinearLayout>
<TextView
android:id="@+id/txt_option1_hms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="Apple">
</TextView>
<TextView
android:id="@+id/txt_option2_hms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="Google">
</TextView>
<TextView
android:id="@+id/txt_option3_hms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="iOS">
</TextView>
<TextView
android:id="@+id/txt_option4_hms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/border_bg"
android:padding="15dp"
android:gravity="center"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="Huawei">
</TextView>
<Button
android:id="@+id/btn_hms_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@color/design_default_color_primary"
android:text="Submit"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</ScrollView>
In the activity_hmsresult.xml we can create the UI screen for result.
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/hmsresult_bg"
android:padding="20dp"
tools:context=".hms.HMSResultActivity">
<TextView
android:id="@+id/txt_hmsresult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="25dp"
android:text="Result"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="22sp">
</TextView>
<ImageView
android:id="@+id/img_hmstrophy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:src="@drawable/victory"/>
<TextView
android:id="@+id/txt_hmscongratulations"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Congrats done HMS Quiz!!"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="25sp">
</TextView>
<TextView
android:id="@+id/txt_hmsscore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="Your Score is 6 out of 10"
android:textColor="@color/black"
android:textSize="20sp">
</TextView>
<Button
android:id="@+id/btn_hmsfinish"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:background="@android:color/white"
android:text="Finish"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
Demo
Tips and Tricks
1. Make sure you are already registered as Huawei developer.
2. Set minSDK version to 24 or later, otherwise you will get AndriodManifest merge issue.
3. Make sure you have added the agconnect-services.json file to app folder.
4. Make sure you have added SHA-256 fingerprint without fail.
5. Make sure all the dependencies are added properly.
Conclusion
In this article, we have learned about the basic questions on Android and HMS of Quiz application. The purpose of conducting quizzes within the business is more fun and educative to understand how the business is running straight away, test the knowledge of the user and helps to form a businessman or woman a far better person in terms of the business process. So, I will provide the series of articles on this Quiz App, in upcoming articles
I hope you have read this article. If you found it is helpful, please provide likes and comments.
Reference
Click the URL - https://www.geeksforgeeks.org/how-to-create-a-quiz-app-in-android/