From f5b474cd3b074fbc2e2377fbc7f9fdf2627f4c02 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Tue, 4 Oct 2022 12:12:07 +0200 Subject: [PATCH] Added support for Android 33, Fixed possible null pointer exception in Android ChromeCustomTabsActivity.java, fix #1299, fix #1223, fix #1269, fix #1234, close #1307 --- CHANGELOG.md | 5 +++ android/build.gradle | 6 +-- .../flutter_inappwebview/Util.java | 21 +++++++++- .../ActionBroadcastReceiver.java | 18 ++++---- .../ChromeCustomTabsActivity.java | 21 +++++++--- .../in_app_webview/InAppWebView.java | 41 +++++++++++++++---- example/android/app/build.gradle | 6 +-- .../webview_flutter_test.dart | 6 +-- example/ios/Flutter/Flutter.podspec | 18 -------- .../ios/Flutter/flutter_export_environment.sh | 5 +-- example/pubspec.yaml | 2 +- pubspec.yaml | 2 +- 12 files changed, 95 insertions(+), 56 deletions(-) delete mode 100644 example/ios/Flutter/Flutter.podspec diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b90338..df723ca3 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.4.4 + +- Added support for Android 33 +- Fixed possible null pointer exception in Android `ChromeCustomTabsActivity.java` + ## 5.4.3+8 - Merged "Xcode 14 build error: Stored properties cannot be marked potentially unavailable with '@available'" [#1238](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1238) (thanks to [CodeEagle](https://github.com/CodeEagle)) diff --git a/android/build.gradle b/android/build.gradle index a6642d58..f6bfa929 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + compileSdkVersion 33 defaultConfig { minSdkVersion 17 @@ -45,9 +45,9 @@ android { } } dependencies { - implementation 'androidx.webkit:webkit:1.4.0' + implementation 'androidx.webkit:webkit:1.5.0' implementation 'androidx.browser:browser:1.4.0' - implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'com.squareup.okhttp3:okhttp:3.14.9' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java index fceaffbf..bbe1e4f1 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java @@ -19,6 +19,8 @@ import org.json.JSONObject; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; @@ -29,7 +31,6 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; @@ -37,7 +38,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.HostnameVerifier; @@ -321,4 +321,21 @@ public class Util { public static Object getOrDefault(Map map, String key, Object defaultValue) { return map.containsKey(key) ? map.get(key) : defaultValue; } + + @Nullable + public static Object invokeMethodIfExists(final O o, final String methodName, Object... args) { + Method[] methods = o.getClass().getMethods(); + for (Method method : methods) { + if (method.getName().equals(methodName)) { + try { + method.invoke(o, args); + } catch (IllegalAccessException e) { + return null; + } catch (InvocationTargetException e) { + return null; + } + } + } + return null; + } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java index 1d3bb700..4e215e83 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java @@ -27,14 +27,16 @@ public class ActionBroadcastReceiver extends BroadcastReceiver { String title = b.getString(KEY_URL_TITLE); String managerId = b.getString(CHROME_MANAGER_ID); - ChromeSafariBrowserManager manager = (ChromeSafariBrowserManager) ChromeSafariBrowserManager.shared.get(managerId); - - MethodChannel channel = new MethodChannel(manager.plugin.messenger, "com.pichillilorenzo/flutter_chromesafaribrowser_" + viewId); - Map obj = new HashMap<>(); - obj.put("url", url); - obj.put("title", title); - obj.put("id", id); - channel.invokeMethod("onChromeSafariBrowserItemActionPerform", obj); + if (managerId != null) { + ChromeSafariBrowserManager manager = (ChromeSafariBrowserManager) ChromeSafariBrowserManager.shared.get(managerId); + if (manager == null || manager.plugin == null|| manager.plugin.messenger == null) return; + MethodChannel channel = new MethodChannel(manager.plugin.messenger, "com.pichillilorenzo/flutter_chromesafaribrowser_" + viewId); + Map obj = new HashMap<>(); + obj.put("url", url); + obj.put("title", title); + obj.put("id", id); + channel.invokeMethod("onChromeSafariBrowserItemActionPerform", obj); + } } } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java index 5533ddc9..7be959bf 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java @@ -190,8 +190,10 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. builder.setInstantAppsEnabled(options.instantAppsEnabled); for (CustomTabsMenuItem menuItem : menuItems) { - builder.addMenuItem(menuItem.getLabel(), - createPendingIntent(menuItem.getId())); + PendingIntent pendingIntent = createPendingIntent(menuItem.getId()); + if (pendingIntent != null) { + builder.addMenuItem(menuItem.getLabel(), pendingIntent); + } } if (actionButton != null) { @@ -201,9 +203,12 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. Bitmap bmp = BitmapFactory.decodeByteArray( data, 0, data.length, bitmapOptions ); - builder.setActionButton(bmp, actionButton.getDescription(), - createPendingIntent(actionButton.getId()), - actionButton.isShouldTint()); + PendingIntent pendingIntent = createPendingIntent(actionButton.getId()); + if (pendingIntent != null) { + builder.setActionButton(bmp, actionButton.getDescription(), + pendingIntent, + actionButton.isShouldTint()); + } } } @@ -237,7 +242,9 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. } } + @Nullable private PendingIntent createPendingIntent(int actionSourceId) { + if (manager == null) return null; Intent actionIntent = new Intent(this, ActionBroadcastReceiver.class); Bundle extras = new Bundle(); @@ -256,11 +263,15 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. } public void dispose() { + onStop(); + onDestroy(); channel.setMethodCallHandler(null); manager = null; } public void close() { + onStop(); + onDestroy(); customTabsSession = null; finish(); Map obj = new HashMap<>(); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java index a1951e6b..6bfca2bb 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java @@ -276,8 +276,11 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie settings.setAllowFileAccessFromFileURLs(options.allowFileAccessFromFileURLs); settings.setAllowUniversalAccessFromFileURLs(options.allowUniversalAccessFromFileURLs); setCacheEnabled(options.cacheEnabled); - if (options.appCachePath != null && !options.appCachePath.isEmpty() && options.cacheEnabled) - settings.setAppCachePath(options.appCachePath); + if (options.appCachePath != null && !options.appCachePath.isEmpty() && options.cacheEnabled) { + // removed from Android API 33+ (https://developer.android.com/sdk/api_diff/33/changes) + // settings.setAppCachePath(options.appCachePath); + Util.invokeMethodIfExists(settings, "setAppCachePath", options.appCachePath); + } settings.setBlockNetworkImage(options.blockNetworkImage); settings.setBlockNetworkLoads(options.blockNetworkLoads); if (options.cacheMode != null) @@ -491,7 +494,11 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie // Disable caching settings.setCacheMode(WebSettings.LOAD_NO_CACHE); - settings.setAppCacheEnabled(false); + + // removed from Android API 33+ (https://developer.android.com/sdk/api_diff/33/changes) + // settings.setAppCacheEnabled(false); + Util.invokeMethodIfExists(settings, "setAppCacheEnabled", false); + clearHistory(); clearCache(true); @@ -501,7 +508,11 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie settings.setSaveFormData(false); } else { settings.setCacheMode(WebSettings.LOAD_DEFAULT); - settings.setAppCacheEnabled(true); + + // removed from Android API 33+ (https://developer.android.com/sdk/api_diff/33/changes) + // settings.setAppCacheEnabled(true); + Util.invokeMethodIfExists(settings, "setAppCacheEnabled", true); + settings.setSavePassword(true); settings.setSaveFormData(true); } @@ -512,13 +523,22 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie if (enabled) { Context ctx = getContext(); if (ctx != null) { - settings.setAppCachePath(ctx.getCacheDir().getAbsolutePath()); + // removed from Android API 33+ (https://developer.android.com/sdk/api_diff/33/changes) + // settings.setAppCachePath(ctx.getCacheDir().getAbsolutePath()); + Util.invokeMethodIfExists(settings, "setAppCachePath", ctx.getCacheDir().getAbsolutePath()); + settings.setCacheMode(WebSettings.LOAD_DEFAULT); - settings.setAppCacheEnabled(true); + + // removed from Android API 33+ (https://developer.android.com/sdk/api_diff/33/changes) + // settings.setAppCacheEnabled(true); + Util.invokeMethodIfExists(settings, "setAppCacheEnabled", true); } } else { settings.setCacheMode(WebSettings.LOAD_NO_CACHE); - settings.setAppCacheEnabled(false); + + // removed from Android API 33+ (https://developer.android.com/sdk/api_diff/33/changes) + // settings.setAppCacheEnabled(false); + Util.invokeMethodIfExists(settings, "setAppCacheEnabled", false); } } @@ -763,8 +783,11 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie if (newOptionsMap.get("cacheEnabled") != null && options.cacheEnabled != newOptions.cacheEnabled) setCacheEnabled(newOptions.cacheEnabled); - if (newOptionsMap.get("appCachePath") != null && (options.appCachePath == null || !options.appCachePath.equals(newOptions.appCachePath))) - settings.setAppCachePath(newOptions.appCachePath); + if (newOptionsMap.get("appCachePath") != null && (options.appCachePath == null || !options.appCachePath.equals(newOptions.appCachePath))) { + // removed from Android API 33+ (https://developer.android.com/sdk/api_diff/33/changes) + // settings.setAppCachePath(newOptions.appCachePath); + Util.invokeMethodIfExists(settings, "setAppCachePath", newOptions.appCachePath); + } if (newOptionsMap.get("blockNetworkImage") != null && options.blockNetworkImage != newOptions.blockNetworkImage) settings.setBlockNetworkImage(newOptions.blockNetworkImage); diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index de44914a..4b28f2e2 100755 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -30,7 +30,7 @@ android { targetCompatibility 1.8 } - compileSdkVersion 31 + compileSdkVersion 33 lintOptions { disable 'InvalidPackage' @@ -40,7 +40,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.pichillilorenzo.flutter_inappwebviewexample" minSdkVersion 17 - targetSdkVersion 31 + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -64,6 +64,6 @@ dependencies { testImplementation 'junit:junit:4.13' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - implementation 'com.google.android.material:material:1.3.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'com.android.support:multidex:1.0.3' } diff --git a/example/integration_test/webview_flutter_test.dart b/example/integration_test/webview_flutter_test.dart index 9b62063f..e73e1397 100644 --- a/example/integration_test/webview_flutter_test.dart +++ b/example/integration_test/webview_flutter_test.dart @@ -5495,7 +5495,7 @@ setTimeout(function() { child: InAppWebView( key: GlobalKey(), initialUrlRequest: - URLRequest(url: Uri.parse('https://mdn.github.io/sw-test/')), + URLRequest(url: Uri.parse('https://mdn.github.io/dom-examples/service-worker/simple-service-worker/')), ), ), ); @@ -5524,7 +5524,7 @@ setTimeout(function() { child: InAppWebView( key: GlobalKey(), initialUrlRequest: - URLRequest(url: Uri.parse('https://mdn.github.io/sw-test/')), + URLRequest(url: Uri.parse('https://mdn.github.io/dom-examples/service-worker/simple-service-worker/')), onLoadStop: (controller, url) { pageLoaded.complete(url!.toString()); }, @@ -5533,7 +5533,7 @@ setTimeout(function() { ); final String url = await pageLoaded.future; - expect(url, "https://mdn.github.io/sw-test/"); + expect(url, "https://mdn.github.io/dom-examples/service-worker/simple-service-worker/"); }, skip: !Platform.isAndroid); }); diff --git a/example/ios/Flutter/Flutter.podspec b/example/ios/Flutter/Flutter.podspec deleted file mode 100644 index 8ce43943..00000000 --- a/example/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# This is a generated file; do not edit or check into version control. -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'A UI toolkit for beautiful and fast apps.' - s.homepage = 'https://flutter.dev' - s.license = { :type => 'BSD' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '11.0' - # Framework linking is handled by Flutter tooling, not CocoaPods. - # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. - s.vendored_frameworks = 'path/to/nothing' -end diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh index de544d92..ee7ac300 100755 --- a/example/ios/Flutter/flutter_export_environment.sh +++ b/example/ios/Flutter/flutter_export_environment.sh @@ -3,12 +3,11 @@ export "FLUTTER_ROOT=/Users/lorenzopichilli/fvm/versions/2.10.4" export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/flutter_inappwebview_v5/example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=integration_test/webview_flutter_test.dart" +export "FLUTTER_TARGET=lib/main.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NUMBER=1" -export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==" export "DART_OBFUSCATION=false" export "TRACK_WIDGET_CREATION=true" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=/Users/lorenzopichilli/flutter_inappwebview_v5/example/.dart_tool/package_config.json" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 7789db22..9ddc5ce6 100755 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: cupertino_icons: ^1.0.4 flutter_downloader: ^1.7.3 path_provider: ^2.0.9 - permission_handler: ^9.2.0 + permission_handler: ^10.0.2 url_launcher: ^6.0.20 # connectivity: ^0.4.5+6 flutter_inappwebview: diff --git a/pubspec.yaml b/pubspec.yaml index 30bce434..6f0eb864 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_inappwebview description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. -version: 5.4.3+8 +version: 5.4.4 homepage: https://github.com/pichillilorenzo/flutter_inappwebview environment: