Skip to content
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

Standardize webview implementation across platforms #1221

Merged
merged 38 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ad1fe3a
use flutter distributor to package windows app
atavism Oct 16, 2024
316114b
update windows release target
atavism Oct 16, 2024
983e470
clean-ups
atavism Oct 16, 2024
8f06ed4
clean-ups
atavism Oct 16, 2024
a70fd37
Add if-no-files-found: error
atavism Oct 16, 2024
3a6afac
Set up MinGW before building library
atavism Oct 16, 2024
ed452a6
Add required input args
atavism Oct 16, 2024
ae520da
update how MinGW is installed
atavism Oct 16, 2024
15f0627
Merge remote-tracking branch 'origin/main' into atavism/flutter-distr…
atavism Oct 22, 2024
ed0f28e
merge latest and updates to build installer
atavism Oct 22, 2024
ce086b2
updates to webview settings and add new packaging configs
atavism Oct 25, 2024
4bc80f2
updates to webview settings and add new packaging configs
atavism Oct 25, 2024
e05d2b9
updates to webview settings and add new packaging configs
atavism Oct 25, 2024
9d7c842
clean-ups
atavism Oct 25, 2024
12fbf63
clean-ups
atavism Oct 25, 2024
94f9e4b
clean-ups
atavism Oct 25, 2024
0792a6f
revert test change
atavism Oct 25, 2024
0b82daa
revert test change
atavism Oct 25, 2024
34fe3de
revert test change
atavism Oct 25, 2024
cfc2948
clean-ups
atavism Oct 25, 2024
d6e5274
use getApplicationSupportDirectory and check the platform is windows
atavism Oct 26, 2024
fee38d1
simplify webview code further
atavism Oct 26, 2024
d6571e8
clean-ups
atavism Oct 26, 2024
3e299ab
clean-ups
atavism Oct 26, 2024
5e9229b
merge latest
atavism Oct 27, 2024
0d2f981
Add optional title to openWebview
atavism Oct 27, 2024
880c2f4
Merge remote-tracking branch 'origin/main' into atavism/webview-updates
atavism Nov 7, 2024
e284651
Merge remote-tracking branch 'origin/main' into atavism/webview-updates
atavism Nov 8, 2024
6601eec
Merge branch 'main' into atavism/webview-updates
atavism Nov 12, 2024
e7d8b2c
merge latest
atavism Nov 18, 2024
58c2a9b
Update dependencies to latest possible versions (#1230)
atavism Nov 18, 2024
7733c48
Merge branch 'main' into atavism/webview-updates
jigar-f Nov 18, 2024
ef70768
Merge branch 'atavism/webview-updates' of https://github.com/getlante…
jigar-f Nov 18, 2024
ccc13ca
Update open system browser method.
jigar-f Nov 18, 2024
d163af5
Merge remote-tracking branch 'origin/main' into atavism/webview-updates
atavism Dec 5, 2024
1d32508
merge latest
atavism Dec 5, 2024
e603855
Merge remote-tracking branch 'origin/main' into atavism/webview-updates
atavism Dec 5, 2024
6d89988
Merge remote-tracking branch 'origin/main' into atavism/webview-updates
atavism Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -12,7 +12,6 @@ import (
"time"

"github.com/getlantern/appdir"
"github.com/getlantern/errors"
"github.com/getlantern/flashlight/v7"
"github.com/getlantern/flashlight/v7/issue"
"github.com/getlantern/flashlight/v7/logging"
Expand Down Expand Up @@ -287,16 +286,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
2 changes: 1 addition & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ github.com/getlantern/fdcount v0.0.0-20210503151800-5decd65b3731/go.mod h1:XZwE+
github.com/getlantern/filepersist v0.0.0-20160317154340-c5f0cd24e799/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8=
github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c h1:mcz27xtAkb1OuOLBct/uFfL1p3XxAIcFct82GbT+UZM=
github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8=
github.com/getlantern/flashlight/v7 v7.6.136 h1:SlscKLFtKtTk6DgZOhgqiuys+hhLOqVrQwZQIQtlDDs=
github.com/getlantern/flashlight/v7 v7.6.136 h1:t5+Z/rFQ7PZrD5tUH3a1EeYgDgUi1io+nioI602Qp9Q=
github.com/getlantern/flashlight/v7 v7.6.136/go.mod h1:PNDmAgH5Y3+Gi7Fnl05f1MW1joM+djK5ukKIaEjJMZA=
github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede h1:yrU6Px3ZkvCsDLPryPGi6FN+2iqFPq+JeCb7EFoDBhw=
github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede/go.mod h1:nhnoiS6DE6zfe+BaCMU4YI01UpsuiXnDqM5S8jxHuuI=
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) {
atavism marked this conversation as resolved.
Show resolved Hide resolved
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',
),
);
}
Loading
Loading