Skip to content

Commit

Permalink
Merge pull request #1 from ionic-team/master
Browse files Browse the repository at this point in the history
Merge master
  • Loading branch information
MarkChrisLevy authored Jan 20, 2019
2 parents 45cecda + 215c4ca commit 5afbb5e
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,17 @@ private static int getFieldId(Context context, String assetType, String assetNam
}

public InputStream openFile(String filePath) throws IOException {
File localFile = new File(filePath);
String realPath = filePath.replace(Bridge.CAPACITOR_FILE_START, "");
File localFile = new File(realPath);
return new FileInputStream(localFile);
}

public InputStream openContentUrl(Uri uri) throws IOException {
String realPath = uri.toString().replace(uri.getScheme() + "://" + uri.getHost() + Bridge.CAPACITOR_CONTENT_START, "content:/");

InputStream stream = null;
try {
stream = context.getContentResolver().openInputStream(Uri.parse(uri.toString().replace(Bridge.CAPACITOR_CONTENT_SCHEME_NAME + ":///", "content://")));
stream = context.getContentResolver().openInputStream(Uri.parse(realPath));
} catch (SecurityException e) {
Log.e(LogUtils.getCoreTag(), "Unable to open content URL: " + uri, e);
}
Expand Down
55 changes: 21 additions & 34 deletions android/capacitor/src/main/java/com/getcapacitor/Bridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ public class Bridge {
// The name of the directory we use to look for index.html and the rest of our web assets
public static final String DEFAULT_WEB_ASSET_DIR = "public";
public static final String CAPACITOR_SCHEME_NAME = "http";
public static final String CAPACITOR_FILE_SCHEME_NAME = "capacitor-file";
public static final String CAPACITOR_CONTENT_SCHEME_NAME = "capacitor-content";
public static final String CAPACITOR_FILE_START = "/_capacitor_file_";
public static final String CAPACITOR_CONTENT_START = "/_capacitor_content_";

// Loaded Capacitor config
private JSONObject config = new JSONObject();
Expand Down Expand Up @@ -163,16 +163,18 @@ private void loadWebView() {
appUrlConfig = Config.getString("server.url");
appAllowNavigationConfig = Config.getArray("server.allowNavigation");

ArrayList authorities = new ArrayList<String>();
if (appAllowNavigationConfig != null) {
authorities.addAll(Arrays.asList(appAllowNavigationConfig));
}
String authority = Config.getString("server.hostname", "localhost");
localUrl = CAPACITOR_SCHEME_NAME + "://" + authority + "/";

boolean isLocal = true;
authorities.add(authority);
localUrl = CAPACITOR_SCHEME_NAME + "://" + authority;

if (appUrlConfig != null) {
try {
URL appUrlObject = new URL(appUrlConfig);
authority = appUrlObject.getAuthority();
isLocal = false;
authorities.add(appUrlObject.getAuthority());
} catch (Exception ex) {
}

Expand All @@ -182,7 +184,7 @@ private void loadWebView() {
final boolean html5mode = Config.getBoolean("server.html5mode", true);

// Start the local web server
localServer = new WebViewLocalServer(context, this, getJSInjector(), authority, html5mode, isLocal);
localServer = new WebViewLocalServer(context, this, getJSInjector(), authorities, html5mode);
localServer.hostAssets(DEFAULT_WEB_ASSET_DIR);


Expand All @@ -197,13 +199,6 @@ public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceReque
return localServer.shouldInterceptRequest(request);
}

@Override
public void onPageFinished(WebView view, final String location) {
if (appUrlConfig != null || appAllowNavigationConfig != null) {
injectScriptFile(view, getJSInjector().getScriptString());
}
}

@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri url = request.getUrl();
Expand Down Expand Up @@ -241,8 +236,8 @@ private boolean launchIntent(Uri url) {

public void handleAppUrlLoadError(Exception ex) {
if (ex instanceof SocketTimeoutException) {
Toast.show(getContext(), "Unable to load app. Are you sure the server is running at " + localServer.getAuthority() + "?");
Log.e(LOG_TAG, "Unable to load app. Ensure the server is running at " + localServer.getAuthority() + ", or modify the " +
Toast.show(getContext(), "Unable to load app. Are you sure the server is running at " + appUrl + "?");
Log.e(LOG_TAG, "Unable to load app. Ensure the server is running at " + appUrl + ", or modify the " +
"appUrl setting in capacitor.config.json (make sure to npx cap copy after to commit changes).", ex);
}
}
Expand Down Expand Up @@ -794,20 +789,6 @@ public void run() {
});
}

private void injectScriptFile(WebView view, String script) {

// Base64 encode string before injecting as innerHTML
String encoded = Base64.encodeToString(script.getBytes(), Base64.NO_WRAP);
view.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
// Base64 decode injected javascript
"script.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(script)" +
"})()");
}

private boolean matchHost(String host, String pattern) {
int offset;

Expand All @@ -826,11 +807,17 @@ private boolean matchHost(String host, String pattern) {
}

private boolean matchHosts(String host, String[] patterns) {
for (String pattern: patterns) {
if (matchHost(host, pattern)) {
return true;
if (patterns != null) {
for (String pattern: patterns) {
if (matchHost(host, pattern)) {
return true;
}
}
}
return false;
}

public String getLocalUrl() {
return localUrl;
}
}
10 changes: 5 additions & 5 deletions android/capacitor/src/main/java/com/getcapacitor/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
*/
public class FileUtils {

private static String CapacitorFileScheme = Bridge.CAPACITOR_FILE_SCHEME_NAME + "://";
private static String CapacitorFileScheme = Bridge.CAPACITOR_FILE_START;

public enum Type {
IMAGE("image");
Expand All @@ -52,14 +52,14 @@ public enum Type {
}
}

public static String getPortablePath(Context c, Uri u) {
public static String getPortablePath(Context c, String host, Uri u) {
String path = getFileUrlForUri(c, u);
if (path.startsWith("file://")) {
path = path.replace("file://", CapacitorFileScheme);
path = path.replace("file://", "");
} else if (path.startsWith("/")) {
path = CapacitorFileScheme + path;
path = path;
}
return path;
return host + Bridge.CAPACITOR_FILE_START + path;
}

public static String getFileUrlForUri(final Context context, final Uri uri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -45,24 +46,19 @@
public class WebViewLocalServer {

private final static String capacitorScheme = Bridge.CAPACITOR_SCHEME_NAME;
private final static String capacitorFileScheme = Bridge.CAPACITOR_FILE_SCHEME_NAME;
private final static String capacitorContentScheme = Bridge.CAPACITOR_CONTENT_SCHEME_NAME;
private final static String capacitorFileStart = Bridge.CAPACITOR_FILE_START;
private final static String capacitorContentStart = Bridge.CAPACITOR_CONTENT_START;
private String basePath;

private final UriMatcher uriMatcher;
private final AndroidProtocolHandler protocolHandler;
private final String authority;
// Whether we're serving local files or proxying (for example, when doing livereload on a
// non-local endpoint (will be false in that case)
private final boolean isLocal;
private final ArrayList<String> authorities;
private boolean isAsset;
// Whether to route all requests to paths without extensions back to `index.html`
private final boolean html5mode;
private final JSInjector jsInjector;
private final Bridge bridge;

public String getAuthority() { return authority; }

/**
* A handler that produces responses for paths on the virtual asset server.
* <p>
Expand Down Expand Up @@ -132,12 +128,11 @@ public Map<String, String> getResponseHeaders() {
}
}

WebViewLocalServer(Context context, Bridge bridge, JSInjector jsInjector, String authority, boolean html5mode, boolean isLocal) {
WebViewLocalServer(Context context, Bridge bridge, JSInjector jsInjector, ArrayList<String> authorities, boolean html5mode) {
uriMatcher = new UriMatcher(null);
this.html5mode = html5mode;
this.protocolHandler = new AndroidProtocolHandler(context.getApplicationContext());
this.authority = authority;
this.isLocal = isLocal;
this.authorities = authorities;
this.bridge = bridge;
this.jsInjector = jsInjector;
}
Expand Down Expand Up @@ -169,6 +164,7 @@ private static Uri parseAndVerifyUrl(String url) {
* @return a response if the request URL had a matching handler, null if no handler was found.
*/
public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
Uri loadingUrl = request.getUrl();
PathHandler handler;
synchronized (uriMatcher) {
handler = (PathHandler) uriMatcher.match(request.getUrl());
Expand All @@ -177,22 +173,30 @@ public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
return null;
}

if (request.getUrl().getScheme().equals(capacitorContentScheme) || request.getUrl().getScheme().equals(capacitorFileScheme)) {
if (isLocalFile(loadingUrl)) {
InputStream responseStream = new LollipopLazyInputStream(handler, request);
String mimeType = getMimeType(request.getUrl().getPath(), responseStream);
int statusCode = getStatusCode(responseStream, handler.getStatusCode());
return new WebResourceResponse(mimeType, handler.getEncoding(),
statusCode, handler.getReasonPhrase(), handler.getResponseHeaders(), responseStream);
}

if (this.isLocal) {
if (loadingUrl.toString().startsWith(bridge.getLocalUrl())) {
Log.d(LogUtils.getCoreTag(), "Handling local request: " + request.getUrl().toString());
return handleLocalRequest(request, handler);
} else {
return handleProxyRequest(request, handler);
}
}

private boolean isLocalFile(Uri uri) {
String path = uri.getPath();
if (path.startsWith(capacitorContentStart) || path.startsWith(capacitorFileStart)) {
return true;
}
return false;
}

private WebResourceResponse handleLocalRequest(WebResourceRequest request, PathHandler handler) {
String path = request.getUrl().getPath();

Expand Down Expand Up @@ -379,58 +383,6 @@ public void hostAssets(String assetPath) {
createHostingDetails();
}


/**
* Hosts the application's resources on an https:// URL. Resources
* <code>https://{uuid}.androidplatform.net/res/{resource_type}/{resource_name}</code>.
*
* @return prefixes under which the resources are hosted.
*/
public void hostResources() {
hostResources("/res");
}

/**
* Hosts the application's resources on an https:// URL. Resources
* <code>https://{domain}/{virtualResourcesPath}/{resource_type}/{resource_name}</code>.
*
* @param virtualResourcesPath the path on the local server under which the resources
* should be hosted.
* @return prefixes under which the resources are hosted.
*/
public void hostResources(final String virtualResourcesPath) {
if (virtualResourcesPath.indexOf('*') != -1) {
throw new IllegalArgumentException(
"virtualResourcesPath cannot contain the '*' character.");
}

Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme(capacitorScheme);
uriBuilder.authority(authority);
uriBuilder.path(virtualResourcesPath);
Uri capacitorPrefix = null;

PathHandler handler = new PathHandler() {
@Override
public InputStream handle(Uri url) {
InputStream stream = protocolHandler.openResource(url);
String mimeType = null;
try {
mimeType = URLConnection.guessContentTypeFromStream(stream);
} catch (Exception ex) {
Log.e(LogUtils.getPluginTag("LN"), "Unable to get mime type" + url);
}

return stream;
}
};

uriBuilder.scheme(capacitorScheme);
capacitorPrefix = uriBuilder.build();
register(Uri.withAppendedPath(capacitorPrefix, "**"), handler);

}

/**
* Hosts the application's files on an https:// URL. Files from the basePath
* <code>basePath/...</code> will be available under
Expand All @@ -453,23 +405,22 @@ private void createHostingDetails() {
throw new IllegalArgumentException("assetPath cannot contain the '*' character.");
}

Uri capacitorPrefix = null;

PathHandler handler = new PathHandler() {
@Override
public InputStream handle(Uri url) {
InputStream stream = null;
String path = url.getPath();
if (!isAsset) {
path = basePath + url.getPath();
}
try {
if (url.getScheme().equals(capacitorScheme) && isAsset) {
stream = protocolHandler.openAsset(assetPath + path);
} else if (url.getScheme().equals(capacitorFileScheme) || !isAsset) {
stream = protocolHandler.openFile(path);
} else if (url.getScheme().equals(capacitorContentScheme)) {
if (path.startsWith(capacitorContentStart)) {
stream = protocolHandler.openContentUrl(url);
} else if (path.startsWith(capacitorFileStart) || !isAsset) {
if (!path.startsWith(capacitorFileStart)) {
path = basePath + url.getPath();
}
System.out.println("path "+path);
stream = protocolHandler.openFile(path);
} else {
stream = protocolHandler.openAsset(assetPath + path);
}
} catch (IOException e) {
Log.e(LogUtils.getCoreTag(), "Unable to open asset URL: " + url);
Expand All @@ -480,9 +431,9 @@ public InputStream handle(Uri url) {
}
};

registerUriForScheme(capacitorScheme, handler, authority);
registerUriForScheme(capacitorFileScheme, handler, "");
registerUriForScheme(capacitorContentScheme, handler, "");
for (String authority: authorities) {
registerUriForScheme(capacitorScheme, handler, authority);
}

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ private void returnFileURI(PluginCall call, ExifWrapper exif, Bitmap bitmap, Uri
JSObject ret = new JSObject();
ret.put("exif", exif.toJson());
ret.put("path", newUri.toString());
ret.put("webPath", FileUtils.getPortablePath(getContext(), newUri));
ret.put("webPath", FileUtils.getPortablePath(getContext(), bridge.getLocalUrl(), newUri));
call.resolve(ret);
} catch (IOException ex) {
call.reject(UNABLE_TO_PROCESS_IMAGE, ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private File getDirectory(String directory) {
private File getFileObject(String path, String directory) {
if (directory == null) {
Uri u = Uri.parse(path);
if (u.getScheme().equals("file")) {
if (u.getScheme() == null || u.getScheme().equals("file")) {
return new File(u.getPath());
}
}
Expand Down
8 changes: 4 additions & 4 deletions core/native-bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -508,13 +508,13 @@
return url;
}
if (url.startsWith('/')) {
return 'capacitor-file://' + url;
return window.WEBVIEW_SERVER_URL + '/_capacitor_file_' + url;
}
if (url.startsWith('file://')) {
return url.replace('file', 'capacitor-file');
return window.WEBVIEW_SERVER_URL + url.replace('file://', '/_capacitor_file_');
}
if (url.startsWith('content:')) {
return url.replace('content:', 'capacitor-content:/');
if (url.startsWith('content://')) {
return window.WEBVIEW_SERVER_URL + url.replace('content:/', '/_capacitor_content_');
}
return url;
}
Expand Down
Loading

0 comments on commit 5afbb5e

Please sign in to comment.