-
Notifications
You must be signed in to change notification settings - Fork 394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(Android): Implement ionic-file and ionic-content urls #242
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,12 +48,10 @@ | |
public class WebViewLocalServer { | ||
private static String TAG = "WebViewAssetServer"; | ||
private String basePath; | ||
/** | ||
* capacitorapp.net is reserved by the Ionic team for use in local capacitor apps. | ||
*/ | ||
public final static String knownUnusedAuthority = "capacitorapp.net"; | ||
private final static String httpScheme = "http"; | ||
private final static String httpsScheme = "https"; | ||
private final static String ionicFileScheme = "ionic-file"; | ||
private final static String ionicContentScheme = "ionic-content"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment above re: |
||
|
||
private final UriMatcher uriMatcher; | ||
private final AndroidProtocolHandler protocolHandler; | ||
|
@@ -169,17 +167,11 @@ public Uri getHttpsPrefix() { | |
this.html5mode = html5mode; | ||
this.parser = parser; | ||
this.protocolHandler = new AndroidProtocolHandler(context.getApplicationContext()); | ||
if (authority != null) { | ||
this.authority = authority; | ||
if (authority.startsWith("localhost")) { | ||
this.isLocal = true; | ||
} else { | ||
this.isLocal = false; | ||
} | ||
|
||
} else { | ||
this.authority = authority; | ||
if (authority.startsWith("localhost")) { | ||
this.isLocal = true; | ||
this.authority = UUID.randomUUID().toString() + "" + knownUnusedAuthority; | ||
} else { | ||
this.isLocal = false; | ||
} | ||
} | ||
|
||
|
@@ -244,14 +236,22 @@ public WebResourceResponse shouldInterceptRequest(Uri uri) { | |
|
||
private WebResourceResponse handleLocalRequest(Uri uri, PathHandler handler) { | ||
String path = uri.getPath(); | ||
|
||
if (uri.getScheme().equals(ionicContentScheme) || uri.getScheme().equals(ionicFileScheme)) { | ||
InputStream responseStream = new LollipopLazyInputStream(handler, uri); | ||
String mimeType = getMimeType(path, responseStream); | ||
return createWebResourceResponse(mimeType, handler.getEncoding(), | ||
handler.getStatusCode(), handler.getReasonPhrase(), handler.getResponseHeaders(), responseStream); | ||
} | ||
|
||
if (path.equals("/") || (!uri.getLastPathSegment().contains(".") && html5mode)) { | ||
InputStream stream; | ||
String launchURL = parser.getLaunchUrl(); | ||
String launchFile = launchURL.substring(launchURL.lastIndexOf("/") + 1, launchURL.length()); | ||
try { | ||
String startPath = this.basePath + "/" + launchFile; | ||
if (isAsset) { | ||
stream = protocolHandler.openAsset(startPath, ""); | ||
stream = protocolHandler.openAsset(startPath); | ||
} else { | ||
stream = protocolHandler.openFile(startPath); | ||
} | ||
|
@@ -267,15 +267,10 @@ private WebResourceResponse handleLocalRequest(Uri uri, PathHandler handler) { | |
|
||
int periodIndex = path.lastIndexOf("."); | ||
if (periodIndex >= 0) { | ||
String ext = path.substring(path.lastIndexOf("."), path.length()); | ||
|
||
InputStream responseStream = new LollipopLazyInputStream(handler, uri); | ||
InputStream stream = responseStream; | ||
|
||
String mimeType = getMimeType(path, stream); | ||
|
||
String mimeType = getMimeType(path, responseStream); | ||
return createWebResourceResponse(mimeType, handler.getEncoding(), | ||
handler.getStatusCode(), handler.getReasonPhrase(), handler.getResponseHeaders(), stream); | ||
handler.getStatusCode(), handler.getReasonPhrase(), handler.getResponseHeaders(), responseStream); | ||
} | ||
|
||
return null; | ||
|
@@ -375,31 +370,12 @@ void register(Uri uri, PathHandler handler) { | |
* | ||
* @param assetPath the local path in the application's asset folder which will be made | ||
* available by the server (for example "/www"). | ||
* @return prefixes under which the assets are hosted. | ||
*/ | ||
public AssetHostingDetails hostAssets(String assetPath) { | ||
return hostAssets(authority, assetPath, "", true, true); | ||
public void hostAssets(String assetPath) { | ||
hostAssets(authority, assetPath); | ||
} | ||
|
||
|
||
/** | ||
* Hosts the application's assets on an http(s):// URL. Assets from the local path | ||
* <code>assetPath/...</code> will be available under | ||
* <code>http(s)://{uuid}.androidplatform.net/{virtualAssetPath}/...</code>. | ||
* | ||
* @param assetPath the local path in the application's asset folder which will be made | ||
* available by the server (for example "/www"). | ||
* @param virtualAssetPath the path on the local server under which the assets should be hosted. | ||
* @param enableHttp whether to enable hosting using the http scheme. | ||
* @param enableHttps whether to enable hosting using the https scheme. | ||
* @return prefixes under which the assets are hosted. | ||
*/ | ||
public AssetHostingDetails hostAssets(final String assetPath, final String virtualAssetPath, | ||
boolean enableHttp, boolean enableHttps) { | ||
return hostAssets(authority, assetPath, virtualAssetPath, enableHttp, | ||
enableHttps); | ||
} | ||
|
||
/** | ||
* Hosts the application's assets on an http(s):// URL. Assets from the local path | ||
* <code>assetPath/...</code> will be available under | ||
|
@@ -408,39 +384,39 @@ public AssetHostingDetails hostAssets(final String assetPath, final String virtu | |
* @param domain custom domain on which the assets should be hosted (for example "example.com"). | ||
* @param assetPath the local path in the application's asset folder which will be made | ||
* available by the server (for example "/www"). | ||
* @param virtualAssetPath the path on the local server under which the assets should be hosted. | ||
* @param enableHttp whether to enable hosting using the http scheme. | ||
* @param enableHttps whether to enable hosting using the https scheme. | ||
* @return prefixes under which the assets are hosted. | ||
*/ | ||
public AssetHostingDetails hostAssets(final String domain, | ||
final String assetPath, final String virtualAssetPath, | ||
boolean enableHttp, boolean enableHttps) { | ||
public void hostAssets(final String domain, | ||
final String assetPath) { | ||
this.isAsset = true; | ||
this.basePath = assetPath; | ||
Uri.Builder uriBuilder = new Uri.Builder(); | ||
uriBuilder.scheme(httpScheme); | ||
uriBuilder.authority(domain); | ||
uriBuilder.path(virtualAssetPath); | ||
|
||
createHostingDetails(); | ||
} | ||
|
||
private void createHostingDetails() { | ||
final String assetPath = this.basePath; | ||
|
||
if (assetPath.indexOf('*') != -1) { | ||
throw new IllegalArgumentException("assetPath cannot contain the '*' character."); | ||
} | ||
if (virtualAssetPath.indexOf('*') != -1) { | ||
throw new IllegalArgumentException( | ||
"virtualAssetPath cannot contain the '*' character."); | ||
} | ||
|
||
Uri httpPrefix = null; | ||
Uri httpsPrefix = null; | ||
|
||
PathHandler handler = new PathHandler() { | ||
@Override | ||
public InputStream handle(Uri url) { | ||
InputStream stream; | ||
String path = url.getPath().replaceFirst(virtualAssetPath, assetPath); | ||
InputStream stream = null; | ||
String path = url.getPath(); | ||
if (!isAsset) { | ||
path = basePath + url.getPath(); | ||
} | ||
try { | ||
stream = protocolHandler.openAsset(path, assetPath); | ||
if ((url.getScheme().equals(httpScheme) || url.getScheme().equals(httpsScheme))&& isAsset) { | ||
stream = protocolHandler.openAsset(assetPath + path); | ||
} else if (url.getScheme().equals(ionicFileScheme) || !isAsset) { | ||
stream = protocolHandler.openFile(path); | ||
} else if (url.getScheme().equals(ionicContentScheme)) { | ||
stream = protocolHandler.openContentUrl(url); | ||
} | ||
} catch (IOException e) { | ||
Log.e(TAG, "Unable to open asset URL: " + url); | ||
return null; | ||
|
@@ -450,18 +426,22 @@ public InputStream handle(Uri url) { | |
} | ||
}; | ||
|
||
if (enableHttp) { | ||
httpPrefix = uriBuilder.build(); | ||
register(Uri.withAppendedPath(httpPrefix, "/"), handler); | ||
register(Uri.withAppendedPath(httpPrefix, "**"), handler); | ||
} | ||
if (enableHttps) { | ||
uriBuilder.scheme(httpsScheme); | ||
httpsPrefix = uriBuilder.build(); | ||
register(Uri.withAppendedPath(httpsPrefix, "/"), handler); | ||
register(Uri.withAppendedPath(httpsPrefix, "**"), handler); | ||
} | ||
return new AssetHostingDetails(httpPrefix, httpsPrefix); | ||
registerUriForScheme(httpScheme, handler, authority); | ||
registerUriForScheme(httpsScheme, handler, authority); | ||
registerUriForScheme(ionicFileScheme, handler, ""); | ||
registerUriForScheme(ionicContentScheme, handler, ""); | ||
|
||
} | ||
|
||
private void registerUriForScheme(String scheme, PathHandler handler, String authority) { | ||
Uri.Builder uriBuilder = new Uri.Builder(); | ||
uriBuilder.scheme(scheme); | ||
uriBuilder.authority(authority); | ||
uriBuilder.path(""); | ||
Uri uriPrefix = uriBuilder.build(); | ||
|
||
register(Uri.withAppendedPath(uriPrefix, "/"), handler); | ||
register(Uri.withAppendedPath(uriPrefix, "**"), handler); | ||
} | ||
|
||
/** | ||
|
@@ -553,62 +533,11 @@ public InputStream handle(Uri url) { | |
* | ||
* @param basePath the local path in the application's data folder which will be made | ||
* available by the server (for example "/www"). | ||
* @return prefixes under which the assets are hosted. | ||
*/ | ||
public AssetHostingDetails hostFiles(String basePath) { | ||
return hostFiles(basePath, true, true); | ||
} | ||
|
||
public AssetHostingDetails hostFiles(final String basePath, boolean enableHttp, | ||
boolean enableHttps) { | ||
public void hostFiles(final String basePath) { | ||
this.isAsset = false; | ||
this.basePath = basePath; | ||
Uri.Builder uriBuilder = new Uri.Builder(); | ||
uriBuilder.scheme(httpScheme); | ||
uriBuilder.authority(authority); | ||
uriBuilder.path(""); | ||
|
||
Uri httpPrefix = null; | ||
Uri httpsPrefix = null; | ||
|
||
PathHandler handler = new PathHandler() { | ||
@Override | ||
public InputStream handle(Uri url) { | ||
InputStream stream; | ||
try { | ||
if (url.getPath().startsWith("/_file_/")) { | ||
stream = protocolHandler.openFile( url.getPath().replace("/_file_/", "")); | ||
} else { | ||
stream = protocolHandler.openFile(basePath + url.getPath()); | ||
} | ||
} catch (IOException e) { | ||
Log.e(TAG, "Unable to open asset URL: " + url); | ||
return null; | ||
} | ||
|
||
String mimeType = null; | ||
try { | ||
mimeType = URLConnection.guessContentTypeFromStream(stream); | ||
} catch (Exception ex) { | ||
Log.e(TAG, "Unable to get mime type" + url); | ||
} | ||
|
||
return stream; | ||
} | ||
}; | ||
|
||
if (enableHttp) { | ||
httpPrefix = uriBuilder.build(); | ||
register(Uri.withAppendedPath(httpPrefix, "/"), handler); | ||
register(Uri.withAppendedPath(httpPrefix, "**"), handler); | ||
} | ||
if (enableHttps) { | ||
uriBuilder.scheme(httpsScheme); | ||
httpsPrefix = uriBuilder.build(); | ||
register(Uri.withAppendedPath(httpsPrefix, "/"), handler); | ||
register(Uri.withAppendedPath(httpsPrefix, "**"), handler); | ||
} | ||
return new AssetHostingDetails(httpPrefix, httpsPrefix); | ||
createHostingDetails(); | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,17 +5,14 @@ var WebView = { | |
if (!url) { | ||
return url; | ||
} | ||
if (!url.startsWith('file://')) { | ||
return url; | ||
} | ||
if (window.WEBVIEW_SERVER_URL.startsWith('ionic://')) { | ||
return url.replace('file', 'ionic-asset'); | ||
if (url.startsWith('file://')) { | ||
return url.replace('file', 'ionic-file');; | ||
} | ||
url = url.substr(7); // len("file://") == 7 | ||
if (url.length === 0 || url[0] !== '/') { // ensure the new URL starts with / | ||
url = '/' + url; | ||
if (url.startsWith('content://')) { | ||
return url.replace('content://', 'ionic-content:///');; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Watch for hard coded strings here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed on last commit |
||
} | ||
return window.WEBVIEW_SERVER_URL + '/_file_' + url; | ||
|
||
return url; | ||
}, | ||
setServerBasePath: function(path) { | ||
exec(null, null, 'IonicWebView', 'setServerBasePath', [path]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure
ionic-content
is the right sheme here? Technically this is just a generic webview plugin and isn't ionic related. Would be the same question for Capacitor. How aboutapp-content://
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to
app-content
andapp-file
, but as it's "Ionic WebView" I think it makes sense to use ionic as prefix on the schemes.This schemes are only understood by the WebView, and we provide the logic to convert the file/content urls to something the WebView understands.
Specially in Capacitor I think it makes sense to have them like
capacitor-*