Handling different typologies through a single control — Part 2
The purpose of this series of articles is to demonstrate how to structure an external library in order to manage different types of devices / software in a simple and independent way from the App, and how to possibly add more types without the need to modify the App.
In the previous article we used the example of a media player, we set up an external library that let a user dynamically choose between the default Android player and ExoPlayer.
The app was set up in an extremely simple way: the user made his choice and a fragment was opened with the required player which used its default media controller.
In this article we will make some improvements to our App, previously we used different fragments depending on the selected player, but this undermines one of the main objectives of this way of proceeding: extensibility. In fact, if we were to add additional types of MediaPlayer we would have to add as many fragments, it is definitely not the optimal solution, especially if our app becomes particularly complex.
In this part we will therefore use a single fragment for both players, we will also use a custom controller with a button to start the video and one to pause it, and finally we will add a loader that will disappear when the video is ready.
Let’s start by preparing our fragment, the layout will be very similar to the two previously created: we insert a PlayerView, but this time we do not specify the player in the attributes, then, in anticipation of the changes we will make, we also begin to add a ContentLoader that we will hide when the video will be ready to be viewed.
Within the library we will now create a PlayerListener interface, this will act as a link between the players and the fragment, it will have to listen to the main states of the player, which may be, for instance, Buffering, Ready, Completed and Idle.
For the moment, only a video is shown in our app and it is loaded upon startup, so for our purpose it is sufficient to check whether our player is buffering or is ready, so that we can hide or show our progress bar.
Now let’s start with the implementation of the methods inside the player, we modify the abstract Media Player class, first of all, since the play method will now no longer start the video at startup, let’s do a refactor and rename it to setPlayer, and we declare the two new functions that we are going to implement: play and pause.
Then we also adapt our ExoPlayer and AndroidMediaPlayer classes, we add a PlayerListener among the input parameters, in the setPlayer method we remove the video autostart and we connect our PlayerListener to the state listener of our Player, so that when the video is ready it is notified to the fragment. Then we implement the play and pause methods and hide the default controller.
Now we are ready to use all of these methods in our PlayerView: first of all we prepare to add our custom controller, we prepare two buttons that call the play and pause methods of the player and we add them to our view, also if we want to pass a listener between fragment and player and if we want to be able to manage more types of player in the same fragment it will no longer be sufficient to pass the selected player through the attributes, so let’s create an initialization method that takes in a String representing the chosen player and a PlayerListener, and let’s create a default one, so that if you want the user can only choose the player, in this case we dynamically create a progress bar that we will hide when the video is ready.
Finally we create the fragment, which will only initialize the PlayerView with the chosen player and, optionally, a custom PlayerListener object that will hide the progress bar when the video is ready.
As we have seen in this way it is possible to add new features without having to go through the app, and at the same time we have also seen how to make these features easily customizable, by importing a library structured in this way a user is free to use it ready or to modify it as he sees fit. In the next and last part we will see how to make our library even more customizable.