add more samples: multiple video, gallery with preloading, shared cache

This commit is contained in:
Alexey Danilov
2015-09-03 19:26:09 +03:00
parent 492706ce69
commit 983ae8d7b7
17 changed files with 689 additions and 84 deletions

View File

@@ -1,8 +1,19 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
}
repositories {
maven { url 'https://github.com/danikula/AndroidVideoCache/raw/mvn-repo' }
maven { url 'https://github.com/dahlgren/vpi-aar/raw/master' }
}
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 23
@@ -17,6 +28,17 @@ android {
}
}
dependencies {
compile('com.danikula:videocache:1.0')
apt {
arguments {
androidManifestFile variant.outputs[0].processResources.manifestFile
resourcePackageName android.defaultConfig.applicationId
}
}
dependencies {
compile 'com.android.support:support-v4:23.0.0'
compile 'org.androidannotations:androidannotations-api:3.3.2'
compile 'com.danikula:videocache:1.0'
compile 'com.viewpagerindicator:library:2.4.2-SNAPSHOT@aar'
apt 'org.androidannotations:androidannotations:3.3.2'
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.danikula.videocache.sample">
package="com.danikula.videocache.sample"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -12,13 +12,17 @@
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.NoActionBar">
<activity
android:name=".VideoActivity"
android:name=".MenuActivity_"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SingleVideoActivity_" />
<activity android:name=".MultipleVideosActivity_" />
<activity android:name=".VideoGalleryActivity_" />
<activity android:name=".SharedCacheActivity_" />
</application>
</manifest>

View File

@@ -0,0 +1,158 @@
package com.danikula.videocache.sample;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.VideoView;
import com.danikula.videocache.Cache;
import com.danikula.videocache.CacheListener;
import com.danikula.videocache.FileCache;
import com.danikula.videocache.HttpProxyCache;
import com.danikula.videocache.HttpUrlSource;
import com.danikula.videocache.ProxyCacheException;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.FragmentArg;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.SeekBarTouchStop;
import org.androidannotations.annotations.ViewById;
import java.io.File;
@EFragment(R.layout.fragment_video)
public class GalleryVideoFragment extends Fragment implements CacheListener {
private static final String LOG_TAG = "VideoFragment";
@FragmentArg String url;
@FragmentArg String cachePath;
@InstanceState int position;
@InstanceState boolean playerStarted;
@ViewById VideoView videoView;
@ViewById ProgressBar progressBar;
private boolean visibleForUser;
private HttpProxyCache proxyCache;
private final VideoProgressUpdater updater = new VideoProgressUpdater();
public static Fragment build(Context context, Video video) {
return build(video.url, video.getCacheFile(context).getAbsolutePath());
}
public static Fragment build(String url, String cachePath) {
return GalleryVideoFragment_.builder()
.url(url)
.cachePath(cachePath)
.build();
}
@AfterViews
void afterViewInjected() {
startProxy();
if (visibleForUser) {
startPlayer();
}
}
private void startPlayer() {
videoView.seekTo(position);
videoView.start();
playerStarted = true;
}
private void startProxy() {
try {
Cache cache = new FileCache(new File(cachePath));
HttpUrlSource source = new HttpUrlSource(url);
proxyCache = new HttpProxyCache(source, cache);
proxyCache.setCacheListener(this);
videoView.setVideoPath(proxyCache.getUrl());
} catch (ProxyCacheException e) {
// do nothing. onError() handles all errors
}
}
// just for videos gallery
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
visibleForUser = isVisibleToUser;
if (videoView != null) {
if (visibleForUser) {
startPlayer();
} else if (playerStarted) {
position = videoView.getCurrentPosition();
videoView.pause();
}
}
}
@Override
public void onResume() {
super.onResume();
updater.start();
}
@Override
public void onPause() {
super.onPause();
updater.stop();
}
@Override
public void onDestroy() {
super.onDestroy();
if (proxyCache != null) {
proxyCache.shutdown();
}
}
@Override
public void onError(ProxyCacheException e) {
Log.e(LOG_TAG, "Error playing video", e);
}
@Override
public void onCacheDataAvailable(int cachePercentage) {
progressBar.setSecondaryProgress(cachePercentage);
}
private void updateVideoProgress() {
int videoProgress = videoView.getCurrentPosition() * 100 / videoView.getDuration();
progressBar.setProgress(videoProgress);
}
@SeekBarTouchStop(R.id.progressBar)
void seekVideo() {
int videoPosition = videoView.getDuration() * progressBar.getProgress() / 100;
videoView.seekTo(videoPosition);
}
private final class VideoProgressUpdater extends Handler {
public void start() {
sendEmptyMessage(0);
}
public void stop() {
removeMessages(0);
}
@Override
public void handleMessage(Message msg) {
updateVideoProgress();
sendEmptyMessageDelayed(0, 200);
}
}
}

View File

@@ -0,0 +1,73 @@
package com.danikula.videocache.sample;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentActivity;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ItemClick;
import org.androidannotations.annotations.ViewById;
import java.io.File;
import java.util.Arrays;
import java.util.List;
@EActivity(R.layout.activity_menu)
public class MenuActivity extends FragmentActivity {
@ViewById ListView listView;
@AfterViews
void onViewInjected() {
ListAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, buildListData());
listView.setAdapter(adapter);
}
@NonNull
private List<ListEntry> buildListData() {
return Arrays.asList(
new ListEntry("Single Video", SingleVideoActivity_.class),
new ListEntry("Multiple Videos", MultipleVideosActivity_.class),
new ListEntry("Video Gallery with pre-caching", VideoGalleryActivity_.class),
new ListEntry("Shared Cache", SharedCacheActivity_.class)
);
}
@ItemClick(R.id.listView)
void onListItemClicked(int position) {
ListEntry item = (ListEntry) listView.getAdapter().getItem(position);
startActivity(new Intent(this, item.activityClass));
}
@Click(R.id.cleanCacheButton)
void onClearCacheButtonClick() {
File externalCacheDir = getExternalCacheDir();
if (externalCacheDir != null) {
for (File cacheEntry : externalCacheDir.listFiles()) {
cacheEntry.delete();
}
}
}
private static final class ListEntry {
private final String title;
private final Class activityClass;
public ListEntry(String title, Class activityClass) {
this.title = title;
this.activityClass = activityClass;
}
@Override
public String toString() {
return title;
}
}
}

View File

@@ -0,0 +1,31 @@
package com.danikula.videocache.sample;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import org.androidannotations.annotations.EActivity;
@EActivity(R.layout.activity_multiple_videos)
public class MultipleVideosActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
if (state == null) {
addVideoFragment(Video.ORANGE_1, R.id.videoContainer0);
addVideoFragment(Video.ORANGE_2, R.id.videoContainer1);
addVideoFragment(Video.ORANGE_3, R.id.videoContainer2);
addVideoFragment(Video.ORANGE_4, R.id.videoContainer3);
}
}
private void addVideoFragment(Video video, int containerViewId) {
Fragment fragment = VideoFragment.build(video.url, video.getCacheFile(this).getAbsolutePath());
getSupportFragmentManager()
.beginTransaction()
.add(containerViewId, fragment)
.commit();
}
}

View File

@@ -0,0 +1,29 @@
package com.danikula.videocache.sample;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import org.androidannotations.annotations.EActivity;
@EActivity(R.layout.activity_shared_cache)
public class SharedCacheActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
if (state == null) {
addVideoFragment(Video.ORANGE_1, R.id.videoContainer0);
addVideoFragment(Video.ORANGE_1, R.id.videoContainer1);
}
}
private void addVideoFragment(Video video, int containerViewId) {
Fragment fragment = VideoFragment.build(video.url, video.getCacheFile(this).getAbsolutePath());
getSupportFragmentManager()
.beginTransaction()
.add(containerViewId, fragment)
.commit();
}
}

View File

@@ -0,0 +1,22 @@
package com.danikula.videocache.sample;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import org.androidannotations.annotations.EActivity;
@EActivity(R.layout.activity_single_video)
public class SingleVideoActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
if (state == null) {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.containerView, VideoFragment.build(this, Video.ORANGE_1))
.commit();
}
}
}

View File

@@ -0,0 +1,24 @@
package com.danikula.videocache.sample;
import android.content.Context;
import java.io.File;
public enum Video {
ORANGE_1("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange1.mp4"),
ORANGE_2("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange2.mp4"),
ORANGE_3("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange3.mp4"),
ORANGE_4("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange4.mp4"),
ORANGE_5("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange5.mp4");
public final String url;
Video(String url) {
this.url = url;
}
public File getCacheFile(Context context) {
return new File(context.getExternalCacheDir(), name());
}
}

View File

@@ -1,75 +0,0 @@
package com.danikula.videocache.sample;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.VideoView;
import com.danikula.videocache.Cache;
import com.danikula.videocache.CacheListener;
import com.danikula.videocache.FileCache;
import com.danikula.videocache.HttpProxyCache;
import com.danikula.videocache.HttpUrlSource;
import com.danikula.videocache.ProxyCacheException;
import java.io.File;
public class VideoActivity extends Activity implements CacheListener {
private static final String LOG_TAG = "VideoActivity";
private static final String VIDEO_CACHE_NAME = "devbytes.mp4";
private static final String VIDEO_URL = "https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/devbytes.mp4";
private VideoView videoView;
private ProgressBar progressBar;
private HttpProxyCache proxyCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUpUi();
playWithCache();
}
private void setUpUi() {
setContentView(R.layout.activity_video);
videoView = (VideoView) findViewById(R.id.videoView);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setMax(100);
}
private void playWithCache() {
try {
Cache cache = new FileCache(new File(getExternalCacheDir(), VIDEO_CACHE_NAME));
HttpUrlSource source = new HttpUrlSource(VIDEO_URL);
proxyCache = new HttpProxyCache(source, cache);
proxyCache.setCacheListener(this);
videoView.setVideoPath(proxyCache.getUrl());
videoView.start();
} catch (ProxyCacheException e) {
// do nothing. onError() handles all errors
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (proxyCache != null) {
proxyCache.shutdown();
}
}
@Override
public void onError(ProxyCacheException e) {
Log.e(LOG_TAG, "Error playing video", e);
}
@Override
public void onCacheDataAvailable(int cachePercentage) {
progressBar.setProgress(cachePercentage);
}
}

View File

@@ -0,0 +1,127 @@
package com.danikula.videocache.sample;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.VideoView;
import com.danikula.videocache.Cache;
import com.danikula.videocache.CacheListener;
import com.danikula.videocache.FileCache;
import com.danikula.videocache.HttpProxyCache;
import com.danikula.videocache.HttpUrlSource;
import com.danikula.videocache.ProxyCacheException;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.FragmentArg;
import org.androidannotations.annotations.SeekBarTouchStop;
import org.androidannotations.annotations.ViewById;
import java.io.File;
@EFragment(R.layout.fragment_video)
public class VideoFragment extends Fragment implements CacheListener {
private static final String LOG_TAG = "VideoFragment";
@FragmentArg String url;
@FragmentArg String cachePath;
@ViewById VideoView videoView;
@ViewById ProgressBar progressBar;
private HttpProxyCache proxyCache;
private final VideoProgressUpdater updater = new VideoProgressUpdater();
public static Fragment build(Context context, Video video) {
return build(video.url, video.getCacheFile(context).getAbsolutePath());
}
public static Fragment build(String url, String cachePath) {
return VideoFragment_.builder()
.url(url)
.cachePath(cachePath)
.build();
}
@AfterViews
void afterViewInjected() {
startVideo();
}
private void startVideo() {
try {
Cache cache = new FileCache(new File(cachePath));
HttpUrlSource source = new HttpUrlSource(url);
proxyCache = new HttpProxyCache(source, cache);
proxyCache.setCacheListener(this);
videoView.setVideoPath(proxyCache.getUrl());
videoView.start();
} catch (ProxyCacheException e) {
// do nothing. onError() handles all errors
}
}
@Override
public void onResume() {
super.onResume();
updater.start();
}
@Override
public void onPause() {
super.onPause();
updater.stop();
}
@Override
public void onDestroy() {
super.onDestroy();
if (proxyCache != null) {
proxyCache.shutdown();
}
}
@Override
public void onError(ProxyCacheException e) {
Log.e(LOG_TAG, "Error playing video", e);
}
@Override
public void onCacheDataAvailable(int cachePercentage) {
progressBar.setSecondaryProgress(cachePercentage);
}
private void updateVideoProgress() {
int videoProgress = videoView.getCurrentPosition() * 100 / videoView.getDuration();
progressBar.setProgress(videoProgress);
}
@SeekBarTouchStop(R.id.progressBar)
void seekVideo() {
int videoPosition = videoView.getDuration() * progressBar.getProgress() / 100;
videoView.seekTo(videoPosition);
}
private final class VideoProgressUpdater extends Handler {
public void start() {
sendEmptyMessage(0);
}
public void stop() {
removeMessages(0);
}
@Override
public void handleMessage(Message msg) {
updateVideoProgress();
sendEmptyMessageDelayed(0, 200);
}
}
}

View File

@@ -0,0 +1,53 @@
package com.danikula.videocache.sample;
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import com.viewpagerindicator.CirclePageIndicator;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById;
@EActivity(R.layout.activity_video_gallery)
public class VideoGalleryActivity extends FragmentActivity {
@ViewById ViewPager viewPager;
@ViewById CirclePageIndicator viewPagerIndicator;
@AfterViews
void afterViewInjected() {
ViewsPagerAdapter viewsPagerAdapter = new ViewsPagerAdapter(this);
viewPager.setAdapter(viewsPagerAdapter);
viewPagerIndicator.setViewPager(viewPager);
}
private static final class ViewsPagerAdapter extends FragmentStatePagerAdapter {
private final Context context;
public ViewsPagerAdapter(FragmentActivity activity) {
super(activity.getSupportFragmentManager());
this.context = activity;
}
@Override
public Fragment getItem(int position) {
Video video = Video.values()[position];
return GalleryVideoFragment.build(context, video);
}
@Override
public int getCount() {
return Video.values().length;
}
@Override
public CharSequence getPageTitle(int position) {
return Video.values()[position].name();
}
}
}

View File

@@ -0,0 +1,23 @@
<LinearLayout
android:id="@+id/containerView"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@android:drawable/divider_horizontal_dark"
android:orientation="vertical"
android:showDividers="middle"
tools:context=".MenuActivity">
<Button
android:id="@+id/cleanCacheButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clean cache" />
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,58 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCC"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/videoContainer0"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#FFF" />
<FrameLayout
android:id="@+id/videoContainer1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#FFF" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/videoContainer2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#FFF" />
<FrameLayout
android:id="@+id/videoContainer3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#FFF" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,25 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCC"
android:orientation="vertical"
android:padding="8dp">
<FrameLayout
android:id="@+id/videoContainer0"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#FFF" />
<FrameLayout
android:id="@+id/videoContainer1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#FFF" />
</LinearLayout>

View File

@@ -0,0 +1,7 @@
<FrameLayout
android:id="@+id/containerView"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".VideoActivity" />

View File

@@ -0,0 +1,23 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.viewpagerindicator.CirclePageIndicator
android:id="@+id/viewPagerIndicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
app:fillColor="#800F"
app:radius="10dp"
app:strokeColor="#800F" />
</LinearLayout>

View File

@@ -10,13 +10,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
<SeekBar
android:id="@+id/progressBar"
style="@android:style/Widget.Holo.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.Holo.ProgressBar.Horizontal"
android:layout_margin="16dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
android:layout_centerHorizontal="true"
android:layout_margin="16dp"
android:max="100" />
</RelativeLayout>