diff --git a/app.js b/app.js
index c0607d3..97719bb 100644
--- a/app.js
+++ b/app.js
@@ -60,11 +60,87 @@ function isMobileDevice() {
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
-function getMobileUrl(service) {
+function getMobilePlatform() {
+ const userAgent = navigator.userAgent || '';
+ if (/Android/i.test(userAgent)) return 'android';
+ if (/iPhone|iPad|iPod/i.test(userAgent)) return 'ios';
+ return 'other';
+}
+
+function getMobileLaunch(service) {
if (service.id === 'drive') {
- return 'nextcloud://openFiles';
+ const fallbackUrl = service.url;
+ if (getMobilePlatform() === 'android') {
+ return {
+ url: `intent://openFiles#Intent;scheme=nextcloud;package=com.nextcloud.client;S.browser_fallback_url=${encodeURIComponent(fallbackUrl)};end`,
+ fallbackUrl
+ };
+ }
+ return {
+ url: 'nextcloud://openFiles',
+ fallbackUrl
+ };
}
- return service.mobileUrl;
+ if (!service.mobileUrl) return null;
+ return {
+ url: service.mobileUrl,
+ fallbackUrl: service.url
+ };
+}
+
+function openMobileLaunch(url, fallbackUrl) {
+ let didLeavePage = false;
+ let fallbackTimer = null;
+ let cleanupTimer = null;
+ let launcherFrame = null;
+
+ const cleanup = () => {
+ window.removeEventListener('pagehide', markPageLeft);
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
+ if (fallbackTimer) window.clearTimeout(fallbackTimer);
+ if (cleanupTimer) window.clearTimeout(cleanupTimer);
+ if (launcherFrame) launcherFrame.remove();
+ };
+
+ const markPageLeft = () => {
+ didLeavePage = true;
+ cleanup();
+ };
+
+ const handleVisibilityChange = () => {
+ if (document.visibilityState === 'hidden') {
+ markPageLeft();
+ }
+ };
+
+ window.addEventListener('pagehide', markPageLeft, { once: true });
+ document.addEventListener('visibilitychange', handleVisibilityChange);
+
+ fallbackTimer = window.setTimeout(() => {
+ cleanup();
+ if (!didLeavePage && document.visibilityState === 'visible') {
+ window.location.href = fallbackUrl;
+ }
+ }, 2200);
+
+ if (/^https?:\/\//i.test(url) || /^intent:/i.test(url)) {
+ window.location.href = url;
+ return;
+ }
+
+ launcherFrame = document.createElement('iframe');
+ launcherFrame.hidden = true;
+ launcherFrame.style.display = 'none';
+ launcherFrame.setAttribute('aria-hidden', 'true');
+ document.body.appendChild(launcherFrame);
+ launcherFrame.src = url;
+
+ cleanupTimer = window.setTimeout(() => {
+ if (launcherFrame && launcherFrame.parentNode) {
+ launcherFrame.remove();
+ launcherFrame = null;
+ }
+ }, 2600);
}
function createServiceCard(service) {
@@ -92,28 +168,13 @@ function createServiceCard(service) {
${service.domain}
`;
- const mobileUrl = getMobileUrl(service);
- if (mobileUrl) {
+ const mobileLaunch = getMobileLaunch(service);
+ if (mobileLaunch) {
card.addEventListener('click', (e) => {
if (!isMobileDevice()) return;
e.preventDefault();
-
- if (/^https?:\/\//i.test(mobileUrl)) {
- window.location.href = mobileUrl;
- return;
- }
-
- const start = Date.now();
- // Try to open the native mobile app through its registered URL scheme.
- window.location.href = mobileUrl;
-
- // Web fallback if the app is not installed or the scheme is not handled.
- setTimeout(() => {
- if (Date.now() - start < 1500) {
- window.open(service.url, '_blank');
- }
- }, 800);
+ openMobileLaunch(mobileLaunch.url, mobileLaunch.fallbackUrl);
});
}
diff --git a/index.html b/index.html
index cee9185..1fdd833 100644
--- a/index.html
+++ b/index.html
@@ -119,6 +119,6 @@
-
+