Flicks App YouTube Activity Feature Guide
=====
This guide outlines the steps involved in implementing the Flicks App user story:
> Allow video posts to be played in full-screen using the [YouTubePlayerView](http://guides.codepath.com/android/Streaming-Youtube-Videos-with-YouTubePlayerView) (_**3 points**_)
> - From the movie details page, show an image preview that allows the user to initiate playing a YouTube video
> - Check the [`/movie/{movie_id}/videos` endpoint] API docs
> - Endpoint will provide a video "key" such as `"key": "SUXWAEX2jlg"` which can be used to construct a YouTube video URL such as [`https://www.youtube.com/watch?v=SUXWAEX2jlg`](https://www.youtube.com/watch?v=SUXWAEX2jlg)
## Conceptual Overview
These instructions assume you have already implemented the basic Flicks functionality _and_ the optional [movie details activity feature](https://hackmd.io/GYRgnAzARgTAHAVgLQFMwwCZICwHYJZRjhIDGKpGpAhlCLsNgGxA?view).
There are two basic tasks involved in implementing this:
1. Setup and configure your Android app to be able to play embedded YouTube videos, including setting up API access, YouTube player support, and a testing environment
2. Implement an additional API call to the [`/movie/{movie_id}/videos` endpoint] to retrieve the identifier of a video on YouTube and use it to launch the video player in a separate activity
## Playing YouTube Videos from Android
> For more context, see [Streaming Youtube Videos with YouTubePlayerView]
There are a number of considerations that make enabling YouTube embedding (making a video play inside your app) less than straightforward:
- A separate YouTube API key is required
- A separate library is required, but it is not distributed via Gradle, so the library must be downloaded and installed manually
- A separate, special activity must be created to play videos
- The YouTube Android app _must_ be installed on the device or emulator in order for this to work
This last requirement can be especially tricky, since installing the official YouTube app is typically done via Google Play services, which is not installed on the standard emulators created via AVD in Android Studio. Here are three options for testing, presented in order of simplicity.
### Option 1: Test on a Physical Device
The simplest approach is to deploy and test on your physical device, once you've installed the official YouTube app. Testing on a real device often reveals many usage issues or quirks that aren't apparent on the emulator, so it's a good habit to get into anyway, even if you don't need to.
### Option 2: Install the YouTube App to the Emulator Manually
So long as the emulator has `Google APIs` or `Google Play` listed in the Target section, you should be able to play the emulator. The main difference is the Nexus 5x devices have the Google Play app installed, but cannot be rooted.
![Imgur](https://i.imgur.com/nKp7JGs.png)
### Get an API Key
You'll need to signup for an API key via [https://console.developers.google.com/](https://console.developers.google.com/), which requires a Google account. Be sure to enable the `YouTube Data API v3`, then go to the `Credentials` section and generate an API key. Once you get the key, you can add it to the `secrets.xml` file you should have created to store your API key for themoviedb.
### Install the YouTube Library Manually
1. Download the [YouTubeAndroidPlayerApi.jar file](https://github.com/codepath/AndroidYoutubeVideoDemo/raw/master/app/libs/YouTubeAndroidPlayerApi.jar)
2. In Android Studio, switch to the **Project** view so that the `app/libs` folder is visible.
3. Copy or drag the downloaded jar file into `app/libs`
4. Right-click on the jar after copying and choose **Add As Library...**
5. Make sure Gradle syncs automatically, or force it via **Tools** ⇨ **Android** ⇨ **Sync project files with Gradle**
![Add-as-library](http://i.imgur.com/k9a6WET.gif)
### Create `MovieTrailerActivity`
You'll need to create a new activity for the video player, and it must extend `YouTubeBaseActivity`. One current drawback is that this library does not inherit from `AppCompatActivity` so some of your styles may not match those that are defined in `styles.xml`.
1. Create a new Activity: via **File** ⇨ **New** ⇨ **Activity** ⇨ **Empty Activity**
- Name: `MovieTrailerActivity`
- Create it in the same package as the list activity
2. Open `MovieTrailerActivity.java` and replace `extends AppCompatActivity` with `extends YouTubeBaseActivity`
3. Open `activity_movie_trailer.xml`, switch to the **Text** view, and add the following element:
```xml
<com.google.android.youtube.player.YouTubePlayerView
android:id="@+id/player"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
```
This will create a displayable activity but more code is needed to cue a video into the player. In `MovieTrailerActivity.java`, add the following code to `onCreate` to ensure the new activity can play videos:
```java
public class MovieTrailerActivity extends YouTubeBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_trailer);
// temporary test video id -- TODO replace with movie trailer video id
final String videoId = "tKodtNFpzBA";
// resolve the player view from the layout
YouTubePlayerView playerView = (YouTubePlayerView) findViewById(R.id.player);
// initialize with API key stored in secrets.xml
playerView.initialize(getString(R.string.youtube_api_key), new YouTubePlayer.OnInitializedListener() {
@Override
public void onInitializationSuccess(YouTubePlayer.Provider provider,
YouTubePlayer youTubePlayer, boolean b) {
// do any work here to cue video, play video, etc.
youTubePlayer.cueVideo(videoId);
}
@Override
public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult youTubeInitializationResult) {
// log the error
Log.e("MovieTrailerActivity", "Error initializing YouTube player");
}
});
}
}
```
### Retrieve the Videos From The API
At this point, you should be ready to play YouTube videos by launching the new activity, and the remaining steps are fairly straightfoward variations of previously-covered tasks.
1. In `Movie`, declare an `Integer id` field and parse its value from the `JSONObject` passed to the constructor using the key: `id`
1. Implement the API call to the [`/movie/{movie_id}/videos` endpoint]. Read the docs! This will be very similar to the API call that returns the movie list, with a few differences:
- The endpoint URL string must contain the movie id, so substitute `{movie_id}` in the URL with the value of `Movie.id`
- In the JSON object response, `results` should parse as a JSONArray, each object of which contains a `key` property -- this is the YouTube ID of the video. Use the first one (if it exists)
1. In the `MovieDetailsActivity`, display the backdrop image and implement a listener that launches `MovieTrailerActivity` when the backdrop image is tapped:
- Use an `Intent` and pass the video id as a `String` extra
- If there is no video id, the tap should be ignored
- In `MovieTrailerActivity`, parse the `videoId` from the `Intent` (replacing the code marked with a TODO comment in snippet above)
One detail you'll need to sort out is _when_ exactly to call the videos endpoint. One idea would be to call it from `MovieDetailsActivity.onCreate()`, but then you'll need to be careful to make sure the API call has returned and a video id is available before trying to launch `MovieTrailerActivity` in case the user taps before the call completes. There are other options for when to fetch the videos, but think carefully about the tradeoffs and any performance impacts. You generally want to maximize the app's responsiveness and minimize the amount of time the user has to wait.
### Polish
Some additional improvements to consider:
- Display a "play" overlay image over the backdrop on the details activity as a visual cue to the user
- Take a closer look at the data returned from the videos endpoint, and instead of just using the first video:
- Use the `site` value to filter out any videos that aren't `YouTube`, as these would not work with the player view
- Use the `size` value to choose the best video to show based on your app's bounds
- Implement the same tap-to-play functionality from the landscape view of the movies list
- Declare a new field on `Movie` to store the video id and use it to avoid fetching the same value more than once
## References
- [Streaming Youtube Videos with YouTubePlayerView]
- [Genymotion Guide]
[Streaming Youtube Videos with YouTubePlayerView]: http://guides.codepath.com/android/Streaming-Youtube-Videos-with-YouTubePlayerView
[`/movie/{movie_id}/videos` endpoint]: https://developers.themoviedb.org/3/movies/get-movie-videos