Handling different typologies through a single control — An introduction
Android is a widely used operating system, which is installed on any type of device, this has led us of OverApp to work on a series of situations in which, to carry out the same task, we had to act extremely differently depending on the device on which our app was running.
This can be caused by several reasons, such as a different version of Android, or library compatibility problems, one solution can work in certain situations but generate crashes in others.
The most obvious solution to manage these situations would seem to carry out a check whenever this problem arises and act accordingly:
But this would be problematic if there were many options to consider, or if there were many methods that require different approaches, moreover, if we needed to add other options, it would be a very long job to find and modify each place where we carried out this control.
Luckily there is a simpler and more elegant solution, since it’s not easy to find a generic situation where all of this is necessary, we will simulate one by creating a media player where the user can choose whether to use the default library or exoplayer.
Let’s start by creating an external module, here we will manage all the logic, so that within the app we will only use basic methods.
Within this module we create an abstract class, MediaPlayer: this will be the model on which we will create the specific classes in which we will actually configure and use the chosen library. Within this class we will declare all the methods we will use, since this is just an example we will only use two methods:
- getView: which returns the view in which we will show the video
- play: which actually plays the video and takes the URL as a parameter.
Then we will implement Default Player: to do this we create the AndroidMediaPlayer class which extends the abstract MediaPlayer class. At the time of its creation we set up MediaController and generate a VideoView in which we will show the video and which we will return to in the getVideoView method, while in the play method we actually play the video.
In the same way we create an Exoplayer class, which extends the abstract MediaPlayer class. At its creation we set the player and generate a PlayerView, where we will show the video and that we will return to the getVideoView method.
We complete this external module by creating a custom view called PlayerView which extends a FrameLayout and which accepts as a custom attribute a playerlib string that points to the player we will use. On init we check the value of this attribute: if it is equal to “exo_player” we initialize the player as an Exoplayer, otherwise we initialize it as a DefaultPlayer. Then we add a view obtained with the getView method of our MediaPlayer and play the video inside it.
In the main App we just need to implement that module and create three fragments:
- ExoplayerFragment: which just inflates a layout with a PlayerView where playerLib attribute equals “exo_player”
- DefaultPlayerFragment: which just inflates a layout with a PlayerView where playerLib attribute equals “default”
- SelectionFragment: with two buttons handling navigation events, one pointing to ExoplayerFragment and the other pointing to DefaultPlayerFragment
As you can see, operating in this way simplifies the management of future changes and, if you need to implement another library, all you have to do is create a class that extends MediaPlayer and add a condition when you initialize the player in your customized PlayerView.
In conclusion, in this way it is possible to quickly and cleanly manage a virtually unlimited number of configurations and / or devices. Delegating the correct device selection activity to the external module allows you to display only indispensable methods within the app, moreover, if you need to add more device or change some of your settings you can do it simply, quickly and without editing your app: to add a device, all you have to do is create a class that extends the abstract base class and add the option to initialize it during the check of correct device.