HMS Video Kit For Movie Promotion Application - Huawei Developers

More information like this, you can visit HUAWEI Developer Forum​
Intoduction:
HUAWEI Video Kit provides an excellent playback experience with video streaming from a third-party cloud platform. It supports streaming media in 3GP, MP4, or TS format and comply with HTTP/HTTPS, HLS, or DASH.
Advantage of Video Kit:
Provides an excellent video experience with no lag, no delay, and high definition.
Provides a complete and rich playback control interfaces.
Provides rich video operation experience.
Prerequisites:
Android Studio 3.X
JDK 1.8 or later
HMS Core (APK) 5.0.0.300 or later
EMUI 3.0 or later
Integration:
1. Create an project in android studio and Huawei AGC.
2. Provide the SHA-256 Key in App Information Section.
3. Download the agconnect-services.json from AGCand save into app directory.
4. In root build.gradle
Navigate to allprojects > repositories and buildscript > repositories and add the given line.
Code:
maven { url 'http://developer.huawei.com/repo/' }
In dependency add class path.
Code:
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
5. In app build.gradle
Configure the Maven dependency
Code:
implementation "com.huawei.hms:videokit-player:1.0.1.300"
Configure the NDK
Code:
android {
defaultConfig {
......
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
......
}
Apply plugin
Code:
apply plugin: 'com.huawei.agconnect'
6. Permissions in Manifest
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.huawei.permission.SECURITY_DIAGNOSE" />
Code Implementation:
A movie promo application has been created to demonstrate HMS Video Kit . The application uses recycleview, cardview and piccaso libraries apart from HMS Video Kit library. Let us go to the details of HMS Video kit code integration.
1. Initializing WisePlayer
We have to implement a class that inherits Application and the onCreate() method has to call the initialization API WisePlayerFactory.initFactory()
Code:
public class VideoKitPlayApplication extends Application {
private static final String TAG = VideoKitPlayApplication.class.getSimpleName();
private static WisePlayerFactory wisePlayerFactory = null;
@Override
public void onCreate() {
super.onCreate();
initPlayer();
}
private void initPlayer() {
// DeviceId test is used in the demo.
WisePlayerFactoryOptions factoryOptions = new WisePlayerFactoryOptions.Builder().setDeviceId("xxx").build();
WisePlayerFactory.initFactory(this, factoryOptions, initFactoryCallback);
}
/**
* Player initialization callback
*/
private static InitFactoryCallback initFactoryCallback = new InitFactoryCallback() {
@Override
public void onSuccess(WisePlayerFactory wisePlayerFactory) {
LogUtil.i(TAG, "init player factory success");
setWisePlayerFactory(wisePlayerFactory);
}
@Override
public void onFailure(int errorCode, String reason) {
LogUtil.w(TAG, "init player factory failed :" + reason + ", errorCode is " + errorCode);
}
};
/**
* Get WisePlayer Factory
*
* @return WisePlayer Factory
*/
public static WisePlayerFactory getWisePlayerFactory() {
return wisePlayerFactory;
}
private static void setWisePlayerFactory(WisePlayerFactory wisePlayerFactory) {
VideoKitPlayApplication.wisePlayerFactory = wisePlayerFactory;
}
}
2. Creating instance of wise player
Code:
wisePlayer = VideoKitPlayApplication.getWisePlayerFactory().createWisePlayer();
3. Initialize the WisePlayer layout and add layout listeners
Code:
private void initView(View view) {
if (view != null) {
surfaceView = (SurfaceView) view.findViewById(R.id.surface_view);
textureView = (TextureView) view.findViewById(R.id.texture_view);
if (PlayControlUtil.isSurfaceView()) {
//SurfaceView display interface
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(thois);
textureView.setVisibility(View.GONE);
surfaceView.setVisibility(View.VISIBLE);
} else {
//TextureView display interface
textureView.setSurfaceTextureListener(this);
textureView.setVisibility(View.VISIBLE);
surfaceView.setVisibility(View.GONE);
}
}
4. Register WisePlayer listeners
Code:
private void setPlayListener() {
if (wisePlayer != null) {
wisePlayer.setErrorListener(onWisePlayerListener);
wisePlayer.setEventListener(onWisePlayerListener);
wisePlayer.setResolutionUpdatedListener(onWisePlayerListener);
wisePlayer.setReadyListener(onWisePlayerListener);
wisePlayer.setLoadingListener(onWisePlayerListener);
wisePlayer.setPlayEndListener(onWisePlayerListener);
wisePlayer.setSeekEndListener(onWisePlayerListener);
}
}
5. Set playback parameters
Code:
player.setVideoType(PlayMode.PLAY_MODE_NORMAL);
player.setBookmark(10000);
player.setCycleMode(CycleMode.MODE_CYCLE);
6. Set URL for video
Code:
wisePlayer.setPlayUrl(new String[] {currentPlayData.getUrl()});
7. Set a view to display the video.
Code:
// SurfaceView listener callback
@Override
public void surfaceCreated(SurfaceHolder holder) {
wisePlayer.setView(surfaceView);
}
// TextureView listener callback
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
wisePlayer.setView(textureView);
// Call the resume API to bring WisePlayer to the foreground.
wisePlayer.resume(ResumeType.KEEP);
}
8. Prepare for the playback and start requesting data.
Code:
wisePlayer.ready();
9. Start the playback upon a success response of the onReady callback method
Code:
@Override
public void onReady(WisePlayer wisePlayer) {
player.start();
}
10. select_play_movie.xml
Code:
<?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="wrap_content"
android:orientation="vertical"
android:tag="cards main container">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="@color/colorbg"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
>
<ImageView
android:id="@+id/videoIcon"
android:tag="image_tag"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:src="@drawable/j1"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_weight="2"
android:orientation="vertical"
>
<TextView
android:id="@+id/play_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="Jurassic Park"
android:textColor="@color/colorTitle"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<TextView
android:id="@+id/releasedYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:textColor="@color/black"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
android:id="@+id/briefStory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:textColor="@color/green"
android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView
android:id="@+id/play_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="@color/select_play_text_color"
android:textSize="20sp"
android:visibility="gone"/>
<TextView
android:id="@+id/play_url"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="2"
android:paddingTop="5dip"
android:singleLine="false"
android:textColor="@color/select_play_text_color"
android:textSize="14sp"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
11.SelectMoviePlayAdapter.java
Code:
/**
* Play recyclerView adapter
*/
public class SelectMoviePlayAdapter extends RecyclerView.Adapter<SelectMoviePlayAdapter.PlayViewHolder> {
private static final String TAG = "SelectMoviePlayAdapter ";
// Data sources list
private List<MovieEntity> playList;
// Context
private Context context;
// Click item listener
private OnItemClickListener onItemClickListener;
/**
* Constructor
*
* @param context Context
* @param onItemClickListener Listener
*/
public SelectMoviePlayAdapter(Context context, OnItemClickListener onItemClickListener) {
this.context = context;
playList = new ArrayList<>();
this.onItemClickListener = onItemClickListener;
}
/**
* Set list data
*
* @param playList Play data
*/
public void setSelectPlayList(List<MovieEntity> playList) {
if (this.playList.size() > 0) {
this.playList.clear();
}
this.playList.addAll(playList);
notifyDataSetChanged();
}
@NonNull
@Override
public PlayViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.select_play_movie, parent, false);
return new PlayViewHolder(view);
}
@Override
public void onBindViewHolder(PlayViewHolder holder, final int position) {
if (playList.size() > position && holder != null) {
MovieEntitymovieEntity = playList.get(position);
if (movieEntity== null) {
LogUtil.i(TAG, "current item data is empty.");
return;
}
holder.playName.setText(movieEntity.getName());
holder.releasedYear.setText(movieEntity.getYear());
holder.briefStory.setText(movieEntity.getStory());
holder.playUrl.setText(movieEntity.getUrl());
holder.playType.setText(String.valueOf(movieEntity.getUrlType()));
holder.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(position);
}
});
Picasso.with(context).load(movieEntity.getIcon()).into(holder.videoIcon);
}
}
@Override
public int getItemCount() {
return playList.size();
}
/**
* Show view holder
*/
static class PlayViewHolder extends RecyclerView.ViewHolder {
private TextView playName;
private TextView releasedYear,briefStory;
private TextView playType;
private TextView playUrl;
private ImageView videoIcon;
/**
* Constructor
*
* @param itemView Item view
*/
public PlayViewHolder(View itemView) {
super(itemView);
if (itemView != null) {
playName = itemView.findViewById(R.id.play_name);
releasedYear = itemView.findViewById(R.id.releasedYear);
briefStory = itemView.findViewById(R.id.briefStory);
playType = itemView.findViewById(R.id.play_type);
playUrl = itemView.findViewById(R.id.play_url);
videoIcon = itemView.findViewById(R.id.videoIcon);
}
}
}
}
ScreenShots:
{
"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"
}
Conclusion:
Video Kit provides an excellent experience in video playback. In future it will support video editing and video hosting, through that users can easily and quickly enjoy an end-to-end video solution for all scenarios
Reference:
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/introduction-0000001050439577-V5

Related

HMS Game Service - How to create a Sign In / Get User Info [Java]

More information like this, you can visit HUAWEI Developer Forum​
Introduction
In this article I would like to address the Game Service topic by doing a practical example in which we will implement the kit. The goal is to achieve a simple application where our user has the possibility to log in with his Huawei ID and obtain information regarding his player on Game Service. In the Huawei repositories we can find projects with all the implementation but in my opinion it is better to create a new project where we have the opportunity to do our own development.
Steps:
1. Create an App in AGC
2. Add the necessary libraries and repositories
3. Permissions in our Application
4. Building the user interface
5. Create the Signing class
6. Write the code in our MainActity
7. Test the App
Create an App in AGC
If you already have experience implementing HMS you will have noticed that the creation of an App in the AGC console is regularly required. If you are in this case, I recommend that you skip this step and go to step 3.
In this link you can find a detailed guide on how to create an App in AGC, generate your finger print and download the json services.
https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0
In case you do not have Android Studio Configured on your device, I also share a guide with the requirements.
https://developer.huawei.com/consumer/en/codelab/HMSAccounts-Kotlin/index.html#1
Once your App is created in AGC, it is important that you activate the Game Service, Account Kit and In App purchases services in case you require it. We can go to the Apis Management tab
{
"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"
}
What have we accomplished so far?
We have an App in AGC connected to our project and with activated services.
Add the necessary libraries and repositories
Once our project is created in Android Studio it will be necessary to add the following lines of code
Let's add the following lines to the project gradle
Code:
buildscript {
repositories {
google()
jcenter()
//Esta linea
maven { url 'http://developer.huawei.com/repo/' }
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.0"
//Tambien esta esta linea
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
}
}
allprojects {
repositories {
google()
jcenter()
//Repositorio de Maven
maven {url 'http://developer.huawei.com/repo/'}
}
}
Now let's add the necessary dependencies in the app gradle.
Code:
implementation'com.huawei.agconnect:agconnect-core:1.3.1.300' //HMSCore
implementation 'com.huawei.hms:hwid:4.0.4.300' //Huawei Id
implementation 'com.huawei.hms:game:4.0.3.301' //Game Service
implementation 'com.huawei.hms:base:4.0.4.301'
implementation 'com.squareup.picasso:picasso:2.71828'
Do not forget to add the plugin.
Code:
apply plugin:'com.huawei.agconnect'
Permissions in our Application
Now let's add the permissions of our Application because we want to use the Game Service services. So in the Android Manifest File, let's add the necessary permissions.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Building the user interface
The time has come to build the user interface, what we will do is use the powerful tool that Android Studio from Constraint offers us, basically what we are looking for is to achieve something like this. Where we will place the elements that we want to show when obtaining the data of our user. Of course you can create the interface that you like the most, but if you want to use this simple distribution of elements this is the code
Code:
<?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=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="68dp"
android:text="Welcome"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/avatarImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="36dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="28dp"
android:text="Player Id"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/idtv" />
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="51dp"
android:text="Player Level"
app:layout_constraintEnd_toStartOf="@+id/leveltv"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintTop_toBottomOf="@+id/textView9" />
<TextView
android:id="@+id/idtv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="24dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.082"
app:layout_constraintStart_toEndOf="@+id/textView9"
app:layout_constraintTop_toBottomOf="@+id/avatarImg" />
<TextView
android:id="@+id/leveltv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="@+id/idtv"
app:layout_constraintTop_toBottomOf="@+id/idtv" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Login"
app:layout_constraintBottom_toBottomOf="parent"
tools:layout_editor_absoluteX="0dp" />
<Button
android:id="@+id/btnInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="88dp"
android:text="PlayerInfo"
app:layout_constraintBottom_toTopOf="@+id/loginButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.475"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView10"
app:layout_constraintVertical_bias="0.178" />
</androidx.constraintlayout.widget.ConstraintLayout>
Create the SignInCenter class
In this class we will make the instance of the class and we will be able to manage the Huawei of Authentication
Code:
public class SignInCenter {
private static SignInCenter INS = new SignInCenter();
private static AuthHuaweiId currentAuthHuaweiId;
public static SignInCenter get() {
return INS;
}
public void updateAuthHuaweiId(AuthHuaweiId AuthHuaweiId) {
currentAuthHuaweiId = AuthHuaweiId;
}
public AuthHuaweiId getAuthHuaweiId() {
return currentAuthHuaweiId;
}
}
Write the code in our MainActity
Let's work on our Main Activity is the time to get our hands dirty, I will put the source code and within the add comments to each method this for reading is easier
Code:
/**
*Variable Declarations
* Buttons, TextViews
* AuthHuaweiId to store retrived elements
* ImageView to show avatar
* Handler to use a service
*/
private Button loginButton;
private Button infoButton;
private TextView welcomeTv,idtv,leveltv;
private AuthHuaweiId mAuthid;
private final static int SIGN_IN_INTENT = 3000;
private String playerId;
private String sessionId = null;
private ImageView avatar;
private Handler handler;
private final static int HEARTBEAT_TIME = 15 * 60 * 1000;
private static final String TAG = "TAG1";
private boolean hasInit = false;
//---------------------------------------------------------------------------------------------
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Call UI elements we can use Butterknife or if you are using Kotlin use the extensions to get rid of this part
loginButton = findViewById(R.id.loginButton);
loginButton.setOnClickListener(this);
welcomeTv = findViewById(R.id.textView);
avatar = findViewById(R.id.avatarImg);
infoButton = findViewById(R.id.btnInfo);
infoButton.setOnClickListener(this);
idtv = findViewById(R.id.idtv);
leveltv = findViewById(R.id.leveltv);
}
//---------------------------------------------------------------------------------------------
/*
* Overide onclick method and by using elements id call the proper methods, dont forget to
* implement View.Onclick Listener Interface
*/
//---------------------------------------------------------------------------------------------
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.loginButton:
signIn();
break;
case R.id.btnInfo:
init();
getCurrentPlayer();
break;
}
}
//---------------------------------------------------------------------------------------------
/*
* This is the method where we will initialize the service by passing the Authentification ID
* if the user has logged in correctly
*/
//---------------------------------------------------------------------------------------------
public void init() {
JosAppsClient appsClient = JosApps.getJosAppsClient(this,mAuthid);
appsClient.init();
Log.d(TAG,"init success");
hasInit = true;
}
//---------------------------------------------------------------------------------------------
/*
*Get the current player by using the ID there are many ways to achive this but for this example
* we will keep it simple
**/
//---------------------------------------------------------------------------------------------
private void getCurrentPlayer() {
PlayersClientImpl client = (PlayersClientImpl) Games.getPlayersClient(this,mAuthid);
//Create a Task to get the Player
Task<Player> task = client.getCurrentPlayer();
task.addOnSuccessListener(new OnSuccessListener<Player>() {
@Override
public void onSuccess(Player player) {
String result = "display:" + player.getDisplayName() + "\n" + "playerId:" + player.getPlayerId() + "\n"
+ "playerLevel:" + player.getLevel() + "\n" + "timestamp:" + player.getSignTs() + "\n"
+ "playerSign:" + player.getPlayerSign();
Log.d("TAG1",result);
idtv.setText(player.getPlayerId());
leveltv.setText(player.getLevel() + "");
playerId = player.getPlayerId();
gameBegin();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//gamePlayExtra();
}
};
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Message message = new Message();
handler.sendMessage(message);
}
}, HEARTBEAT_TIME, HEARTBEAT_TIME);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
Log.d("TAG1",result);
}
}
});
}
//---------------------------------------------------------------------------------------------
/*
*On this method the Game will begin with the obtained ID
*/
//---------------------------------------------------------------------------------------------
public void gameBegin() {
if (TextUtils.isEmpty(playerId)) {
Log.d("TAG1","GetCurrentPlayer first.");
return;
}
String uid = UUID.randomUUID().toString();
PlayersClient client = Games.getPlayersClient(this,mAuthid);
Task<String> task = client.submitPlayerEvent(playerId, uid, "GAMEBEGIN");
task.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String jsonRequest) {
if (jsonRequest == null) {
Log.d("TAG1","jsonRequest is null");
return;
}
try {
JSONObject data = new JSONObject(jsonRequest);
sessionId = data.getString("transactionId");
} catch (JSONException e) {
Log.d("TAG1","parse jsonArray meet json exception");
return;
}
Log.d("TAG1","submitPlayerEvent traceId: " + jsonRequest);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
Log.d("TAG1",result);
}
}
});
}
//---------------------------------------------------------------------------------------------
/*
* This is the method to sign in i have commented most of the lines to understa what we have been doing
*/
//---------------------------------------------------------------------------------------------
private void signIn() {
//Create a task with the Type of AuthHuaweiId
Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.getService(this, getHuaweiIdParams()).silentSignIn();
//Add the proper Listener so we can track the response of the Task
authHuaweiIdTask.addOnSuccessListener(new OnSuccessListener<AuthHuaweiId>() {
//Must overide the Osuccess Method which will return an AuthHuaweiId Object
@Override
public void onSuccess(AuthHuaweiId authHuaweiId) {
//Logs to track the information
Log.d("TAG1","signIn success");
Log.d("TAG1","Id" + authHuaweiId.getDisplayName());
Log.d("TAG1", "Picture" + authHuaweiId.getAvatarUriString());
//Handle the user interface
welcomeTv.setVisibility(View.VISIBLE);
welcomeTv.setText("Welcome back " + authHuaweiId.getDisplayName());
Picasso.get().load(authHuaweiId.getAvatarUriString()).into(avatar);
loginButton.setVisibility(View.INVISIBLE);
Log.d("TAG1","AT:" + authHuaweiId.getAccessToken());
mAuthid = authHuaweiId;
//Sign in Center update
SignInCenter.get().updateAuthHuaweiId(authHuaweiId);
infoButton.setVisibility(View.VISIBLE);
}
}).addOnFailureListener(new OnFailureListener() {
//Something went wrong, use this method to inform your users
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
Log.d("TAG1","signIn failed:" + apiException.getStatusCode());
Log.d("TAG1","start getSignInIntent");
signInNewWay();
}
}
});
}
//---------------------------------------------------------------------------------------------
private void signInNewWay() {
Intent intent = HuaweiIdAuthManager.getService(MainActivity.this, getHuaweiIdParams()).getSignInIntent();
startActivityForResult(intent, SIGN_IN_INTENT);
}
//---------------------------------------------------------------------------------------------
/*
*Create the HuaweiIdParams so we can send it to the service
*/
//---------------------------------------------------------------------------------------------
public HuaweiIdAuthParams getHuaweiIdParams() {
return new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME).createParams();
}
//---------------------------------------------------------------------------------------------
/*
*Dont Forget to overide onActivity Result otherwise we wont have any functionality working
* */
//---------------------------------------------------------------------------------------------
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (SIGN_IN_INTENT == requestCode) {
handleSignInResult(data);
} else {
Log.d("TAG1","unknown requestCode in onActivityResult");
}
}
//---------------------------------------------------------------------------------------------
/*
*Handle the result of signin we can take some desicion here
* */
//---------------------------------------------------------------------------------------------
private void handleSignInResult(Intent data) {
if (null == data) {
Log.d("TAG1","signIn inetnt is null");
return;
}
// HuaweiIdSignIn.getSignedInAccountFromIntent(data);
String jsonSignInResult = data.getStringExtra("HUAWEIID_SIGNIN_RESULT");
if (TextUtils.isEmpty(jsonSignInResult)) {
Log.d("TAG1","SignIn result is empty");
return;
}
try {
HuaweiIdAuthResult signInResult = new HuaweiIdAuthResult().fromJson(jsonSignInResult);
if (0 == signInResult.getStatus().getStatusCode()) {
Log.d("TAG1","Sign in success.");
Log.d("TAG1","Sign in result: " + signInResult.toJson());
SignInCenter.get().updateAuthHuaweiId(signInResult.getHuaweiId());
// getCurrentPlayer();
} else {
Log.d("TAG1","Sign in failed: " + signInResult.getStatus().getStatusCode());
}
} catch (JSONException var7) {
Log.d("TAG1","Failed to convert json from signInResult.");
}
}
Conclusion
Well! Well we have achieved it we have a small implementation of a simple Game Service but that we could add to our video games. Game Service has many very useful methods, check the documentation if you would like to go deeper into this topic.
https://developer.huawei.com/consumer/en/hms/huawei-game

Tripoholic : Explore the cartoon world with us :feat. Huawei Video Kit

Video is visual multimedia source that combines a sequence of images to form a moving picture. The video transmits a signal to a screen and processes the order in which the screen captures should be shown.
Videos usually have audio components that correspond with the pictures being shown on the screen.
Video was first developed for “Mechanical Television” systems which were quickly replaced by cathode ray tube (CRT) and eventually replaced by flat panel displays of several types.
Huawei Video Kit brings the wonderful experience to playback the high quality videos streaming from a third party cloud platform.
Huawei Video Kit supports the streaming media in 3GP,MP4 or TS formats and comply with HTTP/HTTPS, HLS or DASH.
Huawei Video Kit will also be supporting the video hosting and editing feature in coming versions.
Features
Video Kit provides smooth playback.
It provides secure and stable solution.
It leverages the anti-leeching experience so that bandwidth is not drained out.
It allows wide ranging playback controls.
Supports playback authentication.
Development Overview
Prerequisite
Must have a Huawei Developer Account
Must have Android Studio 3.0 or later
Must have a Huawei phone with HMS Core 5.0.0.300 or later
EMUI 3.0 or later
Software Requirements
Java SDK 1.7 or later
Android 5.0 or later
Preparation
Create an app or project in the Huawei AppGallery Connect.
Provide the SHA Key and App Package name of the project in App Information Section and enable the required API.
Create an Android project.
Note: Video Kit SDK can be directly called by devices, without connecting to AppGallery Connect and hence it is not mandatory to download and integrate the agconnect-services.json.
Integration
Add below to build.gradle (project)file, under buildscript/repositories and allprojects/repositories.
Code:
<p style="line-height: normal;">Maven {url 'http://developer.huawei.com/repo/'}</p>
Add below to build.gradle (app) file, under dependencies.
Code:
<p style="line-height: normal;">implementation "com.huawei.hms:videokit-player:1.0.1.300" </p>
Adding permissions
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.huawei.permission.SECURITY_DIAGNOSE" />
Development Process
A small trip application has been created to demonstrate the capabilities to Huawei Video Kit.
It lists all the amusement places to visit around with the use of GridView RecyclerView and card view.
To achieve it below classes are created.
Initializing WisePlayer
We have to implement a class that inherits Application and the onCreate() method has to call the initialization API WisePlayerFactory.initFactory().
Code:
public class VideoKitPlayApplication extends Application {
private static final String TAG = VideoKitPlayApplication.class.getSimpleName();
private static WisePlayerFactory wisePlayerFactory = null;
@Override
public void onCreate() {
super.onCreate();
initPlayer();
}
private void initPlayer() {
// DeviceId test is used in the demo.
WisePlayerFactoryOptions factoryOptions = new WisePlayerFactoryOptions.Builder().setDeviceId("xxx").build();
WisePlayerFactory.initFactory(this, factoryOptions, initFactoryCallback);
}
/**
* Player initialization callback
*/
private static InitFactoryCallback initFactoryCallback = new InitFactoryCallback() {
@Override
public void onSuccess(WisePlayerFactory wisePlayerFactory) {
LogUtil.i(TAG, "init player factory success");
setWisePlayerFactory(wisePlayerFactory);
}
@Override
public void onFailure(int errorCode, String reason) {
LogUtil.w(TAG, "init player factory failed :" + reason + ", errorCode is " + errorCode);
}
};
/**
* Get WisePlayer Factory
*
* @return WisePlayer Factory
*/
public static WisePlayerFactory getWisePlayerFactory() {
return wisePlayerFactory;
}
private static void setWisePlayerFactory(WisePlayerFactory wisePlayerFactory) {
VideoKitPlayApplication.wisePlayerFactory = wisePlayerFactory;
}
}
Creating instance of wise player
Code:
wisePlayer = VideoKitPlayApplication.getWisePlayerFactory().createWisePlayer();
WisePlayer layout
Code:
private void initView(View view) {
if (view != null) {
surfaceView = (SurfaceView) view.findViewById(R.id.surface_view);
textureView = (TextureView) view.findViewById(R.id.texture_view);
if (PlayControlUtil.isSurfaceView()) {
//SurfaceView display interface
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(thois);
textureView.setVisibility(View.GONE);
surfaceView.setVisibility(View.VISIBLE);
} else {
//TextureView display interface
textureView.setSurfaceTextureListener(this);
textureView.setVisibility(View.VISIBLE);
surfaceView.setVisibility(View.GONE);
}
}
Register WisePlayer listeners
Code:
private void setPlayListener() {
if (wisePlayer != null) {
wisePlayer.setErrorListener(onWisePlayerListener);
wisePlayer.setEventListener(onWisePlayerListener);
wisePlayer.setResolutionUpdatedListener(onWisePlayerListener);
wisePlayer.setReadyListener(onWisePlayerListener);
wisePlayer.setLoadingListener(onWisePlayerListener);
wisePlayer.setPlayEndListener(onWisePlayerListener);
wisePlayer.setSeekEndListener(onWisePlayerListener);
}
}
Set playback parameters
Code:
player.setVideoType(PlayMode.PLAY_MODE_NORMAL);
player.setBookmark(10000);
player.setCycleMode(CycleMode.MODE_CYCLE);
Set URL for video
Code:
wisePlayer.setPlayUrl(new String[] {currentPlayData.getUrl()});
Set a view to display the video.
Code:
// SurfaceView listener callback
@Override
public void surfaceCreated(SurfaceHolder holder) {
wisePlayer.setView(surfaceView);
}
// TextureView listener callback
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
wisePlayer.setView(textureView);
// Call the resume API to bring WisePlayer to the foreground.
wisePlayer.resume(ResumeType.KEEP);
}
Prepare for the playback and start requesting data.
Code:
wisePlayer.ready();
Start the playback After success response
Code:
@Override
public void onReady(WisePlayer wisePlayer) {
player.start();
}
activity main.xml
Create a view for Recycler View.
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/player_recycler_view"
android:background="@drawable/images"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp" />
</RelativeLayout>
select_play_view
Create card view for displaying the trip locations.
Code:
<?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="wrap_content"
android:orientation="vertical"
android:tag="cards main container">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="230dp"
android:layout_margin="5dp"
card_view:cardBackgroundColor="@color/cardcolour"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
>
<ImageView
android:id="@+id/videoIcon"
android:tag="image_tag"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:src="@drawable/1"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_weight="2"
android:orientation="vertical"
>
<TextView
android:id="@+id/play_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text=""
android:textColor="@color/colorTitle"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<TextView
android:id="@+id/briefStory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:textColor="@color/green"
android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView
android:id="@+id/play_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="@color/select_play_text_color"
android:textSize="20sp"
android:visibility="gone"/>
<TextView
android:id="@+id/play_url"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="2"
android:paddingTop="5dip"
android:singleLine="false"
android:textColor="@color/select_play_text_color"
android:textSize="14sp"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
Data Adapter to hold the data for Recycler View
Code:
package com.huawei.video.kit.demo.adapter;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.huawei.video.kit.demo.R;
import com.huawei.video.kit.demo.contract.OnItemClickListener;
import com.huawei.video.kit.demo.entity.PlayEntity;
import com.huawei.video.kit.demo.utils.LogUtil;
import com.huawei.video.kit.demo.utils.StringUtil;
import com.squareup.picasso.Picasso;
/**
* Play recyclerView adapter
*/
public class SelectPlayDataAdapter extends RecyclerView.Adapter<SelectPlayDataAdapter.PlayViewHolder> {
private static final String TAG = "SelectPlayDataAdapter";
// Data sources list
private List<PlayEntity> playList;
// Context
private Context context;
// Click item listener
private OnItemClickListener onItemClickListener;
/**
* Constructor
*
* @param context Context
* @param onItemClickListener Listener
*/
public SelectPlayDataAdapter(Context context, OnItemClickListener onItemClickListener) {
this.context = context;
playList = new ArrayList<>();
this.onItemClickListener = onItemClickListener;
}
/**
* Set list data
*
* @param playList Play data
*/
public void setSelectPlayList(List<PlayEntity> playList) {
if (this.playList.size() > 0) {
this.playList.clear();
}
this.playList.addAll(playList);
notifyDataSetChanged();
}
@NonNull
@Override
public PlayViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.select_play_item, parent, false);
return new PlayViewHolder(view);
}
@Override
public void onBindViewHolder(PlayViewHolder holder, final int position) {
if (playList.size() > position && holder != null) {
PlayEntity playEntity = playList.get(position);
if (playEntity == null) {
LogUtil.i(TAG, "current item data is empty.");
return;
}
StringUtil.setTextValue(holder.playName, playEntity.getName());
//StringUtil.setTextValue(holder.releasedYear, playEntity.getYear());
StringUtil.setTextValue(holder.briefStory, playEntity.getStory());
StringUtil.setTextValue(holder.playUrl, playEntity.getUrl());
StringUtil.setTextValue(holder.playType, String.valueOf(playEntity.getUrlType()));
holder.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(position);
}
});
Picasso.with(context).load(playEntity.getIcon()).into(holder.videoIcon);
}
}
@Override
public int getItemCount() {
return playList.size();
}
/**
* Show view holder
*/
static class PlayViewHolder extends RecyclerView.ViewHolder {
// The video name
private TextView playName;
private TextView briefStory;
// The video type
private TextView playType;
// The video url
private TextView playUrl;
private ImageView videoIcon
/**
* Constructor
*
* @param itemView Item view
*/
public PlayViewHolder(View itemView) {
super(itemView);
if (itemView != null) {
playName = itemView.findViewById(R.id.play_name);
briefStory = itemView.findViewById(R.id.briefStory);
playType = itemView.findViewById(R.id.play_type);
playUrl = itemView.findViewById(R.id.play_url);
videoIcon = itemView.findViewById(R.id.videoIcon);
}
}
}
}
HomePageView
This associated with the view of recycler view.
Code:
package com.huawei.video.kit.demo.view;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.huawei.video.kit.demo.R;
import com.huawei.video.kit.demo.adapter.SelectPlayDataAdapter;
import com.huawei.video.kit.demo.contract.OnHomePageListener;
import com.huawei.video.kit.demo.entity.PlayEntity;
import com.huawei.video.kit.demo.utils.DialogUtil;
/**
* Home page view
*/
public class HomePageView {
int numberOfColumns = 2;
// Home page parent view
private View contentView;
// Context
private Context context;
// Play recyclerView
private RecyclerView playRecyclerView;
// Input play url
private EditText addressEt;
// Play button
private Button playBt;
// Play adapter
private SelectPlayDataAdapter selectPlayDataAdapter;
// Listener
private OnHomePageListener onHomePageListener;
/**
* Constructor
*
* @param context Context
* @param onHomePageListener Listener
*/
public HomePageView(Context context, OnHomePageListener onHomePageListener) {
this.context = context;
this.onHomePageListener = onHomePageListener;
initView();
}
/**
* Get parent view
*
* @return Parent view
*/
public View getContentView() {
return contentView;
}
/**
* Init view
*/
private void initView() {
contentView = LayoutInflater.from(context).inflate(R.layout.activity_main, null);
playRecyclerView = (RecyclerView) contentView.findViewById(R.id.player_recycler_view);
GridLayoutManager gridLayoutManager = new GridLayoutManager(context.getApplicationContext(),numberOfColumns);
playRecyclerView.setLayoutManager(gridLayoutManager);
playLoading = (ProgressBar) contentView.findViewById(R.id.play_loading);
addressEt = (EditText) contentView.findViewById(R.id.input_path_ed);
playBt = (Button) contentView.findViewById(R.id.main_play_btn);
playBt.setOnClickListener(onHomePageListener);
selectPlayDataAdapter = new SelectPlayDataAdapter(context, onHomePageListener);
// playRecyclerView.setLayoutManager(new LinearLayoutManager(context));
playRecyclerView.setAdapter(selectPlayDataAdapter);
playRecyclerView.setVisibility(View.GONE);
playLoading.setVisibility(View.VISIBLE);
}
/**
* Set the current data list
*
* @param playList Data list
*/
public void updateRecyclerView(List<PlayEntity> playList) {
selectPlayDataAdapter.setSelectPlayList(playList);
playLoading.setVisibility(View.GONE);
playRecyclerView.setVisibility(View.VISIBLE);
}
/**
* Get input text
*
* @return Text value
*/
public String getInputUrl() {
if (addressEt.getText() == null) {
return "";
} else {
return addressEt.getText().toString();
}
}
}
Results
{
"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"
}
Conclusion
We learnt a simple application which explains the power video kit apis and showcase how easy it to have the videos running in out applications.
References
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/introduction-0000001050439577-V5

Intermediate: Integrating Navigation Application using Huawei Site Kit, Map Kit, Location Kit and Direction API

Overview
This application helps us for getting the direction from current location to the selected place. This app uses Huawei Site Kit, Location Kit, Map kit and Huawei Direction API for showing the direction. Let us see the uses of all kits in this application.
Site Kit: This kit is used for getting the places and near-by places on keyword search.
Location Kit: This kit is used for getting the current location of the user.
Map Kit: This kit is used for showing the map, adding a marker and drawing polyline on the Huawei Map.
Direction API: This API is used for getting the path, steps and polyline between two places.
Let us start with the project configuration part:
Step 1: Create an app on App Gallery Connect.
Step 2: Enable the Site Kit, Location Lit and Map Kit in Manage APIs menu.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Step 3: Create an Android Project with the same package name as App Gallery project package name.
Step 4: Enter the below maven url inside the repositories of buildscript and allprojects (project build.gradle file).
Java:
maven { url ‘http://developer.huawei.com/repo/’ }
Step 5: Add classpath to project’s build.gradle file.
Java:
dependencies {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
}
Step 6: Apply plugin in App’s build.gradle file at top after application plugin.
Java:
apply plugin: 'com.huawei.agconnect'
Step 7: Add below dependencies to app’s build.gradle file.
Java:
implementation 'com.huawei.hms:site:5.0.2.300'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "androidx.cardview:cardview:1.0.0"
implementation 'com.huawei.hms:maps:4.0.0.302'
implementation 'com.huawei.hms:location:4.0.1.300'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.google.code.gson:gson:2.6.1'
Step 8: Add the app ID generated when the creating the app on HUAWEI Developers to manifest file.
Java:
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="appid=your app id" />
Step 9: Add the below permissions to manifest file.
Java:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA"/>
<!-- Allow the app to obtain the coarse longitude and latitude of a user through the Wi-Fi network or base station. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- Allow the app to receive location information from satellites through the GPS chip. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Step 10: Generate SHA 256 key and add to App Gallery Connect Project.
Step 11: download the agconnect-services.json from App Information Section. Copy and paste the Json file in the app folder of the android project.
Step 12: Sync the project.
Let us start with the implementation part:
Part 1: Site Kit Integration
Using the Site Kit, we will search for place and get the latitude and longitude.
Step 1: Get the API_KEY from App Gallery and define the same in your MainActivity.Java.
Java:
public static final String MY_API_KEY = "Your API_KEY will come here";
Step 2: Declare a SearchService object and use SearchServiceFactory to initialize the object.
Java:
// Declare SearchService object
private SearchService searchService;
// Initialize the SearchService object
searchService = SearchServiceFactory.create(this, URLEncoder.encode(MY_API_KEY, "utf-8"));
Step 3: create the layout for search a place.
Java:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_gravity="bottom"
android:gravity="center"
android:paddingLeft="5dp"
android:text="Find your place"
android:textSize="18sp"
android:textStyle="bold"
android:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Query: "
android:visibility="gone" />
<EditText
android:id="@+id/edit_text_text_search_query"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:autofillHints=""
android:background="@drawable/search_bg"
android:hint="Search here "
android:imeOptions="actionGo"
android:inputType="text"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
android:visibility="visible"/>
</LinearLayout>
<Button
android:id="@+id/button_text_search"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:background="@drawable/search_btn_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:text="Search"
android:textAllCaps="false"
android:textColor="@color/upsdk_white" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center"
android:paddingLeft="5dp"
android:text="Note: Get site Id suing Keyword/Nearby/Place suggestion search"
android:textSize="18sp"
android:textStyle="bold"
android:visibility="gone"
android:padding="10dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_gravity="bottom"
android:background="#D3D3D3"
android:gravity="center_vertical"
android:paddingLeft="5dp"
android:text="Result"
android:textSize="16sp"
android:visibility="gone" />
<TextView
android:id="@+id/response_text_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textIsSelectable="true"
android:padding="10dp"
android:textColor="@color/colorPrimary"
android:textSize="18sp"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/searchResultList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:visibility="visible"/>
</LinearLayout>
</LinearLayout>
Step 4: Create the ListAdapter for showing the data.
Java:
public class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.SearchViewHolder> {
List<SearchModel> searchModelList;
Context context;
public SearchListAdapter(List<SearchModel> searchModelList, Context context) {
this.searchModelList = searchModelList;
this.context = context;
}
@NonNull
@Override
public SearchViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new SearchViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.search_result_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull SearchViewHolder holder, final int position) {
final SearchModel searchModel = searchModelList.get(position);
holder.nameTv.setText(searchModel.getName());
holder.formattedAddress.setText(searchModel.getFormattedAddress());
holder.countryCodeTv.setText(searchModel.getCountryCode());
holder.countryTv.setText(searchModel.getCountry());
// Click listener for Row view
holder.btnGetDirection.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"Position is "+position,Toast.LENGTH_SHORT ).show();
Intent intent = new Intent(context, DirectionActivity.class);
intent.putExtra("latitude",searchModel.getLatitude());
intent.putExtra("longitude",searchModel.getLongitude());
context.startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return searchModelList.size();
}
class SearchViewHolder extends RecyclerView.ViewHolder {
TextView nameTv;
TextView formattedAddress;
TextView countryTv;
TextView countryCodeTv;
LinearLayout row_layout;
Button btnGetDirection;
public SearchViewHolder(@NonNull View itemView) {
super(itemView);
nameTv = itemView.findViewById(R.id.name);
formattedAddress = itemView.findViewById(R.id.formattedAddress);
countryTv = itemView.findViewById(R.id.country);
countryCodeTv = itemView.findViewById(R.id.countryCode);
row_layout = itemView.findViewById(R.id.row_layout);
btnGetDirection = itemView.findViewById(R.id.get_direction);
}
}
}
Step 5: Create row_layout.xml inside layout folder.
Java:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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="wrap_content"
app:cardCornerRadius="5dp"
app:cardElevation="5dp"
android:layout_marginBottom="3dp">
<LinearLayout
android:id="@+id/row_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Name: "
android:textStyle="bold"
android:layout_weight="0.3"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:paddingLeft="5dp"
android:id="@+id/name"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Address: "
android:textStyle="bold"
android:layout_weight="0.3"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:id="@+id/formattedAddress"
android:layout_weight="0.7"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Country: "
android:textStyle="bold"
android:layout_weight="0.3"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:id="@+id/country"
android:layout_weight="0.7"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Country code: "
android:textStyle="bold"
android:layout_weight="0.3"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/countryCode"
android:paddingLeft="5dp"
android:layout_weight="0.3"/>
<Button
android:id="@+id/get_direction"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="@drawable/search_btn_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:text="Get Direction"
android:textAllCaps="false"
android:textColor="@color/upsdk_white" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
Step 6: Initialize the Recycler view to MainActivity.Java.
Java:
private RecyclerView searchResultList;
searchResultList.setLayoutManager(new LinearLayoutManager(this));
Step 7: On Search button click, search places and set it to ListAdapter.
Java:
mSearchBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
searchModelList = new ArrayList<>();
search();
}
});
Java:
public void search() {
TextSearchRequest textSearchRequest = new TextSearchRequest();
textSearchRequest.setQuery(queryInput.getText().toString());
textSearchRequest.setHwPoiType(HwLocationType.ADDRESS);
textSearchRequest.setHwPoiType(HwLocationType.ENTERTAINMENT_PLACE);
textSearchRequest.setHwPoiType(HwLocationType.INDIAN_RESTAURANT);
textSearchRequest.setHwPoiType(HwLocationType.CITIES);
textSearchRequest.setHwPoiType(HwLocationType.REGIONS);
searchService.textSearch(textSearchRequest, new SearchResultListener<TextSearchResponse>() {
@Override
public void onSearchResult(TextSearchResponse textSearchResponse) {
List<Site> sites = textSearchResponse.getSites();
if (sites == null || sites == null || sites.size() <= 0) {
return;
}
AddressDetail addressDetail;
if (sites != null && sites.size() > 0) {
for (Site site : sites) {
searchModel = new SearchModel();
addressDetail = site.getAddress();
searchModel.setName(site.getName());
searchModel.setFormattedAddress(site.getFormatAddress());
searchModel.setCountry(addressDetail.getCountry());
searchModel.setCountryCode(addressDetail.getCountryCode());
searchModel.setLatitude(site.getLocation().getLat());
searchModel.setLongitude(site.getLocation().getLng());
searchModelList.add(searchModel);
}
SearchListAdapter searchListAdapter = new SearchListAdapter(searchModelList, MainActivity.this);
searchResultList.setAdapter(searchListAdapter);
}
}
Now getting the list of places completed.
Result:
Part 2: Map Kit Implementation
This Kit is being used for showing the Huawei map. After clicking on Get Direction button in searched places, its navigates to DirectionActivity.Java which loads the Huawei map using Map Kit and getting the current location using Huawei Location Kit.
Step 1: Create the xml layout which contains the MapView.
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="match_parent"
android:orientation="vertical">
<com.huawei.hms.maps.MapView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:mapType="normal"
map:uiCompass="true"/>
</LinearLayout>
Step 2: To use the MapView, implement OnMapReadyCallbackAPI and override the onMapReady(HuaweiMap huaweiMap).
Java:
public class DirectionActivity extends AppCompatActivity implements OnMapReadyCallback{
}
Step 3: Add runtime permissions.
Java:
private static final String[] RUNTIME_PERMISSIONS = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.INTERNET
};
if (!hasPermissions(this, RUNTIME_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
Java:
private static boolean hasPermissions(Context context, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
Step 4: Load MapView inside onCreate() method of DirectionActivity.Java and call getMapAsync() to register the callback.
Java:
private HuaweiMap hMap;
private MapView mMapView;mMapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAP_BUNDLE_KEY);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
Step 5: Inside onMapReady() callback, get the Huawei Map object and set the current location enabled.
Java:
public void onMapReady(HuaweiMap huaweiMap) {
Log.d(TAG, "onMapReady: ");
hMap = huaweiMap;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return;
}
hMap.setMyLocationEnabled(true);
CameraPosition build = new CameraPosition.Builder().target(new LatLng(20.5937, 78.9629)).zoom(4).build();
CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(build);
hMap.animateCamera(cameraUpdate);
}
Step 6: Override the onStart(), onStop(),onDestroy(),onPause(), onResume() and onLowMemory() in the DirectionActivity.Java.
Java:
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
@Override
protected void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
Now integration of map done.
Result:
Part 3: Location Kit Integration
This kit is being used for getting the current location of the user.
Step 1: Initialize the current location instances.
Java:
private LocationCallback mLocationCallback;
private LocationRequest mLocationRequest;
private FusedLocationProviderClient fusedLocationProviderClient;
private SettingsClient settingsClient;private double latitude;
private double longitude;
Step 2: call getCurrentLocation() inside onCreate() method of DirectionActivity.Java and save it.
Java:
private void getCurrentLocation(){
//create a fusedLocationProviderClient
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
//create a settingsClient
settingsClient = LocationServices.getSettingsClient(this);
mLocationRequest = new LocationRequest();
// set the interval for location updates, in milliseconds.
mLocationRequest.setInterval(10000);
// set the priority of the request
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (null == mLocationCallback) {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult != null) {
List<Location> locations = locationResult.getLocations();
if (!locations.isEmpty()) {
Location loc = locations.get(0);
latitude = loc.getLatitude();
longitude = loc.getLongitude();
if(count == 0){
count = count + 1;
getRoutes();
}
}
}
}
@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
if (locationAvailability != null) {
boolean flag = locationAvailability.isLocationAvailable();
Toast.makeText(DirectionActivity.this, "isLocationAvailable:"+flag, Toast.LENGTH_SHORT).show();
}
}
};
}
}
Step 3: call requestLocationUpdatesWithCallback() method after getCurrentLocation()insideonCreate().
Java:
private void requestLocationUpdatesWithCallback() {
try {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// check devices settings before request location updates.
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>()
{
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
Log.i(TAG, "check location settings success");
// request location updates
fusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(DirectionActivity.this,"requestLocationUpdatesWithCallback onFailure:",Toast.LENGTH_SHORT).show();
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e)
{
Toast.makeText(DirectionActivity.this,"checkLocationSetting onFailure:",Toast.LENGTH_SHORT).show();
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(DirectionActivity.this, 0);
} catch (IntentSender.SendIntentException sie) {
Log.e(TAG, "PendingIntent unable to execute request.");
}
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Now getting current location part completed.
Part 4: Direction API Implementation
This API is getting used for getting the routes between source and destination location.
Step 1: Get the destination location from place info and save it inside onCreate()method of DirectionActivity.java.
Java:
// Destination location data
private double destLatitude;
private double destLongitude;destLatitude = getIntent().getDoubleExtra("latitude",0.0);
destLongitude = getIntent().getDoubleExtra("longitude",0.0);
Step 2: Create DirectionService.java for getting the routes between source and destination location.
Java:
public class DirectionService {
public static final String ROOT_URL = "https://mapapi.cloud.huawei.com/mapApi/v1/routeService/";
public static final String conection = "?key=";
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
final MutableLiveData<JsonData> jsonData = new MutableLiveData<>();
private String jsonResponse;
public RouteInfo info;
private static DirectionService directionService;
public static DirectionService getInstance(){
if (directionService == null)
directionService = new DirectionService();
return directionService;
}
public void setRouteInfo(RouteInfo info)
{
this.info = info;
}
public void driving(String serviceName, String apiKey, Route route) throws UnsupportedEncodingException {
JSONObject json = new JSONObject();
JSONObject origin = new JSONObject();
JSONObject destination = new JSONObject();
try {
origin.put("lng",route.getOrigin().getLng());
origin.put("lat", route.getOrigin().getLat());
destination.put("lng", route.getDestination().getLng());
destination.put("lat", route.getDestination().getLat());
json.put("origin", origin);
json.put("destination", destination);
} catch (JSONException e) {
Log.e("error", e.getMessage());
}
RequestBody body = RequestBody.create(JSON, String.valueOf(json));
OkHttpClient client = new OkHttpClient();
Request request =
new Request.Builder().url(ROOT_URL + serviceName + conection + URLEncoder.encode(apiKey, "UTF-8"))
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("driving", e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// Log.d("driving", response.body().string());
info.routeInfo(response.body().string());
}
});
}
}
Step 3: Call the getRoute() method inside getCurrentLocation() of DirectionActivity.Java.
Java:
private void getRoutes()
{
// get the routes
Origin origin = new Origin();
origin.setLat(latitude);
origin.setLng(longitude);
Destination dest = new Destination();
dest.setLat(destLatitude);
dest.setLng(destLongitude);
Route route = new Route();
route.setOrigin(origin);
route.setDestination(dest);
try {
DirectionService.getInstance().setRouteInfo(this);
DirectionService.getInstance().driving("driving",MainActivity.MY_API_KEY,route);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
Step 4: Create an Interface RouteInfo.
Java:
public interface RouteInfo {
void routeInfo(String info);
}
Step 5: DirectionActivity.Java will implement RouteInfo interface.
Java:
public class DirectionActivity extends AppCompatActivity implements OnMapReadyCallback,RouteInfo{
}
Step 6: Override routeInfo() method and convert string response to Json object using gson library.
Java:
@Override
public void routeInfo(String info) {
Gson gson = new Gson();
JsonData obj = gson.fromJson(info,JsonData.class);
addPolyline(obj);
addMarker();
animateCameraToCurrentLocation();
}
Step 7: Add polyline from the routes info.
Java:
private void addPolyline(JsonData obj) {
if(hMap == null){
return;
}
if (null != mPolyline) {
mPolyline.remove();
mPolyline = null;
}
PolylineOptions options = new PolylineOptions();
if(obj != null){
ArrayList<Routes> routes = obj.getRoutes();
if(routes != null && routes.size() > 0){
ArrayList<Path> paths = routes.get(0).getPaths();
if(paths != null && paths.size() > 0){
ArrayList<Step> steps = paths.get(0).getSteps();
if(steps != null && steps.size() > 0)
{
for(Step step : steps) {
ArrayList<com.huawei.sitekitsampleapp.model.Polyline> polylines = step.getPolyline();
if(polylines != null && polylines.size() > 0){
for(com.huawei.sitekitsampleapp.model.Polyline polyline : polylines){
// Add lat lng to options
options.add(new LatLng(polyline.getLat(),polyline.getLng()));
}
}
}
}
}
}
}
options.color(Color.GREEN).width(3);
mPolyline = hMap.addPolyline(options);
}
Step 8: Add a Marker at destination location.
Java:
private void addMarker() {
if (null != mMarker) {
mMarker.remove();
}
MarkerOptions options = new MarkerOptions()
.position(new LatLng(destLatitude, destLongitude)).icon(BitmapDescriptorFactory.fromResource(R.drawable.marker));
mMarker = hMap.addMarker(options);
}
Step 9: Add marker.xml to drawable folder.
XML:
<vector android:height="24dp" android:tint="#FF1730"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>
Step 10: Animate camera to current location.
Java:
private void animateCameraToCurrentLocation()
{
CameraPosition build = new CameraPosition.Builder().target(new LatLng(latitude, longitude)).zoom(13).build();
CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(build);
hMap.animateCamera(cameraUpdate);
}
Now showing the direction between two place done.
Result:
Tips and Tricks:
Set input properly for origin and Destination for getting the routes.
Conclusion:
This application can help to show the direction between your current location to your favorite place. You can get the restaurants, schools and places and navigate to the same.
Reference:
https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/directions-walking-0000001050161494-V5#EN-US_TOPIC_0000001050161494__section12176172981317
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-keyword-search-0000001050156630
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-map-instance-creation-0000001062881706
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/location-develop-steps-0000001050746143

Intermediate: How to Integrate Rest APIs using Huawei Network Kit in Android

Introduction
In this article, we will learn how to implement Huawei Network kit in Android. Network kit is a basic network service suite we can utilizes scenario based REST APIs as well as file upload and download. The Network kit can provide with easy-to-use device-cloud transmission channels featuring low latency and high security.
{
"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"
}
About Huawei Network kit
Huawei Network Kit is a service that allows us to perform our network operations quickly and safely. It provides a powerful interacting with Rest APIs and sending synchronous and asynchronous network requests with annotated parameters. Also it allows us to quickly and easily upload or download files with additional features such as multitasking, multithreading, uploads and downloads. With Huawei Network Kit we can improve the network connection when you want to access to a URL.
Supported Devices
Huawei Network Kit is not for all devices, so first we need to validate if the device support or not, and here is the list of devices supported.
Requirements
1. Any operating system (i.e. MacOS, Linux and Windows).
2. Any IDE with Android SDK installed (i.e. IntelliJ, Android Studio).
3. Minimum API Level 19 is required.
4. Required EMUI 3.0 and later version devices.
Code Integration
Create Application in Android Studio.
App level gradle dependencies.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Gradle dependencies
Code:
implementation "com.huawei.hms:network-embedded:5.0.1.301"
implementation "androidx.multidex:multidex:2.0.1"
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'androidx.recyclerview:recyclerview:1.2.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
Root level gradle dependencies
Code:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Add the below permissions in Android Manifest file.
Code:
<manifest xlmns:android...>
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ...
</manifest>
First we need to implement HMS Network kit with this we will check Network kit initialization status.
Code:
public class HWApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
@Override
public void onCreate() {
super.onCreate();
NetworkKit.init(getApplicationContext(), new NetworkKit.Callback() {
@Override
public void onResult(boolean status) {
if (status) {
Toast.makeText(getApplicationContext(), "Network kit successfully initialized", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "Network kit initialization failed", Toast.LENGTH_SHORT).show();
}
}
});
}
}
Now we need to create ApiClient class, here we will declare the Restclient object.
Code:
public class ApiClient {
private static final String BASE_URL = "https://newsapi.org/v2/";
private static final int CONNECT_TIMEOUT = 10000;
private static final int WRITE_TIMEOUT = 1000;
private static final int TIMEOUT = 10000;
public static RestClient restClient;
public static RestClient getRestClient() {
if (restClient == null) {
restClient = new RestClient
.Builder()
.baseUrl(BASE_URL)
.httpClient(getHttpClient())
.build();
}
return restClient;
}
public static HttpClient getHttpClient() {
return new HttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT)
.readTimeout(TIMEOUT)
.writeTimeout(WRITE_TIMEOUT)
.enableQuic(false)
.build();
}
}
Code:
public interface ApiInterface {
@GET("top-headlines")
Submit<String> getMovies(@Query("country") String country, @Query("category") String category, @Query("apiKey") String apiKey);
}
In Our MainActivity.java class we need to create the instance for ApiInterface, now we need to call the Restclient object to send synchronous or asynchronous requests.
Java:
public class MainActivity extends AppCompatActivity {
ApiInterface apiInterface;
private RecyclerView recyclerView;
private List<NewsInfo.Article> mArticleList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
init();
}
private void init() {
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.smoothScrollToPosition(0);
}
@Override
protected void onResume() {
super.onResume();
loadData();
}
private void loadData() {
apiInterface = ApiClient.getRestClient().create(ApiInterface.class);
apiInterface.getMovies("us", "business", "e4d3e43d2c0b4e2bab6500ec6e469a94")
.enqueue(new Callback<String>() {
@Override
public void onResponse(Submit<String> submit, Response<String> response) {
runOnUiThread(() -> {
Gson gson = new Gson();
NewsInfo newsInfo = gson.fromJson(response.getBody(), NewsInfo.class);
mArticleList = newsInfo.articles;
recyclerView.setAdapter(new ContentAdapter(getApplicationContext(), mArticleList));
recyclerView.smoothScrollToPosition(0);
});
}
@Override
public void onFailure(Submit<String> submit, Throwable throwable) {
Log.i("TAG", "Api failure");
}
});
}
}
NewsInfo.java
Java:
public class NewsInfo {
@SerializedName("status")
public String status;
@SerializedName("totalResults")
public Integer totalResults;
@SerializedName("articles")
public List<Article> articles = null;
public class Article {
@SerializedName("source")
public Source source;
@SerializedName("author")
public String author;
@SerializedName("title")
public String title;
@SerializedName("description")
public String description;
@SerializedName("url")
public String url;
@SerializedName("urlToImage")
public String urlToImage;
@SerializedName("publishedAt")
public String publishedAt;
@SerializedName("content")
public String content;
public String getAuthor() {
return author;
}
public String getTitle() {
return title;
}
public class Source {
@SerializedName("id")
public Object id;
@SerializedName("name")
public String name;
public String getName() {
return name;
}
}
}
}
main_activity.xml
XML:
<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=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="#f2f2f2"
tools:showIn="@layout/activity_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
ContentAdapter.java
Code:
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
private List<NewsInfo.Article> newsInfos;
private Context context;
public ContentAdapter(Context applicationContext, List<NewsInfo.Article> newsInfoArrayList) {
this.context = applicationContext;
this.newsInfos = newsInfoArrayList;
}
@Override
public ContentAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_adapter, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ContentAdapter.ViewHolder viewHolder, int i) {
viewHolder.chanelName.setText(newsInfos.get(i).source.getName());
viewHolder.title.setText(newsInfos.get(i).getTitle());
viewHolder.author.setText(newsInfos.get(i).getAuthor());
Glide.with(context).load(newsInfos.get(i).urlToImage).into(viewHolder.imageView);
}
@Override
public int getItemCount() {
return newsInfos.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView chanelName, author, title;
private ImageView imageView;
public ViewHolder(View view) {
super(view);
chanelName = view.findViewById(R.id.chanelName);
author = view.findViewById(R.id.author);
title = view.findViewById(R.id.title);
imageView = view.findViewById(R.id.cover);
itemView.setOnClickListener(v -> {
});
}
}
}
layout_adapter.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView android:layout_width="match_parent"
android:layout_height="150dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:clickable="true"
android:focusable="true"
android:elevation="60dp"
android:foreground="?android:attr/selectableItemBackground"
xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/cover"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:scaleType="fitXY" />
<TextView
android:id="@+id/chanelName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/cover"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:textStyle="bold" />
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/cover"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_below="@id/chanelName" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/cover"
android:layout_marginLeft="20dp"
android:layout_marginTop="25dp"
android:layout_below="@id/chanelName" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
Demo
Tips and Tricks
1. Add latest Network kit dependency.
2. Minimum SDK 19 is required.
3. Do not forget to add Internet permission in Manifest file.
4. Before sending request you can check internet connection.
Conclusion
That’s it!
This article will help you to use Network kit in your android application, as we have implemented REST API. We can get the data using either HttpClient object or RestClient object.
Thanks for reading! If you enjoyed this story, please click the Like button and Follow. Feel free to leave a Comment below.
Reference
Network kit URL
Original Source

Let's develop Music Station Android app for Huawei Vision S (Smart TV)

{
"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"
}
Article Introduction
In this article, we will develop Music Station android app for Huawei Vision S (Smart TV) devices. Huawei Vision S is a brand new large screen category and important part of Huawei's "1+8+N" full-scenario services and Huawei Developer Ecosystem. Since, Huawei Vision S system architecture supports AOSP project framework, we used Leanback Library which offers extensive features for large screens to develop our user experience.
Why an app for Huawei Vision S?
Large screen offers better visibility and enhanced user experience. Due to Covid-19 lockdown, Smart TV has grown to include over 80% more users than it had this time last year. Total distribution of usage for TV is increasing rapidly. As a result of this, total number of TV apps has jumped dramatically including educational and entertainment apps.
Designing App for Huawei Vision S
While designing an app for Huawei Vision S, we have to keep following key points in our mind:
Build Layout for TV: We must design landscape orientation layout that allows users to easily see the screen 10 feet away from the TV.
Management Controller: Our app must support arrow keys and handle offline controllers as well as inputs from multiple controllers.
For this article, we implemented Leanback Library which offers amazing and interactive user experience for apps such as Audio/Video players and so on.
Pre-Requisites
Before getting started, following are the requirements:
Android Studio (During this tutorial, we used version 4.1.1)
Android SDK 24 or later
Huawei Vision S for testing
Development
Following are the major steps of development for this article:
Step 1: Add Dependencies & Permissions
1.1: Add the following dependencies in the app level build.gradle file:
Java:
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
// TV Libs
implementation 'androidx.leanback:leanback:1.0.0'
implementation 'androidx.leanback:leanback-preference:1.0.0'
// General
implementation 'org.greenrobot:eventbus:3.2.0'
implementation 'com.google.code.gson:gson:2.8.7'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.nabinbhandari.android:permissions:3.8'
// Animation
implementation 'com.airbnb.android:lottie:3.7.0'
implementation 'com.gauravk.audiovisualizer:audiovisualizer:0.9.2'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation ('com.github.bumptech.glide:okhttp3-integration:4.12.0'){
exclude group: 'glide-parent'
}
}
1.2: We are developing this app only for TV. So, we will disable the Touch_Screen requirements and enable the Leanback. For the audio visualizer, we need Record_Audio permission. Add the following permissions and features tag in the AndroidManifest.xml:
Java:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.software.leanback"
android:required="true" />
<uses-feature
android:name="android.hardware.microphone"
android:required="false" />
1.3: Huawei Vision S supports Leanback Library but does not supports Leanback Launcher. So, we added the following tag in the SplashActivity inside the AndroidManifest.xml:
Code:
<activity android:name=".activities.SplashActivity"
android:screenOrientation="landscape"
android:banner="@drawable/app_icon"
android:icon="@drawable/app_icon"
android:label="@string/app_name"
android:launchMode="singleTop"
android:logo="@drawable/app_icon"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!-- HUAWEI Vision S only support CATEGORY_LAUNCHER -->
<category android:name="android.intent.category.LAUNCHER" />
<!-- ADD the below line only if you want to release the same code on Google Play -->
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
1.4: The banner icon is required when we are developing apps for TV. The size for Huawei Vision S banner icon is 496x280 which must be added in the drawable folder.
Step 2: Generating Supported Language JSON
Since our main goal is playing Music, we restricted data source and generated a JSON file locally to avoid API creation and API calling. In real world scenario, an API can be developed or can be used to get the real-time data.
Step 3: Building Layout
3.1: The most important layout of our application is of PlayerActivity. Add the following activity_player.xml layout file in the layout folder of the res. We developed this layout to enhance user experience and add custom views like Audio Visualizer. The layout has two main sub-layouts, Player Content View and Error View.
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:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/main_background"
android:keepScreenOn="true">
<ImageView
android:id="@+id/imgBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/main_background" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/splashAnimation"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:lottie_autoPlay="false"
app:lottie_progress="53"
app:lottie_rawRes="@raw/splash_animation" />
<com.gauravk.audiovisualizer.visualizer.CircleLineVisualizer
android:id="@+id/blastVisualizer"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:avColor="@color/light_red"
custom:avDensity="0.8"
custom:avSpeed="normal"
custom:avType="fill" />
<ImageView
android:id="@+id/imgOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.5"
android:background="@android:color/black" />
<ProgressBar
android:id="@+id/progressBarLoader"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:indeterminate="true"
android:indeterminateTint="@color/colorPrimaryDark"
android:indeterminateTintMode="src_atop"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/rlContentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:paddingStart="50dp"
android:paddingEnd="50dp">
<ImageView
android:id="@+id/imgIcon"
android:layout_width="80dp"
android:layout_height="80dp" />
<TextView
android:id="@+id/txtSongName"
style="@style/TextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/imgIcon"
android:layout_marginTop="5dp"
android:textSize="@dimen/title_text_size" />
<TextView
android:id="@+id/txtArtistName"
style="@style/TextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/txtSongName"
android:layout_marginTop="5dp"
android:textSize="@dimen/artist_text_size" />
<TextView
android:id="@+id/txtCategoryName"
style="@style/TextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/txtArtistName"
android:layout_marginTop="5dp"
android:textSize="@dimen/category_text_size" />
<ImageButton
android:id="@+id/btnPlayPause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/txtCategoryName"
android:layout_marginTop="50dp"
android:background="@drawable/ui_selector_bg"
android:focusable="true"
android:src="@drawable/lb_ic_play" />
<SeekBar
android:id="@+id/seekBarAudio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/btnPlayPause"
android:layout_marginTop="10dp"
android:background="@drawable/ui_selector_bg"
android:colorControlActivated="@color/white"
android:focusable="true"
android:progressTint="@color/white"
android:thumbTint="@color/white" />
<TextView
android:id="@+id/txtTotalDuration"
style="@style/TextViewStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/seekBarAudio"
android:layout_alignEnd="@+id/seekBarAudio"
android:layout_marginTop="5dp"
android:text=" / -"
android:textSize="@dimen/time_text_size" />
<TextView
android:id="@+id/txtCurrentTime"
style="@style/TextViewStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/seekBarAudio"
android:layout_marginTop="5dp"
android:layout_toStartOf="@+id/txtTotalDuration"
android:text="-"
android:textSize="@dimen/time_text_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rlErrorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background"
android:visibility="gone">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<ImageView
android:id="@+id/imgErrorLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@drawable/lb_ic_sad_cloud" />
<TextView
android:id="@+id/txtError"
style="@style/TextViewStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/imgErrorLoading"
android:layout_centerHorizontal="true"
android:text="@string/error_message"
android:textSize="@dimen/time_text_size" />
<Button
android:id="@+id/btnDismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/txtError"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:focusable="true"
android:text="@string/dismiss_error" />
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
3.2: In this article, we used Lottie animation in the SplashActivity for better user experience inside the following activity_splash.xml.
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/colorPrimaryDark"
tools:context=".activities.SplashActivity">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/splashAnimation"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:lottie_autoPlay="true"
app:lottie_repeatCount="1"
app:lottie_rawRes="@raw/splash_animation" />
<ImageView
android:id="@+id/imgAppIcon"
android:src="@drawable/img_music_station_logo"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="40dp"
android:scaleX="0.8"
android:scaleY="0.8"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
Step 4: Adding JAVA Classes
4.1: We extended the MainFragment class from BrowseFragment which is Leanback home layout component. This view handles the landing screen containing interactive side menu, main content area with different cards and navigation between them.
Java:
public class MainFragment extends BrowseFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prepareBackgroundManager();
setupUIElements();
loadRows();
setupEventListeners();
}
private void loadRows() {
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
CardPresenter cardPresenter = new CardPresenter();
int i;
for (i = 0; i < DataUtil.getData(getActivity()).size(); i++) {
CategoryModel categoryModel = DataUtil.getData(getActivity()).get(i);
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
for (int j = 0; j < categoryModel.getCategorySongs().size(); j++) {
listRowAdapter.add(categoryModel.getCategorySongs().get(j));
}
HeaderItem header = new HeaderItem(i, categoryModel.getCategoryName());
rowsAdapter.add(new ListRow(header, listRowAdapter));
}
setAdapter(rowsAdapter);
}
private void prepareBackgroundManager() {
BackgroundManager mBackgroundManager = BackgroundManager.getInstance(getActivity());
mBackgroundManager.attach(getActivity().getWindow());
mBackgroundManager.setColor(getResources().getColor(R.color.main_background));
DisplayMetrics mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
}
private void setupUIElements() {
setTitle(getString(R.string.app_name));
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
setBrandColor(ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark));
setSearchAffordanceColor(ContextCompat.getColor(getActivity(), R.color.colorPrimary));
}
private void setupEventListeners() {
setOnItemViewClickedListener(new ItemViewClickedListener());
}
private final class ItemViewClickedListener implements OnItemViewClickedListener {
@Override
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof Song) {
Song song = (Song) item;
Intent intent = new Intent(getActivity(), PlayerActivity.class);
intent.putExtra(DataUtil.SONG_DETAIL, song);
getActivity().startActivity(intent);
}
}
}
}
4.2: Cards are displayed using CardPresenter which is extended by Presenter from the Leanback library.
Java:
public class CardPresenter extends Presenter {
private static final int CARD_WIDTH = 313;
private static final int CARD_HEIGHT = 176;
private static int sSelectedBackgroundColor;
private static int sDefaultBackgroundColor;
private static void updateCardBackgroundColor(ImageCardView view, boolean selected) {
int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor;
view.setBackgroundColor(color);
view.findViewById(R.id.info_field).setBackgroundColor(color);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
sDefaultBackgroundColor = ContextCompat.getColor(parent.getContext(), R.color.background);
sSelectedBackgroundColor = ContextCompat.getColor(parent.getContext(), R.color.colorPrimaryDark);
ImageCardView cardView =
new ImageCardView(parent.getContext()) {
@Override
public void setSelected(boolean selected) {
updateCardBackgroundColor(this, selected);
super.setSelected(selected);
}
};
cardView.setFocusable(true);
cardView.setFocusableInTouchMode(true);
updateCardBackgroundColor(cardView, false);
return new ViewHolder(cardView);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, Object item) {
Song song = (Song) item;
ImageCardView cardView = (ImageCardView) viewHolder.view;
cardView.setTitleText(song.getSongName());
cardView.setContentText(song.getArtistName());
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT);
Glide.with(viewHolder.view.getContext())
.load(song.getImageURL())
.centerCrop()
.placeholder(R.drawable.app_icon)
.error(R.drawable.app_icon)
.into(cardView.getMainImageView());
}
@Override
public void onUnbindViewHolder(ViewHolder viewHolder) {
ImageCardView cardView = (ImageCardView) viewHolder.view;
cardView.setBadgeImage(null);
cardView.setMainImage(null);
}
}
4.3: Whenever user click on any Card Item, PlayerActivity is opened. Following are some of the important functions of the PlayerActivity.java. Please refer to the github link for complete code of this class.
Java:
private void handlePlayer(){
if (currentState == MediaPlayerHolder.PlayerState.PLAYING) {
pauseSong();
} else if (currentState == MediaPlayerHolder.PlayerState.PAUSED ||
currentState == MediaPlayerHolder.PlayerState.COMPLETED ||
currentState == MediaPlayerHolder.PlayerState.RESET) {
playSong();
}
}
private void setUI() {
if (song != null) {
txtSongName.setText(song.getSongName());
txtArtistName.setText(song.getArtistName());
txtCategoryName.setText(song.getCategoryName());
Glide.with(this)
.load(song.getImageURL())
.centerCrop()
.circleCrop()
.placeholder(R.drawable.app_icon)
.error(R.drawable.app_icon)
.into(imgIcon);
}
setupSeekBar();
}
private void pauseSong() {
EventBus.getDefault().post(new LocalEventFromMainActivity.PausePlayback());
}
private void playSong() {
EventBus.getDefault().post(new LocalEventFromMainActivity.StartPlayback());
}
private void resetSong() {
EventBus.getDefault().post(new LocalEventFromMainActivity.ResetPlayback());
}
public void log(StringBuffer formattedMessage) {
Log.d(PlayerActivity.class.getSimpleName(), String.format("log: %s", formattedMessage));
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(LocalEventFromMediaPlayerHolder.UpdateLog event) {
log(event.formattedMessage);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(LocalEventFromMediaPlayerHolder.PlaybackDuration event) {
seekBarAudio.setMax(event.duration);
setTotalDuration(event.duration);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(LocalEventFromMediaPlayerHolder.PlaybackPosition event) {
if (!isUserSeeking) {
seekBarAudio.setProgress(event.position, true);
updateProgressTime(event.position);
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(LocalEventFromMediaPlayerHolder.StateChanged event) {
hideLoader();
currentState = event.currentState;
switch (event.currentState) {
case PLAYING:
btnPlayPause.setImageResource(R.drawable.lb_ic_pause);
if (mMediaPlayerHolder.getAudioSessionId() != -1 && blastVisualizer != null){
blastVisualizer.setAudioSessionId(mMediaPlayerHolder.getAudioSessionId());
blastVisualizer.show();
}
break;
case PAUSED:
case RESET:
case COMPLETED:
btnPlayPause.setImageResource(R.drawable.lb_ic_play);
if (blastVisualizer != null){
blastVisualizer.hide();
}
break;
case ERROR:
showError();
break;
}
}
private void showError() {
if(song != null){
String title = song.getSongName();
String artist = song.getArtistName();
String songName = "Unable to play " + title + " (" + artist + ")";
txtError.setText(songName);
}
rlErrorLayout.setVisibility(View.VISIBLE);
btnDismiss.requestFocus();
}
private void setTotalDuration(int duration) {
long totalSecs = TimeUnit.MILLISECONDS.toSeconds(duration);
long hours = totalSecs / 3600;
long minutes = (totalSecs % 3600) / 60;
long seconds = totalSecs % 60;
String totalDuration = String.format(Locale.ENGLISH, "%02d:%02d:%02d", hours, minutes, seconds);
if (hours == 0) {
totalDuration = String.format(Locale.ENGLISH, "%02d:%02d", minutes, seconds);
}
String text = " / " + totalDuration;
txtTotalDuration.setText(text);
}
private void updateProgressTime(int position){
long currentSecs = TimeUnit.MILLISECONDS.toSeconds(position);
long hours = currentSecs / 3600;
long minutes = (currentSecs % 3600) / 60;
long seconds = currentSecs % 60;
String currentDuration = String.format(Locale.ENGLISH, "%02d:%02d:%02d", hours, minutes, seconds);
if (hours == 0) {
currentDuration = String.format(Locale.ENGLISH, "%02d:%02d", minutes, seconds);
}
txtCurrentTime.setText(currentDuration);
}
4.4: We used EventBus to asynchronously notify the PlayerActivity UI changes. The MediaPlayerHolder.java class handles the MediaPlayer states and manages the functionalities like playing, pause and seekbar position updates. Please refer to the github link for complete code of this class.
Java:
public int getAudioSessionId(){
if(mMediaPlayer != null){
return mMediaPlayer.getAudioSessionId();
} else
return -1;
}
public void release() {
logToUI("release() and mMediaPlayer = null");
mMediaPlayer.release();
EventBus.getDefault().unregister(this);
}
public void stop() {
logToUI("stop() and mMediaPlayer = null");
mMediaPlayer.stop();
}
public void play() {
if (!mMediaPlayer.isPlaying()) {
logToUI(String.format("start() %s", urlPath));
mMediaPlayer.start();
startUpdatingSeekbarWithPlaybackProgress();
EventBus.getDefault()
.post(new LocalEventFromMediaPlayerHolder.StateChanged(PlayerState.PLAYING));
}
}
public void pause() {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
logToUI("pause()");
EventBus.getDefault()
.post(new LocalEventFromMediaPlayerHolder.StateChanged(PlayerState.PAUSED));
}
}
public void reset() {
logToUI("reset()");
mMediaPlayer.reset();
load(urlPath);
stopUpdatingSeekbarWithPlaybackProgress(true);
EventBus.getDefault()
.post(new LocalEventFromMediaPlayerHolder.StateChanged(PlayerState.RESET));
}
public void load(String url) {
this.urlPath = url.replaceAll(" ", "%20");
if (mMediaPlayer != null) {
try {
mMediaPlayer.setAudioAttributes(
new AudioAttributes
.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build());
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer player) {
initSeekbar();
play();
}
});
mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
EventBus.getDefault()
.post(new LocalEventFromMediaPlayerHolder.StateChanged(PlayerState.ERROR));
return false;
}
});
mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(MediaPlayer arg0) {
EventBus.getDefault().post(new LocalEventFromMediaPlayerHolder.StateChanged(PlayerState.PLAYING));
SystemClock.sleep(200);
mMediaPlayer.start();
}
});
logToUI("load() {1. setDataSource}");
mMediaPlayer.setDataSource(urlPath);
logToUI("load() {2. prepare}");
mMediaPlayer.prepareAsync();
} catch (Exception e) {
EventBus.getDefault().post(new LocalEventFromMediaPlayerHolder.StateChanged(PlayerState.ERROR));
logToUI(e.toString());
}
}
}
public void seekTo(int duration) {
logToUI(String.format(Locale.ENGLISH, "seekTo() %d ms", duration));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
mMediaPlayer.seekTo(duration, MediaPlayer.SEEK_CLOSEST);
else
mMediaPlayer.seekTo(duration);
}
private void stopUpdatingSeekbarWithPlaybackProgress(boolean resetUIPlaybackPosition) {
if (mExecutor != null) {
mExecutor.shutdownNow();
}
mExecutor = null;
mSeekbarProgressUpdateTask = null;
if (resetUIPlaybackPosition) {
EventBus.getDefault().post(new LocalEventFromMediaPlayerHolder.PlaybackPosition(0));
}
}
private void startUpdatingSeekbarWithPlaybackProgress() {
// Setup a recurring task to sync the mMediaPlayer position with the Seekbar.
if (mExecutor == null) {
mExecutor = Executors.newSingleThreadScheduledExecutor();
}
if (mSeekbarProgressUpdateTask == null) {
mSeekbarProgressUpdateTask = new Runnable() {
@Override
public void run() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
int currentPosition = mMediaPlayer.getCurrentPosition();
EventBus.getDefault().post(
new LocalEventFromMediaPlayerHolder.PlaybackPosition(
currentPosition));
}
}
};
}
mExecutor.scheduleAtFixedRate(
mSeekbarProgressUpdateTask,
0,
SEEKBAR_REFRESH_INTERVAL_MS,
TimeUnit.MILLISECONDS
);
}
public void initSeekbar() {
// Set the duration.
final int duration = mMediaPlayer.getDuration();
EventBus.getDefault().post(new LocalEventFromMediaPlayerHolder.PlaybackDuration(duration));
logToUI(String.format(Locale.ENGLISH, "setting seekbar max %d sec", TimeUnit.MILLISECONDS.toSeconds(duration)));
}
When user click Select button on the Remote Control of Huawei Vision S on any item, the player view is opened and MediaPlayer start loading the song url. Once the song is loaded, it starts playing and the icon of Play changes to Pause. The Audio Visualizer takes AudioSessionId to sync with audio song. By default, seekbar is selected for the user to skip the songs using Right and Left buttons of the Remote Control. The duration of the song updates based on seekbar position.
Step 5: Run the application
We have added all the required code. Now, just build the project, run the application and test on Huawei Vision S.
Conclusion
Using Leanback, developers can develop beautifully crafted android applications with amazing UI/UX experience for Huawei Vision S. They can also enhance their user engagement and behavior. Combining different Huawei Kits supported by Vision S like Account or IAP can yield amazing results.
Tips and Tricks
You must use default Launcher if you are developing app for Huawei Vision S.
Leanback Launcher is not supported by Huawei Vision S.
If you have same code base for Mobile and Vision S devices, you can use TVUtils class to check at run-time about the device and offer functionalities based on it.
Make sure to add all the permissions like RECORD_AUDIO, INTERNET.
Make sure to add run-time permissions check. In this article, we used 3rd party Permission Check library with custom Dialog if user deny any of the required permission.
Always use animation libraries like Lottie or ViewAnimator to enhance UI/UX in your application.
We used AudioVisualizer library to bring Music feel on our Player UI.
References
Android TV Documentation:
https://developer.android.com/training/tv/start
https://developer.android.com/training/tv/start/layouts
https://developer.android.com/training/tv/start/controllers
https://developer.android.com/training/tv/start/navigation
Lottie Android Documentation:
http://airbnb.io/lottie/#/android
Github Code Link:
https://github.com/yasirtahir/MusicStationTV
Original Source

Categories

Resources