Skip to content

Custom UI controls series

Custom timeline

We are missing a very important UI element that every respectable player should have, and that is the timeline, a progress bar indicating how much was played from our current video.

We will use a very basic custom UI element, just to demonstrate the basics of a timeline.

The visual part of it works as following:

...
public class TimelineView extends View{
...
    private int timelinePad = 15;
...
    @Override
    protected void onDraw(Canvas canvas) {
        //draw the whole bar
        paint.setColor(Color.BLACK);
        canvas.drawRect(canvas.getClipBounds(),paint);

        //draw the progress
        Rect rect = new Rect();
        rect.set(timelinePad ,timelinePad, ((int) ((getMeasuredWidth()-2*timelinePad)*currentPos)+timelinePad), getMeasuredHeight()-timelinePad);
        paint.setColor(Color.GREEN);
        float cornerRadius = timelinePad;
        canvas.drawRoundRect(new RectF(rect), cornerRadius, cornerRadius, paint);

        //draw time bounds
        paint.setColor(Color.WHITE);
        paint.setTextSize(1.5f*(getMeasuredHeight()-timelinePad*2)/2);
        float fontPad = (getMeasuredHeight()-paint.getFontMetrics(null)*0.8f)/2;
        if(pos != null && end != null) {
            String text = pos.toString(timelinePositionFormat)+" / "+end.toString(timelinePositionFormat);
            canvas.drawText(text, rect.left+(getMeasuredWidth()-timelinePad*2-paint.measureText(text))/2, getMeasuredHeight()-fontPad, paint);
        }
    }
...
}

We have some unknowns here, so let's try to clarify them one by one.

currentPos represents the current position of the player from the time perspective, and pos represents the progress perspective, so in order to get them we need to see how our timeline integrates with the enigma player:

...
public void connectTo(IEnigmaPlayer enigmaPlayer) {
    this.controls = enigmaPlayer.getControls();
    enigmaPlayer.getTimeline().addListener(new BaseTimelineListener() {
        ...

        private void recalculatePos() {
            if(start != null && end != null && pos != null) {
                TimelineView.this.currentPos = pos.subtract(start).inUnits(Duration.Unit.MILLISECONDS)/end.subtract(start).inUnits(Duration.Unit.MILLISECONDS);
            } else {
                TimelineView.this.currentPos = 0f;
            }
            TimelineView.this.postInvalidate();
        }

        @Override
        public void onCurrentPositionChanged(ITimelinePosition timelinePosition) {
            TimelineView.this.pos = timelinePosition;
            recalculatePos();
        }

        ...
    }, handler);
}
...

timelinePositionFormat is just a simple format to display the time:

private TimelinePositionFormat timelinePositionFormat = TimelinePositionFormat.newFormat("${minutes}m${sec}s", new SimpleDateFormat("hh:mm:ss a"));

And just like with the play/pause button, we connect the player with the timeline in the playback Activity:

...
IEnigmaPlayer player = new EnigmaPlayer(session, exoPlayerTech);
...
timelineView.connectTo(player);
...

And that's all there is to it!

Please follow the next chapters to learn about more components.


Table of Contents
Prerequisites
Basics: play, pause and seeking
Play/Pause Button
• Custom timeline (current)
Spinner and Live Indicator
Custom UI app