Skip to content

Commit

Permalink
Standardize webview implementation across platforms (#1221)
Browse files Browse the repository at this point in the history
* use flutter distributor to package windows app

* update windows release target

* clean-ups

* clean-ups

* Add if-no-files-found: error

* Set up MinGW before building library

* Add required input args

* update how MinGW is installed

* merge latest and updates to build installer

* updates to webview settings and add new packaging configs

* updates to webview settings and add new packaging configs

* updates to webview settings and add new packaging configs

* clean-ups

* clean-ups

* clean-ups

* revert test change

* revert test change

* revert test change

* clean-ups

* use getApplicationSupportDirectory and check the platform is windows

* simplify webview code further

* clean-ups

* clean-ups

* Add optional title to openWebview

* Update dependencies to latest possible versions (#1230)

* Update Flutter dependencies

* update loader_overlay and ffigen

* clean-ups, move error check above checking response error

* clean-ups

* update dependencies

* Update more packages.

---------

Co-authored-by: Jigar-f <jigar@getlantern.org>

* Update open system browser method.

---------

Co-authored-by: Jigar-f <jigar@getlantern.org>
  • Loading branch information
atavism and jigar-f authored Dec 6, 2024
1 parent 58f68e0 commit 0604184
Show file tree
Hide file tree
Showing 25 changed files with 573 additions and 439 deletions.
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
id 'org.jlleitschuh.gradle.ktlint'
id 'kotlin-parcelize'
id 'com.google.protobuf'
id "io.sentry.android.gradle" version "4.11.0"
id "io.sentry.android.gradle" version "4.13.0"
}

android {
Expand Down
13 changes: 5 additions & 8 deletions desktop/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"time"

"github.com/getlantern/appdir"
"github.com/getlantern/errors"
"github.com/getlantern/flashlight/v7/issue"
"github.com/getlantern/flashlight/v7/logging"
"github.com/getlantern/flashlight/v7/ops"
Expand Down Expand Up @@ -270,16 +269,14 @@ func redeemResellerCode(email, currency, deviceName, resellerCode *C.char) *C.ch
ResellerCode: C.GoString(resellerCode),
Provider: "reseller-code",
})
log.Debugf("DEBUG: redeeming reseller code response: %v", response)
if response.Error != "" {
log.Debugf("DEBUG: error while redeeming reseller code reponse is: %v", response.Error)
return sendError(errors.New("Error while redeeming reseller code: %v", response.Error))
}
if err != nil {
log.Debugf("DEBUG: error while redeeming reseller code: %v", err)
log.Errorf("error redeeming reseller code: %v", err)
return sendError(err)
} else if response.Error != "" {
log.Errorf("error redeeming reseller code: %v", response.Error)
return sendError(err)
}
log.Debug("DEBUG: redeeming reseller code success")
log.Debug("redeeming reseller code success")
return C.CString("true")
}

Expand Down
3 changes: 1 addition & 2 deletions lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,8 @@ class _LanternAppState extends State<LanternApp>
(context, lang, child) {
Localization.locale = lang.startsWith('en') ? "en_us" : lang;
return GlobalLoaderOverlay(
useDefaultLoading: false,
overlayColor: Colors.black.withOpacity(0.5),
overlayWidget: Center(
overlayWidgetBuilder: (_) => Center(
child: AnimatedLoadingBorder(
borderWidth: 5,
borderColor: yellow3,
Expand Down
264 changes: 104 additions & 160 deletions lib/core/app/app_webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class AppWebView extends StatefulWidget {

class _AppWebViewState extends State<AppWebView> {
late InAppWebViewController webViewController;
bool isLoading = true;

void showErrorDialog(String title, String message) {
showDialog(
Expand All @@ -42,121 +43,31 @@ class _AppWebViewState extends State<AppWebView> {
);
}

@override
Widget build(BuildContext context) {
return BaseScreen(
title: widget.title,
body: InAppWebView(
initialUrlRequest: URLRequest(url: WebUri(widget.url)),
onWebViewCreated: (controller) {
webViewController = controller;
},
webViewEnvironment: webViewEnvironment,
onReceivedHttpError: (controller, request, response) {
appLogger.i("HTTP error: ${response.statusCode} for ${request.url}");
showErrorDialog("HTTP Error",
"Status code: ${response.statusCode}\nDescription: ${response.reasonPhrase ?? ''}");
},
onReceivedError: (controller, request, error) =>
showErrorDialog("Failed to load", error.description),
initialSettings: InAppWebViewSettings(
isInspectable: kDebugMode,
javaScriptEnabled: true,
supportZoom: true,
domStorageEnabled: true,
allowFileAccess: true,
useWideViewPort: !isDesktop(),
loadWithOverviewMode: !isDesktop(),
clearCache: true,
mixedContentMode: MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW,
builtInZoomControls: Platform.isAndroid,
displayZoomControls: false,
mediaPlaybackRequiresUserGesture: false,
allowsInlineMediaPlayback: false,
underPageBackgroundColor: Colors.white,
transparentBackground: true,
allowFileAccessFromFileURLs: true,
preferredContentMode: UserPreferredContentMode.MOBILE,
),
onProgressChanged: (controller, progress) {
appLogger.i("Loading progress: $progress%");
},
),
);
}
}

class AppBrowser extends InAppBrowser {
final VoidCallback? onClose;

static final InAppBrowserClassSettings settings = InAppBrowserClassSettings(
browserSettings: InAppBrowserSettings(
hideTitleBar: false,
hideToolbarBottom: false,
hideCloseButton: false,
hideUrlBar: true,
hidden: false,
presentationStyle: ModalPresentationStyle.FULL_SCREEN,
),
webViewSettings: InAppWebViewSettings(
sharedCookiesEnabled: true,
javaScriptEnabled: true,
useOnDownloadStart: true,
useShouldOverrideUrlLoading: true,
isInspectable: kDebugMode,
),
);

AppBrowser({
this.onClose,
});

static Future setProxyAddr() async {
Future setProxyAddr() async {
try {
var proxyAvailable = await WebViewFeature.isFeatureSupported(
WebViewFeature.PROXY_OVERRIDE);
if (proxyAvailable) {
ProxyController proxyController = ProxyController.instance();
final proxyAddr = await sessionModel.proxyAddr();
if (proxyAddr.isEmpty) {
return;
if (proxyAddr.isNotEmpty) {
ProxyController.instance()
..clearProxyOverride()
..setProxyOverride(
settings: ProxySettings(
proxyRules: [ProxyRule(url: "http://$proxyAddr")],
bypassRules: [],
));
appLogger.i("Proxy set as: http://$proxyAddr");
}
await proxyController.clearProxyOverride();
await proxyController.setProxyOverride(
settings: ProxySettings(
proxyRules: [ProxyRule(url: "http://$proxyAddr")],
bypassRules: [],
));
appLogger.e("Proxy set as :http://$proxyAddr");
}
} catch (e) {
appLogger.e("Error setting proxy address: $e");
}
}

@override
Future onBrowserCreated() async {
appLogger.i("Browser created");
}

@override
Future onLoadStart(url) async {
appLogger.i("Started displaying $url");
}

@override
Future onLoadStop(url) async {
appLogger.i("Stopped displaying $url");
}

@override
void onReceivedError(WebResourceRequest request, WebResourceError error) =>
appLogger.e("Can't load ${request.url}.. Error: ${error.description}",
error: error);

@override
Future<NavigationActionPolicy> shouldOverrideUrlLoading(
navigationAction) async {
InAppWebViewController controller,
NavigationAction navigationAction) async {
final url = navigationAction.request.url!;
if (url.scheme.startsWith("alipay")) {
launchUrl(
Expand All @@ -169,79 +80,112 @@ class AppBrowser extends InAppBrowser {
}

@override
void onProgressChanged(progress) {
appLogger.i("Progress: $progress");
Widget build(BuildContext context) {
return BaseScreen(
title: widget.title,
body: Stack(
children: [
InAppWebView(
initialUrlRequest: URLRequest(url: WebUri(widget.url)),
onWebViewCreated: (controller) {
webViewController = controller;
setProxyAddr();
},
webViewEnvironment: webViewEnvironment,
onLoadStart: (controller, url) => setState(() => isLoading = true),
onLoadStop: (controller, url) => setState(() => isLoading = false),
onReceivedHttpError: (controller, request, response) {
appLogger
.i("HTTP error: ${response.statusCode} for ${request.url}");
showErrorDialog("HTTP Error",
"Status code: ${response.statusCode}\nDescription: ${response.reasonPhrase ?? ''}");
},
shouldOverrideUrlLoading: shouldOverrideUrlLoading,
onReceivedError: (controller, request, error) =>
showErrorDialog("Failed to load", error.description),
initialSettings: InAppWebViewSettings(
isInspectable: kDebugMode,
javaScriptEnabled: true,
supportZoom: true,
domStorageEnabled: true,
allowFileAccess: true,
useWideViewPort: !isDesktop(),
loadWithOverviewMode: !isDesktop(),
clearCache: true,
mixedContentMode: MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW,
builtInZoomControls: Platform.isAndroid,
displayZoomControls: false,
mediaPlaybackRequiresUserGesture: false,
allowsInlineMediaPlayback: false,
underPageBackgroundColor: Colors.white,
transparentBackground: true,
allowFileAccessFromFileURLs: true,
preferredContentMode: isMobile()
? UserPreferredContentMode.MOBILE
: UserPreferredContentMode.RECOMMENDED,
),
onProgressChanged: (controller, progress) =>
appLogger.i("Loading progress: $progress%"),
),
if (isLoading) const Center(child: CircularProgressIndicator()),
],
),
);
}
}

@override
Future<void> onExit() async {
appLogger.i("Browser closed");
onClose?.call();
}
WebViewEnvironment? webViewEnvironment;

// navigateWebview navigates to the webview route and displays the given url
static Future<void> navigateWebview(BuildContext context, String url) async =>
await context.pushRoute(
AppWebview(
url: url,
title: 'lantern_pro_checkout'.i18n,
),
);
Future<void> initializeWebViewEnvironment() async {
if (Platform.isWindows) {
final directory = await getApplicationSupportDirectory();
final localAppDataPath = directory.path;

// Ensure WebView2 runtime is available
final availableVersion = await WebViewEnvironment.getAvailableVersion();
assert(availableVersion != null,
'Failed to find WebView2 Runtime or non-stable Microsoft Edge installation.');

webViewEnvironment = await WebViewEnvironment.create(
settings: WebViewEnvironmentSettings(
userDataFolder: '$localAppDataPath\\Lantern\\WebView2',
),
);
}
}

// openWithSystemBrowser opens a URL in the browser
static Future<void> openWithSystemBrowser(String url) async =>
// openWithSystemBrowser opens a URL in the browser
Future<void> openWithSystemBrowser(String url) async {
switch (Platform.operatingSystem) {
case 'linux':
final webview = await WebviewWindow.create();
webview.launch(url);
break;
default:
await InAppBrowser.openWithSystemBrowser(url: WebUri(url));
}
}

static Future<void> openWebview(BuildContext context, String url) async {
Future<void> openWebview(BuildContext context, String url,
[String? title]) async {
try {
switch (Platform.operatingSystem) {
case 'android':
case 'macos':
case 'windows':
await navigateWebview(context, url);
await context.pushRoute(AppWebview(url: url, title: title ?? ''));
break;
case 'linux':
try {
final webview = await WebviewWindow.create();
webview.launch(url);
} catch (e) {
mainLogger.e("Error opening linux webview: $e");
}
break;
case 'macos':
await openWithSystemBrowser(url);
final webview = await WebviewWindow.create();
webview.launch(url);
break;
case 'ios':
await openWithSystemBrowser(url);
break;
default:
await setProxyAddr();
await _openUrlRequest(url);
break;
throw UnsupportedError('Platform not supported');
}
} catch (e) {
appLogger.e("Failed to open webview", error: e);
}

static Future<void> _openUrlRequest(String url) async {
final instance = AppBrowser();
await instance.openUrlRequest(
urlRequest: URLRequest(url: WebUri(url), allowsCellularAccess: true),
settings: settings,
);
}
}

WebViewEnvironment? webViewEnvironment;

Future<void> initializeWebViewEnvironment() async {
if (!isDesktop()) return;
final directory = await getApplicationDocumentsDirectory();
final localAppDataPath = directory.path;

// Ensure WebView2 runtime is available
final availableVersion = await WebViewEnvironment.getAvailableVersion();
assert(availableVersion != null,
'Failed to find WebView2 Runtime or non-stable Microsoft Edge installation.');

webViewEnvironment = await WebViewEnvironment.create(
settings: WebViewEnvironmentSettings(
userDataFolder: '$localAppDataPath\\Lantern\\WebView2',
),
);
}
16 changes: 0 additions & 16 deletions lib/core/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ bool isProdPlay() {
return false;
}


String getPlanDisplayName(String plan) {
if (Platform.isIOS) {
if (plan == '1y') {
Expand Down Expand Up @@ -155,21 +154,6 @@ extension PlansExtension on Plan {
}
}

Future<void> openDesktopPaymentWebview(
{required BuildContext context,
required String redirectUrl,
required Providers provider,
VoidCallback? onClose}) async {
switch (Platform.operatingSystem) {
case 'macos':
case 'windows':
await AppBrowser.navigateWebview(context, redirectUrl);
break;
default:
await AppBrowser.openWebview(context, redirectUrl);
}
}

List<PathAndValue<PaymentMethod>> sortProviders(
Iterable<PathAndValue<PaymentMethod>> paymentMethods,
) =>
Expand Down
Loading

0 comments on commit 0604184

Please sign in to comment.