Webview download/cache video - android

I have a webview in android that goes to website that has a video tag in it. What would be the best way to either download the video or cache it so that it won't download every time it's being played. Couldn't find anything that works.

Download the video when you load it first time, and store it in your app's data folder. After that intercept the video url request in your webview and stream the video from local.
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
//Here check whether /request.getUrl().toString()/ is video url, if Yes, then
Get the locally saved video and convert it as inputstream and return as follows,
return new WebResourceResponse(getMimeType(request.getUrl().toString()), "UTF-8", 200, "OK", responseHeaders, inputStream);

Here is a example using Okhttp.
In this example it downloads a file and also shows a progress bar to the user with the progress of the file being downloaded.
class VideoDownloader extends AsyncTask<Void, Long, Boolean> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Void... params) {
OkHttpClient client = new OkHttpClient();
String url = "http://myamazingvideo.mp4";
Call call = client.newCall(new Request.Builder().url(url).get().build());
try {
Response response = call.execute();
if (response.code() == 200 || response.code() == 201) {
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
Log.d(LOG_TAG, responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
InputStream inputStream = null;
try {
inputStream = response.body().byteStream();
byte[] buff = new byte[1024 * 4];
long downloaded = 0;
long target = response.body().contentLength();
mediaFile = new File(getActivity().getCacheDir(), "mySuperVideo.mp4");
OutputStream output = new FileOutputStream(mediaFile);
publishProgress(0L, target);
while (true) {
int readed = inputStream.read(buff);
if (readed == -1) {
break;
}
output.write(buff, 0, readed);
//write buff
downloaded += readed;
publishProgress(downloaded, target);
if (isCancelled()) {
return false;
}
}
output.flush();
output.close();
return downloaded == target;
} catch (IOException ignore) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
}
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
#Override
protected void onProgressUpdate(Long... values) {
super.onProgressUpdate(values);
progressBar.setMax(values[1].intValue());
progressBar.setProgress(values[0].intValue());
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
progressBar.setVisibility(View.GONE);
if (mediaFile != null && mediaFile.exists()) {
playVideo();
}
}
}

You can play video by yourself with help of MediaPlayer and use AndroidVideoCache for caching.

Related

how to getting ContentLength in Retrofit

I am using a service in my app that I used to retrofit to download some "apk" files in the background.
I want to check downloaded file size with received file size to make sure I get "apk" completely.
but when i use response.body().contentLength() it is -1!
This is my code:
private void downloadAppFromServer(String url, final String fileId) {
APIService downloadService = ServiceGenerator.createServiceFile(APIService.class, "181412655", "181412655");
Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(url);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
if (response.isSuccess()) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
Log.d("LOGO", "server contacted and has file");
Log.d("LOGO", "FILE SIZE: " + response.body().contentLength());
boolean writtenToDisk = writeResponseBodyToDisk(response.body(), fileId);
Log.d("LOGO", "file download was a success? " + writtenToDisk);
return null;
}
}.execute();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
});
}
And this is my writeToDisk Method:
private boolean writeResponseBodyToDisk(ResponseBody body, String fileId) {
try {
// Location to save downloaded file and filename
File DownloadFile = new File(G.DIR_APK + "/" + fileId + ".apk");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
Log.d("LOGO", "file size is: " + fileSize ); //This is -1 !
outputStream = new FileOutputStream(DownloadFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
}
outputStream.flush();
if (fileSize == fileSizeDownloaded) {
return true;
} else {
return false;
}
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
-1 means that web server doesnt give you any information about length of file.
Retrofit gets header content-length. If it doesnt exist response.body().contentLength() returns -1.
Try to make HEAD HTTP request to the same URL, it might return the Content-Length.

Pass parameter in RxJava for OnCompleted in Android

I am downloading multiple images using RxJava. Beside downloading I also showing progress for each downloaded file. My problem, is that I need to know when file completely downloaded(currently last part of downloaded progress is not emitted). Since I can download multiple files at same time, I need to pass mediaId of download completed file. Or I should be able to receive last download progress part How I can accomplish this?
downloadThread = new Downloader(media.getMediaUrl(), media.getId(), context);
downloadThread.start();
downloadThread.getProgressObservable()
.sample(30, java.util.concurrent.TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<DownloadProgressEvent>() {
#Override
public void call(DownloadProgressEvent event) {
ProgressBar downloadProgress = (ProgressBar) view.findViewById(R.id.download_progress);
if (event.getTotal() > 0) {
int downloadPercent = (int) ((event.getLoaded() * 100l) / event.getTotal());
downloadProgress.setProgress(downloadPercent);
Log.d(TAG, "download progress: " + String.format("%s / %s /percent: %s / mediaId: %s", event.getLoadedBytes(), event.getTotalBytes(), downloadPercent, event.getMediaId()));
}
}
});
}
Code from Downloader class.
public void loadInBackground() {
try {
URL toDownload = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) toDownload.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
String mimeType = MimeTypeMap.getFileExtensionFromUrl(url);
File outFile = new File(helper.getTmpFolder() + "/" + helper.generateUniqueName() + "test." + mimeType);
FileOutputStream fileOutput = new FileOutputStream(outFile);
InputStream inputStream = urlConnection.getInputStream();
totalSize = urlConnection.getContentLength();
loadedSize = 0;
byte[] buffer = new byte[1024];
int bufferLength = 0; //used to store a temporary size of the buffer
while (!killed && (bufferLength = inputStream.read(buffer)) > 0) {
while(!running && !killed) {
Thread.sleep(500);
}
if (!killed) {
fileOutput.write(buffer, 0, bufferLength);
loadedSize += bufferLength;
reportProgress();
}
}
fileOutput.close();
if (killed && outFile.exists()) {
outFile.delete();
}
progressSubject.onCompleted();
} catch (MalformedURLException e) {
e.printStackTrace();
progressSubject.onError(e);
} catch (IOException e) {
e.printStackTrace();
progressSubject.onError(e);
} catch (InterruptedException e) {
e.printStackTrace();
progressSubject.onError(e);
}
}
private void reportProgress() {
progressSubject.onNext(new DownloadProgressEvent(loadedSize, totalSize, mediaId));
}
#Override
public void run() {
loadInBackground();
}
public Subject<DownloadProgressEvent, DownloadProgressEvent> getProgressObservable() {
return progressSubject;
}
After learning that I can't pass parameter via onCompleted I solved my problem like this.
downloadThread.getProgressObservable()
.sample(30, java.util.concurrent.TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.doOnCompleted(new Action0() {
#Override
public void call() {
downloadProgress.setProgress(100);
putGoodQualityThumbnail(media, view);;
}
})
.subscribe(new Action1<DownloadProgressEvent>() {
#Override
public void call(DownloadProgressEvent event) {
if (event.getTotal() > 0) {
int downloadPercent = (int) ((event.getLoaded() * 100l) / event.getTotal());
downloadProgress.setProgress(downloadPercent);
}
}
});

Square`s OkHttp. Download progress

Is there any way to get downloading file progress when using square`s OkHttp?
I did not find any solution in Recipes.
They have class Call which can get content asynchronically, but there is no method to get current progress.
Oops answer was obvious. Sorry for dumb question. Just need to read InputStream as usual.
private class AsyncDownloader extends AsyncTask<Void, Long, Boolean> {
private final String URL = "file_url";
#Override
protected Boolean doInBackground(Void... params) {
OkHttpClient httpClient = new OkHttpClient();
Call call = httpClient.newCall(new Request.Builder().url(URL).get().build());
try {
Response response = call.execute();
if (response.code() == 200) {
InputStream inputStream = null;
try {
inputStream = response.body().byteStream();
byte[] buff = new byte[1024 * 4];
long downloaded = 0;
long target = response.body().contentLength();
publishProgress(0L, target);
while (true) {
int readed = inputStream.read(buff);
if(readed == -1){
break;
}
//write buff
downloaded += readed;
publishProgress(downloaded, target);
if (isCancelled()) {
return false;
}
}
return downloaded == target;
} catch (IOException ignore) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
}
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
#Override
protected void onProgressUpdate(Long... values) {
progressBar.setMax(values[1].intValue());
progressBar.setProgress(values[0].intValue());
textViewProgress.setText(String.format("%d / %d", values[0], values[1]));
}
#Override
protected void onPostExecute(Boolean result) {
textViewStatus.setText(result ? "Downloaded" : "Failed");
}
}
You can use the okHttp receipe : Progress.java
every time in the while loop ,you publish the progress.So it's like being blocked

Android - download failed at 4.0+ devices AsyncTask

I have an AsyncTask to download files one by one, and I make it as a queue, when it's running on android 2.x, good. In android 4.0+ it stop working. Here I passed a ProgressBar to AsyncTask, so it will update the loading progress bar, and indicate where it is.
The strange part is the progress bar will go 100% very quick not match the real size of file. And the length of file output in logcat also wrong...
All tasks will execute serially so it won't hurt the parallel limitation above SDK 11. I guess the problem might be inside the download part, just don't know where it is.
public function download ()
{
.....
if (task != null) {
task.cancel (true);
}
task = new OnlineDownloadTask (progress);
task.execute (url, path);
.....
}
class OnlineDownloadTask extends AsyncTask<String, String, String> {
private final WeakReference<OfflineQueueIndicatorView> progressbarReference;
public OnlineDownloadTask(OfflineQueueIndicatorView progress) {
progressbarReference = new WeakReference<OfflineQueueIndicatorView>(
progress);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... aurl) {
int count;
try {
URL url = new URL(aurl[0]);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.setRequestMethod("GET");
conn.setAllowUserInteraction(false);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.connect();
int lengthOfFile = conn.getContentLength();
android.util.Log.v("offline.downloader", lengthOfFile + "");
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(aurl[1]);
try {
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress(""
+ (int) ((total * 100) / lengthOfFile));
if (stopoffline) {
android.util.Log.v("file.downloader", "stopped");
break;
}
output.write(data, 0, count);
}
if (stopoffline) {
output.flush();
output.close();
input.close();
conn.disconnect();
File file = new File(aurl[1]);
if (file.exists()) {
file.delete();
}
stopoffline = false;
return null;
} else {
output.flush();
output.close();
input.close();
conn.disconnect();
if (DiskCache.getInstance().offlineDirectoryExist(
DiskCache.getInstance().offlineCurrentFolder)) {
} else {
if (!DiskCache
.getInstance()
.makeOfflineFolder(
DiskCache.getInstance().offlineCurrentFolder)) {
return null;
}
}
android.util.Log.v("offline",
DiskCache.getInstance().offlineCurrentFolder);
unzip(aurl[1],
DiskCache.getInstance().offlineCurrentFolder);
DiskCache.getInstance().deleteFile(aurl[1]);
return "succ";
}
} finally {
if (output != null) {
output.flush();
output.close();
}
if (input != null) {
input.close();
}
if (conn != null) {
conn.disconnect();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onProgressUpdate(String... progress) {
try {
if (progressbarReference != null) {
OfflineQueueIndicatorView p = progressbarReference.get();
if (p != null) {
int i = Integer.parseInt(progress[0]);
p.setProgress(i);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onPostExecute(String ret) {
try {
if (progressbarReference != null) {
if (ret != null) {
queue.get(currentId).put("state", "complete");
} else {
if (queue != null) {
if (currentId != null) {
queue.get(currentId).put("state", "failed");
}
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
download();
}
}
It's possible that the newer version of HttpUrlConnection in Android 4.0 is causing the server to use Chunked Transfer Encoding, which is supported in HTTP/1.1. The Android 2.x version may not have supported CTE. When sending a response with CTE (e.g., during file/video streaming), the server will not return a content length. As such, you may want to show an indeterminate ProgressBar when the content length is not available.
I finally found what's wrong after I remove conn.setDoOutput(true), it working well on both android 2.x and 4.x emulator, I think also acj has the point, sometimes Chunked Transfer Encoding is the reason too.

Getting a file size mismatch with android download code

I'm downloading a file from a server and for some reason i can't determine, the downloaded file size doesn't match the original file size. Here's my code.
private class dl extends AsyncTask<String,Integer,Void>
{
int size;
#Override
protected Void doInBackground(String... arg0) {
// TODO Auto-generated method stub
try{
URL myFileUrl = new URL("http://10.0.2.2:8080/testdlapps/chrome-beta.zip");
HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.setConnectTimeout(5000);
conn.connect();
InputStream is = conn.getInputStream();
size = conn.getContentLength();
Log.v("INFO---------------------", "size is " +size);
FileOutputStream fout1 = new FileOutputStream(Environment.getExternalStorageDirectory()+"/"+"xyz.zip");
BufferedOutputStream bos = new BufferedOutputStream(fout1);
byte[] b = new byte[1024]; int i=0, count=0;
while((count = is.read(b)) != -1)
{
bos.write(b,0,count);
i+=count;
publishProgress(i);
Log.v("INFO----------------------------",""+count);
}
fout1.close();
}catch(Exception e){
Log.v("INFO--------------------------","Error!!");
Log.v("INFO--------------------------",e.getMessage());
e.printStackTrace();
}
return null;
}
protected void onProgressUpdate(Integer... progress) {
tv.setText("downloaded " + progress[0] + "/" + size ); //tv is a TextView
}
}
When i run the app, after the download completes, count and size are the same but the actual file size i.e /mnt/sdcard/xyz.zip is always less than size. Any ideas what going wrong?
override onPostExecute and check if actually it finishes, perhaps here a code to download with resume support,
pay attention because if you press back the download may still run:
if (isCancelled())
return false;
in the loop is needed because the close() on the socket will hang on exit without you noticeing it
here is the code:
class DownloaderTask extends AsyncTask<String, Integer, Boolean>
{
private ProgressDialog mProgress;
private Context mContext;
private Long mFileSize;
private Long mDownloaded;
private String mDestFile;
public DownloaderTask(Context context, String path)
{
mContext = context;
mFileSize = 1L;
mDownloaded = 0L;
mDestFile = path;
}
#Override
protected void onPreExecute()
{
mProgress = new ProgressDialog(mContext);
mProgress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgress.setMessage("Downloading...");
mProgress.setCancelable(true);
mProgress.setCanceledOnTouchOutside(false);
mProgress.setOnCancelListener(new DialogInterface.OnCancelListener()
{
#Override
public void onCancel(DialogInterface dialog)
{
DownloaderTask.this.cancel(true);
}
});
mProgress.show();
}
#Override
protected void onProgressUpdate(Integer... percent)
{
mProgress.setProgress(percent[0]);
}
#Override
protected Boolean doInBackground(String... urls)
{
FileOutputStream fos = null;
BufferedInputStream in = null;
BufferedOutputStream out = null;
AndroidHttpClient mClient = AndroidHttpClient.newInstance("AndroidDownloader");
try
{
HttpResponse response = null;
HttpHead head = new HttpHead(urls[0]);
response = mClient.execute(head);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
return false;
Boolean resumable = response.getLastHeader("Accept-Ranges").getValue().equals("bytes");
File file = new File(mDestFile);
mFileSize = (long) Integer.parseInt(response.getLastHeader("Content-Length").getValue());
mDownloaded = file.length();
if (!resumable || (mDownloaded >= mFileSize))
{
Log.e(TAG, "Invalid size / Non resumable - removing file");
file.delete();
mDownloaded = 0L;
}
HttpGet get = new HttpGet(urls[0]);
if (mDownloaded > 0)
{
Log.i(TAG, "Resume download from " + mDownloaded);
get.setHeader("Range", "bytes=" + mDownloaded + "-");
}
response = mClient.execute(get);
if ((response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) && (response.getStatusLine().getStatusCode() != HttpStatus.SC_PARTIAL_CONTENT))
return false;
if (mDownloaded > 0)
publishProgress((int) ((mDownloaded / mFileSize) * 100));
in = new BufferedInputStream(response.getEntity().getContent());
fos = new FileOutputStream(file, true);
out = new BufferedOutputStream(fos);
byte[] buffer = new byte[8192];
int n = 0;
while ((n = in.read(buffer, 0, buffer.length)) != -1)
{
if (isCancelled())
return false;
out.write(buffer, 0, n);
mDownloaded += n;
publishProgress((int) ((mDownloaded / (float) mFileSize) * 100));
}
} catch (Exception e)
{
e.printStackTrace();
return false;
} finally
{
try
{
mClient.close();
if (in != null)
in.close();
if (out != null)
out.close();
if (fos != null)
fos.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
return true;
}
#Override
protected void onCancelled()
{
finish();
}
#Override
protected void onPostExecute(Boolean result)
{
if (mProgress.isShowing())
mProgress.dismiss();
if (result)
// done
else
// error
}
}
If it is a chunked response, the content-length in the header will be a guess at best.

Categories

Resources