10 Commits

Author SHA1 Message Date
Alexey Danilov
cd0b4111a4 ability to check was content fully cached to file or not 2016-07-27 23:14:06 +03:00
Alexey Danilov
eb67640212 update readme 2016-07-27 21:07:35 +03:00
Alexey Danilov
1d50807fe0 fix HttpUrlSourceTest test 2016-07-27 20:56:21 +03:00
Alexey Danilov
6da9650030 update build tools 2016-07-27 20:36:41 +03:00
Alexey Danilov
a1d00fec7b use static files for tests and sample from github instead of dropbox 2016-03-03 14:13:46 +03:00
Alexey Danilov
f5a779266d add test files 2016-03-03 13:50:01 +03:00
Alexey Danilov
ca6f36ada3 update the latest version in README 2015-12-08 19:06:07 +03:00
Arthur Alves
33b7e5e9d3 Catch NullPointerException to avoid crash due to unsupported async disconnects on Android L (which uses an outdated okhttp version) 2015-12-08 18:59:37 +03:00
Alexey Danilov
0aad13f118 fix dividing by zero while loading empty file (#26) 2015-11-06 13:43:08 +03:00
Alexey Danilov
a270460c8d fix fetching content info (#27) 2015-10-29 16:14:20 +03:00
35 changed files with 187 additions and 44 deletions

View File

@@ -14,14 +14,14 @@ Because there is no sense to download video a lot of times while streaming!
Note `AndroidVideoCache` works only with **direct urls** to media file, it [**doesn't support**](https://github.com/danikula/AndroidVideoCache/issues/19) any streaming technology like DASH, SmoothStreaming, HLS. Note `AndroidVideoCache` works only with **direct urls** to media file, it [**doesn't support**](https://github.com/danikula/AndroidVideoCache/issues/19) any streaming technology like DASH, SmoothStreaming, HLS.
## How to use? ## Get started
Just add dependency (`AndroidVideoCache` is available in jcenter): Just add dependency (`AndroidVideoCache` is available in jcenter):
``` ```
repositories { repositories {
jcenter() jcenter()
} }
dependencies { dependencies {
compile 'com.danikula:videocache:2.3.2' compile 'com.danikula:videocache:2.4.0'
} }
``` ```
@@ -64,6 +64,8 @@ public class App extends Application {
or use [simple factory](http://pastebin.com/s2fafSYS). or use [simple factory](http://pastebin.com/s2fafSYS).
More preferable way is use some dependency injector like [Dagger](http://square.github.io/dagger/). More preferable way is use some dependency injector like [Dagger](http://square.github.io/dagger/).
## Recipes
### Disk cache limit
By default `HttpProxyCacheServer` uses 512Mb for caching files. You can change this value: By default `HttpProxyCacheServer` uses 512Mb for caching files. You can change this value:
```java ```java
@@ -82,9 +84,17 @@ private HttpProxyCacheServer newProxy() {
.maxCacheFilesCount(20) .maxCacheFilesCount(20)
.build(); .build();
} }
``` ```
See `sample` app for details. ### Listen caching progress
Use `HttpProxyCacheServer.registerCacheListener(CacheListener listener)` method to set listener with callback `onCacheAvailable(File cacheFile, String url, int percentsAvailable)` to be aware of caching progress. Do not forget to to unsubscribe listener with help of `HttpProxyCacheServer.unregisterCacheListener(CacheListener listener)` method to avoid memory leaks.
Use `HttpProxyCacheServer.isCached(String url)` method to check was url's content fully cached to file or not.
See `sample` app for more details.
### Sample
See `sample` app.
## Whats new ## Whats new
See Release Notes [here](https://github.com/danikula/AndroidVideoCache/releases) See Release Notes [here](https://github.com/danikula/AndroidVideoCache/releases)
@@ -94,9 +104,11 @@ If it's a feature that you think would need to be discussed please open an issue
1. [Fork the project](http://help.github.com/fork-a-repo/) 1. [Fork the project](http://help.github.com/fork-a-repo/)
2. Create a feature branch (git checkout -b my_branch) 2. Create a feature branch (git checkout -b my_branch)
3. Push your changes to your new branch (git push origin my_branch) 3. Fix a problem. Your code **must** contain test for reproducing problem. Your tests **must be passed** with help of your fix
4. Initiate a [pull request](http://help.github.com/send-pull-requests/) on github 4. Push your changes to your new branch (git push origin my_branch)
5. Your pull request will be reviewed and hopefully merged :) 5. Initiate a [pull request](http://help.github.com/send-pull-requests/) on github
6. Rebase [master branch](https://github.com/danikula/AndroidVideoCache) if your local branch is not actual. Merging is not acceptable, only rebase
6. Your pull request will be reviewed and hopefully merged :)
## Where published? ## Where published?
[Here](https://bintray.com/alexeydanilov/maven/videocache/view) [Here](https://bintray.com/alexeydanilov/maven/videocache/view)
@@ -106,7 +118,7 @@ If it's a feature that you think would need to be discussed please open an issue
## License ## License
Copyright 2014-2015 Alexey Danilov Copyright 2014-2016 Alexey Danilov
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -3,7 +3,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.3.0' classpath 'com.android.tools.build:gradle:2.1.2'
} }
} }

BIN
files/android Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
files/android.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
files/devbytes.mp4 Normal file

Binary file not shown.

0
files/empty.txt Normal file
View File

BIN
files/orange1.mp4 Normal file

Binary file not shown.

BIN
files/orange2.mp4 Normal file

Binary file not shown.

BIN
files/orange3.mp4 Normal file

Binary file not shown.

BIN
files/orange4.mp4 Normal file

Binary file not shown.

BIN
files/orange5.mp4 Normal file

Binary file not shown.

BIN
files/phones.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip

View File

@@ -3,7 +3,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.novoda:bintray-release:0.2.10' classpath 'com.novoda:bintray-release:0.3.4'
} }
} }
@@ -18,6 +18,9 @@ idea {
} }
} }
targetCompatibility = '1.7'
sourceCompatibility = '1.7'
dependencies { dependencies {
compile 'com.google.android:android:1.6_r2' compile 'com.google.android:android:1.6_r2'
} }
@@ -26,7 +29,7 @@ publish {
userOrg = 'alexeydanilov' userOrg = 'alexeydanilov'
groupId = 'com.danikula' groupId = 'com.danikula'
artifactId = 'videocache' artifactId = 'videocache'
publishVersion = '2.3.2' publishVersion = '2.4.0'
description = 'Cache support for android VideoView' description = 'Cache support for android VideoView'
website = 'https://github.com/danikula/AndroidVideoCache' website = 'https://github.com/danikula/AndroidVideoCache'
} }

View File

@@ -91,7 +91,7 @@ public class HttpProxyCacheServer {
private void makeSureServerWorks() { private void makeSureServerWorks() {
int maxPingAttempts = 3; int maxPingAttempts = 3;
int delay = 200; int delay = 300;
int pingAttempts = 0; int pingAttempts = 0;
while (pingAttempts < maxPingAttempts) { while (pingAttempts < maxPingAttempts) {
try { try {
@@ -107,7 +107,7 @@ public class HttpProxyCacheServer {
pingAttempts++; pingAttempts++;
delay *= 2; delay *= 2;
} }
Log.e(LOG_TAG, "Shutdown server… Error pinging server [attempt: " + pingAttempts + ", timeout: " + delay + "]. " + Log.e(LOG_TAG, "Shutdown server… Error pinging server [attempts: " + pingAttempts + ", max timeout: " + delay / 2 + "]. " +
"If you see this message, please, email me danikula@gmail.com"); "If you see this message, please, email me danikula@gmail.com");
shutdown(); shutdown();
} }
@@ -173,6 +173,20 @@ public class HttpProxyCacheServer {
} }
} }
/**
* Checks is cache contains fully cached file for particular url.
*
* @param url an url cache file will be checked for.
* @return {@code true} if cache contains fully cached file for passed in parameters url.
*/
public boolean isCached(String url) {
checkNotNull(url, "Url can't be null!");
File cacheDir = config.cacheRoot;
String fileName = config.fileNameGenerator.generate(url);
File cacheFile = new File(cacheDir, fileName);
return cacheFile.exists();
}
public void shutdown() { public void shutdown() {
Log.i(LOG_TAG, "Shutdown proxy server"); Log.i(LOG_TAG, "Shutdown proxy server");

View File

@@ -58,7 +58,7 @@ public class HttpUrlSource implements Source {
@Override @Override
public void open(int offset) throws ProxyCacheException { public void open(int offset) throws ProxyCacheException {
try { try {
connection = openConnection(offset, "GET", -1); connection = openConnection(offset, -1);
mime = connection.getContentType(); mime = connection.getContentType();
inputStream = new BufferedInputStream(connection.getInputStream(), DEFAULT_BUFFER_SIZE); inputStream = new BufferedInputStream(connection.getInputStream(), DEFAULT_BUFFER_SIZE);
length = readSourceAvailableBytes(connection, offset, connection.getResponseCode()); length = readSourceAvailableBytes(connection, offset, connection.getResponseCode());
@@ -76,7 +76,13 @@ public class HttpUrlSource implements Source {
@Override @Override
public void close() throws ProxyCacheException { public void close() throws ProxyCacheException {
if (connection != null) { if (connection != null) {
connection.disconnect(); try {
connection.disconnect();
} catch (NullPointerException e) {
// https://github.com/danikula/AndroidVideoCache/issues/32
// https://github.com/danikula/AndroidVideoCache/issues/29
throw new ProxyCacheException("Error disconnecting HttpUrlConnection", e);
}
} }
} }
@@ -99,7 +105,7 @@ public class HttpUrlSource implements Source {
HttpURLConnection urlConnection = null; HttpURLConnection urlConnection = null;
InputStream inputStream = null; InputStream inputStream = null;
try { try {
urlConnection = openConnection(0, "HEAD", 10000); urlConnection = openConnection(0, 10000);
length = urlConnection.getContentLength(); length = urlConnection.getContentLength();
mime = urlConnection.getContentType(); mime = urlConnection.getContentType();
inputStream = urlConnection.getInputStream(); inputStream = urlConnection.getInputStream();
@@ -114,7 +120,7 @@ public class HttpUrlSource implements Source {
} }
} }
private HttpURLConnection openConnection(int offset, String method, int timeout) throws IOException, ProxyCacheException { private HttpURLConnection openConnection(int offset, int timeout) throws IOException, ProxyCacheException {
HttpURLConnection connection; HttpURLConnection connection;
boolean redirected; boolean redirected;
int redirectCount = 0; int redirectCount = 0;
@@ -122,7 +128,6 @@ public class HttpUrlSource implements Source {
do { do {
Log.d(LOG_TAG, "Open connection " + (offset > 0 ? " with offset " + offset : "") + " to " + url); Log.d(LOG_TAG, "Open connection " + (offset > 0 ? " with offset " + offset : "") + " to " + url);
connection = (HttpURLConnection) new URL(url).openConnection(); connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod(method);
if (offset > 0) { if (offset > 0) {
connection.setRequestProperty("Range", "bytes=" + offset + "-"); connection.setRequestProperty("Range", "bytes=" + offset + "-");
} }

View File

@@ -100,10 +100,11 @@ class ProxyCache {
} }
} }
protected void onCacheAvailable(long cacheAvailable, long sourceAvailable) { protected void onCacheAvailable(long cacheAvailable, long sourceLength) {
int percents = (int) (cacheAvailable * 100 / sourceAvailable); boolean zeroLengthSource = sourceLength == 0;
int percents = zeroLengthSource ? 100 : (int) (cacheAvailable * 100 / sourceLength);
boolean percentsChanged = percents != percentsAvailable; boolean percentsChanged = percents != percentsAvailable;
boolean sourceLengthKnown = sourceAvailable >= 0; boolean sourceLengthKnown = sourceLength >= 0;
if (sourceLengthKnown && percentsChanged) { if (sourceLengthKnown && percentsChanged) {
onCachePercentsAvailableChanged(percents); onCachePercentsAvailableChanged(percents);
} }

View File

@@ -16,10 +16,10 @@ apply plugin: 'com.neenbedankt.android-apt'
android { android {
compileSdkVersion 23 compileSdkVersion 23
buildToolsVersion '23.0.1' buildToolsVersion '24'
defaultConfig { defaultConfig {
applicationId "com.danikula.videocache.sample" applicationId 'com.danikula.videocache.sample'
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 23 targetSdkVersion 23
versionCode 1 versionCode 1
@@ -38,7 +38,7 @@ dependencies {
// compile project(':library') // compile project(':library')
compile 'com.android.support:support-v4:23.1.0' compile 'com.android.support:support-v4:23.1.0'
compile 'org.androidannotations:androidannotations-api:3.3.2' compile 'org.androidannotations:androidannotations-api:3.3.2'
compile 'com.danikula:videocache:2.3.2' compile 'com.danikula:videocache:2.4.0'
compile 'com.viewpagerindicator:library:2.4.2-SNAPSHOT@aar' compile 'com.viewpagerindicator:library:2.4.2-SNAPSHOT@aar'
apt 'org.androidannotations:androidannotations:3.3.2' apt 'org.androidannotations:androidannotations:3.3.2'
} }

View File

@@ -6,11 +6,11 @@ import java.io.File;
public enum Video { public enum Video {
ORANGE_1("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange1.mp4"), ORANGE_1(Config.ROOT + "orange1.mp4"),
ORANGE_2("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange2.mp4"), ORANGE_2(Config.ROOT + "orange2.mp4"),
ORANGE_3("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange3.mp4"), ORANGE_3(Config.ROOT + "orange3.mp4"),
ORANGE_4("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange4.mp4"), ORANGE_4(Config.ROOT + "orange4.mp4"),
ORANGE_5("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/orange5.mp4"); ORANGE_5(Config.ROOT + "orange5.mp4");
public final String url; public final String url;
@@ -21,4 +21,8 @@ public enum Video {
public File getCacheFile(Context context) { public File getCacheFile(Context context) {
return new File(context.getExternalCacheDir(), name()); return new File(context.getExternalCacheDir(), name());
} }
private class Config {
private static final String ROOT = "https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/";
}
} }

View File

@@ -5,6 +5,7 @@ import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.util.Log; import android.util.Log;
import android.widget.ImageView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.VideoView; import android.widget.VideoView;
@@ -27,6 +28,7 @@ public class VideoFragment extends Fragment implements CacheListener {
@FragmentArg String url; @FragmentArg String url;
@FragmentArg String cachePath; @FragmentArg String cachePath;
@ViewById ImageView cacheStatusImageView;
@ViewById VideoView videoView; @ViewById VideoView videoView;
@ViewById ProgressBar progressBar; @ViewById ProgressBar progressBar;
@@ -45,9 +47,16 @@ public class VideoFragment extends Fragment implements CacheListener {
@AfterViews @AfterViews
void afterViewInjected() { void afterViewInjected() {
checkCachedState();
startVideo(); startVideo();
} }
private void checkCachedState() {
HttpProxyCacheServer proxy = App.getProxy(getActivity());
boolean fullyCached = proxy.isCached(url);
setCachedState(fullyCached);
}
private void startVideo() { private void startVideo() {
HttpProxyCacheServer proxy = App.getProxy(getActivity()); HttpProxyCacheServer proxy = App.getProxy(getActivity());
proxy.registerCacheListener(this, url); proxy.registerCacheListener(this, url);
@@ -78,6 +87,7 @@ public class VideoFragment extends Fragment implements CacheListener {
@Override @Override
public void onCacheAvailable(File file, String url, int percentsAvailable) { public void onCacheAvailable(File file, String url, int percentsAvailable) {
progressBar.setSecondaryProgress(percentsAvailable); progressBar.setSecondaryProgress(percentsAvailable);
setCachedState(percentsAvailable == 100);
Log.d(LOG_TAG, String.format("onCacheAvailable. percents: %d, file: %s, url: %s", percentsAvailable, file, url)); Log.d(LOG_TAG, String.format("onCacheAvailable. percents: %d, file: %s, url: %s", percentsAvailable, file, url));
} }
@@ -92,6 +102,11 @@ public class VideoFragment extends Fragment implements CacheListener {
videoView.seekTo(videoPosition); videoView.seekTo(videoPosition);
} }
private void setCachedState(boolean cached) {
int statusIconId = cached ? R.drawable.ic_cloud_done : R.drawable.ic_cloud_download;
cacheStatusImageView.setImageResource(statusIconId);
}
private final class VideoProgressUpdater extends Handler { private final class VideoProgressUpdater extends Handler {
public void start() { public void start() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

View File

@@ -10,6 +10,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<ImageView
android:id="@+id/cacheStatusImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:src="@drawable/ic_cloud_download" />
<SeekBar <SeekBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
style="@android:style/Widget.Holo.ProgressBar.Horizontal" style="@android:style/Widget.Holo.ProgressBar.Horizontal"

View File

@@ -82,7 +82,7 @@ public class HttpProxyCacheServerTest {
@Test @Test
public void testMimeFromResponse() throws Exception { public void testMimeFromResponse() throws Exception {
Pair<File, Response> response = readProxyData("https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/android"); Pair<File, Response> response = readProxyData("https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/android");
assertThat(response.second.contentType).isEqualTo("application/octet-stream"); assertThat(response.second.contentType).isEqualTo("application/octet-stream");
} }
@@ -210,10 +210,55 @@ public class HttpProxyCacheServerTest {
assertThat(file(cacheFolder, HTTP_DATA_URL_6_REDIRECTS)).exists(); assertThat(file(cacheFolder, HTTP_DATA_URL_6_REDIRECTS)).exists();
} }
@Test
public void testCheckFileExistForNotCachedUrl() throws Exception {
HttpProxyCacheServer proxy = newProxy(cacheFolder);
proxy.shutdown();
assertThat(proxy.isCached(HTTP_DATA_URL)).isFalse();
}
@Test
public void testCheckFileExistForFullyCachedUrl() throws Exception {
HttpProxyCacheServer proxy = newProxy(cacheFolder);
readProxyResponse(proxy, HTTP_DATA_URL, 0);
proxy.shutdown();
assertThat(proxy.isCached(HTTP_DATA_URL)).isTrue();
}
@Test
public void testCheckFileExistForPartiallyCachedUrl() throws Exception {
File cacheDir = RuntimeEnvironment.application.getExternalCacheDir();
File file = file(cacheDir, HTTP_DATA_URL);
int partialCacheSize = 1000;
byte[] partialData = ProxyCacheTestUtils.generate(partialCacheSize);
File partialCacheFile = ProxyCacheTestUtils.getTempFile(file);
IoUtils.saveToFile(partialData, partialCacheFile);
HttpProxyCacheServer proxy = newProxy(cacheDir);
assertThat(proxy.isCached(HTTP_DATA_URL)).isFalse();
readProxyResponse(proxy, HTTP_DATA_URL);
proxy.shutdown();
assertThat(proxy.isCached(HTTP_DATA_URL)).isTrue();
}
@Test
public void testCheckFileExistForDeletedCacheFile() throws Exception {
HttpProxyCacheServer proxy = newProxy(cacheFolder);
readProxyResponse(proxy, HTTP_DATA_URL, 0);
proxy.shutdown();
File cacheFile = file(cacheFolder, HTTP_DATA_URL);
boolean deleted = cacheFile.delete();
assertThat(deleted).isTrue();
assertThat(proxy.isCached(HTTP_DATA_URL)).isFalse();
}
private Pair<File, Response> readProxyData(String url, int offset) throws IOException { private Pair<File, Response> readProxyData(String url, int offset) throws IOException {
File externalCacheDir = RuntimeEnvironment.application.getExternalCacheDir(); File file = file(cacheFolder, url);
File file = file(externalCacheDir, url); HttpProxyCacheServer proxy = newProxy(cacheFolder);
HttpProxyCacheServer proxy = newProxy(externalCacheDir);
Response response = readProxyResponse(proxy, url, offset); Response response = readProxyResponse(proxy, url, offset);
proxy.shutdown(); proxy.shutdown();

View File

@@ -12,6 +12,7 @@ import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.Socket; import java.net.Socket;
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL; import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL;
@@ -20,6 +21,7 @@ import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -73,4 +75,24 @@ public class HttpProxyCacheTest {
assertThat(response.data).isEqualTo(partialData); assertThat(response.data).isEqualTo(partialData);
assertThat(response.code).isEqualTo(206); assertThat(response.code).isEqualTo(206);
} }
@Test
public void testLoadEmptyFile() throws Exception {
String zeroSizeUrl = "https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/empty.txt";
HttpUrlSource source = new HttpUrlSource(zeroSizeUrl);
HttpProxyCache proxyCache = new HttpProxyCache(source, new FileCache(ProxyCacheTestUtils.newCacheFile()));
GetRequest request = new GetRequest("GET /" + HTTP_DATA_URL + " HTTP/1.1");
ByteArrayOutputStream out = new ByteArrayOutputStream();
Socket socket = mock(Socket.class);
when(socket.getOutputStream()).thenReturn(out);
CacheListener listener = Mockito.mock(CacheListener.class);
proxyCache.registerCacheListener(listener);
proxyCache.processRequest(request, socket);
proxyCache.registerCacheListener(null);
Response response = new Response(out.toByteArray());
Mockito.verify(listener).onCacheAvailable(Mockito.<File>any(), eq(zeroSizeUrl), eq(100));
assertThat(response.data).isEmpty();
}
} }

View File

@@ -11,6 +11,7 @@ import org.robolectric.annotation.Config;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.util.Arrays; import java.util.Arrays;
import static com.danikula.videocache.ProxyCacheUtils.DEFAULT_BUFFER_SIZE;
import static com.danikula.videocache.support.ProxyCacheTestUtils.ASSETS_DATA_BIG_NAME; import static com.danikula.videocache.support.ProxyCacheTestUtils.ASSETS_DATA_BIG_NAME;
import static com.danikula.videocache.support.ProxyCacheTestUtils.ASSETS_DATA_NAME; import static com.danikula.videocache.support.ProxyCacheTestUtils.ASSETS_DATA_NAME;
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_BIG_SIZE; import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_BIG_SIZE;
@@ -83,7 +84,7 @@ public class HttpUrlSourceTest {
HttpUrlSource source = new HttpUrlSource(HTTP_DATA_URL_ONE_REDIRECT); HttpUrlSource source = new HttpUrlSource(HTTP_DATA_URL_ONE_REDIRECT);
source.open(0); source.open(0);
byte[] readData = new byte[HTTP_DATA_SIZE]; byte[] readData = new byte[HTTP_DATA_SIZE];
source.read(readData); readSource(source, readData);
source.close(); source.close();
byte[] expectedData = Arrays.copyOfRange(loadAssetFile(ASSETS_DATA_NAME), 0, HTTP_DATA_SIZE); byte[] expectedData = Arrays.copyOfRange(loadAssetFile(ASSETS_DATA_NAME), 0, HTTP_DATA_SIZE);
@@ -96,7 +97,7 @@ public class HttpUrlSourceTest {
HttpUrlSource source = new HttpUrlSource(HTTP_DATA_URL_ONE_REDIRECT); HttpUrlSource source = new HttpUrlSource(HTTP_DATA_URL_ONE_REDIRECT);
source.open(offset); source.open(offset);
byte[] readData = new byte[HTTP_DATA_SIZE - offset]; byte[] readData = new byte[HTTP_DATA_SIZE - offset];
source.read(readData); readSource(source, readData);
source.close(); source.close();
byte[] expectedData = Arrays.copyOfRange(loadAssetFile(ASSETS_DATA_NAME), offset, HTTP_DATA_SIZE); byte[] expectedData = Arrays.copyOfRange(loadAssetFile(ASSETS_DATA_NAME), offset, HTTP_DATA_SIZE);
@@ -109,7 +110,7 @@ public class HttpUrlSourceTest {
HttpUrlSource source = new HttpUrlSource(HTTP_DATA_URL_3_REDIRECTS); HttpUrlSource source = new HttpUrlSource(HTTP_DATA_URL_3_REDIRECTS);
source.open(offset); source.open(offset);
byte[] readData = new byte[HTTP_DATA_SIZE - offset]; byte[] readData = new byte[HTTP_DATA_SIZE - offset];
source.read(readData); readSource(source, readData);
source.close(); source.close();
byte[] expectedData = Arrays.copyOfRange(loadAssetFile(ASSETS_DATA_NAME), offset, HTTP_DATA_SIZE); byte[] expectedData = Arrays.copyOfRange(loadAssetFile(ASSETS_DATA_NAME), offset, HTTP_DATA_SIZE);
@@ -130,4 +131,14 @@ public class HttpUrlSourceTest {
assertThat(new HttpUrlSource("http://mysite.by/video.mp4").getMime()).isEqualTo("video/mp4"); assertThat(new HttpUrlSource("http://mysite.by/video.mp4").getMime()).isEqualTo("video/mp4");
assertThat(new HttpUrlSource(HTTP_DATA_URL).getMime()).isEqualTo("image/jpeg"); assertThat(new HttpUrlSource(HTTP_DATA_URL).getMime()).isEqualTo("image/jpeg");
} }
private void readSource(Source source, byte[] target) throws ProxyCacheException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int totalRead = 0;
int readBytes;
while ((readBytes = source.read(buffer)) != -1) {
System.arraycopy(buffer, 0, target, totalRead, readBytes);
totalRead += readBytes;
}
}
} }

View File

@@ -20,12 +20,12 @@ import java.util.UUID;
*/ */
public class ProxyCacheTestUtils { public class ProxyCacheTestUtils {
public static final String HTTP_DATA_URL = "https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/android.jpg"; public static final String HTTP_DATA_URL = "https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/android.jpg";
public static final String HTTP_DATA_URL_ONE_REDIRECT = "http://bit.ly/1V5PeY5"; public static final String HTTP_DATA_URL_ONE_REDIRECT = "http://bit.ly/1LAJKAy";
public static final String HTTP_DATA_URL_3_REDIRECTS = "http://bit.ly/1KvVmgZ"; public static final String HTTP_DATA_URL_3_REDIRECTS = "http://bit.ly/1QtKJiB";
public static final String HTTP_DATA_URL_6_REDIRECTS = "http://ow.ly/SugRH"; public static final String HTTP_DATA_URL_6_REDIRECTS = "http://ow.ly/Z17wz";
public static final String HTTP_DATA_BIG_URL = "https://dl.dropboxusercontent.com/u/15506779/persistent/proxycache/phones.jpg"; public static final String HTTP_DATA_BIG_URL = "https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/phones.jpg";
public static final String HTTP_DATA_BIG_URL_ONE_REDIRECT = "http://bit.ly/1iJ69yA"; public static final String HTTP_DATA_BIG_URL_ONE_REDIRECT = "http://bit.ly/24DdZ06";
public static final String ASSETS_DATA_NAME = "android.jpg"; public static final String ASSETS_DATA_NAME = "android.jpg";
public static final String ASSETS_DATA_BIG_NAME = "phones.jpg"; public static final String ASSETS_DATA_BIG_NAME = "phones.jpg";
public static final int HTTP_DATA_SIZE = 4768; public static final int HTTP_DATA_SIZE = 4768;
@@ -40,7 +40,11 @@ public class ProxyCacheTestUtils {
} }
public static Response readProxyResponse(HttpProxyCacheServer proxy, String url, int offset) throws IOException { public static Response readProxyResponse(HttpProxyCacheServer proxy, String url, int offset) throws IOException {
URL proxiedUrl = new URL(proxy.getProxyUrl(url)); String proxyUrl = proxy.getProxyUrl(url);
if (!proxyUrl.startsWith("http://127.0.0.1")) {
throw new IllegalStateException("Url " + url + " is not proxied!");
}
URL proxiedUrl = new URL(proxyUrl);
HttpURLConnection connection = (HttpURLConnection) proxiedUrl.openConnection(); HttpURLConnection connection = (HttpURLConnection) proxiedUrl.openConnection();
try { try {
if (offset >= 0) { if (offset >= 0) {