diff --git a/sample/build.gradle b/sample/build.gradle index a66532c..96443a3 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -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' } diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index fecdc85..3b38e1d 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + package="com.danikula.videocache.sample" + xmlns:android="http://schemas.android.com/apk/res/android"> @@ -12,13 +12,17 @@ android:label="@string/app_name" android:theme="@android:style/Theme.Holo.Light.NoActionBar"> + + + + diff --git a/sample/src/main/java/com/danikula/videocache/sample/GalleryVideoFragment.java b/sample/src/main/java/com/danikula/videocache/sample/GalleryVideoFragment.java new file mode 100644 index 0000000..2950242 --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/GalleryVideoFragment.java @@ -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); + } + } +} diff --git a/sample/src/main/java/com/danikula/videocache/sample/MenuActivity.java b/sample/src/main/java/com/danikula/videocache/sample/MenuActivity.java new file mode 100644 index 0000000..eb15edc --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/MenuActivity.java @@ -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 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; + } + } + +} diff --git a/sample/src/main/java/com/danikula/videocache/sample/MultipleVideosActivity.java b/sample/src/main/java/com/danikula/videocache/sample/MultipleVideosActivity.java new file mode 100644 index 0000000..811bd03 --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/MultipleVideosActivity.java @@ -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(); + } +} diff --git a/sample/src/main/java/com/danikula/videocache/sample/SharedCacheActivity.java b/sample/src/main/java/com/danikula/videocache/sample/SharedCacheActivity.java new file mode 100644 index 0000000..a1447d0 --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/SharedCacheActivity.java @@ -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(); + } +} diff --git a/sample/src/main/java/com/danikula/videocache/sample/SingleVideoActivity.java b/sample/src/main/java/com/danikula/videocache/sample/SingleVideoActivity.java new file mode 100644 index 0000000..5041f6d --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/SingleVideoActivity.java @@ -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(); + } + } +} diff --git a/sample/src/main/java/com/danikula/videocache/sample/Video.java b/sample/src/main/java/com/danikula/videocache/sample/Video.java new file mode 100644 index 0000000..d69f301 --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/Video.java @@ -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()); + } +} diff --git a/sample/src/main/java/com/danikula/videocache/sample/VideoActivity.java b/sample/src/main/java/com/danikula/videocache/sample/VideoActivity.java deleted file mode 100644 index b1d6b28..0000000 --- a/sample/src/main/java/com/danikula/videocache/sample/VideoActivity.java +++ /dev/null @@ -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); - } - -} diff --git a/sample/src/main/java/com/danikula/videocache/sample/VideoFragment.java b/sample/src/main/java/com/danikula/videocache/sample/VideoFragment.java new file mode 100644 index 0000000..c9f3d15 --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/VideoFragment.java @@ -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); + } + } +} diff --git a/sample/src/main/java/com/danikula/videocache/sample/VideoGalleryActivity.java b/sample/src/main/java/com/danikula/videocache/sample/VideoGalleryActivity.java new file mode 100644 index 0000000..06fc438 --- /dev/null +++ b/sample/src/main/java/com/danikula/videocache/sample/VideoGalleryActivity.java @@ -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(); + } + } +} diff --git a/sample/src/main/res/layout/activity_menu.xml b/sample/src/main/res/layout/activity_menu.xml new file mode 100644 index 0000000..2cc2732 --- /dev/null +++ b/sample/src/main/res/layout/activity_menu.xml @@ -0,0 +1,23 @@ + + +