Related
My question is very simple and hopefully the great teachers in this forum can answer.
how to go and change the file in system / build.prop without activating usb debuging?
how to restore all my apps in / system / which have been erased without activating usb debuging?
<< ycusoy | http://admin-belajar.blogspot.com >>
Bonus: text mouse rotate
Code:
<style type="text/css">
/* Circle Text Styles */
#outerCircleText {
/* Optional - DO NOT SET FONT-SIZE HERE, SET IT IN THE SCRIPT */
font-style: italic;font-weight: bold;font-family: 'comic sans ms', verdana, arial;color: #000;
/* End Optional *//* Start Required - Do Not Edit*/
position: absolute;top: 0;left: 0;z-index: 3000;cursor: default;}#outerCircleText div {position: relative;}#outerCircleText div div {position: absolute;top: 0;left: 0;text-align: center;}
/* End Required *//* End Circle Text Styles */
</style>
<script type="text/javascript">
;(function(){ // Your message here (QUOTED STRING)
var msg = "http://forum.xda-developers.com/editpost.php?do=editpost&postid=26043694";
/* THE REST OF THE EDITABLE VALUES BELOW ARE ALL UNQUOTED NUMBERS */// Set font's style size for calculating dimensions// Set to number of desired pixels font size (decimal and negative numbers not allowed)
var size = 14;
// Set both to 1 for plain circle, set one of them to 2 for oval// Other numbers & decimals canhave interesting effects, keep these low (0 to 3)
var circleY = 0.75; var circleX = 2;
// The larger this divisor, the smaller the spaces between letters// (decimals allowed, not negative numbers)
var letter_spacing = 5;
// The larger this multiplier, the bigger the circle/oval// (decimals allowed, not negative numbers, some rounding is applied)
var diameter = 10; // Rotation speed, set it negative if you want it to spin clockwise (decimals allowed)
var rotation = 0.4; // This is not the rotation speed,its the reaction speed, keep low!// Set this to 1 or a decimal less than one (decimals allowed, not negative numbers)
var speed = 0.3;
////////////////////// Stop Editing //////////////////////
if (!window.addEventListener &&!window.attachEvent ||!document.createElement) return;msg = msg.split('');var n = msg.length - 1, a = Math.round(size * diameter * 0.208333), currStep = 20,ymouse = a * circleY + 20, xmouse = a * circleX + 20, y = [], x = [], Y = [], X = [],o = document.createElement('div'), oi= document.createElement('div'),b = document.compatMode && document.compatMode !="BackCompat"? document.documentElement : document.body,mouse = function(e){e = e || window.event;ymouse = !isNaN(e.pageY)? e.pageY : e.clientY;
//
y-positionxmouse = !isNaN(e.pageX)? e.pageX : e.clientX;
//
x-position},makecircle = function(){
// rotation/positioning
if(init.nopy){ o.style.top = (b || document.body).scrollTop + 'px'; o.style.left = (b || document.body).scrollLeft + 'px';};currStep -= rotation;for (var d, i = n; i > -1; --i){
// makes the circle
d = document.getElementById('iemsg'+ i).style; d.top = Math.round(y[i] + a * Math.sin((currStep + i) / letter_spacing) * circleY - 15) + 'px'; d.left = Math.round(x[i] + a * Math.cos((currStep + i) / letter_spacing) * circleX) + 'px';};},drag = function(){
// makes the resistancey
[0] = Y[0] += (ymouse - Y[0]) * speed;x[0] = X[0] += (xmouse - 20 - X[0]) * speed;for (var i = n; i > 0; --i){ y[i] = Y[i] += (y[i-1] - Y[i]) * speed; x[i] = X[i] += (x[i-1] - X[i]) * speed;};makecircle();},init = function(){
// appends message divs, & sets initial values for positioning arrays
if(!isNaN(window.pageYOffset)){ ymouse += window.pageYOffset; xmouse += window.pageXOffset;} else init.nopy = true;for (var d, i = n; i > -1; --i){ d = document.createElement('div'); d.id = 'iemsg' + i; d.style.height = d.style.width = a+ 'px'; d.appendChild(document.createTextNode(msg[i])); oi.appendChild(d); y[i] = x[i] = Y[i]= X[i] = 0;};o.appendChild(oi); document.body.appendChild(o);setInterval(drag, 25);},ascroll = function(){ymouse += window.pageYOffset;xmouse += window.pageXOffset;window.removeEventListener('scroll', ascroll, false);};o.id = 'outerCircleText'; o.style.fontSize = size + 'px';if (window.addEventListener){window.addEventListener('load', init, false);document.addEventListener('mouseover', mouse, false);document.addEventListener('mousemove', mouse, false); if (/Apple/.test(navigator.vendor)) window.addEventListener('scroll', ascroll, false);}else if (window.attachEvent){window.attachEvent('onload', init);document.attachEvent('onmousemove', mouse);};})();</script>
paste before closing tag </head>
I want to add several lines of code to flash AS3. How would i be able to do that if those lines are in a xml and the data that is needed is taken with a function that loads the xml. I need to add those lines in flash? How can i do that?
Example:
<?xml version="1.0" encoding="utf-8"?>
<game>
<!-- "Level id" number is not required. Just I added it for fast calculating -->
<!-- Page 1 -->
<Level id="1" star3="6" star2="10">
<Row>2,2,2,2</Row>
<Row>2,2,0,0</Row>
<Row>2,2,2,2</Row>
<Row>1,1,4,4</Row>
<Row>1,1,4,4</Row>
</Level>
<Level id="2" star3="8" star2="12">
<Row>2,2,2,2</Row>
<Row>2,2,2,2</Row>
<Row>0,1,1,0</Row>
<Row>3,3,4,4</Row>
<Row>3,3,4,4</Row>
</Level>
<Level id="3" star3="8" star2="12">
<Row>1,0,1,1</Row>
<Row>1,0,1,1</Row>
<Row>1,1,4,4</Row>
<Row>1,1,4,4</Row>
<Row>1,1,1,1</Row>
</Level>
<Level id="4" star3="8" star2="12">
<Row>3,1,1,0</Row>
<Row>3,1,1,0</Row>
<Row>2,2,2,2</Row>
<Row>3,3,4,4</Row>
<Row>3,3,4,4</Row>
</Level>
.
.
.
etc
The function to load this xml is the following one:
//playBtn.visible=false;
loadXML();
//loading data from the xml file.
function loadXML():void {
var loader:URLLoader=new URLLoader();
loader.addEventListener(Event.COMPLETE,completeXMLHandler);
//defining request.;
var request:URLRequest = new URLRequest("data.xml");
//try catch any error
try {
loader.load(request);
} catch (error:Error) {
trace('error');
}
}
//loading function
function completeXMLHandler(event:Event):void {
var loader:URLLoader = URLLoader(event.target);
var result:XML = new XML(loader.data);
//defining new xml document
var myXML:XMLDocument=new XMLDocument();
myXML.ignoreWhite = true;
myXML.parseXML(result.toXMLString());
//defining node;
var node:XMLNode = myXML.firstChild;
//defining levelNum from the lenght of the node
totalLevel = int(node.childNodes.length);
//pushing the words and their clues to the array
//creating a temporary array for each level, than pushing temporary array to the main array (dataArray)
for (var i:int=0; i<totalLevel; i++) {
scoreArray.push(9999)
var temp_array:Array=new Array();
var row:int = int(node.childNodes.childNodes.length);
starInfoArray.push(node.childNodes.attributes.star3+"_"+node.childNodes.attributes.star2);
for (var j:int=0; j<row; j++) {
var myData = node.childNodes.childNodes[j].firstChild.nodeValue;
temp_array.push(myData);
}
dataArray.push(temp_array);
}
}
Please help me
Introduction
In this article, we can learn about the CRUD (Create, Read, Update and Delete) operations in Room Database in Attendance Tracker app.
Room is an ORM (Object Relational Mapping) library. In other words, Room will map our database objects to Java objects. Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Room can provide to create sqlite databases and perform the operations like create, read, update and delete.
So, I will provide the series of articles on this Attendance Tracker App, in upcoming articles I will integrate other Huawei Kits.
If you are new to this application, follow my previous articles
Beginner: Integration of Huawei Account Kit of Obtaining Icon Resources feature in Attendance Tracker Android app (Kotlin) - Part 1
Beginner: Find Log in via SMS and Forgot password? Features using Huawei Account Kit, and Ads Kit in Attendance Tracker 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.
Entities annotations
Before we get started with modelling our entities, we need to know some useful annotations and their attributes.
@entity - every model class with this annotation will have a mapping table in DB.
foreignKeys — names of foreign keys
indices — list of indicates on the table
primaryKeys — names of entity primary keys
tableName
@primarykey - as name indicates, this annotation points is the primary key of the entity. autoGenerate - if set to true, then SQLite will be generating a unique id for the column.
Example: @PrimaryKey(autoGenerate = true)
@ColumnInfo - allows specifying custom information about column.
Example: @ColumnInfo(name = “column_name”)
@ignore - field will not be persisted by Room.
@embeded - nested fields can be referenced directly in the SQL queries.
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.
The class which is annotated with @database should satisfy the following conditions:
Be an abstract class that extends RoomDatabase.
Include the list of entities associated with the database within the annotation.
Contain an abstract method that has 0 arguments and returns the class that is annotated with @dao.
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.
{
"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"
}
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 DataRecord.kt class annotated with @entity to create a table for each class.
Java:
@Entity(tableName = "datarecords")
data class DataRecord(
@PrimaryKey(autoGenerate = true)
val id: Long,
val name: String,
val age: String,
val phone: String
)
Create a DataRecordDao.kt interface class annotated with @dao and responsible for defining the methods that access the database.
Java:
@Dao
interface DataRecordDao {
@Query("SELECT * from datarecords")
fun getall(): LiveData<List<DataRecord>>
@Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun insert(item: DataRecord)
@Query("SELECT * FROM datarecords WHERE datarecords.id == :id")
fun get(id: Long): LiveData<DataRecord>
@Update
suspend fun update(vararg items: DataRecord)
@Delete
suspend fun delete(vararg items: DataRecord)
}
Create a AppRoomDatabase.kt abstract class that extends RoomDatabase annotated with @database[/USER] to lists the entities contained in the database, and the DAOs which access them.
Java:
@Database(entities = [DataRecord::class], version = 1)
abstract class AppRoomDatabase : RoomDatabase() {
abstract fun datarecordDao(): DataRecordDao
companion object {
@Volatile
private var INSTANCE: RoomDatabase? = null
fun getDatabase(context: Context): AppRoomDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance as AppRoomDatabase
}
synchronized(this) {
val instance = Room.databaseBuilder(context.applicationContext,AppRoomDatabase::class.java,
"datarecords_database").fallbackToDestructiveMigration()
.build()
INSTANCE = instance
return instance
}
}
}
}
Create a Repository.kt class to find the functions.
Java:
class Repository(private val dataDao: DataRecordDao) {
val allItems: LiveData<List<DataRecord>> = dataDao.getall()
fun get(id: Long):LiveData<DataRecord> {
return dataDao.get(id)
}
suspend fun update(item: DataRecord) {
dataDao.update(item)
}
suspend fun insert(item: DataRecord) {
dataDao.insert(item)
}
suspend fun delete(item: DataRecord) {
dataDao.delete(item)
}
}
Create a ViewModel.kt class that extends AndroidViewModel and provides the Repository functions.
Java:
class ViewModel(application: Application): AndroidViewModel(application) {
private val repository: Repository
val allItems: LiveData<List<DataRecord>>
init {
Log.d(TAG, "Inside ViewModel init")
val dao = AppRoomDatabase.getDatabase(application).datarecordDao()
repository = Repository(dao)
allItems = repository.allItems
}
fun insert(item: DataRecord) = viewModelScope.launch {
repository.insert(item)
}
fun update(item: DataRecord) = viewModelScope.launch {
repository.update(item)
}
fun delete(item: DataRecord) = viewModelScope.launch {
repository.delete(item)
}
fun get(id: Long) = repository.get(id)
}
In the EmployeesList.kt activity to find the business logic for button click.
Java:
class EmployeesList : AppCompatActivity() {
private var buttonAddEmp: Button? = null
private lateinit var myViewModel: ViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_employees_list)
val recyclerView = recyclerview_tasks
val adapter = EmpAdapter(this)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
buttonAddEmp = findViewById(R.id.btn_add)
buttonAddEmp!!.setOnClickListener(View.OnClickListener {
val intent = Intent(this, AddEmployees::class.java)
startActivity(intent)
})
myViewModel = ViewModelProvider(this)[ViewModel::class.java]
myViewModel.allItems.observe(this, Observer { items ->
items?.let { adapter.setItems(it) }
})
}
}
Create a EmpAdapter.kt adapter class to hold the list.
Java:
const val TAG = "EmpAdapter"
class EmpAdapter internal constructor (context: Context) : RecyclerView.Adapter<EmpAdapter.EmpRecordViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var itemsList = emptyList<DataRecord>().toMutableList()
private val onClickListener: View.OnClickListener
init {
onClickListener = View.OnClickListener { v ->
val item = v.tag as DataRecord
Log.d(TAG, "Setting onClickListener for item ${item.id}")
val intent = Intent(v.context, AddEmployees::class.java).apply {
putExtra(DATA_RECORD_ID, item.id)
}
v.context.startActivity(intent)
}
}
inner class EmpRecordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemId: TextView = itemView.findViewById(R.id.datarecord_viewholder_id)
val itemName: TextView = itemView.findViewById(R.id.txt_name)
val itemAge: TextView = itemView.findViewById(R.id.txt_age)
val itemPhone: TextView = itemView.findViewById(R.id.txt_phone)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmpRecordViewHolder {
val itemView = inflater.inflate(R.layout.detail_list, parent, false)
return EmpRecordViewHolder(itemView)
}
override fun onBindViewHolder(holder: EmpRecordViewHolder, position: Int) {
val current = itemsList[position]
// It will be referenced in the View.OnClickListener above
holder.itemView.tag = current
with(holder) {
// Set UI values
itemId.text = current.id.toString()
// itemRecord.text = current.record
itemName.text = current.name
itemAge.text = current.age
itemPhone.text = current.phone
// Set handlers
itemView.setOnClickListener(onClickListener)
}
}
internal fun setItems(items: List<DataRecord>) {
this.itemsList = items.toMutableList()
notifyDataSetChanged()
}
override fun getItemCount() = itemsList.size
companion object {
const val DATA_RECORD_ID : String = "datarecord_id"
}
}
In the AddEmployees.kt activity to find the business logic create, update, read and delete functions.
Java:
class AddEmployees : AppCompatActivity() {
private lateinit var dataViewModel: ViewModel
private var recordId: Long = 0L
private var isEdit: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_employees)
dataViewModel = ViewModelProvider(this)[ViewModel::class.java]
if (intent.hasExtra(DATA_RECORD_ID)) {
recordId = intent.getLongExtra(DATA_RECORD_ID, 0L)
dataViewModel.get(recordId).observe(this, Observer {
val viewId = findViewById<TextView>(R.id.datarecord_id)
val viewName = findViewById<EditText>(R.id.edt_name)
val viewAge = findViewById<EditText>(R.id.edt_age)
val viewPhone = findViewById<EditText>(R.id.edt_phone)
if (it != null) {
// populate with data
viewId.text = it.id.toString()
viewName.setText(it.name)
viewAge.setText(it.age)
viewPhone.setText(it.phone)
}
})
isEdit = true
}
val save = btn_save
save.setOnClickListener { view ->
val id = 0L
val mName = edt_name.text.toString()
val mAge = edt_age.text.toString()
val mPhone = edt_phone.text.toString()
if (mName.isBlank() ) {
Toast.makeText(this, "Name is blank", Toast.LENGTH_SHORT).show()
}
else if (mAge.isBlank()) {
Toast.makeText(this, "Age is blank", Toast.LENGTH_SHORT).show()
}
else if(mPhone.isBlank()) {
Toast.makeText(this, "Phone is blank", Toast.LENGTH_SHORT).show()
}
else {
val item = DataRecord( id = id, name = mName, age = mAge, phone = mPhone)
dataViewModel.insert(item)
finish()
}
}
val update = btn_update
update.setOnClickListener { view ->
val id = datarecord_id.text.toString().toLong()
val nName = edt_name.text.toString()
val nAge = edt_age.text.toString()
val nPhone = edt_phone.text.toString()
if (nName.isBlank() && nAge.isBlank() && nPhone.isBlank()) {
Toast.makeText(this, "Empty data is not allowed", Toast.LENGTH_SHORT).show()
} else {
val item = DataRecord(id = id, name = nName, age = nAge, phone = nPhone)
dataViewModel.update(item)
finish()
}
}
val delete = btn_del
delete.setOnClickListener {
val id = datarecord_id.text.toString().toLong()
val nName = edt_name.text.toString()
val nAge = edt_age.text.toString()
val nPhone = edt_phone.text.toString()
val item = DataRecord( id =id, name = nName, age = nAge, phone = nPhone)
dataViewModel.delete(item)
finish()
}
}
}
In the activity_employees_list.xml we can create the UI screen.
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"
tools:context=".RoomDatabase.EmployeesList">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:layout_gravity="center_horizontal"
android:text="Employees List"
android:textColor="#B602D5"
android:textSize="24sp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:padding="10dp"
android:layout_marginTop="40dp"
android:clickable="true" >
<ImageView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginRight="20dp"
android:drawableTint="@color/design_default_color_primary"
android:src="@drawable/add_emp"/>
<Button
android:id="@+id/btn_add"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="18dp"
android:textAllCaps="false"
android:text="Add Employees"/>
</LinearLayout>
</LinearLayout>
In the activity_add_employees.xml we can create the UI screen.
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"
tools:context=".RoomDatabase.AddEmployees">
<TextView
android:id="@+id/datarecord_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="id "
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textSize="18sp"
android:textColor="@color/black" />
<EditText
android:id="@+id/edt_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="Name "
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textSize="18sp"
android:textColor="@color/black" />
<EditText
android:id="@+id/edt_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="Age "
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textSize="18sp"
android:textColor="@color/black" />
<EditText
android:id="@+id/edt_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="Phone "
android:textSize="18sp"
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textColor="@color/black" />
<Button
android:id="@+id/btn_save"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:textSize="18dp"
android:textAllCaps="false"
android:layout_marginTop="5dp"
android:text="Save"/>
<Button
android:id="@+id/btn_update"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:textSize="18dp"
android:textAllCaps="false"
android:layout_marginTop="5dp"
android:text="Update"/>
<Button
android:id="@+id/btn_del"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:textSize="18dp"
android:textAllCaps="false"
android:layout_marginTop="5dp"
android:text="Delete"/>
</LinearLayout>
In the detail_list.xml we can create the UI screen for customized items.
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/datarecord_viewholder_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="10dp"
android:textSize="18sp"
android:text="id: "
android:textColor="@color/purple_500"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="10dp"
android:textStyle="bold"
android:textColor="@color/purple_500"
android:text="name: " />
<TextView
android:id="@+id/txt_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="10dp"
android:textStyle="bold"
android:textColor="@color/purple_500"
android:text="age: " />
<TextView
android:id="@+id/txt_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="10dp"
android:textStyle="bold"
android:textColor="@color/purple_500"
android:text="phone: " />
</LinearLayout>
</androidx.cardview.widget.CardView>
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
{
"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 that how to enter In Time and Out time of an users by the Room Database and also we can integrate the Analytics Kit for event tracking.
Room is an ORM (Object Relational Mapping) library. In other words, Room will map our database objects to Java objects. Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Room can provide to create sqlite databases and perform the operations like create, read, update and delete.
So, I will provide the series of articles on this Attendance Tracker App, in upcoming articles I will integrate other Huawei Kits.
If you are new to this application, follow my previous articles
Beginner: Integration of Huawei Account Kit of Obtaining Icon Resources feature in Attendance Tracker Android app (Kotlin) - Part 1
Beginner: Find Log in via SMS and Forgot password? Features using Huawei Account Kit, and Ads Kit in Attendance Tracker Android app (Kotlin) – Part 2
Beginner: Find the CRUD operations in Room Database in Attendance Tracker Android app (Kotlin) – Part 3
Beginner: Integration of Huawei Crash Service in Attendance Tracker Android app (Kotlin) – Part 4
Analytics Kit
HUAWEI Analytics Kit provides analysis models to understand user behaviour and gain in-depth insights into users, products and content. It helps you to gain insight about user behaviour on different platforms based on the user behaviour events and user attributes reported by through apps.
AppGallery Connect
Find the Analytics using AppGallery connect dashboard.
Choose My Projects > Huawei Analytics > Overview > Project overview.
Project overview displays the core indicators of current project, such as the number of new users, User activity, User acquisition, User revisit, New user retention, Active user retention, User characteristics and Popular pages etc. providing a quick overview of the users and how they are using your app.
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. Click Manage APIs tab and enable HUAWEI Analytics.
9. 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'
10. 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'
// Huawei Analytics
implementation 'com.huawei.hms:hianalytics:6.4.0.300'
11. Now Sync the gradle.
12. Add the required permission to the AndroidManifest.xml file.
XML:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
Let us move to development
I have created a project on Android studio with empty activity let us start coding.
Initialize Huawei Analytics and enable it.
Java:
// Initialize the Analytics
var mInstance: HiAnalyticsInstance? = null
// Initialize the Analytics function
initAna()
private fun initAna() {
// Enable Analytics Kit Log
HiAnalyticsTools.enableLog()
// Generate the Analytics Instance
mInstance = HiAnalytics.getInstance(this)
// Enable collection capability
mInstance?.setAnalyticsEnabled(true)
}
Create a PunchRecord.kt class annotated with @entity to create a table for each class.
Java:
@Entity(tableName = "punchrecords")
data class PunchRecord(
@PrimaryKey(autoGenerate = true)
val id: Long,
val empid: String,
val intime: String,
val outtime: String
)
Create a PunchRecordDao.kt interface class annotated with @dao and responsible for defining the methods that access the database.
Code:
@Dao
interface PunchRecordDao {
@Query("SELECT * from punchrecords")
fun punchgetall(): LiveData<List<PunchRecord>>
@Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun insert(item: PunchRecord)
@Query("SELECT * FROM punchrecords WHERE punchrecords.id == :id")
fun get(id: Long): LiveData<PunchRecord>
@Update
suspend fun update(vararg items: PunchRecord)
@Delete
suspend fun delete(vararg items: PunchRecord)
}
Create a AppRoomDatabase.kt abstract class that extends RoomDatabase annotated with @database to lists the entities contained in the database, and the DAOs which access them.
Code:
@Database(entities = [PunchRecord::class], version = 1)
abstract class PunchAppRoomDatabase : RoomDatabase() {
abstract fun punchrecordDao(): PunchRecordDao
companion object {
@Volatile
private var INSTANCE: RoomDatabase? = null
fun getDatabase(context: Context): PunchAppRoomDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance as PunchAppRoomDatabase
}
synchronized(this) {
val instance = Room.databaseBuilder(context.applicationContext,PunchAppRoomDatabase::class.java,
"datarecords_database").fallbackToDestructiveMigration()
.build()
INSTANCE = instance
return instance
}
}
}
}
Create a PunchRepository.kt class to find the functions.
Code:
class PunchRepository(private val punchDao: PunchRecordDao) {
val myallItems: LiveData<List<PunchRecord>> = punchDao.punchgetall()
fun getrepo(id: Long):LiveData<PunchRecord> {
return punchDao.get(id)
}
suspend fun updatepunch(item: PunchRecord) {
punchDao.update(item)
}
suspend fun insertpunch(item: PunchRecord) {
punchDao.insert(item)
}
suspend fun deletepunch(item: PunchRecord) {
punchDao.delete(item)
}
}
Create a PunchViewModel.kt class that extends AndroidViewModel and provides the PunchRepository functions.
Java:
class PunchViewModel(application: Application): AndroidViewModel(application) {
private val repository: PunchRepository
val allPunchItems: LiveData<List<PunchRecord>>
init {
Log.d(ContentValues.TAG, "Inside ViewModel init")
val dao = PunchAppRoomDatabase.getDatabase(application).punchrecordDao()
repository = PunchRepository(dao)
allPunchItems = repository.myallItems
}
fun insert(item: PunchRecord) = viewModelScope.launch {
repository.insertpunch(item)
}
fun update(item: PunchRecord) = viewModelScope.launch {
repository.updatepunch(item)
}
fun delete(item: PunchRecord) = viewModelScope.launch {
repository.deletepunch(item)
}
fun get(id: Long) = repository.getrepo(id)
}
In the EmpLoginActivity.kt activity to find the business logic for button click.
Java:
class EmpLoginActivity : AppCompatActivity() {
private var buttonAddPunch: Button? = null
private lateinit var myViewModel: PunchViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_emp_login)
val recyclerView = recyclerview_list
val adapter = PunchAdapter(this)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
buttonAddPunch = findViewById(R.id.btn_punch)
buttonAddPunch!!.setOnClickListener(View.OnClickListener {
val intent = Intent(this, TimeActivity::class.java)
startActivity(intent)
})
myViewModel = ViewModelProvider(this)[PunchViewModel::class.java]
myViewModel.allPunchItems.observe(this, Observer { items ->
items?.let { adapter.setItems(it) }
})
}
}
Create a PunchAdapter.kt adapter class to hold the list.
Java:
class PunchAdapter internal constructor (context: Context) : RecyclerView.Adapter<PunchAdapter.PunchRecordViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var itemsList = emptyList<PunchRecord>().toMutableList()
private val onClickListener: View.OnClickListener
init {
onClickListener = View.OnClickListener { v ->
val item = v.tag as PunchRecord
Log.d(TAG, "Setting onClickListener for item ${item.id}")
val intent = Intent(v.context, TimeActivity::class.java).apply {
putExtra(DATA_RECORD_ID, item.id)
}
v.context.startActivity(intent)
}
}
inner class PunchRecordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemId: TextView = itemView.findViewById(R.id.datarecord_viewholder_id)
val itemID: TextView = itemView.findViewById(R.id.punch_id)
val itemInTime: TextView = itemView.findViewById(R.id.punch_in)
val itemOutTime: TextView = itemView.findViewById(R.id.punch_out)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PunchRecordViewHolder {
val itemView = inflater.inflate(R.layout.punch_list, parent, false)
return PunchRecordViewHolder(itemView)
}
override fun onBindViewHolder(holder: PunchRecordViewHolder, position: Int) {
val current = itemsList[position]
// Needed: will be referenced in the View.OnClickListener above
holder.itemView.tag = current
with(holder) {
// Set UI values
itemId.text = current.id.toString()
// itemRecord.text = current.record
itemID.text = current.empid
itemInTime.text = current.intime
itemOutTime.text = current.outtime
// Set handlers
itemView.setOnClickListener(onClickListener)
}
}
internal fun setItems(items: List<PunchRecord>) {
this.itemsList = items.toMutableList()
notifyDataSetChanged()
}
override fun getItemCount() = itemsList.size
companion object {
const val DATA_RECORD_ID : String = "datarecord_id"
}
}
In the TimeActivity.kt activity to find the business logic to add In Time and Out Time.
Java:
class TimeActivity : AppCompatActivity(), DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener {
private lateinit var dataViewModel: PunchViewModel
private var recordId: Long = 0L
private var isEdit: Boolean = false
var day = 0
var month = 0
var year = 0
var hour = 0
var minute = 0
var savedDay = 0
var savedMonth = 0
var savedYear = 0
var savedHour = 0
var savedMinute = 0
var isInTime : Boolean = false
var isOutTime : Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_time)
dataViewModel = ViewModelProvider(this)[PunchViewModel::class.java]
if (intent.hasExtra(PunchAdapter.DATA_RECORD_ID)) {
recordId = intent.getLongExtra(PunchAdapter.DATA_RECORD_ID, 0L)
dataViewModel.get(recordId).observe(this, Observer {
val viewId = findViewById<TextView>(R.id.datarecord_id)
val viewEmpid = findViewById<EditText>(R.id.edtpunch_id)
val viewInTime = findViewById<EditText>(R.id.edtpunch_intime)
val viewOutTime = findViewById<EditText>(R.id.edtpunch_outtime)
if (it != null) {
// populate with data
viewId.text = it.id.toString()
viewEmpid.setText(it.empid)
viewInTime.setText(it.intime)
viewOutTime.setText(it.outtime)
}
})
isEdit = true
}
edtpunch_intime.setOnClickListener{
isInTime = true
isOutTime = false
getDateTimeCalendar()
DatePickerDialog(this,this,year,month,day ).show()
}
edtpunch_outtime.setOnClickListener{
getDateTimeCalendar()
isInTime = false
isOutTime = true
DatePickerDialog(this,this,year, month, day ).show()
}
btnpunch_save.setOnClickListener { view ->
val id = 0L
val mEmpid = edtpunch_id.text.toString()
val mInTime = edtpunch_intime.text.toString()
val mOutTime = edtpunch_outtime.text.toString()
if (mEmpid.isBlank() ) {
Toast.makeText(this, "Employee ID is blank", Toast.LENGTH_SHORT).show()
}
else if (mInTime.isBlank()) {
Toast.makeText(this, "In Time is blank", Toast.LENGTH_SHORT).show()
}
else if(mOutTime.isBlank()) {
Toast.makeText(this, "Out Time is blank", Toast.LENGTH_SHORT).show()
}
else {
val item = PunchRecord(id = id, empid = mEmpid, intime = mInTime, outtime = mOutTime)
dataViewModel.insert(item)
finish()
}
}
btnpunch_update.setOnClickListener { view ->
val id = datarecord_id.text.toString().toLong()
val nEmpid = edtpunch_id.text.toString()
val nInTime = edtpunch_intime.text.toString()
val nOutTime = edtpunch_outtime.text.toString()
if (nEmpid.isBlank() && nInTime.isBlank() && nOutTime.isBlank()) {
Toast.makeText(this, "Empty data is not allowed", Toast.LENGTH_SHORT).show()
} else {
val item = PunchRecord(id = id, empid = nEmpid, intime = nInTime, outtime = nOutTime)
dataViewModel.update(item)
finish()
}
}
btnpunch_delete.setOnClickListener {
val id = datarecord_id.text.toString().toLong()
val nEmpid = edtpunch_id.text.toString()
val nInTime = edtpunch_intime.text.toString()
val nOutTime = edtpunch_outtime.text.toString()
val item = PunchRecord( id =id, empid = nEmpid, intime = nInTime, outtime = nOutTime)
dataViewModel.delete(item)
finish()
}
}
private fun getDateTimeCalendar(){
val cal: Calendar = Calendar.getInstance()
day = cal.get(Calendar.DAY_OF_MONTH)
month = cal.get(Calendar.MONTH)
year = cal.get(Calendar.YEAR)
hour = cal.get(Calendar.HOUR)
minute = cal.get(Calendar.MINUTE)
}
override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
savedDay = dayOfMonth
savedMonth = month
savedYear = year
getDateTimeCalendar()
TimePickerDialog(this, this, hour, minute, true).show()
}
override fun onTimeSet(view: TimePicker?, hourOfDay: Int, minute: Int) {
savedHour = hourOfDay
savedMinute = minute
if (isInTime) {
edtpunch_intime.setText("$savedDay-$savedMonth-$savedYear, $savedHour:$savedMinute")
}
if (isOutTime) {
edtpunch_outtime.setText("$savedDay-$savedMonth-$savedYear, $savedHour:$savedMinute")
}
}
}
In the activity_emp_login.xml we can create the UI screen.
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"
tools:context=".RoomDatabase.EmpLoginActivity">
<Button
android:id="@+id/btn_punch"
android:layout_width="200dp"
android:layout_height="50dp"
android:textSize="18dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_gravity="center_horizontal"
android:textAllCaps="false"
android:text="Click to Punch"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
In the activity_time.xml we can create the UI screen.
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"
tools:context=".RoomDatabase.TimeActivity">
<TextView
android:id="@+id/datarecord_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="id "
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textSize="18sp"
android:textColor="@color/black" />
<EditText
android:id="@+id/edtpunch_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="EmpID: "
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textSize="18sp"
android:textColor="@color/black" />
<EditText
android:id="@+id/edtpunch_intime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="In Time: "
android:textSize="18sp"
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textColor="@color/black" />
<EditText
android:id="@+id/edtpunch_outtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="left"
android:hint="Out Time: "
android:textSize="18sp"
android:padding="5dp"
android:layout_marginLeft="10dp"
android:textColor="@color/black" />
<Button
android:id="@+id/btnpunch_save"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:textSize="18dp"
android:textAllCaps="false"
android:layout_marginTop="5dp"
android:text="Save"/>
<Button
android:id="@+id/btnpunch_update"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:textSize="18dp"
android:textAllCaps="false"
android:layout_marginTop="5dp"
android:text="Update"/>
<Button
android:id="@+id/btnpunch_delete"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:textSize="18dp"
android:textAllCaps="false"
android:layout_marginTop="5dp"
android:text="Delete"/>
</LinearLayout>
In the punch_list.xml we can create the UI screen for customized items.
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/datarecord_viewholder_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="10dp"
android:textSize="18sp"
android:text="id: "
android:textColor="@color/black"
android:textStyle="bold" />
<TextView
android:id="@+id/punch_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="10dp"
android:textStyle="bold"
android:textColor="@color/black"
android:text="ID: " />
<TextView
android:id="@+id/punch_in"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="10dp"
android:textStyle="bold"
android:textColor="@color/black"
android:text="IN Time: " />
<TextView
android:id="@+id/punch_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="10dp"
android:textStyle="bold"
android:textColor="@color/black"
android:text="OUT Time: " />
</LinearLayout>
</androidx.cardview.widget.CardView>
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 can learn about that how to enter In Time and Out time of an users by the Room Database and also, we can integrate the Analytics Kit for event tracking.
I hope you have read this article. If you found it is helpful, please provide likes and comments.
Reference
Analytics Kit - Documentation
Analytics Kit – Training Video
Room Database
{
"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 series of article, we will learn about Navigation Glove application and also we will learn about integration of the Huawei Direction API in Navigation Glove IoT application. We will learn how to draw polyline on the map.
If you are new to this series of articles, follow my previous articles.
Beginner: Integration of Huawei Account kit in Navigation Glove IoT application Using Kotlin - Part 1
Beginner: Integration of Huawei Map kit in Navigation Glove IoT application Using Kotlin - Part 2
Beginner: Integration of Huawei Site kit in Navigation Glove IoT application Using Kotlin - Part 3
What is Direction API?
Huawei Map Kit provides a set of HTTP/HTTPS APIs, which you can use to build map data functions like route planning, Static map, Raster map.
Directions API is a set of HTTPS-based APIs it is used to plans routes. The direction API returns data in JSON format. You can parse and draw route on the map.
It has following types of routes:
Walking: You can plan route max 150 kilometers.
Cycling: You can plan route max 100 kilometers.
Driving: Driving route gives some following functions:
1. It returns 3 routes for request.
2. It supports 5 waypoints.
3. It gives real time traffic condition.
Using Rest services we are integrating the following.
Direction API
Prerequisite
AppGallery Account
Android Studio 3.X
SDK Platform 19 or later
Gradle 4.6 or later
HMS Core (APK) 4.0.0.300 or later
Huawei Phone EMUI 3.0 or later
Non-Huawei Phone Android 4.4 or later
Service integration on AppGallery.
1. We need to register as a developer account in AppGallery Connect.
2. Create an app by referring to Creating a Project and Creating an App in the Project.
3. Set the data storage location based on the current location.
4. Enabling Map Kit Service on AppGallery Connect.
5. Generating a Signing Certificate Fingerprint.
6. Configuring the Signing Certificate Fingerprint.
7. Get your agconnect-services.json file to the app root directory.
Client development
1. Create android project in android studio IDE.
2. Add the maven URL inside the repositories of buildscript and allprojects respectively (project level build.gradle file).
Code:
maven { url 'https://developer.huawei.com/repo/' }
3. Add the classpath inside the dependency section of the project level build.gradle file.
Code:
classpath 'com.huawei.agconnect:agcp:1.5.2.300'
4. Add the plugin in the app-level build.gradle file.
Code:
apply plugin: 'com.huawei.agconnect'
5. Add the below library in the app-level build.gradle file dependencies section.
Code:
implementation 'com.huawei.hms:maps:4.0.0.302'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:retrofit:2.7.2'
implementation 'com.squareup.retrofit2:converter-gson:2.7.2'
6. Add all the below permissions in the AndroidManifest.xml.
XML:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7. Sync the project.
By now user has select the Source location and Destination Location. And also we have added marker on the map.
Direction API Code
For direction API, we are using retrofit library to get the direction API.
So first let’s see what will be the Request and what will be response. And also end point.
Method Type: POST
URL:
https://mapapi.cloud.huawei.com/mapApi/v1/routeService/driving?key=
API key
Note: Replace API key with you project API key
Data Format: Request: Content-Type: application/json
Response: Content-Type: application/json
Request Example
POST https://mapapi.cloud.huawei.com/mapApi/v1/routeService/driving?key=API key HTTP/1.1
Content-Type: application/json
Accept: application/json
JSON:
{
"destination":{
"lat":12.982126,
"lng":77.533103
},
"origin":{
"lat":12.9702763,
"lng":77.5373127
}
}
Response
HTTP/1.1 200 OK
Content-type: application/json
Just check it in the Postman or RestClient or any other tools which give response for HTTP request
Now let us set up
Step 1: Create Const class.
package com.huawei.navigationglove.api
object Const {
const val BASE_URL = "https://mapapi.cloud.huawei.com/mapApi/v1/"
}Copy codeCopy code
Step 2: Create ApiClient class.
Java:
package com.huawei.navigationglove.api
import android.content.Context
import android.text.TextUtils
import okhttp3.Interceptor
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.OkHttpClient
import retrofit2.converter.gson.GsonConverterFactory
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import okhttp3.HttpUrl
import okhttp3.Request
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
object ApiClient {
private val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(Const.BASE_URL)
.client(setInterceptors())
.addConverterFactory(GsonConverterFactory.create())
.build()
private fun setInterceptors(): OkHttpClient {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BODY
return OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.addInterceptor { chain ->
val url: HttpUrl = chain.request().url.newBuilder()
.addQueryParameter(
"key",
"ADD_YOUR_API_KEY_HERE"
)
.build()
val request = chain.request().newBuilder()
.header("Content-Type", "application/json")
.url(url)
.build()
chain.proceed(request)
}
.addInterceptor(logger)
.build()
}
fun createApiService(): ApiService {
return ApiClient.retrofit.create(ApiService::class.java)
}
fun <S> createService(serviceClass: Class<S>?): S {
return retrofit.create(serviceClass)
}
}
Step 3: Create the ApiService interface.
Java:
package com.huawei.navigationglove.api
import com.huawei.navigationglove.api.request.DirectionRequest
import com.huawei.navigationglove.api.response.DirectionResponse
import io.reactivex.Single
import retrofit2.Call
import retrofit2.http.*
interface ApiService {
@POST("routeService/{type}")
fun getDirectionsWithType(
@Path(value = "type",encoded = true) type : String,
@Body directionRequest: DirectionRequest
): Call<DirectionResponse>
}Copy codeCopy code
Step 4: Create TypeOfDirection Enum to get routes for driving/walking/cycling
package com.huawei.navigationglove.api
enum class TypeOfDirection(val type: String) {
WALKING("walking"),
BICYCLING("bicycling"),
DRIVING("driving")
}
Before creating request and response class, let us add one plugin to convert from JSON to Kotlin data classes
Choose File > Setting > Plugin
Step 5: Now we need to create Request kotlin classes.
Origin.kt
Java:
package com.huawei.navigationglove.api.request
import com.google.gson.annotations.SerializedName
data class Origin(@SerializedName("lng")
val lng: Double = 0.0,
@SerializedName("lat")
val lat: Double = 0.0)
Destination.kt
Java:
package com.huawei.navigationglove.api.request
import com.google.gson.annotations.SerializedName
data class Destination(@SerializedName("lng")
val lng: Double = 0.0,
@SerializedName("lat")
val lat: Double = 0.0)
DirectionRequest.kt
Java:
package com.huawei.navigationglove.api.request
import com.google.gson.annotations.SerializedName
data class DirectionRequest(@SerializedName("origin")
val origin: Origin,
@SerializedName("destination")
val destination: Destination)
Step 6: Now create Response Class. Same as the Request class created.
DirectionResponse.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class DirectionResponse(@SerializedName("routes")
val routes: List<RoutesItem>?,
@SerializedName("returnCode")
val returnCode: String = "",
@SerializedName("returnDesc")
val returnDesc: String = "")
Bounds.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class Bounds(@SerializedName("southwest")
val southwest: Southwest,
@SerializedName("northeast")
val northeast: Northeast)
EndLocation.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class EndLocation(@SerializedName("lng")
val lng: Double = 0.0,
@SerializedName("lat")
val lat: Double = 0.0)
Northeast.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class Northeast(@SerializedName("lng")
val lng: Double = 0.0,
@SerializedName("lat")
val lat: Double = 0.0)
PathItems.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class PathsItem(@SerializedName("duration")
val duration: Double = 0.0,
@SerializedName("durationText")
val durationText: String = "",
@SerializedName("durationInTrafficText")
val durationInTrafficText: String = "",
@SerializedName("durationInTraffic")
val durationInTraffic: Double = 0.0,
@SerializedName("distance")
val distance: Double = 0.0,
@SerializedName("startLocation")
val startLocation: StartLocation,
@SerializedName("startAddress")
val startAddress: String = "",
@SerializedName("distanceText")
val distanceText: String = "",
@SerializedName("steps")
val steps: List<StepsItem>?,
@SerializedName("endLocation")
val endLocation: EndLocation,
@SerializedName("endAddress")
val endAddress: String = "")
PolylineItems.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class PolylineItem(@SerializedName("lng")
val lng: Double = 0.0,
@SerializedName("lat")
val lat: Double = 0.0)
RoutesItem.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class RoutesItem(@SerializedName("trafficLightNum")
val trafficLightNum: Int = 0,
@SerializedName("dstInDiffTimeZone")
val dstInDiffTimeZone: Int = 0,
@SerializedName("crossCountry")
val crossCountry: Int = 0,
@SerializedName("hasRestrictedRoad")
val hasRestrictedRoad: Int = 0,
@SerializedName("hasRoughRoad")
val hasRoughRoad: Int = 0,
@SerializedName("hasTrafficLight")
val hasTrafficLight: Int = 0,
@SerializedName("crossMultiCountries")
val crossMultiCountries: Int = 0,
@SerializedName("dstInRestrictedArea")
val dstInRestrictedArea: Int = 0,
@SerializedName("overviewPolyline")
val overviewPolyline: String = "",
@SerializedName("paths")
val paths: List<PathsItem>?,
@SerializedName("bounds")
val bounds: Bounds,
@SerializedName("hasTolls")
val hasTolls: Int = 0,
@SerializedName("hasFerry")
val hasFerry: Int = 0)
Southwest.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class Southwest(@SerializedName("lng")
val lng: Double = 0.0,
@SerializedName("lat")
val lat: Double = 0.0)
StartLocation.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class StartLocation(@SerializedName("lng")
val lng: Double = 0.0,
@SerializedName("lat")
val lat: Double = 0.0)
StepsItem.kt
Java:
package com.huawei.navigationglove.api.response
import com.google.gson.annotations.SerializedName
data class StepsItem(@SerializedName("duration")
val duration: Double = 0.0,
@SerializedName("orientation")
val orientation: Int = 0,
@SerializedName("durationText")
val durationText: String = "",
@SerializedName("distance")
val distance: Double = 0.0,
@SerializedName("startLocation")
val startLocation: StartLocation,
@SerializedName("instruction")
val instruction: String = "",
@SerializedName("action")
val action: String = "",
@SerializedName("distanceText")
val distanceText: String = "",
@SerializedName("roadName")
val roadName: String = "",
@SerializedName("endLocation")
val endLocation: EndLocation,
@SerializedName("polyline")
val polyline: List<PolylineItem>?)=
Now everything set now call the API.
If your question about originReq and destinationReq follow my previous article. These two request has been created when user selects the source location and destination location.
Java:
navigate.setOnClickListener {
val directionRequest = DirectionRequest(originReq!!, destinationReq!!)
getDirection(directionType, directionRequest)
}Copy codeCopy code
private fun getDirection(directionType: String, directionRequest: DirectionRequest) {
ApiClient.createApiService()
.getDirectionsWithType(directionType, directionRequest)
.enqueue(object : Callback<DirectionResponse> {
override fun onFailure(call: Call<DirectionResponse>, t: Throwable) {
Toast.makeText(
[email protected],
"Failure" + t.localizedMessage + "\n" + t.message,
Toast.LENGTH_SHORT
).show()
}
override fun onResponse(
call: Call<DirectionResponse>,
response: Response<DirectionResponse>
) {
if (response.isSuccessful) {
response.body()?.let {
it.routes?.get(0)?.paths?.get(0)?.let { it1 -> addPolyLines(it1) }
Toast.makeText([email protected], "Success", Toast.LENGTH_SHORT)
.show()
}
//startActivity(Intent([email protected], MapsActivity::class.java))
}
}
})
}
Adding polyline
Java:
var polyLine: Polyline? = null
private fun addPolyLines(path: PathsItem) {
if (polyLine != null) {
polyLine!!.remove()
}
val options = PolylineOptions()
options.add(LatLng(path.startLocation.lat, path.startLocation.lng))
path.steps!!.forEach {
it.polyline!!.forEach { it1 ->
options.add(LatLng(it1.lat, it1.lng))
}
}
options.add(LatLng(path.endLocation.lat, path.endLocation.lng))
options.color(Color.BLACK)
options.width(6f)
polyLine = hMap!!.addPolyline(options)
}
Now let us see the full code.
HomeScreenActivity.kt
Java:
package com.huawei.navigationglove.ui
import android.content.Intent
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import com.huawei.hms.maps.*
import com.huawei.hms.maps.model.*
import com.huawei.hms.site.api.model.Site
import com.huawei.hms.site.widget.SearchIntent
import com.huawei.navigationglove.R
import com.huawei.navigationglove.api.ApiClient
import com.huawei.navigationglove.api.TypeOfDirection
import com.huawei.navigationglove.api.request.Destination
import com.huawei.navigationglove.api.request.DirectionRequest
import com.huawei.navigationglove.api.request.Origin
import com.huawei.navigationglove.api.response.DirectionResponse
import com.huawei.navigationglove.api.response.PathsItem
import kotlinx.android.synthetic.main.activity_home_screen.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.net.URLEncoder
import android.widget.RadioButton
class HomeScreenActivity : AppCompatActivity(), OnMapReadyCallback {
private val TAG = HomeScreenActivity::class.java.name
private val API_KEY: String =
"Add your API Key here"
private var originReq: Origin? = null
private var destinationReq: Destination? = null
var hMap: HuaweiMap? = null
private val searchIntent = SearchIntent()
private var mMarker: Marker? = null
private var mCircle: Circle? = null
private var directionType: String = TypeOfDirection.WALKING.type
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home_screen)
val mSupportMapFragment: SupportMapFragment? =
supportFragmentManager.findFragmentById(R.id.mapfragment_mapfragmentdemo) as SupportMapFragment?
mSupportMapFragment!!.getMapAsync(this)
//Site kit
val apiKey = URLEncoder.encode(
API_KEY,
"utf-8"
)
searchIntent.setApiKey(apiKey)
//Select source location
start.setOnClickListener {
//You can try the below one for older method
//selectSourceLocation()
/*val intent = searchIntent.getIntent(this)
startActivityForResult(intent, SearchIntent.SEARCH_REQUEST_CODE)*/
selectSourceLocation()
}
//Select Destination Location
destination.setOnClickListener {
selectDestinationLocation()
}
navigate.setOnClickListener {
val directionRequest = DirectionRequest(originReq!!, destinationReq!!)
getDirection(directionType, directionRequest)
}
directionTypeGroup.setOnCheckedChangeListener { group, checkedId -> // checkedId is the RadioButton selected
val directionTypeRadioButton = findViewById<View>(checkedId) as RadioButton
when (directionTypeRadioButton.text.toString().toLowerCase()) {
"walking" -> directionType = TypeOfDirection.WALKING.type
"driving" -> directionType = TypeOfDirection.DRIVING.type
"cycling" -> directionType = TypeOfDirection.BICYCLING.type
}
Toast.makeText(applicationContext, directionTypeRadioButton.text, Toast.LENGTH_SHORT)
.show()
}
}
private fun selectSourceLocation() {
val intent = searchIntent.getIntent(this)
sourceLocationLauncher.launch(intent)
}
private var sourceLocationLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val data: Intent? = result.data
if (data != null) {
if (SearchIntent.isSuccess(result.resultCode)) {
val site: Site = searchIntent.getSiteFromIntent(data)
start.text = site.getName()
originReq = Origin(site.location.lng, site.location.lat)
//originReq = Origin(-4.66529, 54.216608)
//Toast.makeText(application, site.getName(), Toast.LENGTH_LONG).show()
val build = CameraPosition.Builder()
.target(LatLng(site.location.lat, site.location.lng)).zoom(16f).build()
val cameraUpdate = CameraUpdateFactory.newCameraPosition(build)
hMap!!.animateCamera(cameraUpdate)
//Setting max and min zoom
//hMap!!.setMaxZoomPreference(10f)
//hMap!!.setMinZoomPreference(1f)
// Marker can be add by HuaweiMap
mMarker = hMap!!.addMarker(
MarkerOptions().position(LatLng(site.location.lat, site.location.lng))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_baseline_location_on_24))
.clusterable(true)
)
mMarker?.showInfoWindow()
// circle can be added to HuaweiMap
/*mCircle = hMap!!.addCircle(
CircleOptions().center(LatLng(28.7041, 77.1025)).radius(45000.0).fillColor(
Color.GREEN))
mCircle?.fillColor = Color.TRANSPARENT*/
}
} else {
Toast.makeText(application, "Unable to find the data", Toast.LENGTH_LONG).show()
}
}
private fun selectDestinationLocation() {
val intent = searchIntent.getIntent(this)
destinationLocationLauncher.launch(intent)
}
private var destinationLocationLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val data: Intent? = result.data
if (data != null) {
if (SearchIntent.isSuccess(result.resultCode)) {
val site: Site = searchIntent.getSiteFromIntent(data)
destination.text = site.getName()
destinationReq = Destination(site.location.lng, site.location.lat)
//destinationReq = Destination(-4.66552, 54.2166)
val build = CameraPosition.Builder()
.target(LatLng(site.location.lat, site.location.lng)).zoom(16f).build()
val cameraUpdate = CameraUpdateFactory.newCameraPosition(build)
hMap!!.animateCamera(cameraUpdate)
mMarker = hMap!!.addMarker(
MarkerOptions().position(LatLng(site.location.lat, site.location.lng))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_baseline_destination_location_on_24))
.clusterable(true)
)
mMarker?.showInfoWindow()
}
} else {
Toast.makeText(application, "Unable to find the data", Toast.LENGTH_LONG).show()
}
}
override fun onMapReady(huaweiMap: HuaweiMap) {
Log.d(TAG, "onMapReady: ")
hMap = huaweiMap
hMap!!.mapType = HuaweiMap.MAP_TYPE_NORMAL
hMap!!.uiSettings.isMyLocationButtonEnabled = true
// Add a polyline (mPolyline) to a map.
/* val mPolyline = hMap!!.addPolyline(
PolylineOptions().add(
LatLng(47.893478, 2.334595),
LatLng(48.993478, 3.434595),
LatLng(48.693478, 2.134595),
LatLng(48.793478, 2.334595)
)
)
// Set the color of the polyline (mPolyline) to red.
mPolyline.color = Color.RED
// Set the width of the polyline (mPolyline) to 10 pixels.
mPolyline.width = 10f*/
//Adding Polygon
/*hMap!!.addPolygon(PolygonOptions().addAll(createRectangle(LatLng(12.9716, 77.5946), 0.1, 0.1))
.fillColor(Color.GREEN)
.strokeColor(Color.BLACK))*/
//Adding Circle on the Map
/*hMap!!.addCircle(CircleOptions()
.center(LatLng(12.9716, 77.5946))
.radius(500.0)
.fillColor(Color.GREEN))*/
}
private fun createRectangle(
center: LatLng,
halfWidth: Double,
halfHeight: Double
): List<LatLng> {
return listOf(
LatLng(center.latitude - halfHeight, center.longitude - halfWidth),
LatLng(center.latitude - halfHeight, center.longitude + halfWidth),
LatLng(center.latitude + halfHeight, center.longitude + halfWidth),
LatLng(center.latitude + halfHeight, center.longitude - halfWidth)
)
}
private fun getDirection(directionType: String, directionRequest: DirectionRequest) {
ApiClient.createApiService()
.getDirectionsWithType(directionType, directionRequest)
.enqueue(object : Callback<DirectionResponse> {
override fun onFailure(call: Call<DirectionResponse>, t: Throwable) {
Toast.makeText(
[email protected],
"Failure" + t.localizedMessage + "\n" + t.message,
Toast.LENGTH_SHORT
).show()
}
override fun onResponse(
call: Call<DirectionResponse>,
response: Response<DirectionResponse>
) {
if (response.isSuccessful) {
response.body()?.let {
it.routes?.get(0)?.paths?.get(0)?.let { it1 -> addPolyLines(it1) }
Toast.makeText([email protected], "Success", Toast.LENGTH_SHORT)
.show()
}
//startActivity(Intent([email protected], MapsActivity::class.java))
}
}
})
}
var polyLine: Polyline? = null
private fun addPolyLines(path: PathsItem) {
if (polyLine != null) {
polyLine!!.remove()
}
val options = PolylineOptions()
options.add(LatLng(path.startLocation.lat, path.startLocation.lng))
path.steps!!.forEach {
it.polyline!!.forEach { it1 ->
options.add(LatLng(it1.lat, it1.lng))
}
}
options.add(LatLng(path.endLocation.lat, path.endLocation.lng))
options.color(Color.BLACK)
options.width(6f)
polyLine = hMap!!.addPolyline(options)
}
}
activity_home_screen.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:map="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=".ui.HomeScreenActivity">
<fragment
android:id="@+id/mapfragment_mapfragmentdemo"
class="com.huawei.hms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraTargetLat="12.9716"
map:cameraTargetLng="77.5946"
map:cameraZoom="10" />
<androidx.cardview.widget.CardView
android:id="@+id/cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:elevation="100dp"
app:cardBackgroundColor="@android:color/white"
app:cardCornerRadius="8dp">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/locationLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="9.5"
android:background="@color/white"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:background="@android:color/transparent"
android:hint="Choose a starting point..."
android:maxLines="1"
android:textColor="@color/purple_200"
android:textSize="18sp" />
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="50dp"
android:layout_marginBottom="5dp"
android:background="@drawable/dottet_line" />
<TextView
android:id="@+id/destination"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:hint="Choose a destination..."
android:maxLines="1"
android:textSize="18sp"
android:textColor="@color/purple_200"/>
</LinearLayout>
<ImageView
android:id="@+id/navigate"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center"
android:layout_marginRight="10dp"
android:layout_weight="0.5"
android:src="@drawable/ic_baseline_send_24" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="@drawable/dottet_line" />
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center"
android:id="@+id/directionTypeGroup"
android:orientation="horizontal">
<RadioButton
android:id="@+id/walking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:checked="true"
android:text="Walking" />
<RadioButton
android:id="@+id/driving"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:text="Driving" />
<RadioButton
android:id="@+id/cycling"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:text="Cycling" />
</RadioGroup>
</LinearLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
Result
Tips and Tricks
1. Make sure you are already registered as a Huawei developer.
2. Set min SDK version to 21 or later, otherwise you will get AndriodManifest to merge issue.
3. Make sure you have added the agconnect-services.json file to the app folder.
4. Make sure you have added the SHA-256 fingerprint without fail.
5. Make sure all the dependencies are added properly.
6. If you want location feature in the Map, make sure you add location permission in AndroidManifest.xml file.
7. If you install app in android version 6 or greater than make sure you handled run time permission.
8. Make sure you have encoded your API key with URLEncode UTF-8.
Conclusion
In this article, we have learnt the integration of the Direction API in Navigation Glove application using Android Studio and Kotlin. And also we have learnt how to convert the JSON to kotlin data classes. And also we have learnt the integration of the retrofit in the application.
Reference
Map Kit - Official document
Map Kit - Code lab
Map Kit - Training Video
Map Kit - Direction API