Skip to content

Commit

Permalink
Update Android to use flutter_inappwebview (#1180)
Browse files Browse the repository at this point in the history
* Fix issue with flutter in app webview on macOS

* Fix macOS build error and upgrade sentry_flutter

* update Sentry/HybridSDK

* run pod repo update

* Add back dependency overrides

* use flutter_inappwebview on Android

* Add ProxyManager, set webview proxy address after Lantern starts

* Update setProxyAddr in AppBrowser

* few more clean-ups

* revert changes

* remove unused
  • Loading branch information
atavism authored Sep 19, 2024
1 parent 2b2786e commit f4f4ee6
Show file tree
Hide file tree
Showing 16 changed files with 174 additions and 113 deletions.
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />

<permission
android:name="${applicationId}.mobiamo.PAYMENT_BROADCAST_PERMISSION"
Expand Down
3 changes: 3 additions & 0 deletions android/app/src/main/kotlin/io/lantern/model/SessionModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ class SessionModel internal constructor(

}

"proxyAddr" -> result.success(LanternApp.session.hTTPAddr)

"isPlayServiceAvailable" -> {
result.success(LanternApp.getInAppBilling().isPlayStoreAvailable())
}
Expand Down Expand Up @@ -249,6 +251,7 @@ class SessionModel internal constructor(
} else {
startResult!!.httpAddr
}

val sOCKS5Addr: String
get() = if (startResult == null) {
""
Expand Down
8 changes: 8 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ allprojects {
maven { url 'https://jitpack.io' }
}

/// TODO: remove. hotfix for flutter_inappwebview (using androidx.webkit:webkit:1.8.0)
/// webview_flutter_android: ^3.16.2 androidx.webkit:webkit ^1.9.0 remove SUPPRESS_ERROR_PAGE
configurations.all {
resolutionStrategy {
force 'androidx.webkit:webkit:1.8.0'
}
}

// This code fixes a 'namespace not specified' error upgrading AGP to >= 8.x.x.
subprojects { subproject ->
subproject.tasks.whenTaskAdded {
Expand Down
3 changes: 1 addition & 2 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx4048m -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4048m
android.useAndroidX=true
android.enableJetifier=true
org.gradle.caching=true
Expand All @@ -9,5 +9,4 @@ kotlin.jvm.target.validation.mode = IGNORE

# Additional Settings
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
2 changes: 2 additions & 0 deletions desktop/app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type ChatOptions struct {
type ConfigOptions struct {
DevelopmentMode bool `json:"developmentMode"`
ReplicaAddr string `json:"replicaAddr"`
HttpProxyAddr string `json:"httpProxyAddr"`
AuthEnabled bool `json:"authEnabled"`
ChatEnabled bool `json:"chatEnabled"`
SplitTunneling bool `json:"splitTunneling"`
Expand Down Expand Up @@ -83,6 +84,7 @@ func (app *App) sendConfigOptions() {
DevelopmentMode: common.IsDevEnvironment(),
AppVersion: common.ApplicationVersion,
ReplicaAddr: "",
HttpProxyAddr: app.settings.GetAddr(),
AuthEnabled: authEnabled(app),
ChatEnabled: false,
SplitTunneling: false,
Expand Down
1 change: 1 addition & 0 deletions lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class _LanternAppState extends State<LanternApp>
if (!hasConnection) {
return;
}

final vpnConnected = await vpnModel.isVpnConnected();

/// If vpn is not connected then we should not show the connectivity warning
Expand Down
1 change: 1 addition & 0 deletions lib/common/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export 'once.dart';
export 'session_model.dart';
export 'single_value_subscriber.dart';
export 'ui/app_buttons.dart';
export 'ui/app_webview.dart';
export 'ui/audio.dart';
export 'ui/base_screen.dart';
export 'ui/basic_memory_image.dart';
Expand Down
3 changes: 3 additions & 0 deletions lib/common/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class ConfigOptions {
final String sdkVersion;
final String deviceId;
final String expirationDate;
final String httpProxyAddr;

final Map<String, Plan>? plans;
Devices devices = Devices();
Expand All @@ -57,6 +58,7 @@ class ConfigOptions {
this.fetchedProxiesConfig = false,
this.expirationDate = '',
this.sdkVersion = '',
this.httpProxyAddr = '',
this.deviceId = '',
this.plans = null,
this.paymentMethods = null,
Expand All @@ -82,6 +84,7 @@ class ConfigOptions {
developmentMode: parsedJson['developmentMode'],
authEnabled: parsedJson['authEnabled'],
chatEnabled: parsedJson['chatEnabled'],
httpProxyAddr: parsedJson['httpProxyAddr'],
splitTunneling: parsedJson['splitTunneling'],
hasSucceedingProxy: parsedJson['hasSucceedingProxy'],
fetchedGlobalConfig: parsedJson['fetchedGlobalConfig'],
Expand Down
12 changes: 5 additions & 7 deletions lib/common/session_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class SessionModel extends Model {
late ValueNotifier<String?> country;
late ValueNotifier<String?> referralNotifier;
late ValueNotifier<String?> deviceIdNotifier;
ValueNotifier<String?> langNotifier=ValueNotifier('en_us');
ValueNotifier<String?> langNotifier = ValueNotifier('en_us');
late ValueNotifier<bool> proxyAllNotifier;
late ValueNotifier<ServerInfo?> serverInfoNotifier;
late ValueNotifier<String?> userEmail;
Expand Down Expand Up @@ -431,6 +431,10 @@ class SessionModel extends Model {
throw Exception("Not supported on mobile");
}

Future<String> proxyAddr() async {
return await methodChannel.invokeMethod('proxyAddr', <String, dynamic>{});
}

/// Auth API end
Future<String> getCountryCode() async {
return await methodChannel
Expand Down Expand Up @@ -864,12 +868,6 @@ class SessionModel extends Model {
await compute(LanternFFI.emailExists, email);
}

Future<void> openWebview(String url) {
return methodChannel.invokeMethod('openWebview', <String, dynamic>{
'url': url,
});
}

Future<void> refreshAppsList() async {
await methodChannel.invokeMethod('refreshAppsList');
}
Expand Down
58 changes: 50 additions & 8 deletions lib/common/ui/app_webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AppWebView extends StatefulWidget {

class _AppWebViewState extends State<AppWebView> {
final InAppWebViewSettings settings = InAppWebViewSettings(
isInspectable: false,
isInspectable: kDebugMode,
javaScriptEnabled: true,
mediaPlaybackRequiresUserGesture: false,
allowsInlineMediaPlayback: false,
Expand All @@ -47,15 +47,41 @@ class _AppWebViewState extends State<AppWebView> {

class AppBrowser extends InAppBrowser {
final VoidCallback? onClose;
final InAppBrowserClassSettings settings = InAppBrowserClassSettings(
browserSettings: InAppBrowserSettings(hideUrlBar: true),
webViewSettings: InAppWebViewSettings(
javaScriptEnabled: true, isInspectable: kDebugMode));

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

AppBrowser({
required this.onClose,
this.onClose,
});

static Future setProxyAddr() async {
var proxyAvailable =
await WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE);
if (proxyAvailable) {
ProxyController proxyController = ProxyController.instance();
final proxyAddr = await sessionModel.proxyAddr();
await proxyController.clearProxyOverride();
await proxyController.setProxyOverride(
settings: ProxySettings(
proxyRules: [ProxyRule(url: "http://$proxyAddr")],
bypassRules: [],
));
}
}

@override
Future onBrowserCreated() async {
print("Browser created");
Expand All @@ -76,13 +102,24 @@ class AppBrowser extends InAppBrowser {
print("Can't load ${request.url}.. Error: ${error.description}");
}

@override
Future<NavigationActionPolicy> shouldOverrideUrlLoading(
navigationAction) async {
final url = navigationAction.request.url!;
if (url.scheme.startsWith("alipay")) {
await launchUrl(url, mode: LaunchMode.platformDefault);
return NavigationActionPolicy.CANCEL;
}
return NavigationActionPolicy.ALLOW;
}

@override
void onProgressChanged(progress) {
print("Progress: $progress");
}

@override
void onExit() {
Future<void> onExit() async {
print("Browser closed");
onClose?.call();
}
Expand Down Expand Up @@ -115,7 +152,12 @@ class AppBrowser extends InAppBrowser {
InAppBrowser.openWithSystemBrowser(url: WebUri(url));
break;
default:
await launchUrl(Uri.parse(url), mode: LaunchMode.platformDefault);
await setProxyAddr();
final instance = AppBrowser();
await instance.openUrlRequest(
urlRequest: URLRequest(url: WebUri(url)),
settings: settings,
);
break;
}
}
Expand Down
25 changes: 13 additions & 12 deletions lib/plans/checkout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,13 @@ class _CheckoutState extends State<Checkout>
Iterable<PathAndValue<PaymentMethod>> paymentMethods,
) {
var widgets = <Widget>[];
for (final paymentMethod in paymentMethods) {
for (final pathAndValue in sortProviders(paymentMethods)) {
final paymentMethod = pathAndValue.value;
if (widgets.length == 2) {
widgets.add(options());
if (paymentMethods.length != 2) widgets.add(options());
if (!showMoreOptions) break;
}
widgets.addAll(paymentProviders(paymentMethod.value));
widgets.addAll(paymentProviders(paymentMethod));
}
return widgets;
}
Expand Down Expand Up @@ -286,7 +287,7 @@ class _CheckoutState extends State<Checkout>

context.loaderOverlay.hide();
final btcPayURL = value;
await sessionModel.openWebview(btcPayURL);
await AppBrowser.openWebview(btcPayURL);
} catch (error, stackTrace) {
context.loaderOverlay.hide();
showError(context, error: error, stackTrace: stackTrace);
Expand All @@ -304,7 +305,7 @@ class _CheckoutState extends State<Checkout>

context.loaderOverlay.hide();
final froPayURL = value;
await sessionModel.openWebview(froPayURL);
await AppBrowser.openWebview(froPayURL);
} catch (error, stackTrace) {
context.loaderOverlay.hide();
showError(context, error: error, stackTrace: stackTrace);
Expand Down Expand Up @@ -339,14 +340,13 @@ class _CheckoutState extends State<Checkout>

context.loaderOverlay.hide();
final shepherdURL = value;
await sessionModel.openWebview(shepherdURL);
await AppBrowser.openWebview(shepherdURL);
} catch (error, stackTrace) {
context.loaderOverlay.hide();
showError(context, error: error, stackTrace: stackTrace);
}
}


// This methods is responsible for polling for user data
// so if user has done payment or renew plans and show
void hasPlansUpdateOrBuy() {
Expand Down Expand Up @@ -403,7 +403,7 @@ class _CheckoutState extends State<Checkout>

context.loaderOverlay.hide();
final btcPayURL = value;
await sessionModel.openWebview(btcPayURL);
await AppBrowser.openWebview(btcPayURL);
} catch (error, stackTrace) {
context.loaderOverlay.hide();
showError(context, error: error, stackTrace: stackTrace);
Expand Down Expand Up @@ -464,9 +464,10 @@ class _CheckoutState extends State<Checkout>
assert(widget.authFlow != null, 'authFlow is null');
switch (widget.authFlow!) {
case AuthFlow.createAccount:
/// There is edge case where user is signup with email and password but not pro
/// this happens when does restore purchase on other device so older device
/// does not have pro status but have email and password

/// There is edge case where user is signup with email and password but not pro
/// this happens when does restore purchase on other device so older device
/// does not have pro status but have email and password
if (sessionModel.hasUserSignedInNotifier.value ?? false) {
showSuccessDialog(context, widget.isPro);
return;
Expand All @@ -493,7 +494,7 @@ class _CheckoutState extends State<Checkout>
case AuthFlow.updateAccount:
// TODO: Handle this case.
case AuthFlow.restoreAccount:
// TODO: Handle this case.
// TODO: Handle this case.
}
}
}
Loading

0 comments on commit f4f4ee6

Please sign in to comment.