Skip to content

Commit e293151

Browse files
committed
Remove Android deep link handling from native code and transition to Capacitor-based solution
1 parent c8390ad commit e293151

4 files changed

Lines changed: 22 additions & 108 deletions

File tree

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ android {
1111
applicationId "com.compassconnections.app"
1212
minSdkVersion rootProject.ext.minSdkVersion
1313
targetSdkVersion rootProject.ext.targetSdkVersion
14-
versionCode 163
14+
versionCode 164
1515
versionName "1.42.0"
1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
aaptOptions {

android/app/src/main/java/com/compassconnections/app/MainActivity.java

Lines changed: 18 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package com.compassconnections.app;
22

3-
import android.Manifest;
43
import android.content.ContentResolver;
54
import android.content.ContentValues;
65
import android.content.Context;
76
import android.content.Intent;
8-
import android.content.pm.PackageManager;
97
import android.media.MediaScannerConnection;
108
import android.net.Uri;
119
import android.os.Build;
@@ -18,14 +16,9 @@
1816
import android.webkit.WebView;
1917
import android.widget.Toast;
2018

21-
import androidx.activity.result.ActivityResultLauncher;
22-
import androidx.activity.result.contract.ActivityResultContracts;
2319
import androidx.annotation.RequiresApi;
24-
import androidx.core.content.ContextCompat;
2520

26-
import com.capacitorjs.plugins.pushnotifications.PushNotificationsPlugin;
2721
import com.getcapacitor.BridgeActivity;
28-
import com.getcapacitor.BridgeWebViewClient;
2922
import com.getcapacitor.Plugin;
3023
import com.getcapacitor.PluginHandle;
3124
import com.google.android.play.core.appupdate.AppUpdateInfo;
@@ -35,60 +28,43 @@
3528
import com.google.android.play.core.install.model.AppUpdateType;
3629
import com.google.android.play.core.install.model.UpdateAvailability;
3730

38-
import org.json.JSONException;
39-
import org.json.JSONObject;
40-
4131
import java.io.File;
4232
import java.io.FileOutputStream;
4333
import java.io.IOException;
4434
import java.io.OutputStream;
45-
import java.net.URL;
4635
import java.nio.charset.StandardCharsets;
4736

4837
import ee.forgr.capacitor.social.login.GoogleProvider;
4938
import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin;
5039
import ee.forgr.capacitor.social.login.SocialLoginPlugin;
5140

41+
/**
42+
* Deliberately thin. Three things that used to live here are now handled on the JS side, where they
43+
* also work on iOS and — unlike native code running inside {@code onCreate} — can be sure the WebView
44+
* has actually loaded:
45+
*
46+
* <ul>
47+
* <li><b>Deep links</b> — {@code App.getLaunchUrl()} / the {@code appUrlOpen} listener in
48+
* {@code web/pages/_app.tsx}. Capacitor stashes the launch intent's Uri itself.</li>
49+
* <li><b>Notification taps</b> — {@code pushNotificationActionPerformed} in
50+
* {@code web/lib/service/native-push.ts}. Capacitor retains the event until a listener
51+
* consumes it, so a cold-start tap is not lost.</li>
52+
* <li><b>POST_NOTIFICATIONS</b> — {@code PushNotifications.requestPermissions()}, which asks at
53+
* login rather than at first launch.</li>
54+
* </ul>
55+
*
56+
* What is left is what Capacitor has no plugin for: the Downloads-folder writer and the in-app
57+
* update prompt.
58+
*/
5259
public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin {
5360

54-
private String pendingDeepLink = null;
55-
56-
// Declare this at class level
57-
private final ActivityResultLauncher<String> requestPermissionLauncher =
58-
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
59-
if (isGranted) {
60-
Log.i("CompassApp", "Permission granted");
61-
// Permission granted – you can show notifications
62-
} else {
63-
Log.i("CompassApp", "Permission denied");
64-
// Permission denied – handle gracefully
65-
}
66-
});
67-
68-
private void askNotificationPermission() {
69-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // API 33
70-
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
71-
!= PackageManager.PERMISSION_GRANTED) {
72-
// Permission not yet granted; request it
73-
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
74-
}
75-
}
76-
}
77-
7861
public class WebAppInterface {
7962
private final Context context;
8063

8164
public WebAppInterface(Context context) {
8265
this.context = context;
8366
}
8467

85-
@JavascriptInterface
86-
public String getPendingDeepLink() {
87-
String link = pendingDeepLink;
88-
pendingDeepLink = null; // consume it
89-
return link;
90-
}
91-
9268
@JavascriptInterface
9369
public void downloadFile(String filename, String content) {
9470
try {
@@ -183,38 +159,12 @@ private String getMimeType(String filename) {
183159
}
184160

185161

186-
@Override
187-
protected void onNewIntent(Intent intent) {
188-
super.onNewIntent(intent);
189-
190-
String endpoint = intent.getStringExtra("endpoint");
191-
Log.i("CompassApp", "onNewIntent called with endpoint: " + endpoint);
192-
if (endpoint != null) {
193-
Log.i("CompassApp", "redirecting to endpoint: " + endpoint);
194-
try {
195-
String payload = new JSONObject().put("endpoint", endpoint).toString();
196-
Log.i("CompassApp", "Handling notif click: " + payload);
197-
bridge.getWebView().post(() -> bridge.getWebView().evaluateJavascript("handleAppLink(" + payload + ");", null));
198-
} catch (JSONException e) {
199-
Log.i("CompassApp", "Failed to encode JSON payload", e);
200-
}
201-
} else {
202-
Uri data = intent.getData();
203-
if (data != null) {
204-
handleDeepLink(data.toString());
205-
} else {
206-
Log.i("CompassApp", "No relevant data");
207-
}
208-
}
209-
}
210-
211162
@Override
212163
public void onCreate(Bundle savedInstanceState) {
213164
Log.i("CompassApp", "onCreate called");
214165
super.onCreate(savedInstanceState);
215166

216167
WebView webView = this.bridge.getWebView();
217-
webView.setWebViewClient(new BridgeWebViewClient(this.bridge));
218168

219169
if (BuildConfig.ENABLE_WEBVIEW_DEBUG) {
220170
WebView.setWebContentsDebuggingEnabled(true);
@@ -223,42 +173,15 @@ public void onCreate(Bundle savedInstanceState) {
223173
WebSettings settings = webView.getSettings();
224174
settings.setUserAgentString(settings.getUserAgentString() + " CompassAppWebView");
225175

226-
settings.setJavaScriptEnabled(true);
227176
webView.addJavascriptInterface(new WebAppInterface(this), "AndroidBridge");
228177

229-
registerPlugin(PushNotificationsPlugin.class);
230178
// Initialize the Bridge with Push Notifications plugin
231179
// this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
232180
// add(com.getcapacitor.plugin.PushNotifications.class);
233181
// }});
234182

235-
askNotificationPermission();
236-
237183
appUpdateManager = AppUpdateManagerFactory.create(this);
238184
checkForUpdates();
239-
240-
Uri data = getIntent().getData();
241-
if (data != null) {
242-
pendingDeepLink = data.toString();
243-
} else {
244-
// Check for notification endpoint when app is opened from cold start via notification click
245-
String endpoint = getIntent().getStringExtra("endpoint");
246-
if (endpoint != null) {
247-
Log.i("CompassApp", "onCreate found endpoint from notification: " + endpoint);
248-
pendingDeepLink = endpoint;
249-
}
250-
}
251-
}
252-
253-
private void handleDeepLink(String url) {
254-
try {
255-
String path = new URL(url).getPath();
256-
String payload = new JSONObject().put("url", url).put("endpoint", path).toString();
257-
Log.i("CompassApp", "Handling deep link: " + url);
258-
bridge.getWebView().post(() -> bridge.getWebView().evaluateJavascript("handleAppLink(" + payload + ");", null));
259-
} catch (Exception e) {
260-
Log.e("CompassApp", "Failed to handle deep link for " + url, e);
261-
}
262185
}
263186

264187
@Override
@@ -343,11 +266,5 @@ public void onResume() {
343266
}
344267
});
345268
}
346-
347-
@Override
348-
public void onDestroy() {
349-
super.onDestroy();
350-
appUpdateManager = null;
351-
}
352269
}
353270

web/pages/_app.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,16 +234,14 @@ function MyApp(props: AppProps<PageProps>) {
234234

235235
// Cross-platform deep links: Universal Links on iOS, App Links on Android. `getLaunchUrl` covers
236236
// the cold-start case, the listener covers a link arriving while the app is already running.
237-
// Android additionally still has the hand-written `AndroidBridge` path below — MainActivity
238-
// pushes notification endpoints in through `handleAppLink` directly, and the two are harmless
239-
// together because handleAppLink no-ops when the endpoint is already the current path.
237+
// This is the only path on either platform — Android's MainActivity used to push the same links
238+
// in through a hand-written `AndroidBridge`, which was redundant with these two and, running
239+
// from `onCreate`, fired before `handleAppLink` was even defined on `window`.
240240
const listener = App.addListener('appUrlOpen', ({url}) => openLink(url))
241241
App.getLaunchUrl()
242242
.then((res) => openLink(res?.url))
243243
.catch((e) => debug('No launch url', e))
244244

245-
openLink(window.AndroidBridge?.getPendingDeepLink?.())
246-
247245
return () => {
248246
listener.then((l) => l.remove()).catch(() => {})
249247
}

web/pages/_document.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ declare global {
88
interface Window {
99
AndroidBridge?: {
1010
downloadFile: (filename: string, content: string) => void
11-
getPendingDeepLink: () => string | null
1211
}
1312
}
1413
}

0 commit comments

Comments
 (0)