Commit 9c30ed77 authored by pichillilorenzo's avatar pichillilorenzo

updated android code, code cleanup, added new API

parent e0982e36
This diff is collapsed.
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:theme="@style/AppTheme"> android:theme="@style/AppTheme" >
<activity android:name=".WebViewActivity"></activity> <activity android:name=".WebViewActivity" android:configChanges="orientation|screenSize"></activity>
</application> </application>
</manifest> </manifest>
\ No newline at end of file
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package com.pichillilorenzo.flutter_inappbrowser;
import android.app.Dialog;
import android.content.Context;
public class InAppBrowserDialog extends Dialog {
Context context;
InAppBrowser inAppBrowser = null;
public InAppBrowserDialog(Context context, int theme) {
super(context, theme);
this.context = context;
}
public void setInAppBroswer(InAppBrowser browser) {
this.inAppBrowser = browser;
}
public void onBackPressed () {
if (this.inAppBrowser == null) {
this.dismiss();
} else {
// better to go through the in inAppBrowser
// because it does a clean up
if (this.inAppBrowser.hardwareBack() && this.inAppBrowser.canGoBack()) {
this.inAppBrowser.goBack();
} else {
this.inAppBrowser.closeDialog();
}
}
}
}
\ No newline at end of file
...@@ -13,23 +13,21 @@ public class InAppBrowserOptions { ...@@ -13,23 +13,21 @@ public class InAppBrowserOptions {
boolean clearCache = false; boolean clearCache = false;
boolean clearSessionCache = false; boolean clearSessionCache = false;
String userAgent = "";
boolean spinner = true; boolean spinner = true;
boolean hidden = false; boolean hidden = false;
boolean toolbarTop = true; boolean toolbarTop = true;
String toolbarTopColor = "toolbarTopColor"; String toolbarTopColor = "toolbarTopColor";
boolean hideUrlBar = false; boolean hideUrlBar = false;
boolean enableViewportScale = false; boolean mediaPlaybackRequiresUserGesture = true;
boolean keyboardDisplayRequiresUserAction = true;
boolean suppressesIncrementalRendering = false;
boolean allowsAirPlayForMediaPlayback = true;
boolean mediaTypesRequiringUserActionForPlayback = true;
boolean allowsBackForwardNavigationGestures = true;
boolean allowsLinkPreview = true;
boolean ignoresViewportScaleLimits = false;
boolean allowsInlineMediaPlayback = false;
boolean allowsPictureInPictureMediaPlayback = true;
boolean javaScriptCanOpenWindowsAutomatically = false; boolean javaScriptCanOpenWindowsAutomatically = false;
boolean javaScriptEnabled = true; boolean javaScriptEnabled = true;
boolean builtInZoomControls = false;
boolean supportZoom = true;
boolean databaseEnabled = true;
boolean domStorageEnabled = true;
boolean useWideViewPort = true;
boolean safeBrowsingEnabled = true;
@RequiresApi(api = Build.VERSION_CODES.KITKAT) @RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void parse(HashMap<String, Object> options) { public void parse(HashMap<String, Object> options) {
...@@ -42,7 +40,6 @@ public class InAppBrowserOptions { ...@@ -42,7 +40,6 @@ public class InAppBrowserOptions {
// silent // silent
} }
} }
} }
public HashMap<String, Object> getHashMap() { public HashMap<String, Object> getHashMap() {
......
package com.pichillilorenzo.flutter_inappbrowser;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
public class InAppBrowserWebChromeClient extends WebChromeClient {
protected static final String LOG_TAG = "IABWebChromeClient";
private WebViewActivity activity;
private ValueCallback<Uri[]> mUploadMessageArray;
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE=1;
public InAppBrowserWebChromeClient(WebViewActivity activity) {
super();
this.activity = activity;
}
@Override
public void onProgressChanged(WebView view, int progress) {
if (activity.progressBar != null) {
activity.progressBar.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
activity.progressBar.setProgress(progress, true);
}
else {
activity.progressBar.setProgress(progress);
}
if (progress == 100) {
activity.progressBar.setVisibility(View.GONE);
}
}
super.onProgressChanged(view, progress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (activity.getSupportActionBar() != null)
activity.getSupportActionBar().setTitle(title);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
}
//The undocumented magic method override
//Eclipse will swear at you if you try to put @Override here
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i,"File Chooser"), FILECHOOSER_RESULTCODE);
}
// For Android 3.0+
public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
activity.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE);
}
//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult( Intent.createChooser( i, "File Chooser" ), FILECHOOSER_RESULTCODE );
}
//For Android 5.0+
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams){
if(mUploadMessageArray != null){
mUploadMessageArray.onReceiveValue(null);
}
mUploadMessageArray = filePathCallback;
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent[] intentArray;
intentArray = new Intent[0];
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
activity.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
return true;
}
}
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package com.pichillilorenzo.flutter_inappbrowser; package com.pichillilorenzo.flutter_inappbrowser;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.util.Log;
import android.webkit.CookieManager; import android.webkit.CookieManager;
import android.webkit.CookieSyncManager; import android.webkit.CookieSyncManager;
import android.webkit.HttpAuthHandler; import android.webkit.HttpAuthHandler;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.widget.EditText;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.MethodChannel; public class InAppBrowserWebViewClient extends WebViewClient {
/** protected static final String LOG_TAG = "IABWebViewClient";
* The webview client receives notifications about appView private WebViewActivity activity;
*/
public class InAppBrowserClient extends WebViewClient {
protected static final String LOG_TAG = "InAppBrowser"; public InAppBrowserWebViewClient(WebViewActivity activity) {
private static final String LOAD_START_EVENT = "loadstart"; super();
private static final String LOAD_STOP_EVENT = "loadstop";
private static final String LOAD_ERROR_EVENT = "loaderror";
private String[] allowedSchemes;
private EditText edittext;
private Activity activity;
private final MethodChannel channel;
/**
* Constructor.
*
* @param mEditText
* @param activity
*/
public InAppBrowserClient(EditText mEditText, Activity activity, MethodChannel channel) {
this.edittext = mEditText;
this.activity = activity; this.activity = activity;
this.channel = channel;
} }
/**
* Override the URL that should be loaded
*
* This handles a small subset of all the URIs that would be encountered.
*
* @param webView
* @param url
*/
@Override @Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) { public boolean shouldOverrideUrlLoading(WebView webView, String url) {
if (url.startsWith(WebView.SCHEME_TEL)) { if (url.startsWith(WebView.SCHEME_TEL)) {
try { try {
Intent intent = new Intent(Intent.ACTION_DIAL); Intent intent = new Intent(Intent.ACTION_DIAL);
...@@ -88,7 +35,8 @@ public class InAppBrowserClient extends WebViewClient { ...@@ -88,7 +35,8 @@ public class InAppBrowserClient extends WebViewClient {
} catch (android.content.ActivityNotFoundException e) { } catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
} }
} else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:") || url.startsWith("intent:")) { }
else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:") || url.startsWith("intent:")) {
try { try {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url)); intent.setData(Uri.parse(url));
...@@ -104,7 +52,7 @@ public class InAppBrowserClient extends WebViewClient { ...@@ -104,7 +52,7 @@ public class InAppBrowserClient extends WebViewClient {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
// Get address // Get address
String address = null; String address;
int parmIndex = url.indexOf('?'); int parmIndex = url.indexOf('?');
if (parmIndex == -1) { if (parmIndex == -1) {
address = url.substring(4); address = url.substring(4);
...@@ -129,25 +77,8 @@ public class InAppBrowserClient extends WebViewClient { ...@@ -129,25 +77,8 @@ public class InAppBrowserClient extends WebViewClient {
Log.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString()); Log.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
} }
} }
// Test for whitelisted custom scheme names like mycoolapp:// or twitteroauthresponse:// (Twitter Oauth Response) else {
else if (!url.startsWith("http:") && !url.startsWith("https:") && url.matches("^[A-Za-z0-9+.-]*://.*?$")) { return super.shouldOverrideUrlLoading(webView, url);
if (allowedSchemes == null) {
String allowed = activity.getPreferences(0).getString("AllowedSchemes", null);
if(allowed != null) {
allowedSchemes = allowed.split(",");
}
}
if (allowedSchemes != null) {
for (String scheme : allowedSchemes) {
if (url.startsWith(scheme)) {
Map<String, Object> obj = new HashMap<>();
obj.put("type", "customscheme");
obj.put("url", url);
channel.invokeMethod("customscheme", obj);
return true;
}
}
}
} }
return false; return false;
...@@ -164,27 +95,16 @@ public class InAppBrowserClient extends WebViewClient { ...@@ -164,27 +95,16 @@ public class InAppBrowserClient extends WebViewClient {
@Override @Override
public void onPageStarted(WebView view, String url, Bitmap favicon) { public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon); super.onPageStarted(view, url, favicon);
String newloc = "";
if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
newloc = url;
}
else
{
// Assume that everything is HTTP at this point, because if we don't specify,
// it really should be. Complain loudly about this!!!
Log.e(LOG_TAG, "Possible Uncaught/Unknown URI");
newloc = "http://" + url;
}
// Update the UI if we haven't already activity.isLoading = true;
if (!newloc.equals(edittext.getText().toString())) {
edittext.setText(newloc); if (activity.searchView != null && !url.equals(activity.searchView.getQuery().toString())) {
activity.searchView.setQuery(url, false);
} }
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("type", LOAD_START_EVENT); obj.put("url", url);
obj.put("url", newloc); InAppBrowser.channel.invokeMethod("loadstart", obj);
channel.invokeMethod(LOAD_START_EVENT, obj);
} }
...@@ -192,6 +112,8 @@ public class InAppBrowserClient extends WebViewClient { ...@@ -192,6 +112,8 @@ public class InAppBrowserClient extends WebViewClient {
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url); super.onPageFinished(view, url);
activity.isLoading = false;
// CB-10395 InAppBrowser's WebView not storing cookies reliable to local device storage // CB-10395 InAppBrowser's WebView not storing cookies reliable to local device storage
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush(); CookieManager.getInstance().flush();
...@@ -204,20 +126,20 @@ public class InAppBrowserClient extends WebViewClient { ...@@ -204,20 +126,20 @@ public class InAppBrowserClient extends WebViewClient {
view.requestFocus(); view.requestFocus();
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("type", LOAD_STOP_EVENT);
obj.put("url", url); obj.put("url", url);
channel.invokeMethod(LOAD_STOP_EVENT, obj); InAppBrowser.channel.invokeMethod("loadstop", obj);
} }
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl); super.onReceivedError(view, errorCode, description, failingUrl);
activity.isLoading = false;
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("type", LOAD_ERROR_EVENT);
obj.put("url", failingUrl); obj.put("url", failingUrl);
obj.put("code", errorCode); obj.put("code", errorCode);
obj.put("message", description); obj.put("message", description);
channel.invokeMethod(LOAD_ERROR_EVENT, obj); InAppBrowser.channel.invokeMethod("loaderror", obj);
} }
/** /**
......
package com.pichillilorenzo.flutter_inappbrowser; package com.pichillilorenzo.flutter_inappbrowser;
import android.app.SearchManager; import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
...@@ -8,37 +9,98 @@ import android.support.annotation.RequiresApi; ...@@ -8,37 +9,98 @@ import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.webkit.CookieManager;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView; import android.webkit.WebView;
import android.widget.EditText; import android.widget.ProgressBar;
import android.widget.SearchView; import android.widget.SearchView;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
public class WebViewActivity extends AppCompatActivity { public class WebViewActivity extends AppCompatActivity {
WebView wv; WebView webView;
InAppBrowserWebViewClient inAppBrowserWebViewClient;
InAppBrowserWebChromeClient inAppBrowserWebChromeClient;
SearchView searchView; SearchView searchView;
InAppBrowserOptions options; InAppBrowserOptions options;
ProgressBar progressBar;
public boolean isLoading = false;
@RequiresApi(api = Build.VERSION_CODES.KITKAT) @RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
webView = findViewById(R.id.webView);
progressBar = findViewById(R.id.progressBar);
progressBar.setMax(100);
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
String url = b.getString("url"); String url = b.getString("url");
options = new InAppBrowserOptions(); options = new InAppBrowserOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options")); options.parse((HashMap<String, Object>) b.getSerializable("options"));
InAppBrowser.webViewActivity = this;
setContentView(R.layout.activity_web_view); prepareWebView();
wv = (WebView) findViewById(R.id.webView);
InAppBrowser.webViewActivity = this; webView.loadUrl(url);
}
public void prepareWebView() {
inAppBrowserWebChromeClient = new InAppBrowserWebChromeClient(this);
webView.setWebChromeClient(inAppBrowserWebChromeClient);
inAppBrowserWebViewClient = new InAppBrowserWebViewClient(this);
webView.setWebViewClient(inAppBrowserWebViewClient);
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(options.javaScriptEnabled);
settings.setJavaScriptCanOpenWindowsAutomatically(options.javaScriptCanOpenWindowsAutomatically);
settings.setBuiltInZoomControls(options.builtInZoomControls);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
settings.setSafeBrowsingEnabled(options.safeBrowsingEnabled);
}
settings.setMediaPlaybackRequiresUserGesture(options.mediaPlaybackRequiresUserGesture);
settings.setDatabaseEnabled(options.databaseEnabled);
settings.setDomStorageEnabled(options.domStorageEnabled);
if (!options.userAgent.isEmpty()) {
settings.setUserAgentString(options.userAgent);
}
wv.loadUrl(url); if (options.clearCache) {
getSupportActionBar().setTitle(wv.getTitle()); clearCache();
} else if (options.clearSessionCache) {
CookieManager.getInstance().removeSessionCookie();
}
// Enable Thirdparty Cookies on >=Android 5.0 device
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(webView,true);
}
settings.setLoadWithOverviewMode(true);
settings.setUseWideViewPort(options.useWideViewPort);
settings.setSupportZoom(options.supportZoom);
} }
...@@ -49,13 +111,13 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -49,13 +111,13 @@ public class WebViewActivity extends AppCompatActivity {
inflater.inflate(R.menu.menu_main, menu); inflater.inflate(R.menu.menu_main, menu);
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setQuery(wv.getUrl(), false); searchView.setQuery(webView.getUrl(), false);
getSupportActionBar().setTitle(wv.getTitle()); getSupportActionBar().setTitle(webView.getTitle());
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override @Override
public boolean onQueryTextSubmit(String query) { public boolean onQueryTextSubmit(String query) {
wv.loadUrl(query); webView.loadUrl(query);
return false; return false;
} }
...@@ -68,4 +130,131 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -68,4 +130,131 @@ public class WebViewActivity extends AppCompatActivity {
return true; return true;
} }
public void loadUrl (String url, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url);
}
else {
result.error("Cannot load url", "", null);
}
}
public void loadUrl (String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url, headers);
}
else {
result.error("Cannot load url", "", null);
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
// @TargetApi(Build.VERSION_CODES.KITKAT)
// void eval(MethodCall call, final MethodChannel.Result result) {
// String code = call.argument("code");
//
// webView.evaluateJavascript(code, new ValueCallback<String>() {
// @Override
// public void onReceiveValue(String value) {
// result.success(value);
// }
// });
// }
public void close() {
finish();
}
public void reload() {
if (webView != null)
webView.reload();
}
public void goBack() {
if (webView != null && canGoBack())
webView.goBack();
}
public void goForward() {
if (webView != null && canGoForward())
webView.goForward();
}
public boolean canGoBack() {
return webView.canGoBack();
}
public boolean canGoForward() {
return webView.canGoForward();
}
public void hide() {
if (webView != null)
webView.setVisibility(View.INVISIBLE);
}
public void show() {
if (webView != null)
webView.setVisibility(View.VISIBLE);
}
public boolean isLoading() {
if (webView != null)
return isLoading;
return false;
}
public void stopLoading(){
if (webView != null)
webView.stopLoading();
}
private void clearCookies() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean aBoolean) {
}
});
} else {
CookieManager.getInstance().removeAllCookie();
}
}
private void clearCache() {
webView.clearCache(true);
clearCookies();
webView.clearFormData();
}
public void goBackButtonClicked(MenuItem item) {
goBack();
}
public void goForwardButtonClicked(MenuItem item) {
goForward();
}
public void shareButtonClicked(MenuItem item) {
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
startActivity(Intent.createChooser(share, "Share"));
}
public void reloadButtonClicked(MenuItem item) {
reload();
}
public void closeButtonClicked(MenuItem item) {
close();
}
} }
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>
...@@ -8,6 +8,15 @@ ...@@ -8,6 +8,15 @@
android:orientation="vertical" android:orientation="vertical"
tools:context=".WebViewActivity"> tools:context=".WebViewActivity">
<ProgressBar
android:id="@+id/progressBar"
style="@android:style/Widget.Holo.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_gravity="top"
android:progress="0"
android:visibility="gone" />
<WebView <WebView
android:id="@+id/webView" android:id="@+id/webView"
android:layout_width="match_parent" android:layout_width="match_parent"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:appcompat="http://schemas.android.com/apk/res-auto" xmlns:appcompat="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/tools"> xmlns:app="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".WebViewActivity">
<item <item
android:id="@+id/action_settings" android:id="@+id/action_go_back"
android:title="@string/action_settings" android:onClick="goBackButtonClicked"
android:orderInCategory="100" android:orderInCategory="100"
android:title="@string/action_go_back"
app:showAsAction="never" />
<item
android:id="@+id/action_go_forward"
android:title="@string/action_go_forward"
android:orderInCategory="101"
android:onClick="goForwardButtonClicked"
app:showAsAction="never"/>
<item
android:id="@+id/action_share"
android:title="@string/action_share"
android:orderInCategory="102"
android:onClick="shareButtonClicked"
app:showAsAction="never"/>
<item
android:id="@+id/action_reload"
android:title="@string/action_reload"
android:orderInCategory="103"
android:onClick="reloadButtonClicked"
app:showAsAction="never"/>
<item
android:id="@+id/action_close"
android:title="@string/action_close"
android:orderInCategory="104"
android:onClick="closeButtonClicked"
app:showAsAction="never"/> app:showAsAction="never"/>
<item <item
android:id="@+id/menu_search" android:id="@+id/menu_search"
android:title="@string/menu_search" android:title="@string/menu_search"
appcompat:actionViewClass="android.widget.SearchView" appcompat:actionViewClass="android.widget.SearchView"
appcompat:showAsAction="always" /> appcompat:showAsAction="ifRoom" />
</menu> </menu>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="action_settings">Settings</string> <string name="action_go_back">Go Back</string>
<string name="action_go_forward">Go Forward</string>
<string name="action_reload">Reload</string>
<string name="action_share">Share</string>
<string name="action_close">Close</string>
<string name="menu_search">Search</string> <string name="menu_search">Search</string>
</resources> </resources>
...@@ -9,10 +9,11 @@ import Foundation ...@@ -9,10 +9,11 @@ import Foundation
@objcMembers @objcMembers
public class InAppBrowserOptions: NSObject { public class InAppBrowserOptions: NSObject {
var closeButtonCaption = "" var closeButtonCaption = ""
var closeButtonColor = "" var closeButtonColor = ""
var clearCache = false var clearCache = false
var clearSessionCache = false var userAgent = ""
var spinner = true var spinner = true
var hidden = false var hidden = false
var disallowOverScroll = false var disallowOverScroll = false
...@@ -38,6 +39,10 @@ public class InAppBrowserOptions: NSObject { ...@@ -38,6 +39,10 @@ public class InAppBrowserOptions: NSObject {
var javaScriptCanOpenWindowsAutomatically = false var javaScriptCanOpenWindowsAutomatically = false
var javaScriptEnabled = true var javaScriptEnabled = true
override init(){
super.init()
}
public func parse(options: [String: Any]) { public func parse(options: [String: Any]) {
for (key, value) in options { for (key, value) in options {
if self.value(forKey: key) != nil { if self.value(forKey: key) != nil {
......
...@@ -78,6 +78,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -78,6 +78,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
var currentURL: URL? var currentURL: URL?
var tmpWindow: UIWindow? var tmpWindow: UIWindow?
var browserOptions: InAppBrowserOptions? var browserOptions: InAppBrowserOptions?
var initHeaders: [String: String]?
required init(coder aDecoder: NSCoder) { required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)! super.init(coder: aDecoder)!
...@@ -117,7 +118,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -117,7 +118,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
spinner.isHidden = false spinner.isHidden = false
spinner.stopAnimating() spinner.stopAnimating()
navigate(to: self.currentURL!) loadUrl(url: self.currentURL!, headers: self.initHeaders)
} }
// Prevent crashes on closing windows // Prevent crashes on closing windows
...@@ -223,6 +224,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -223,6 +224,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
} else { } else {
// Fallback on earlier versions // Fallback on earlier versions
self.webView.configuration.mediaPlaybackRequiresUserAction = true
} }
} }
...@@ -248,6 +250,33 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -248,6 +250,33 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
} }
self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = (browserOptions?.javaScriptCanOpenWindowsAutomatically)! self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = (browserOptions?.javaScriptCanOpenWindowsAutomatically)!
self.webView.configuration.preferences.javaScriptEnabled = (browserOptions?.javaScriptEnabled)! self.webView.configuration.preferences.javaScriptEnabled = (browserOptions?.javaScriptEnabled)!
if ((browserOptions?.userAgent)! != "") {
if #available(iOS 9.0, *) {
self.webView.customUserAgent = (browserOptions?.userAgent)!
} else {
// Fallback on earlier versions
}
}
if (browserOptions?.clearCache)! {
clearCache()
}
}
func loadUrl(url: URL, headers: [String: String]?) {
var request = URLRequest(url: url)
currentURL = url
updateUrlTextField(url: (currentURL?.absoluteString)!)
if headers != nil {
for (key, value) in headers! {
request.setValue(value, forHTTPHeaderField: key)
}
}
webView.load(request)
} }
// Load user requested url // Load user requested url
...@@ -270,6 +299,24 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -270,6 +299,24 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
webView.frame = frame webView.frame = frame
} }
func clearCache() {
if #available(iOS 9.0, *) {
//let websiteDataTypes = NSSet(array: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache])
let date = NSDate(timeIntervalSince1970: 0)
WKWebsiteDataStore.default().removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: date as Date, completionHandler:{ })
} else {
var libraryPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, false).first!
libraryPath += "/Cookies"
do {
try FileManager.default.removeItem(atPath: libraryPath)
} catch {
print("can't clear cache")
}
URLCache.shared.removeAllCachedResponses()
}
}
@objc func reload () { @objc func reload () {
webView.reload() webView.reload()
} }
...@@ -305,22 +352,23 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -305,22 +352,23 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
}) })
} }
func navigate(to url: URL) { func canGoBack() -> Bool {
let request = URLRequest(url: url) return webView.canGoBack
currentURL = url
updateUrlTextField(url: (currentURL?.absoluteString)!)
webView.load(request)
} }
@objc func goBack() { @objc func goBack() {
if webView.canGoBack { if canGoBack() {
webView.goBack() webView.goBack()
updateUrlTextField(url: (webView?.url?.absoluteString)!) updateUrlTextField(url: (webView?.url?.absoluteString)!)
} }
} }
func canGoForward() -> Bool {
return webView.canGoForward
}
@objc func goForward() { @objc func goForward() {
if webView.canGoForward { if canGoForward() {
webView.goForward() webView.goForward()
updateUrlTextField(url: (webView?.url?.absoluteString)!) updateUrlTextField(url: (webView?.url?.absoluteString)!)
} }
......
...@@ -47,6 +47,9 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -47,6 +47,9 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
case "open": case "open":
self.open(arguments: arguments!, result: result) self.open(arguments: arguments!, result: result)
break break
case "loadUrl":
self.loadUrl(arguments: arguments!, result: result)
break
case "close": case "close":
self.close() self.close()
result(true) result(true)
...@@ -67,12 +70,18 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -67,12 +70,18 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
self.webViewController?.goBack() self.webViewController?.goBack()
result(true) result(true)
break break
case "canGoBack":
result(self.webViewController?.canGoBack() ?? false)
break
case "goForward": case "goForward":
self.webViewController?.goForward() self.webViewController?.goForward()
result(true) result(true)
break break
case "canGoForward":
result(self.webViewController?.canGoForward() ?? false)
break
case "isLoading": case "isLoading":
result(self.webViewController?.webView.isLoading == true) result((self.webViewController?.webView.isLoading ?? false) == true)
break break
case "stopLoading": case "stopLoading":
self.webViewController?.webView.stopLoading() self.webViewController?.webView.stopLoading()
...@@ -117,6 +126,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -117,6 +126,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
public func open(arguments: NSDictionary, result: @escaping FlutterResult) { public func open(arguments: NSDictionary, result: @escaping FlutterResult) {
let url: String? = (arguments["url"] as? String)! let url: String? = (arguments["url"] as? String)!
let headers = (arguments["headers"] as? [String: String])!
var target: String? = (arguments["target"] as? String)! var target: String? = (arguments["target"] as? String)!
target = target != nil ? target : "_self" target = target != nil ? target : "_self"
let options = (arguments["options"] as? [String: Any])! let options = (arguments["options"] as? [String: Any])!
...@@ -129,45 +139,42 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -129,45 +139,42 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
} }
if (target == "_self" || target == "_target") { if (target == "_self" || target == "_target") {
openIn(inAppBrowser: absoluteUrl!, withOptions: options) open(inAppBrowser: absoluteUrl!, headers: headers, withOptions: options)
} }
else if (target == "_system") { else if (target == "_system") {
open(inSystem: absoluteUrl!) open(inSystem: absoluteUrl!)
} }
else { else {
// anything else // anything else
openIn(inAppBrowser: absoluteUrl!, withOptions: options) open(inAppBrowser: absoluteUrl!, headers: headers,withOptions: options)
} }
} }
else { else {
print("url is empty")
result(false) result(false)
} }
result(true) result(true)
} }
func openIn(inAppBrowser url: URL, withOptions options: [String: Any]) { public func loadUrl(arguments: NSDictionary, result: @escaping FlutterResult) {
let url: String? = (arguments["url"] as? String)!
let browserOptions = InAppBrowserOptions() let headers = (arguments["headers"] as? [String: String])!
browserOptions.parse(options: options)
if browserOptions.clearCache { if url != nil {
let _: HTTPCookie? let absoluteUrl = URL(string: url!)!.absoluteURL
let storage = HTTPCookieStorage.shared webViewController?.loadUrl(url: absoluteUrl, headers: headers)
for cookie in storage.cookies! {
if !(cookie.domain.isEqual(".^filecookies^") ) {
storage.deleteCookie(cookie)
} }
else {
print("url is empty")
result(false)
} }
result(true)
} }
if browserOptions.clearSessionCache { func open(inAppBrowser url: URL, headers: [String: String], withOptions options: [String: Any]) {
let storage = HTTPCookieStorage.shared
for cookie in storage.cookies! { let browserOptions = InAppBrowserOptions()
if !(cookie.domain.isEqual(".^filecookies^") && cookie.isSessionOnly) { browserOptions.parse(options: options)
storage.deleteCookie(cookie)
}
}
}
if webViewController == nil { if webViewController == nil {
...@@ -182,6 +189,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -182,6 +189,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
webViewController?.browserOptions = browserOptions webViewController?.browserOptions = browserOptions
webViewController?.tmpWindow = tmpWindow webViewController?.tmpWindow = tmpWindow
webViewController?.currentURL = url webViewController?.currentURL = url
webViewController?.initHeaders = headers
webViewController?.navigationDelegate = self webViewController?.navigationDelegate = self
} }
...@@ -258,7 +266,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -258,7 +266,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
// //
// If no wrapper is supplied, then the source string is executed directly. // If no wrapper is supplied, then the source string is executed directly.
func injectDeferredObject(_ source: String, withWrapper jsWrapper: String) { func injectDeferredObject(_ source: String, withWrapper jsWrapper: String) {
if jsWrapper != nil { //if jsWrapper != nil {
let jsonData: Data? = try? JSONSerialization.data(withJSONObject: [source], options: []) let jsonData: Data? = try? JSONSerialization.data(withJSONObject: [source], options: [])
let sourceArrayString = String(data: jsonData!, encoding: String.Encoding.utf8) let sourceArrayString = String(data: jsonData!, encoding: String.Encoding.utf8)
if sourceArrayString != nil { if sourceArrayString != nil {
...@@ -266,10 +274,10 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -266,10 +274,10 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
let jsToInject = String(format: jsWrapper, sourceString!) let jsToInject = String(format: jsWrapper, sourceString!)
webViewController?.webView?.evaluateJavaScript(jsToInject) webViewController?.webView?.evaluateJavaScript(jsToInject)
} }
} //}
else { //else {
webViewController?.webView?.evaluateJavaScript(source) // webViewController?.webView?.evaluateJavaScript(source)
} //}
} }
public func injectScriptCode(arguments: NSDictionary) { public func injectScriptCode(arguments: NSDictionary) {
...@@ -294,23 +302,23 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -294,23 +302,23 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
func webViewDidStartLoad(_ webView: WKWebView) { func webViewDidStartLoad(_ webView: WKWebView) {
let url: String = webViewController!.currentURL!.absoluteString let url: String = webViewController!.currentURL!.absoluteString
channel.invokeMethod("loadstart", arguments: ["type": "loadstart", "url": url]) channel.invokeMethod("loadstart", arguments: ["url": url])
} }
func webViewDidFinishLoad(_ webView: WKWebView) { func webViewDidFinishLoad(_ webView: WKWebView) {
let url: String = webViewController!.currentURL!.absoluteString let url: String = webViewController!.currentURL!.absoluteString
channel.invokeMethod("loadstop", arguments: ["type": "loadstop", "url": url]) channel.invokeMethod("loadstop", arguments: ["url": url])
} }
func webView(_ webView: WKWebView, didFailLoadWithError error: Error) { func webView(_ webView: WKWebView, didFailLoadWithError error: Error) {
let url: String = webViewController!.currentURL!.absoluteString let url: String = webViewController!.currentURL!.absoluteString
let arguments = ["type": "loaderror", "url": url, "code": error._code, "message": error.localizedDescription] as [String : Any] let arguments = ["url": url, "code": error._code, "message": error.localizedDescription] as [String : Any]
channel.invokeMethod("loaderror", arguments: arguments) channel.invokeMethod("loaderror", arguments: arguments)
} }
func browserExit() { func browserExit() {
channel.invokeMethod("exit", arguments: ["type": "exit"]) channel.invokeMethod("exit", arguments: [])
// Set navigationDelegate to nil to ensure no callbacks are received from it. // Set navigationDelegate to nil to ensure no callbacks are received from it.
webViewController?.navigationDelegate = nil webViewController?.navigationDelegate = nil
......
...@@ -51,10 +51,6 @@ class InAppBrowser { ...@@ -51,10 +51,6 @@ class InAppBrowser {
case "exit": case "exit":
onExit(); onExit();
break; break;
case "customscheme":
String url = call.arguments["url"];
onCustomScheme(url);
break;
} }
return new Future.value(""); return new Future.value("");
} }
...@@ -121,14 +117,22 @@ class InAppBrowser { ...@@ -121,14 +117,22 @@ class InAppBrowser {
/// - __transitionstyle__: Set to `fliphorizontal`, `crossdissolve` or `coververtical` to set the [transition style](http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/modalTransitionStyle) (defaults to `coververtical`). /// - __transitionstyle__: Set to `fliphorizontal`, `crossdissolve` or `coververtical` to set the [transition style](http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/modalTransitionStyle) (defaults to `coververtical`).
/// - __toolbarposition__: Set to `top` or `bottom` (default is `bottom`). Causes the toolbar to be at the top or bottom of the window. /// - __toolbarposition__: Set to `top` or `bottom` (default is `bottom`). Causes the toolbar to be at the top or bottom of the window.
/// - __hidespinner__: Set to `yes` or `no` to change the visibility of the loading indicator (defaults to `no`). /// - __hidespinner__: Set to `yes` or `no` to change the visibility of the loading indicator (defaults to `no`).
Future<void> open(String url, {String target = "_self", Map<String, dynamic> options = const {}}) async { Future<void> open(String url, {Map<String, String> headers = const {}, String target = "_self", Map<String, dynamic> options = const {}}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers);
args.putIfAbsent('target', () => target); args.putIfAbsent('target', () => target);
args.putIfAbsent('options', () => options); args.putIfAbsent('options', () => options);
return await _channel.invokeMethod('open', args); return await _channel.invokeMethod('open', args);
} }
Future<void> loadUrl(String url, {Map<String, String> headers = const {}}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers);
return await _channel.invokeMethod('loadUrl', args);
}
///Displays an [InAppBrowser] window that was opened hidden. Calling this has no effect if the [InAppBrowser] was already visible. ///Displays an [InAppBrowser] window that was opened hidden. Calling this has no effect if the [InAppBrowser] was already visible.
Future<void> show() async { Future<void> show() async {
return await _channel.invokeMethod('show'); return await _channel.invokeMethod('show');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment