ARVI - Autoplayable RecyclerView Items

ExoPlayer-based Android library that makes the implementation of the autoplayable RecyclerView video items an easy task

ARVI will enable you to make your feeds more interactive and appealing to your end users without the need to spend a lot of your valuable time on the implementation.

Download License Platform Android Arsenal

Contents

Demo (YouTube)

YouTube Video

Getting Started

Prerequisites

1. Make sure that you've added the jcenter() repository to your top-level build.gradle file.

buildscript {
    //...
    repositories {
        //...
        jcenter()
    }
    //...
}

2. Enable the jetifier and androidX support in the top-level gradle.properties file.

//...
android.enableJetifier=true
android.useAndroidX=true
//....

3. Update your compileSdkVersion in the module-level build.gradle file to 28+.

//...
android {
    //...
    compileSdkVersion 28
    //...
}
//...

4. Replace your com.android.support.appcompat.* dependency with the new androidx.appcompat.* alternative.

//...
dependencies {
    //...
    implementation "androidx.appcompat:appcompat:1.0.1"
    //...
}
//...

5. Add the ExoPlayer dependency to the module-level build.gradle file.

//...
dependencies {
    //...
    implementation "com.google.android.exoplayer:exoplayer:2.9.2"
    //...
}
//...

ARVI Dependencies

ARVI is comprised of several library modules, namely:

The basic implementation would have to include the core module

Latest version: Download

implementation "com.arthurivanets.arvi:arvi:X.Y.Z"

Which should be added to your module-level build.gradle file.

ext {
    //...
    arviLibraryVersion = "1.0.0"
}

dependencies {
    //...
    implementation "com.arthurivanets.arvi:arvi:$arviLibraryVersion"
}

After that you can proceed with further implementation.

See: Basic Implementation and Adapster-based Implementation

Basic Implementation

Basic implementation consists of 3 straightforward steps, which include the proper handling of the system memory claims, creation of the playable Item View Holder, and the incorporation of the Playable Items Container.

The steps you need to take:

1. Ensure the proper release of the active players when the application goes into background (System Memory Claims)

Kotlin (click to expand)

****Basic**** ````kotlin //... import com.arthurivanets.arvi.PlayerProviderImpl class ArviApplication : Application() { //... override fun onTrimMemory(level : Int) { super.onTrimMemory(level) if(level >= TRIM_MEMORY_BACKGROUND) { PlayerProviderImpl.getInstance(this).release() } } //... } ```` ****With**** `arvi-ktx` ````kotlin //... import com.arthurivanets.arvi.ktx.playerProvider class ArviApplication : Application() { //... override fun onTrimMemory(level : Int) { super.onTrimMemory(level) if(level >= TRIM_MEMORY_BACKGROUND) { playerProvider.release() } } //... } ````


Java (click to expand)

````java //... import com.arthurivanets.arvi.PlayerProviderImpl; public final class YourApplication extends Application { //... @Override public void onTrimMemory(int level) { super.onTrimMemory(level); if(level >= TRIM_MEMORY_BACKGROUND) { PlayerProviderImpl.getInstance(this).release(); } } //... } ````


**2. Implement your Item's `ViewHolder` based on the [`PlayableItemViewHolder`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/PlayableItemViewHolder.java)** ****IMPORTANT****: Your `ViewHolder`'s `layout.xml` file must contain a [`PlayerView`](https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/ui/PlayerView.html) child with an id `@id/player_view`. > ***See: [BasicVideoItemViewHolder](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/basic/BasicVideoItemViewHolder.kt) and [item_video.xml](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/basic/BasicVideoItemViewHolder.kt)***
Kotlin (click to expand)

````kotlin class BasicVideoItemViewHolder( parent : ViewGroup, itemView : View ) : PlayableItemViewHolder(parent, itemView) { //... override fun getUrl() : String { return "video_url..." } //... } ````


Java (click to expand)

````java public final class BasicVideoItemViewHolder extends PlayableItemViewHolder { //... @Override public final String getUrl() { return "video_url..."; } //... } ````


**3. Replace the regular [`RecyclerView`](https://developer.android.com/reference/android/support/v7/widget/RecyclerView) with the [`PlayableItemsRecyclerView`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/PlayableItemsRecyclerView.java)** ****IMPORTANT****: [`PlayableItemsRecyclerView`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/PlayableItemsRecyclerView.java) should be bound to the lifecycle of the Activity/Fragment (Activity/Fragment lifecycle events should be propagated to the [`PlayableItemsRecyclerView`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/PlayableItemsRecyclerView.java)) in order to ensure the correct handling of the item video playback. > ***See: [`PlayableItemsRecyclerView`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/PlayableItemsRecyclerView.java), [`BasicVideoItemsRecyclerViewAdapter`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/basic/BasicVideoItemsRecyclerViewAdapter.kt), [`BasicVideosFragment`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/ui/basic/BasicVideosFragment.kt) and [`fragment_videos.xml`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/res/layout/fragment_videos.xml)***
Kotlin (click to expand)

````kotlin class BasicVideosFragment : BaseFragment() { //... override fun init(savedInstanceState : Bundle?) { with(recyclerView) { // PlayableItemRecyclerView configuration setPlaybackTriggeringStates( PlayableItemsContainer.PlaybackTriggeringState.IDLING, PlayableItemsContainer.PlaybackTriggeringState.DRAGGING ) autoplayMode = PlayableItemsContainer.AutoplayMode.ONE_AT_A_TIME adapter = BasicVideoItemsRecyclerViewAdapter( context = context!!, items = VideoProvider.getVideos(count = 100, mute = true).toMutableList(), arviConfig = Config.Builder() .cache(ExoPlayerUtils.getCache(context!!)) .build() ) } } //... override fun onResume() { super.onResume() recyclerView.onResume() } override fun onPause() { super.onPause() recyclerView.onPause() } override fun onDestroy() { recyclerView?.onDestroy() super.onDestroy() } //... } ````


Java (click to expand)

````java public final class BasicVideosFragment extends BaseFragment { //... @Override public void init(Bundle savedInstanceState) { mRecyclerView.setPlaybackTriggeringStates( PlayableItemsContainer.PlaybackTriggeringState.IDLING, PlayableItemsContainer.PlaybackTriggeringState.DRAGGING ); mRecyclerView.setAutoplayMode(PlayableItemsContainer.AutoplayMode.ONE_AT_A_TIME); mRecyclerView.setAdapter(new BasicVideoItemsRecyclerViewAdapter( context, VideoProvider.getVideos(100, true), new Config.Builder() .cache(ExoPlayerUtils.getCache(context)) .build() )); } //... @Override public void onResume() { super.onResume(); mRecyclerView.onResume(); } @Override public void onPause() { super.onPause(); mRecyclerView.onPause(); } @Override public void onDestroy() { mRecyclerView.onDestroy(); super.onDestroy(); } //... } ````


For more advanced use cases > ***See: [Advanced Use Cases](#advanced-use-cases)*** ## Adapster-based Implementation [Adapster](https://github.com/arthur3486/adapster)-based implementation requires both the official [Adapster](https://github.com/arthur3486/adapster) and `arvi-adapster` module dependencies. > ***Latest ARVI version:*** [ ![Download](https://api.bintray.com/packages/arthurimsacc/maven/arvi/images/download.svg) ](https://bintray.com/arthurimsacc/maven/arvi/_latestVersion) `implementation "com.arthurivanets.arvi:arvi-adapster:X.Y.Z"` While the implementation itself shares most of the steps with the [Basic Implementation](#basic-implementation), one of the things that should be taken into account is the fact that the implementation of your Item `ViewHolder` should be based on the [`AdapsterPlayableItemViewHolder`](https://github.com/arthur3486/ARVI/blob/master/arvi-adapster/src/main/java/com/arthurivanets/arvi/adapster/AdapsterPlayableItemViewHolder.java) instead of the [`PlayableItemViewHolder`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/PlayableItemViewHolder.java).
Kotlin (click to expand)

````kotlin //... import com.arthurivanets.arvi.adapster.AdapsterPlayableItemViewHolder class VideoItemViewHolder( parent : ViewGroup, itemView : View, private val resources : VideoItemResources? ) : AdapsterPlayableItemViewHolder


Java (click to expand)

````java //... import com.arthurivanets.arvi.adapster.AdapsterPlayableItemViewHolder; public final class VideoItemViewHolder extends AdapsterPlayableItemViewHolder


> ***See: [`VideoItemsRecyclerViewAdapter`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/adapster/VideoItemsRecyclerViewAdapter.kt), [`AdapsterVideosFragment`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/ui/adapster/AdapsterVideosFragment.kt), [`AdapsterPlayableItemViewHolder`](https://github.com/arthur3486/ARVI/blob/master/arvi-adapster/src/main/java/com/arthurivanets/arvi/adapster/AdapsterPlayableItemViewHolder.java) and [`VideoItemViewHolder`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/adapster/VideoItemViewHolder.kt)*** For more advanced use cases > ***See: [Advanced Use Cases](#advanced-use-cases)*** ## Advanced Use Cases Sometimes you require something more than a basic implementation, whether it's an ability to enable the caching of your videos or a way to authorize your HTTP Video requests, you name it; for that reason a list of the most common advanced use cases has been compiled. Most common advanced use cases include, but not limited to: **1. Video Caching** In order to enable the video caching you should provide an instance of the [`ExoPlayer Cache`](https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/cache/Cache.html) via the ARVI [`Config`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/Config.java) to your Item ViewHolders, and then use the provided [`Config`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/Config.java) within each corresponding Item `ViewHolder`.
Kotlin (click to expand)

````kotlin //... import com.arthurivanets.arvi.Config class BasicVideoItemViewHolder( parent : ViewGroup, itemView : View, val arviConfig : Config ) : PlayableItemViewHolder(parent, itemView) { //... override fun getUrl() : String { return "video_url..." } override fun getConfig() : Config { return arviConfig } //... } ```` ****Adapster-based**** ````kotlin //... import com.arthurivanets.arvi.adapster.AdapsterPlayableItemViewHolder class VideoItemViewHolder( parent : ViewGroup, itemView : View, private val resources : VideoItemResources? ) : AdapsterPlayableItemViewHolder


Java (click to expand)

````java //... import com.arthurivanets.arvi.Config; public final class BasicVideoItemViewHolder extends PlayableItemViewHolder { //... @Override public final String getUrl() { return "video_url..."; } @Override public final Config getConfig() { return arviConfig; } //... } ```` ****Adapster-based**** ````java //... import com.arthurivanets.arvi.adapster.AdapsterPlayableItemViewHolder; public final class VideoItemViewHolder extends AdapsterPlayableItemViewHolder


The general [`ExoPlayer Cache`](https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/cache/Cache.html) instance can be easily created using the utility extension methods found in the [`ArviExtensions`](https://github.com/arthur3486/ARVI/blob/master/arvi-ktx/src/main/java/com/arthurivanets/arvi/ktx/ArviExtensions.kt) of the `arvi-ktx` module, or you can resort to your own [`ExoPlayer Cache`](https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/cache/Cache.html) instance creation approach; choose the approach that fits your requirements the best. For more details > ***See: [`BasicVideosFragment`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/ui/basic/BasicVideosFragment.kt), [`BasicVideoItemsRecyclerViewAdapter`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/basic/BasicVideoItemsRecyclerViewAdapter.kt), [`BasicVideoItemViewHolder`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/basic/BasicVideoItemViewHolder.kt), [`AdapsterVideosFragment`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/ui/adapster/AdapsterVideosFragment.kt), [`VideoItemsRecyclerViewAdapter`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/adapster/VideoItemsRecyclerViewAdapter.kt), [`VideoItem`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/adapster/VideoItem.kt), [`VideoItemViewHolder`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/adapster/VideoItemViewHolder.kt), [`VideoItemResources`](https://github.com/arthur3486/ARVI/blob/master/sample/src/main/java/com/arthurivanets/sample/adapters/adapster/VideoItemResources.kt), [`Config`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/Config.java), [`ArviExtensions`](https://github.com/arthur3486/ARVI/blob/master/arvi-ktx/src/main/java/com/arthurivanets/arvi/ktx/ArviExtensions.kt), [`Cache`](https://github.com/arthur3486/ARVI/blob/master/arvi-ktx/src/main/java/com/arthurivanets/arvi/ktx/ArviExtensions.kt)*** **2. HTTP Video Request Authorization** In cases when your video requests require authorization, you can use the [`RequestAuthorizer`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/player/datasource/RequestAuthorizer.java) to provide the necessary auth token whenever the player requests it. The created [`RequestAuthorizer`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/player/datasource/RequestAuthorizer.java) should be associated with the [`ArviHttpDataSourceFactory`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/player/datasource/ArviHttpDataSourceFactory.java) and passed around in the ARVI [`Config`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/Config.java) object. ****RequestAuthorizer****
Kotlin (click to expand)

````kotlin //... import com.arthurivanets.arvi.player.datasource.RequestAuthorizer class ArviRequestAuthorizer(private val authTokenProvider : AuthTokenProvider) : RequestAuthorizer { override fun getAuthorization() : String { return "Bearer ${authTokenProvider.getAuthToken()}" } } ````


Java (click to expand)

````java //... import com.arthurivanets.arvi.player.datasource.RequestAuthorizer; public final class ArviRequestAuthorizer extends RequestAuthorizer { //... @Override public final String getAuthorization() { return ("Bearer " + authTokenProvider.getAuthToken()); } } ````


****ARVI Config****
Kotlin (click to expand)

````kotlin //... val config = Config.Builder() .dataSourceFactory( ArviHttpDataSourceFactory(context.playerProvider.libraryName).apply { setConnectTimeout(REQUEST_CONNECT_TIMEOUT_IN_MILLIS) setReadTimeout(REQUEST_READ_TIMEOUT_IN_MILLIS) // Your request authorizer setRequestAuthorizer(ArviRequestAuthorizer(...)) } ) .build() ````


Java (click to expand)

````java //... final ArviHttpDataSourceFactory dataSourceFactory = new ArviHttpDataSourceFactory(PlayerProviderImpl.getInstance(context).getLibraryName()); dataSourceFactory.setConnectTimeout(REQUEST_CONNECT_TIMEOUT_IN_MILLIS); dataSourceFactory.setReadTimeout(REQUEST_READ_TIMEOUT_IN_MILLIS); // Your request authorizer dataSourceFactory.setRequestAuthorizer(new ArviRequestAuthorizer(...)); // the final Config final Config config = new Config.Builder() .dataSourceFactory(dataSourceFactory) .build(); ````


> ***See: [`RequestAuthorizer`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/player/datasource/RequestAuthorizer.java), [`ArviHttpDataSourceFactory`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/player/datasource/ArviHttpDataSourceFactory.java), [`Config`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/Config.java)*** **3. ViewHolder Playback Control** All [`Playable`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/Playable.java) Item `ViewHolder`s are capable of controlling almost every aspect of the corresponding playback, thus giving you more power in terms of the actual implementation. For more details on what possibilities the [`Playable`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/Playable.java) gives you > ***See: [`Playable`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/widget/Playable.java)*** **4. ARVI Players** All the [`Player`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/player/Player.java)s created using the [`PlayerProviderImpl`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/PlayerProviderImpl.java) can be used as stand alone players, as they are totally independent entities, the only thing to remember here is that you should properly handle the player binding/unbinding events to avoid the potential memory leaks and other related issues. > ***See: [`Player`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/player/Player.java), [`PlayerProvider`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/PlayerProvider.java), [`PlayerProviderImpl`](https://github.com/arthur3486/ARVI/blob/master/arvi/src/main/java/com/arthurivanets/arvi/PlayerProviderImpl.java)*** ## Contribution See the [CONTRIBUTING.md](CONTRIBUTING.md) file. ## Hall of Fame
Owly
> Using ARVI in your app and want it to get listed here? Email me at [email protected]! ## License ARVI is licensed under the [Apache 2.0 License](LICENSE).