[Huawei Developers] Freemind R:
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

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814053815.03631020616249247125480286358776:50510828025637:2800:8F232F58485E036950BD936D82C09AF38DC87C4C75F40F8A7CDCB35CE74F0654.jpeg"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814053815.03631020616249247125480286358776:50510828025637:2800:8F232F58485E036950BD936D82C09AF38DC87C4C75F40F8A7CDCB35CE74F0654.jpeg[/IMG]

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:

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814054010.22066449138964565713375864681531:50510828023520:2800:2882566AC5844E284ED85013EB2AEA4DCDD83649EB85DC87C17029A9E63D1FF3.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814054010.22066449138964565713375864681531:50510828023520:2800:2882566AC5844E284ED85013EB2AEA4DCDD83649EB85DC87C17029A9E63D1FF3.png[/IMG]

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.

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.

@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.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814055130.60319348350962576259731487848896:50510828023520:2800:BE8B72E29E7C9E1857FA0694C31560734A9423F2FCACD1AB6295A69792262718.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814055130.60319348350962576259731487848896:50510828023520:2800:BE8B72E29E7C9E1857FA0694C31560734A9423F2FCACD1AB6295A69792262718.png[/IMG]

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.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814055214.88056334893984340717666628099350:50510828023520:2800:CB6EAD6A12D219F363EB91835D62C24E22F8CF10201B8363569B52D252B7DD47.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/505/491/5190090000026505491.20200814055214.88056334893984340717666628099350:50510828023520:2800:CB6EAD6A12D219F363EB91835D62C24E22F8CF10201B8363569B52D252B7DD47.png[/IMG]

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:

@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:

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.

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!