Insider's Guide To Udacity Android Developer Nanodegree Part 3 - Making the Baking App
Written by Nikos Vaggalis   
Monday, 03 July 2017
Article Index
Insider's Guide To Udacity Android Developer Nanodegree Part 3 - Making the Baking App
Step 1 Fragments
Step 2 - Libraries & Networking
Step 3 - Adding Exoplayer
Step 4 - Widgets
Step 5 - The Widget Provider
Step 6 - UI Testing
Step 7 - Testing Intents

The next and last step in this chain of conversions is do the same from POJO to Parcelable. Well there's a converter for that too! Head over to www.parcelabler.com, paste each of the Recipe, Ingredient and Step Java classes into its webform and viola, we got our Parcelables back!


image16
As a sample of the generated files, let's take a look at what Ingredient.java class was converted to:


/****
Ingredient POJO turned Parcelable
****/

package com.example.android.recipe.pojo;


import android.os.Parcel;
import android.os.Parcelable;

public class Ingredient implements Parcelable {

    private Double quantity;
    private String measure;
    private String ingredient;

    public Double getQuantity() {
        return quantity;
    }

    public void setQuantity(Double quantity) {
        this.quantity = quantity;
    }

    public String getMeasure() {
        return measure;
    }

    public void setMeasure(String measure) {
        this.measure = measure;
    }

    public String getIngredient() {
        return ingredient;
    }

    public void setIngredient(String ingredient) {
        this.ingredient = ingredient;
    }


    protected Ingredient(Parcel in) {
        quantity = in.readByte() == 0x00 ?
                           null : in.readDouble();
        measure = in.readString();
        ingredient = in.readString();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(
                      Parcel dest, int flags) {

        if (quantity == null) {
            dest.writeByte((byte) (0x00));
        } else {
            dest.writeByte((byte) (0x01));
            dest.writeDouble(quantity);
        }
        dest.writeString(measure);
        dest.writeString(ingredient);
    }

    @SuppressWarnings("unused")
    public static final Parcelable.Creator<Ingredient>
       CREATOR = new Parcelable.Creator<Ingredient>() {
        @Override
        public Ingredient createFromParcel(Parcel in) {
            return new Ingredient(in);
        }

        @Override
        public Ingredient[] newArray(int size) {
            return new Ingredient[size];
        }
    };
}


I didn't wrap those objects into Parcelables for fun. I did it so that a ParcelableArrayList of Recipe super objects can cross Activities and Fragment boundaries, initially from RecipeActivity to RecipeDetailActivity and RecipeDetailFragment so that I can extract both the list of Ingredients and Steps to fill their associated views.


Two extra notes on the lambda expression used. The Jack compiler, which is now deprecated in the latest version of Android Studio, is still necessary for enabling for Java 8 features such as lambdas, but this also has the side effect of disabling the Android Studio's 'instant run' feature, therefore a full project re-compilation is necessary every time something has changed. This increases the time spent on the building phase of the project.

Initially I aimed for:

     
  Integer count=new Integer();
   
        ingredients.forEach((a) ->
            {  
                count=count+1;
                mTextView.append(count.toString(),
                         +" "+a.getIngredient()+"\n");
                mTextView.append("\t\t Quantiy: "
                        +a.getQuantity().toString()+"\n");
                mTextView.append("\t\t Measure: "
                        +a.getMeasure()+"\n\n");
            });
           

which would print sequence numbers in front of each ingredient. But the compiler complained that 'count' should be final therefore it cannot be assigned within the lambda's scope, in effect turning off closures. As it turns out, Java can't close over variables so in reality it has got limited closures. Scrapping that, I went with just printing a dot in front of each ingredient with "\u2022 "


Step 3 - Adding Exoplayer

In this step I'm going to satisfy the following three requirements:

  • Application uses Exoplayer to display videos.

  • Application should properly retrieve media assets from the provided network links. It should properly handle network requests.

  • Application properly initializes and releases video assets when appropriate.


Two components are required to play video and audio on the Android platform: the Media Player, the component which takes the digital media and renders it as audio or video; and the Media Controller, the piece of UI that has the playback buttons which you can control the Media Player with.

image16a

 

While the framework provides a MediaPlayer implementation this comes with basic functionality and few customization options. However, the lesson utilizes the Exoplayer library, which offers rich customization and advanced features. An Exoplayer instance should be instatiated within the RecipeStepDetailFragment, the fragment that is responsible for displaying the videos.
        
First set up an Exoplayer view, the container in which the media will play, then call the step's object getter method getVideoURL() to get the URL pointing to the media file, always checking that in fact there's a URL:

 

 
  String videoURL = steps.get(selectedIndex).getVideoURL();
  if (!videoURL.isEmpty()) {
 

 

If there's a URL, initialize the actual player object by calling:

 

 
 initializePlayer(Uri.parse(steps.get(selectedIndex).getVideoURL()));


which takes care of the media player and controller talked about earlier.

 

image17
In case there's no video to display, but just instructions in textual form, I set up a small 300x300 area in which to display an icon suggesting that there's no video to display, to preserve a consistent UI experience and offer a uniform user interface no matter the size and orientation of the device. This icon will always be 36dp wide and occupy a 300x300dp box.

 

image18        
The last thing remaining is to release the acquired video resources at the appropriate stages of the fragment's lifecycle such as onDetach


@Override
    public void onDetach() {
        super.onDetach();
        if (player!=null) {
            player.stop();
            player.release();
        }
    }


The same goes for onDestroyView, onStop and onPause.  

 



Last Updated ( Monday, 20 November 2017 )