Skip to content

Commit

Permalink
Merge pull request #26 from odbol/master
Browse files Browse the repository at this point in the history
Fixed to work on KitKat (issue #20)
  • Loading branch information
jaeger25 committed Nov 1, 2014
2 parents 90fac32 + f47d39c commit fe2fcc2
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 4 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ This plugin enables the use of normal HTML5 <video> tags for local video - other
Android Webview (which Cordova is based upon) limits access to local files (such as videos) and prohibits reading them, both via relative files and `file:///` URIs. Cordova Html5Video Plugin solves this by giving each of you video a `android.resource://` path and updating your `<video>` tags accordingly.

### Limitations ###
For Android only. Tested on Android API 15-18 (testing for API 19 is undergoing).
For Android only. Tested on Android API 15-19.

For API >= 19, a workaround is employed to copy the video files over to your application's data directory, as world-readable.

*WARNING*: this is potentially insecure - other apps will be able to read your videos! However it is the only way to get around Chrome's strict limitations on content:// URLs. See [Issue #20](https://github.com/jaeger25/Html5Video/issues/20) for details.

### Install ###

Expand Down
142 changes: 139 additions & 3 deletions src/android/Html5Video.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
package org.apache.cordova.plugin;

import java.util.Timer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;

import org.apache.cordova.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources.NotFoundException;
import android.os.Build;
import android.util.Log;

public class Html5Video extends CordovaPlugin {
private static final String TAG = "Html5VideoCordovaPlugin";

public final String ACTION_INITIALIZE = "initialize";
public final String ACTION_PLAY = "play";

FileDataStore dataStore = new FileDataStore();

@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {

Expand All @@ -25,8 +39,29 @@ public boolean execute(String action, JSONArray args, final CallbackContext call
if (tagNames != null) {
for (int i = 0; i < tagNames.length(); i++) {
String[] video = videos.getString(tagNames.getString(i)).split("\\.");
int videoId = this.cordova.getActivity().getResources().getIdentifier(video[0], "raw", packageName);
convertedVideos.put(tagNames.getString(i), "android.resource://" + packageName + "/" + videoId);
String newUrl = null;

// Kitkat and above don't allow loading android.resource:// URLs,
// (see https://code.google.com/p/android/issues/detail?id=63033 ),
// so we do a little trick.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// copy vids to global-accessible directory, then replace with that URL.
// idea from this thread: https://github.com/jaeger25/Html5Video/issues/23#issuecomment-51174558
String newFilePath = dataStore.getFilePath(video[0]);

if (newFilePath != null) {
newUrl = "file://" + newFilePath;
Log.d(TAG, "Using KitKat, can't load local videos. Loading from: " + newUrl);
}
}


if (newUrl == null) { // do the old way if the KitKat way failed (or if we're not on KitKat)
int videoId = this.cordova.getActivity().getResources().getIdentifier(video[0], "raw", packageName);
newUrl = "android.resource://" + packageName + "/" + videoId;
}

convertedVideos.put(tagNames.getString(i), newUrl);

LOG.d("Html5Video", "Id: " + tagNames.getString(i) + " , src: " + convertedVideos.getString(tagNames.getString(i)));
}
Expand Down Expand Up @@ -55,4 +90,105 @@ public void run() {
public Object onMessage(String id, Object data) {
return super.onMessage(id, data);
}


private class FileDataStore {

/***
* Gets the file:// URL of a video file in the res/raw directory, copying it over if necessary.
*
* Note: the file will be world-readable (not-recommended by Google), so be careful: other apps will be able to read your videos!
*
* @param rawFileName Name of the file in the raw directory.
* @return The absolute path of the file, or null if copy failed.
*/
public String getFilePath(String rawFileName) {
String fileName = "html5video_" + rawFileName; // path separators not allowed
File f = cordova.getActivity().getFileStreamPath(fileName);

// transfer raw resrouce file into app data directory so it can be read globally.
if (!f.exists()) {

if (!copyResourceFileToDataDir(rawFileName, fileName)) {
return null;
}
}

return f.getAbsolutePath();
}

/***
* Copies a raw file from resources directory to the app's private data directory.
* @param rawFileName
* @param newFileName
* @return True on success, false otherwise
*/
private boolean copyResourceFileToDataDir(String rawFileName, String newFileName) {
String packageName = cordova.getActivity().getPackageName();

AssetFileDescriptor fd = null;
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
Activity activity = cordova.getActivity();
int id = activity.getResources().getIdentifier(rawFileName, "raw", packageName);
fd = activity.getResources().openRawResourceFd(id);
if (fd == null) {
throw new NotFoundException("Raw file not found: " + rawFileName);
}

in = new BufferedInputStream(fd.createInputStream()); // this is auto-close!
// well we need world readable because that's the only way chrome can read it!
// don't worry about their scare-tactic "oooh security holes" warnings: who cares if other apps can read your videos?
out = new BufferedOutputStream(activity.openFileOutput(newFileName, Context.MODE_WORLD_READABLE));

// copy the file over. it's 2014, we still have to do this by hand?
byte buffer[] = new byte[8192];
while(in.available() > 0) {
int bytesRead = in.read(buffer);

if (bytesRead > 0) {
out.write(buffer, 0, bytesRead);
}
}

in.close();
out.close();

return true;
} catch (NotFoundException e) {
Log.e(TAG, "Failed to copy video file to data dir.", e);
} catch (IOException e) {
Log.e(TAG, "Failed to copy video file to data dir.", e);
}
finally {

try {
if (fd != null) {
fd.close();
}
} catch (IOException e) {
Log.e(TAG, "Failed to fail to copy video file to data dir.", e);
}

try {
if (in != null) {
in.close();
}
} catch (IOException e) {
Log.e(TAG, "Failed to fail to copy video file to data dir.", e);
}

try {
if (out != null) {
out.close();
}
} catch (IOException e) {
Log.e(TAG, "Failed to fail to copy video file to data dir.", e);
}
}

return false;
}
}
}

0 comments on commit fe2fcc2

Please sign in to comment.