Make your own music player with Audio Kit: Part 3 - Huawei Developers

More information like this, you can visit HUAWEI Developer Forum​
Original article link: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201327669408150034&fid=0101187876626530001
{
"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"
}
After completing part two, which is "); background-size: 1px 1px; background-position: 0px calc(1em + 1px); font-size: 18px; text-decoration: underline;">here, not much left to implement, don’t you worry. Now. we will implement our playlist and a few additional UX-related features about it. Then we will implement advanced playback control buttons to further develop our versatility.
We will be using RecyclerView for the playlist and AudioKit play modes for the advanced playback controls. AudioKit makes it incredibly easy to implement those modes. Also, for viewing the playlist, I will use a bit “unconventional” ways and you can decide how unconventional it is.
If you remember our "); background-size: 1px 1px; background-position: 0px calc(1em + 1px); font-size: 18px; text-decoration: underline;">part 1, I said this:
Now, it is time to explain that code, because we will first implement the playlist feature, before implementing the advanced playback controls.
There I did this:
It first gets my custom adapter called PlaylistAdapter, set the layout manager of my RecyclerView (my playlist), sets the onClickListeners (to choose a song from) and finally calls the super method so that after initializing our managers in the task, let the AsyncTask do the rest that needs to be done.
If you have uncommented here previously, it is time to uncomment now and also let me share and explain the code of PlaylistAdapter, so you will not get ‘undefined’ errors. Create a new Java file for this, as you do for all adapters.
Code:
public class PlaylistAdapter extends RecyclerView.Adapter {
public interface OnItemClickListener {
void onItemClick (List myPlayList, int position);
}
private PlaylistAdapter.OnItemClickListener onItemClickListener;
List myPlayList;
public PlaylistAdapter(List myPlayList){
this.myPlayList = myPlayList;
}
public void setOnItemClickListener(PlaylistAdapter.OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
static class PlayListView extends RecyclerView.ViewHolder {
TextView songNameTextView;
TextView songArtistTextView;
TextView durationTextView;
ImageView moreOptionsImageView;
public PlayListView(View itemView) {
super(itemView);
songNameTextView = itemView.findViewById(R.id.songTitleTextView);
songArtistTextView = itemView.findViewById(R.id.songArtistTextView);
durationTextView = itemView.findViewById(R.id.durationTextView);
moreOptionsImageView = itemView.findViewById(R.id.moreOptionsImageView);
}
}
@NonNull
@Override
public PlayListView onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.playlist_detail_layout, parent, false);
return new PlayListView(layoutView);
}
@Override
public void onBindViewHolder(final PlayListView holder, final int position) {
HwAudioPlayItem currentItem = myPlayList.get(holder.getAdapterPosition());
holder.songNameTextView.setText(currentItem.getAudioTitle());
holder.songArtistTextView.setText(currentItem.getSinger());
long durationOfSong = currentItem.getDuration();
String totalDurationText = String.format(Locale.US, "d:d",
TimeUnit.MILLISECONDS.toMinutes(durationOfSong),
TimeUnit.MILLISECONDS.toSeconds(durationOfSong) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(durationOfSong))
);
holder.durationTextView.setText(totalDurationText);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(onItemClickListener != null) {
onItemClickListener.onItemClick(myPlayList, position);
}
}
});
}
@Override
public int getItemCount() {
return myPlayList.size();
}
}
This is one of the efficient and expandable (or let’s say: future-proof) ways of implementing onClickListener for a RecyclerView. There are other methods too and if you are knowledgable, you can implement your own way clicking RecyclerView items.
Since in part 1, I assumed some level of Android knowledge for this tutorial, I will not explain everything in the adapter, because it is not very very different than a normal custom adapter. I additionally implemented button interfaces. My constructor only has the playlist element, which I later process to view it in the playlist. I still convert my time 00:00 format because it is still a long value.
Also, do not forget to do this in your MainActivity:
And, because of that, you must implement onItemClick(…) method in the activity.
Code:
@Override
public void onItemClick(List myPlayList, int position) {
if (mHwAudioPlayerManager != null && mHwAudioQueueManager != null && mHwAudioQueueManager.getAllPlaylist() != null) {
/*
* 1. Obtains a playlist using the mHwAudioQueueManager.getAllPlaylist() method.
* 2. Compare myPlayList with mHwAudioQueueManager.
*/
if (mHwAudioQueueManager.getAllPlaylist() == myPlayList) {
//If the two playlists are the same, the user-specified song is played.
mHwAudioPlayerManager.play(position);
} else {
//If the two playlists are different, the mHwAudioPlayerManager playlist is updated.
//The music specified by the user is played.
mHwAudioPlayerManager.playList(playList, position, 0);
mHwAudioPlayerManager.setPlayMode(0);
mHwAudioQueueManager.setPlaylist(playList);
Log.w("Playlist", mHwAudioQueueManager.getAllPlaylist() + "");
}
}
}
/*
@Override
public void onItemClick(int position) {
if(mHwAudioPlayerManager != null){
mHwAudioPlayerManager.play(position);
}
}*/
And in MainActivity’s onItemClick(…) method, comments are put to further explain the code. If you do not like the verbose and think that this code looks complex, just comment the whole method and uncomment the below method (which is the same method with a simpler implementation). Be aware though, you should test it yourself to see whether it works for all cases.
Should you have any other questions regarding here (or anywhere else), please comment below, so I can address them.
Control Visibility
Now that our adapter is ready, we should control when/how the user can open it and when/how s/he can close it.
As I said before, my method may be a bit unconventional, so if you think you have a better idea you can implement it yourself. However, what I do is to add a constraint layout to the screen from the cover image to the bottom of the screen. Then, I control its visibility from GONE to VISIBLE, whenever the user clicks on the music button; and from VISIBLE to GONE whenever the user clicks on the music button and additionally, clicks on the back button.
Programmatically, I control the visibility in onCreate(…) method of the activity. And for the back button I override the onBackPressed(…) method. You can comment the onBackPressed(…) method completely and run the app, to see why I did it. This completely for user experience, in case the user wants to close the playlist with the back button click. I do it like this, it is simple enough to code them both:
Code:
@Override
protected void onCreate(Bundle savedInstanceState) {
//... your other codes
binding.containerLayout.setVisibility(View.GONE);
binding.playlistImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(binding.containerLayout.getVisibility() == View.GONE){
binding.containerLayout.setVisibility(View.VISIBLE);
}
else{
binding.containerLayout.setVisibility(View.GONE);
}
}
});
}
@Override
public void onBackPressed() {
if(binding.containerLayout.getVisibility() == View.VISIBLE){
binding.containerLayout.setVisibility(View.GONE);
}
else{
super.onBackPressed();
}
}
At first, I programmatically ensuring that the visibility of constraint layout is gone. You can also make it gone in the layout screen after you are done with the xml changes.
You should be done with the playlist now.
Advanced Playback Controls
Although they are called advanced controls, with the help of Huawei AudioKit, they are simpler to implement than they sound.
AudioKit offers 4 playback modes:
Code:
Playback modes:
0: sequential playback
1: shuffling songs
2: repeating a playlist
3: repeating a song
If you remember the code above, I set the playback mode as 0 in onItemClick(…) method, because we want the playback to be sequential if the user does not change anything explicitly.
Code:
protected void onCreate(Bundle savedInstanceState) {
//... your other codes
final Drawable shuffleDrawable = getDrawable(R.drawable.menu_shuffle_normal);
final Drawable orderDrawable = getDrawable(R.drawable.menu_order_normal);
final Drawable loopItself = getDrawable(R.drawable.menu_loop_one_normal);
final Drawable loopPlaylist = getDrawable(R.drawable.menu_loop_normal);
binding.shuffleButtonImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mHwAudioPlayerManager != null){
if(binding.shuffleButtonImageView.getDrawable().getConstantState().equals(shuffleDrawable.getConstantState())){
mHwAudioPlayerManager.setPlayMode(0);
binding.shuffleButtonImageView.setImageDrawable(getDrawable(R.drawable.menu_order_normal));
Toast.makeText(MainActivity.this,"Normal order",Toast.LENGTH_SHORT).show();
}
else if(binding.shuffleButtonImageView.getDrawable().getConstantState().equals(orderDrawable.getConstantState())){
mHwAudioPlayerManager.setPlayMode(1);
binding.shuffleButtonImageView.setImageDrawable(getDrawable(R.drawable.menu_shuffle_normal));
Toast.makeText(MainActivity.this,"Shuffle songs",Toast.LENGTH_SHORT).show();
}
}
}
});
binding.loopButtonImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mHwAudioPlayerManager != null){
if(binding.loopButtonImageView.getDrawable().getConstantState().equals(loopItself.getConstantState())){
mHwAudioPlayerManager.setPlayMode(2);
binding.loopButtonImageView.setImageDrawable(getDrawable(R.drawable.menu_loop_normal));
Toast.makeText(MainActivity.this,"Loop playlist",Toast.LENGTH_SHORT).show();
}
else if(binding.loopButtonImageView.getDrawable().getConstantState().equals(loopPlaylist.getConstantState())){
mHwAudioPlayerManager.setPlayMode(3);
binding.loopButtonImageView.setImageDrawable(getDrawable(R.drawable.menu_loop_one_normal));
Toast.makeText(MainActivity.this,"Loop the song",Toast.LENGTH_SHORT).show();
}
}
}
});
}
Let’s understand here. I get my drawables at first to compare them to each other. (You should know where to get them from by now, if you do not have them already.) After that, I implement onClicks of the buttons and change the playback modes as per the list I have given above. Also, I change the drawable to current playback drawable for better usability. At the end of every change, I notify the user about the change so that s/he knows what s/he just has changed into.
This feature also gives a good competitive edge, because with these buttons implemented, our music player looks more professional.
That is the end of my tutorial. I hope that you have benefitted from it. If you have any questions about any part of this tutorial, please comment in the related section. See you in the next tutorial!

Related

‘GymOut’- An Awareness Kit Application designed For Work Out.

This article is originally from HUAWEI Developer Foum
Forum link: https://forums.developer.huawei.com/forumPortal/en/home​
About GymOut:
GymOut is a simple work out App which uses some of the features of HMS awareness Kit. In this, we have to set Gym location (here it is taking current location) and work out time. Once we enter into Gym it prompts us to plug in the head set to listen the work out music. The music will stop playing after the completion of work out /on leaving the gym.
{
"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"
}
An Introduction for Awareness Kit:
HUAWEI Awareness Kit provides your app with the ability to obtain contextual information including users' current time, location, behavior, audio device status, ambient light, weather, and nearby beacons. Through this features the app can gain insight into a user's current situation more efficiently, making it possible to deliver a smarter, more considerate user experience
· Capture API
The Capture API allows the app to request the current user status, such as time, location, behaviour, and whether a headset is connected. For example, it can request location for getting latitude and longitude.
· BarrierAPI
The Barrier API allows the app to set a combination of contextual conditions. When the preset contextual conditions are met, the app will receive a notification. We can even accommodate our app with different combinations of contextual conditions to support different use cases. For example, we will get notification once we enter into a specified location.
Software Requirements
Android Studio
Java JDK 1.8 or later
Huawei Mobile Services (APK) 4.0.0.300 or later
Integration
1. Create a project in android studio and Huawei AGC.
2. Provide the SHA-256 Key in App Information Section.
3. Provide storage location. (Selected Singapore here)
4. Download the agconnect-services.json from AGCand save into app directory.
5. In root build.gradle
Go to allprojects->repositories and buildscript->repositories and the given line.
Code:
maven { url 'http://developer.huawei.com/repo/' }
In dependency add class path
Code:
classpath 'com.huawei.agconnect:agcp:1.2.1.301'
6. In app build.gradle
We have to add signingConfigs and buildTypes, otherwise we have to create signed apk for awareness testing.
Code:
signingConfigs {
release {
storeFile file('store.jks')
keyAlias 'mykey'
keyPassword 'hmsapp'
storePassword 'hmsapp'
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
signingConfig signingConfigs.release
debuggable true
}
}
Add Implementation
Code:
implementation 'com.huawei.hms:awareness:1.0.4.301'
Apply plugin
Code:
apply plugin: 'com.huawei.agconnect'
7. The app need the following permissions
Code:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="com.huawei.hms.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Code Implementation:
In this application we are using the following HMS Features:
Capture API
Location Capture: For getting gym Latitude and Longitude.
Barrier API
Location Barrier: To get notification when entering into Gym and exit from Gym .
Headset Barrier:To get alert to connect Head set for workout music
Time Barrier: To alert, once the time elapses the specified workout time.
To capture Location API using getLocation () function
Code:
private void getLocation() {
Awareness.getCaptureClient(this).getLocation()
.addOnSuccessListener(new OnSuccessListener<LocationResponse>() {
@Override
public void onSuccess(LocationResponse locationResponse) {
Location location = locationResponse.getLocation();
latitude=location.getLatitude();
longitude=location.getLongitude();
Toast.makeText(getApplicationContext(),"Longitude:" + longitude
+ ",Latitude:" + latitude,Toast.LENGTH_SHORT).show();
mLogView.printLog("Longitude:" + longitude
+ ",Latitude:" + latitude);
mp = MediaPlayer.create(getApplicationContext(), R.raw.audio);
mp.setLooping(true);
alert_stayTimeDialog();
//add_locationBarrier_enter
AwarenessBarrier enterBarrier = LocationBarrier.enter(latitude, longitude, radius);
Utils.addBarrier(getApplicationContext(), ENTER_BARRIER_LABEL, enterBarrier, mPendingIntent);
AwarenessBarrier exitBarrier = LocationBarrier.exit(latitude, longitude, radius);
Utils.addBarrier(getApplicationContext(), EXIT_BARRIER_LABEL, exitBarrier, mPendingIntent);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(getApplicationContext(),"Failed to get the location.",Toast.LENGTH_SHORT).show();
mLogView.printLog("Failed to get the location.");
Log.e(TAG, "Failed to get the location.", e);
}
});
mScrollView.postDelayed(()-> mScrollView.smoothScrollTo(0,mScrollView.getBottom()),200);
}
Using GymBarrierReceiver to receive the broadcast sent by Awareness Kit when the barrier status changes.
Code:
final class GymBarrierReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
BarrierStatus barrierStatus = BarrierStatus.extract(intent);
String label = barrierStatus.getBarrierLabel();
int barrierPresentStatus = barrierStatus.getPresentStatus();
switch (label) {
case ENTER_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
mLogView.printLog("You are in Gym");
alert_startWorkOutDialog();
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
// mLogView.printLog("You are away from Gym");
} else {
mLogView.printLog("The location status is unknown.");
}
break;
case STAY_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
mLogView.printLog("You have to spent "
+ workOutTime + " minutes in Gym");
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
mLogView.printLog("Your time of duration in Gym is Over.");
} else {
mLogView.printLog("The location status is unknown.");
}
break;
case EXIT_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
mLogView.printLog("You are exiting Gym");
mp.pause();
play_pause.setBackground(getDrawable(android.R.drawable.ic_media_play));
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
// mLogView.printLog("You are in Gym");
} else {
mLogView.printLog("The location status is unknown.");
}
break;
case KEEPING_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
mLogView.printLog("Audio is playing...");
play_pause.setVisibility(View.VISIBLE);
mp.start();
play_pause.setBackground(getDrawable(android.R.drawable.ic_media_pause));
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
mLogView.printLog("Connect headset to play audio");
mp.pause();
play_pause.setBackground(getDrawable(android.R.drawable.ic_media_play));
} else {
mLogView.printLog("The headset status is unknown.");
}
break;
case CONNECTING_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
mLogView.printLog("The headset is connecting. Audio is playing");
play_pause.setVisibility(View.VISIBLE);
mp.start();
play_pause.setBackground(getDrawable(android.R.drawable.ic_media_pause));
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
} else {
mLogView.printLog("The headset status is unknown.");
}
break;
case DURING_TIME_PERIOD_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
mLogView.printLog("Your Work out is "+workOutTime+" minutes.");
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
mLogView.printLog("Work out time reached "+workOutTime+" minutes.");
} else {
mLogView.printLog("The time status is unknown.");
}
break;
default:
break;
}
mScrollView.postDelayed(()-> mScrollView.smoothScrollTo(0,mScrollView.getBottom()*3),200);
}
}
MediaPlayer is added as given :
Code:
MediaPlayer mp = MediaPlayer.create(this, R.raw.audio);
mp.setLooping(true);
CountDownTimer to notify remaining workout time:
Code:
public void countDown(long workOutInMs){
new CountDownTimer(workOutInMs,1000) {
@Override
public void onTick(long millisUntilFinished) {
int seconds = (int) (millisUntilFinished / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
workOut_txt.setText(String.format("%d:%02d", minutes, seconds));
// counter++;
}
@Override
public void onFinish() {
workOut_txt.setText("Your work out is over");
mp.pause();
play_pause.setBackground(getDrawable(android.R.drawable.ic_media_play));
play_pause.setVisibility(View.GONE);
}
}.start();
}
Added different barriers as given:
Code:
AwarenessBarrier enterBarrier = LocationBarrier.enter(latitude, longitude, radius);
Utils.addBarrier(getApplicationContext(), ENTER_BARRIER_LABEL, enterBarrier, mPendingIntent);
AwarenessBarrier exitBarrier = LocationBarrier.exit(latitude, longitude, radius);
Utils.addBarrier(getApplicationContext(), EXIT_BARRIER_LABEL, exitBarrier, mPendingIntent);
AwarenessBarrier stayBarrier = LocationBarrier.stay(latitude, longitude, radius, workOutInMs);
Utils.addBarrier(getApplicationContext(), STAY_BARRIER_LABEL, stayBarrier, mPendingIntent);
AwarenessBarrier keepingConnectedBarrier = HeadsetBarrier.keeping(HeadsetStatus.CONNECTED);
Utils.addBarrier(getApplicationContext(), KEEPING_BARRIER_LABEL, keepingConnectedBarrier, mPendingIntent);
AwarenessBarrier connectingBarrier = HeadsetBarrier.connecting();
Utils.addBarrier(getApplicationContext(), CONNECTING_BARRIER_LABEL, connectingBarrier, mPendingIntent);
AwarenessBarrier timePeriodBarrier = TimeBarrier.duringTimePeriod(currentTimeStamp,
currentTimeStamp + workOutInMs);
Utils.addBarrier(getApplicationContext(), DURING_TIME_PERIOD_BARRIER_LABEL,
timePeriodBarrier, mPendingIntent);
The function alert_stayTimeDialog() is used to capture user workout time.
Code:
void alert_stayTimeDialog(){
final EditText edittext = new EditText(this);
edittext.setText("0");
edittext.setInputType(InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_CLASS_NUMBER);
AlertDialog.Builder alert = new AlertDialog.Builder(
this);
alert.setMessage("Your Workout Time");
alert.setTitle("Enter time in minutes ");
alert.setView(edittext);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//What ever you want to do with the value
Editable YouEditTextValue = edittext.getText();
//OR
int stayTime = Integer.parseInt(edittext.getText().toString());
//Add the code after success
SharedPreferences.Editor editor = gymOutPref.edit();
editor.putBoolean("SET_WORKOUT", true);
editor.putInt("SET_WORKOUT_TIME", stayTime);
editor.commit();
workOutTime=stayTime;
set_workout.setEnabled(false);
set_workout.setBackground(getDrawable(R.drawable.button_style_disable));
delete_workout.setEnabled(true);
delete_workout.setBackground(getDrawable(R.drawable.button_style));
workOut_txt.setText("WorkOut Time:"+stayTime+" Mnts");
Toast.makeText(getApplicationContext(), "WorkOut Time: " + stayTime, Toast.LENGTH_LONG).show();
}
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// what ever you want to do with No option.
}
});
alert.show();
}

Behavior Awareness : A Journey Through HMS Awareness - Part 2

More articles like this, you can visit HUAWEI Developer Forum and Medium.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction:
Behavior Awareness is being used to obtain user current behaviour or detect the behavior change.
For calling Behavior Awareness capability we have to assign the given permissions in the manifest file.
Code:
<!-- Behavior detection permission (Android 10 or later). This permission is sensitive and needs to be dynamically applied for in the code after being declared. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<!-- Behavior detection permission (Android 9). This permission is sensitive and needs to be dynamically applied for in the code after being declared. -->
<uses-permission android:name="com.huawei.hms.permission.ACTIVITY_RECOGNITION" />
Capture API
We can use the Capture API to detect user behaviour such as walking, running,cycling,driving etc.
To get current behavior status of a user, we need to call the getBehavior() method - this will return an instance of the BehaviorResponse class that if successful, will contain information about the current user behavior.
Code:
private void getBehavorInfo() {
Awareness.getCaptureClient(this).getBehavior()
.addOnSuccessListener(new OnSuccessListener<BehaviorResponse>() {
@Override
public void onSuccess(BehaviorResponse behaviorResponse) {
BehaviorStatus behaviorStatus = behaviorResponse.getBehaviorStatus();
DetectedBehavior mostLikelyBehavior = behaviorStatus.getMostLikelyBehavior();
String str1 = "Most likely behavior type is " + mostLikelyBehavior.getType() +
",the confidence is " + mostLikelyBehavior.getConfidence();
behavior_info_capture.setTextColor(getColor(R.color.green));
if(mostLikelyBehavior.getType()==0){
behavior_info_capture.setText("You are in Vehicle");
behavior_image.setImageDrawable(getDrawable(R.drawable.vehicle));
}else if(mostLikelyBehavior.getType()==1){
behavior_info_capture.setText("You are in Bicycle");
behavior_image.setImageDrawable(getDrawable(R.drawable.bicycle));
}else if(mostLikelyBehavior.getType()==2){
behavior_info_capture.setText("You are on Foot");
behavior_image.setImageDrawable(getDrawable(R.drawable.foot));
} else if(mostLikelyBehavior.getType()==3){
behavior_info_capture.setText("You are still now");
behavior_image.setImageDrawable(getDrawable(R.drawable.still));
}else if(mostLikelyBehavior.getType()==4){
behavior_info_capture.setText("Unknown behavior");
}else if(mostLikelyBehavior.getType()==7){
behavior_info_capture.setText("You are Walking");
behavior_image.setImageDrawable(getDrawable(R.drawable.walking));
}else if(mostLikelyBehavior.getType()==8){
behavior_info_capture.setText("You are Running");
behavior_image.setImageDrawable(getDrawable(R.drawable.running));
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "get behavior failed", e);
behavior_info_capture.setTextColor(getColor(R.color.red));
behavior_info_capture.setText("get behavior failed: "+e);
}
});
}
Barrier API
We can use the Barrier API to detect the behavior change such as from walking to running.
Public Methods:
Behavior barriers, including the beginning, ending, and keeping barriers of behaviors. Given three mathods which we use in behavior barrier.
public static AwarenessBarrier beginning(int… behaviorTypes)
When a user is in the behavior status, the barrier status is TRUE and a barrier event is reported. After 5s, the barrier status changes to FALSE.
Code:
AwarenessBarrier awarenessBarrier = BehaviorBarrier.beginning(BehaviorBarrier.BEHAVIOR_WALKING);
public static AwarenessBarrier keeping(int… behaviorTypes)
When a user is in the behavior status , the barrier status is TRUE and a barrier event is reported.
Code:
AwarenessBarrier awarenessBarrier = BehaviorBarrier.keeping(BehaviorBarrier.BEHAVIOR_WALKING);
public static AwarenessBarrier ending(int… behaviorTypes)
When a user stops the behavior, the barrier status is TRUE and a barrier event is reported. After 5s, the barrier status changes to FALSE.
Code:
AwarenessBarrier awarenessBarrier = BehaviorBarrier.ending(BehaviorBarrier.BEHAVIOR_WALKING);
Sample Code
Given an example in which a barrier is triggered by the keeping condition. That is, the barrier will be triggered when a user is in stand still.
Code:
public class BehaviorBarrierActivity extends AppCompatActivity {
private static final String TAG ="BehaviorBarrierActivity" ;
private TextView behavior_info_barrier;
private ImageView behavior_info_barrier_image;
PendingIntent pendingIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.behavior_barrier_activity);
behavior_info_barrier=findViewById(R.id.behavior_info_barrier);
behavior_info_barrier_image=findViewById(R.id.behavior_info_barrier_image);
// define PendingIntent that will be triggered upon a barrier status change.
final String BARRIER_RECEIVER_ACTION = getApplication().getPackageName() + "BEHAVIOR_BARRIER_RECEIVER_ACTION";
Intent intent = new Intent(BARRIER_RECEIVER_ACTION);
pendingIntent = PendingIntent.getBroadcast(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
BehaviorBarrierReceiver barrierReceiver = new BehaviorBarrierReceiver();
registerReceiver(barrierReceiver, new IntentFilter(BARRIER_RECEIVER_ACTION));
addbarrier(this);
}
private void addbarrier(Context context) {
//define the barrier
AwarenessBarrier keepStillBarrier = BehaviorBarrier.keeping(BehaviorBarrier.BEHAVIOR_STILL);
//define the label for the barrier and add the barrier
String behaviorBarrierLabel = "behavior keeping barrier";
//add the barrier
BarrierUpdateRequest.Builder builder = new BarrierUpdateRequest.Builder();
BarrierUpdateRequest request = builder.addBarrier(behaviorBarrierLabel, keepStillBarrier,pendingIntent).build();
Awareness.getBarrierClient(context).updateBarriers(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(), "add barrier success", Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(getApplicationContext(), "add barrier failed", Toast.LENGTH_SHORT).show();
Log.e(TAG, "add barrier failed", e);
}
});
}
// define the broadcast receiver to listen for the barrier event.
class BehaviorBarrierReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
BarrierStatus barrierStatus = BarrierStatus.extract(intent);
String label = barrierStatus.getBarrierLabel();
switch(barrierStatus.getPresentStatus()) {
case BarrierStatus.TRUE:
Log.i(TAG, label + " status:true");
behavior_info_barrier.setText("You are standing now");
behavior_info_barrier_image.setImageDrawable(getDrawable(R.drawable.still));
break;
case BarrierStatus.FALSE:
Log.i(TAG, label + " status:false");
behavior_info_barrier.setText("Looks like you are not standing");
behavior_info_barrier_image.setImageDrawable(getDrawable(R.drawable.foot));
break;
case BarrierStatus.UNKNOWN:
Log.i(TAG, label + " status:unknown");
break;
}
}
}
}
References:
https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/awareness-behavior-dev

Send Notification when Headset is Connected: Foreground Service with Huawei Awareness Kit

Hello everyone, in this article we will learn how to use Huawei Awareness Kit with foreground service to send notification when certain condition is met.
{
"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"
}
Huawei Awareness Kit enables us to observe some environmental factors such as time, location, behavior, audio device status, ambient light, weather and nearby beacons. So, why don’t we create our own conditions to be met and observe them even when the application is not running?
First of all, we need to do HMS Core integration to be able to use Awareness Kit. I will not go into the details of that because it is already covered here.
If you are done with the integration, let’s start coding.
Activity Class
We will keep our activity class pretty simple to prevent any confusion. It will only be responsible for starting the service:
Java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent serviceStartIntent = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceStartIntent);
}
else {
startService(serviceStartIntent);
}
}
}
However, we need to add the following permission to start a service correctly:
XML:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Service Class
Now, let’s talk about the service class. We are about to create a service which will run even when the application is killed. However, this comes with some restrictions. Since the Android Oreo, if an application wants to start a foreground service, it must inform the user by a notification which needs to be visible during the lifetime of the foreground service. Also, this notification needs to be used to start foreground. Therefore, our first job in the service class is to create this notification and call the startForeground() method with it:
Java:
Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
notification = createCustomNotification();
else
notification = new Notification();
startForeground(1234, notification);
And here is how we create the information notification we need for SDK versions later than 25:
Java:
@RequiresApi(api = Build.VERSION_CODES.O)
private Notification createCustomNotification() {
NotificationChannel notificationChannel = new NotificationChannel("1234", "name", NotificationManager.IMPORTANCE_HIGH);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(notificationChannel);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "com.awarenesskit.demo");
return notificationBuilder
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("Observing headset status")
.setPriority(NotificationManager.IMPORTANCE_HIGH)
.build();
}
Note: You should replace the application id above with the application id of your application.
Now, it is time to prepare the parameters to create a condition to be met called barrier:
Java:
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
headsetBarrierReceiver = new HeadsetBarrierReceiver();
registerReceiver(headsetBarrierReceiver, new IntentFilter(barrierReceiverAction));
AwarenessBarrier headsetBarrier = HeadsetBarrier.connecting();
createBarrier(this, HEADSET_BARRIER_LABEL, headsetBarrier, pendingIntent);
Here we have sent the required parameters to a method which will create a barrier for observing headset status. If you want, you can use other awareness barriers too.
Creating a barrier is a simple and standard process which will be taken care of the following method:
Java:
private void createBarrier(Context context, String barrierLabel, AwarenessBarrier barrier, PendingIntent pendingIntent) {
BarrierUpdateRequest.Builder builder = new BarrierUpdateRequest.Builder();
BarrierUpdateRequest request = builder.addBarrier(barrierLabel, barrier, pendingIntent).build();
Awareness.getBarrierClient(context).updateBarriers(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void void1) {
System.out.println("Barrier Create Success");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
System.out.println("Barrier Create Fail");
}
});
}
We will be done with the service class after adding the following methods:
Java:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onDestroy() {
unregisterReceiver(headsetBarrierReceiver);
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
And of course, we shouldn’t forget to add our service to the manifest file:
XML:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />
Broadcast Receiver Class
Lastly, we need to create a broadcast receiver where we will observe and handle the changes in the headset status:
Java:
public class HeadsetBarrierReceiver extends BroadcastReceiver {
public static final String HEADSET_BARRIER_LABEL = "HEADSET_BARRIER_LABEL";
@Override
public void onReceive(Context context, Intent intent) {
BarrierStatus barrierStatus = BarrierStatus.extract(intent);
String barrierLabel = barrierStatus.getBarrierLabel();
int barrierPresentStatus = barrierStatus.getPresentStatus();
if (HEADSET_BARRIER_LABEL.equals(barrierLabel)) {
if (barrierPresentStatus == BarrierStatus.TRUE) {
System.out.println("The headset is connected.");
createNotification(context);
}
else if (barrierPresentStatus == BarrierStatus.FALSE) {
System.out.println("The headset is disconnected.");
}
}
}
When a change occurs in the headset status, this method will receive the information. Here, the value of barrierPresentStatus will determine if headset is connected or disconnected.
At this point, we can detect that headset is just connected, so it is time to send a notification. The following method will take care of that:
Java:
private void createNotification(Context context) {
// Create PendingIntent to make user open the application when clicking on the notification
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1234, new Intent(context, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, "channelId")
.setSmallIcon(R.drawable.ic_headset)
.setContentTitle("Cool Headset!")
.setContentText("Want to listen to some music ?")
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel("channelId", "ChannelName", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(mChannel);
}
notificationManager.notify(1234, notificationBuilder.build());
}
Output
When headset is connected, the following notification will be created even if the application is not running:
​
Final Thoughts
We have learned how to use Barrier API of Huawei Awareness Kit with a foreground service to observe the changes in environmental factors even when the application is not running.
As you may notice, the permanent notification indicating that the application is running in the background is not dismissible by the user which can be annoying. Even though these notifications can be dismissed by some third party applications, not every user has those applications, so you should be careful when deciding to build such services.
In this case, observing the headset status was just an example, so it might not be the best scenario for this use case, but Huawei Awareness Kit has many other great features that you can use with foreground services in your projects.
References
You can check the complete project on GitHub.
Note that, you will not be able to run this project because you don’t have the agconnect-services.json file for it. Therefore, you should only take it as a reference to create your own project.
Does Awareness Kit support wearable devices such as smart watches?

HMS Video Kit — 2

HMS Video Kit — 2
{
"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"
}
In this article, we are going to use some of the other important features of the video kit.
Settings Dialog
With this dialog, users will be able to change the playback options of the video. When the user clicks on TextView which is called Settings, the options will pop up through alert dialog.
Settings
First of all, I will shortly explain how we get the click actions on one of the settings clicked and then I will explain them all separately.
For the settings dialog, we create DialogUtil.java and PlaySettingDialog.java class that implements DialogInterface.OnClickListener. In its initDialog method, we get the playSettingType and OnPlaysettingListener interface and assign them to their local instance variables.
Java:
public PlaySettingDialog initDialog(OnPlaySettingListener playSettingListener, int playSettingType) {
this.onPlaySettingListener = playSettingListener;
this.playSettingType = playSettingType;
return this;
}
Here, OnPlaySettingListener is an interface that we will get its callback on our PlayActivity.java class to determine the selected item and setting type.
Java:
/**
* The player setting listener
*/
public interface OnPlaySettingListener {
/**
* Dialog select listener
*
* @param itemSelect The selected text
* @param settingType The corresponding operation type of player
*/
void onSettingItemClick(String itemSelect, int settingType);
}
When the user clicks on an item from the settings dialog, inside of its OnClick() method, we set the onSettingItemClick and cancel the alert dialog.
Java:
@Override
public void onClick(DialogInterface dialog, int which) {
if (onPlaySettingListener != null) {
onPlaySettingListener.onSettingItemClick(showTextList.get(which).first, playSettingType);
}
dialog.dismiss();
}
Now, I would like to go top again to our PlayActivity.java class. As you remember we have a play_view.xml layout file and onClick listener for some of our View components. We call onSettingDialog method when the user clicks on Settings TextView.
Java:
@Override
public void onClick(View v) {
switch (v.getId()) {
...
case R.id.setting_tv:
onSettingDialog();
break;
...
}
}
We prepare the text list of our settings dialog and call the showSettingDialog of our playView object. Then it calls the onSettingDialogSelectIndex method of DialogUtil class that will create a new PlaySettingDialog object and prepare it for the user.
Java:
/**
* Show the Settings dialog
*/
private void onSettingDialog() {
List<String> showTextList = new ArrayList<>();
showTextList.add(StringUtil.getStringFromResId(this, R.string.video_set_bandwidth_mode));
showTextList.add(StringUtil.getStringFromResId(this, R.string.video_stop_downloading));
showTextList.add(StringUtil.getStringFromResId(this, R.string.video_set_play_speed));
showTextList.add(StringUtil.getStringFromResId(this, R.string.play_mode));
showTextList.add(StringUtil.getStringFromResId(this, R.string.video_set_loop_play));
showTextList.add(StringUtil.getStringFromResId(this, R.string.video_mute_setting));
showTextList.add(StringUtil.getStringFromResId(this, R.string.video_set_volume));
playView.showSettingDialog(Constants.MSG_SETTING, showTextList, 0);
}
Lastly, when the user clicks on one of the items from the settings dialog, we get the itemSelect and settingType values in our PlayActivity’s onSettingItemClick callback thanks to OnPlaySettingListener interface.
Java:
@Override
public void onSettingItemClick(String itemSelect, int settingType) {
switch (settingType) {
case Constants.MSG_SETTING:
Log.d(TAG, "onSettingItemClick: MSG_SETTING CASE.");
if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.video_mute_setting))) {
switchVideoMute();
} else if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.video_set_bandwidth_mode))) {
switchBandwidthMode();
} else if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.video_set_play_speed))) {
setPlaySpeed();
} else if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.video_stop_downloading))) {
stopRequestStream();
} else if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.video_set_volume))) {
setVideoVolume();
} else if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.play_mode))) {
switchPlayMode();
} else if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.video_set_loop_play))) {
switchPlayLoop();
} else {
LogUtil.i(TAG, "current settings type is " + itemSelect);
}
break;
case Constants.PLAYER_SWITCH_PLAY_SPEED:
onSwitchPlaySpeed(itemSelect);
break;
case Constants.PLAYER_SWITCH_BANDWIDTH_MODE:
if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.open_adaptive_bandwidth))) {
playControl.setBandwidthSwitchMode(PlayerConstants.BandwidthSwitchMode.AUTO_SWITCH_MODE, true);
} else {
playControl.setBandwidthSwitchMode(PlayerConstants.BandwidthSwitchMode.MANUAL_SWITCH_MODE, true);
}
break;
...
}
}
I explained how the settings dialog works. You can also refer to my demo code for a better understanding. From now on, I will keep going with the features of the video kit.
Bandwidth Adaptation
It allows you to dynamically adapt the bitrate of the stream to varying network conditions and selects the most suitable bitrate for your app. Adaptive bitrate streaming works by detecting a user’s bandwidth and CPU capacity in real-time and adjusting the quality of the media stream accordingly. When the user clicks on Bandwidth adaptation we call the switchBandwidthMode() method.
Java:
/**
* Set the bandwidth switch
*/
private void switchBandwidthMode() {
List<String> showTextList = new ArrayList<>();
showTextList.add(getResources().getString(R.string.close_adaptive_bandwidth));
showTextList.add(getResources().getString(R.string.open_adaptive_bandwidth));
playView.showSettingDialog(Constants.PLAYER_SWITCH_BANDWIDTH_MODE, showTextList, Constants.DIALOG_INDEX_ONE);
}
When the user makes the choice, again we get it in onSettingItemClick callback of PlayActivity class.
Java:
@Override
public void onSettingItemClick(String itemSelect, int settingType) {
switch (settingType) {
...
case Constants.PLAYER_SWITCH_BANDWIDTH_MODE:
if (TextUtils.equals(itemSelect,
StringUtil.getStringFromResId(PlayActivity.this, R.string.open_adaptive_bandwidth))) {
playControl.setBandwidthSwitchMode(PlayerConstants.BandwidthSwitchMode.AUTO_SWITCH_MODE, true);
} else {
playControl.setBandwidthSwitchMode(PlayerConstants.BandwidthSwitchMode.MANUAL_SWITCH_MODE, true);
}
break;
...
}
}
Our playControl object’s setBandwidthSwitchMode() method will be called.
Java:
/**
* Set the bandwidth switching mode
*
* @param mod The bandwidth switching mode
* @param updateLocate Whether to update the local configuration
*/
public void setBandwidthSwitchMode(int mod, boolean updateLocate) {
if (wisePlayer != null) {
wisePlayer.setBandwidthSwitchMode(mod);
}
if (updateLocate) {
PlayControlUtil.setBandwidthSwitchMode(mod);
}
}
Here wisePlayer.setBandwidthSwitchMode() sets whether to enable adaptive bitrate streaming or keep using the current bitrate.
Download Control
This feature allows us to stop buffering if required. For instance, if you are watching a video on a mobile network and pause it, you can stop video buffering.
Java:
/**
* Whether to stop the downloading
*
* @param selectValue Select text value
*/
private void onSwitchRequestMode(String selectValue) {
if (selectValue.equals(StringUtil.getStringFromResId(PlayActivity.this, R.string.video_keep_download))) {
streamRequestMode = 0;
} else if (selectValue.equals(StringUtil.getStringFromResId(PlayActivity.this, R.string.video_stop_download))) {
streamRequestMode = 1;
}
LogUtil.i(TAG, "mStreamRequestMode:" + streamRequestMode);
playControl.setBufferingStatus(streamRequestMode == 0 ? true : false, true);
}
Here, for demonstration, if the user selects the stop download option, wisePlayer.setBufferingStatus() method will be called and the video buffering will stop.
Java:
public void setBufferingStatus(boolean status, boolean isUpdateLocal) {
if (wisePlayer != null && (isUpdateLocal || PlayControlUtil.isLoadBuff())) {
wisePlayer.setBufferingStatus(status);
if (isUpdateLocal) {
PlayControlUtil.setLoadBuff(status);
}
}
}
Playback Speed
Wiseplayer video kit allows us to change playback speed with setPlaySpeed() method call. In the demo project, I put that option to both settings dialog and the play speed button in activity_play.xml.
Java:
/**
* Set the speed
*
* @param speedValue The speed of the string
*/
public void setPlaySpeed(String speedValue) {
if (speedValue.equals("1.25x")) {
wisePlayer.setPlaySpeed(1.25f);
} else if (speedValue.equals("2.0x")) {
wisePlayer.setPlaySpeed(2.0f);
}
...
else {
wisePlayer.setPlaySpeed(1.0f);
}
}
Playback Mode
With this feature, we can set the playback mode to audio-only or audio+video.
Java:
/**
* Set play mode
*
* @param playMode Play mode
* @param updateLocate Whether to update the local configuration
*/
public void setPlayMode(int playMode, boolean updateLocate) {
if (wisePlayer != null) {
wisePlayer.setPlayMode(playMode);
}
if (updateLocate) {
PlayControlUtil.setPlayMode(playMode);
}
}
Repeat Mode
If we enable the repeat mode, after playing a video is finished, WisePlayer will not call PlayEndListener. Instead, it will play the video again from the beginning of it. To achieve it, we call the setCycleMode() method of the API.
Java:
/**
* Set cycle mode
*
* @param isCycleMode Whether open loop
*/
public void setCycleMode(boolean isCycleMode) {
if (wisePlayer != null) {
wisePlayer.setCycleMode(isCycleMode ? PlayerConstants.CycleMode.MODE_CYCLE : PlayerConstants.CycleMode.MODE_NORMAL);
}
}
Mute/Unmute
We can use the setMute(boolean status) method in order to indicate whether to mute a video or not.
Java:
/**
* Set the mute
*
* @param status Whether quiet
*/
public void setMute(boolean status) {
if (wisePlayer != null) {
wisePlayer.setMute(status);
}
PlayControlUtil.setIsMute(status);
}
Volume
We can set the playback volume by using the setVolume() method. The value ranges from 0 to 1.0. In the demo app, I have used a volume button in the activity_play.xml. We can set the volume either selecting it from the settings dialog or clicking on the volume button. I also used a seek bar for setting the desired volume level easily.
Java:
/**
* Set the volume, the current player is interval [0, 1]
*
* @param volume The volume interval [0, 1]
*/
public void setVolume(float volume) {
if (wisePlayer != null) {
LogUtil.d(TAG, "current set volume is " + volume);
wisePlayer.setVolume(volume);
}
}
In order to control the volume through the seek bar, we can add the code below.
Java:
public static void showSetVolumeDialog(Context context,
final OnDialogInputValueListener onDialogInputValueListener) {
View view = LayoutInflater.from(context).inflate(R.layout.set_volume_dialog, null);
final AlertDialog dialog =
new AlertDialog.Builder(context).setTitle(StringUtil.getStringFromResId(context, R.string.video_set_volume))
.setView(view)
.create();
dialog.show();
final SeekBar volumeSeek = (SeekBar) view.findViewById(R.id.seek_bar_volume);
volumeSeek.setProgress(0);
volumeSeek.setMax(100);
volumeSeek.setProgress(last_state);
volumeSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
float progress_f;
progress_f = (float) progress / 100;
onDialogInputValueListener.dialogInputListener(Float.toString(progress_f));
last_state = progress;
}
...
}
The maximum playback volume is determined by the current media volume of your phone.
Click to expand...
Click to collapse
You can find the source code of the demo app here.
In this article, we have used some of the important features of the video kit. HUAWEI Video Kit will support video editing and video hosting in later versions. Once the new features are released, I will be sharing a demo application that implements them.
For more information and features about the Video Kit, you can refer to the sources below.
RESOURCES
About the Service
API Reference
What are all the video format currently supports ,is there any limitation on file size?
sujith.e said:
What are all the video format currently supports ,is there any limitation on file size?
Click to expand...
Click to collapse
There is no limitation on file size. Also, the maximum resolution is 4K and the minimum is 270p.
It supports streaming media in 3GP, MP4, or TS format and complies with HTTP/HTTPS, HLS, or DASH.
How many errors code does it require?
Your explanations are very interesting.

How a Programmer Developed a Text Reader App for His 80-Year-Old Grandpa

"John, have you seen my glasses?"
Our old friend John, a programmer at Huawei, has a grandpa who despite his old age, is an avid reader. Leaning back, struggling to make out what was written on the newspaper through his glasses, but unable to take his eyes off the text — this was how my grandpa used to read, John explained.
Reading this way was harmful on his grandpa's vision, and it occurred to John that the ears could take over the role of "reading" from the eyes. He soon developed a text-reading app that followed this logic, recognizing and then reading out text from a picture. Thanks to this app, John's grandpa now can ”read” from the comfort of his rocking chair, without having to strain his eyes.
How to Implement
The user takes a picture of a text passage. The app then automatically identifies the location of the text within the picture, and adjusts the shooting angle to an angle directly facing the text.
The app recognizes and extracts the text from the picture.
The app converts the recognized text into audio output by leveraging text-to-speech technology.
These functions are easy to implement, when relying on three services in HUAWEI ML Kit: document skew correction, text recognition, and text to speech (TTS).
{
"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"
}
Preparations
1. Configure the Huawei Maven repository address.
2. Add the build dependencies for the HMS Core SDK.
Code:
dependencies {
// Import the base SDK.
implementation 'com.huawei.hms:ml-computer-voice-tts:2.1.0.300'
// Import the bee voice package.
implementation 'com.huawei.hms:ml-computer-voice-tts-model-bee:2.1.0.300'
// Import the eagle voice package.
implementation 'com.huawei.hms:ml-computer-voice-tts-model-eagle:2.1.0.300'
// Import a PDF file analyzer.
implementation 'com.itextpdf:itextg:5.5.10'
}
Tap PREVIOUS or NEXT to turn to the previous or next page. Tap speak to start reading; tap it again to pause reading.
Development process
1. Create a TTS engine by using the custom configuration class MLTtsConfig. Here, on-device TTS is used as an example.
Java:
private void initTts() {
// Set authentication information for your app to download the model package from the server of Huawei.
MLApplication.getInstance().setApiKey(AGConnectServicesConfig.
fromContext(getApplicationContext()).getString("client/api_key"));
// Create a TTS engine by using MLTtsConfig.
mlTtsConfigs = new MLTtsConfig()
// Set the text converted from speech to English.
.setLanguage(MLTtsConstants.TTS_EN_US)
// Set the speaker with the English male voice (eagle).
.setPerson(MLTtsConstants.TTS_SPEAKER_OFFLINE_EN_US_MALE_EAGLE)
// Set the speech speed whose range is (0, 5.0]. 1.0 indicates a normal speed.
.setSpeed(.8f)
// Set the volume whose range is (0, 2). 1.0 indicates a normal volume.
.setVolume(1.0f)
// Set the TTS mode to on-device.
.setSynthesizeMode(MLTtsConstants.TTS_OFFLINE_MODE);
mlTtsEngine = new MLTtsEngine(mlTtsConfigs);
// Update the configuration when the engine is running.
mlTtsEngine.updateConfig(mlTtsConfigs);
// Pass the TTS callback function to the TTS engine to perform TTS.
mlTtsEngine.setTtsCallback(callback);
// Create an on-device TTS model manager.
manager = MLLocalModelManager.getInstance();
isPlay = false;
}
2. Create a TTS callback function for processing the TTS result.
Java:
MLTtsCallback callback = new MLTtsCallback() {
@Override
public void onError(String taskId, MLTtsError err) {
// Processing logic for TTS failure.
}
@Override
public void onWarn(String taskId, MLTtsWarn warn) {
// Alarm handling without affecting service logic.
}
@Override
// Return the mapping between the currently played segment and text. start: start position of the audio segment in the input text; end (excluded): end position of the audio segment in the input text.
public void onRangeStart(String taskId, int start, int end) {
// Process the mapping between the currently played segment and text.
}
@Override
// taskId: ID of a TTS task corresponding to the audio.
// audioFragment: audio data.
// offset: offset of the audio segment to be transmitted in the queue. One TTS task corresponds to a TTS queue.
// range: text area where the audio segment to be transmitted is located; range.first (included): start position; range.second (excluded): end position.
public void onAudioAvailable(String taskId, MLTtsAudioFragment audioFragment, int offset,
Pair<Integer, Integer> range, Bundle bundle) {
// Audio stream callback API, which is used to return the synthesized audio data to the app.
}
@Override
public void onEvent(String taskId, int eventId, Bundle bundle) {
// Callback method of a TTS event. eventId indicates the event name.
boolean isInterrupted;
switch (eventId) {
case MLTtsConstants.EVENT_PLAY_START:
// Called when playback starts.
break;
case MLTtsConstants.EVENT_PLAY_STOP:
// Called when playback stops.
isInterrupted = bundle.getBoolean(MLTtsConstants.EVENT_PLAY_STOP_INTERRUPTED);
break;
case MLTtsConstants.EVENT_PLAY_RESUME:
// Called when playback resumes.
break;
case MLTtsConstants.EVENT_PLAY_PAUSE:
// Called when playback pauses.
break;
// Pay attention to the following callback events when you focus on only the synthesized audio data but do not use the internal player for playback.
case MLTtsConstants.EVENT_SYNTHESIS_START:
// Called when TTS starts.
break;
case MLTtsConstants.EVENT_SYNTHESIS_END:
// Called when TTS ends.
break;
case MLTtsConstants.EVENT_SYNTHESIS_COMPLETE:
// TTS is complete. All synthesized audio streams are passed to the app.
isInterrupted = bundle.getBoolean(MLTtsConstants.EVENT_SYNTHESIS_INTERRUPTED);
break;
default:
break;
}
}
};
3. Extract text from a PDF file.
Java:
private String loadText(String path) {
String result = "";
try {
PdfReader reader = new PdfReader(path);
result = result.concat(PdfTextExtractor.getTextFromPage(reader,
mCurrentPage.getIndex() + 1).trim() + System.lineSeparator());
reader.close();
} catch (IOException e) {
showToast(e.getMessage());
}
// Obtain the position of the header.
int header = result.indexOf(System.lineSeparator());
// Obtain the position of the footer.
int footer = result.lastIndexOf(System.lineSeparator());
if (footer != 0){
// Do not display the text in the header and footer.
return result.substring(header, footer - 5);
}else {
return result;
}
}
4. Perform TTS in on-device mode.
Java:
// Create an MLTtsLocalModel instance to set the speaker so that the language model corresponding to the speaker can be downloaded through the model manager.
MLTtsLocalModel model = new MLTtsLocalModel.Factory(MLTtsConstants.TTS_SPEAKER_OFFLINE_EN_US_MALE_EAGLE).create();
manager.isModelExist(model).addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
// If the model is not downloaded, call the download API. Otherwise, call the TTS API of the on-device engine.
if (aBoolean) {
String source = loadText(mPdfPath);
// Call the speak API to perform TTS. source indicates the text to be synthesized.
mlTtsEngine.speak(source, MLTtsEngine.QUEUE_APPEND);
if (isPlay){
// Pause playback.
mlTtsEngine.pause();
tv_speak.setText("speak");
}else {
// Resume playback.
mlTtsEngine.resume();
tv_speak.setText("pause");
}
isPlay = !isPlay;
} else {
// Call the API for downloading the on-device TTS model.
downloadModel(MLTtsConstants.TTS_SPEAKER_OFFLINE_EN_US_MALE_EAGLE);
showToast("The offline model has not been downloaded!");
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
showToast(e.getMessage());
}
});
5. Release resources when the current UI is destroyed.
Java:
@Override
protected void onDestroy() {
super.onDestroy();
try {
if (mParcelFileDescriptor != null) {
mParcelFileDescriptor.close();
}
if (mCurrentPage != null) {
mCurrentPage.close();
}
if (mPdfRenderer != null) {
mPdfRenderer.close();
}
if (mlTtsEngine != null){
mlTtsEngine.shutdown();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Other Applicable Scenarios
TTS can be used across a broad range of scenarios. For example, you could integrate it into an education app to read bedtime stories to children, or integrate it into a navigation app, which could read out instructions aloud.
For more details, you can go to:
Reddit to join our developer discussion
GitHub to download demos and sample codes
Stack Overflow to solve any integration problems
Original Source
Well explained will it supports all languages?

Categories

Resources