Handle android app links

Update android/app/src/main/AndroidManifest.xml

Co-authored-by: Ben Allen <108441023+benalleng@users.noreply.github.com>
This commit is contained in:
Tony Giorgio
2023-08-22 20:57:16 -05:00
committed by Tony Giorgio
parent 1eedc7baaa
commit d567943e8f
11 changed files with 165 additions and 84 deletions

View File

@@ -10,6 +10,7 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-app')
implementation project(':capacitor-browser')
implementation project(':capacitor-clipboard')
implementation project(':capacitor-filesystem')
implementation project(':capacitor-toast')

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
@@ -8,6 +9,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:hardwareAccelerated="true"
android:enableOnBackInvokedCallback="true"
android:theme="@style/AppTheme">
<activity
@@ -23,6 +25,24 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="app.mutinywallet.com" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="http"
android:host="app.mutinywallet.com" />
</intent-filter>
</activity>
<provider
@@ -41,10 +61,12 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.READ_CLIPBOARD" />
<uses-permission android:name="android.permission.WRITE_CLIPBOARD" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

View File

@@ -3,5 +3,5 @@
<string name="app_name">Mutiny Wallet</string>
<string name="title_activity_main">Mutiny Wallet</string>
<string name="package_name">com.mutinywallet.mutinywallet</string>
<string name="custom_url_scheme">com.mutinywallet.mutinywallet</string>
<string name="custom_url_scheme">mutiny</string>
</resources>

View File

@@ -5,6 +5,9 @@ project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capa
include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/app/android')
include ':capacitor-browser'
project(':capacitor-browser').projectDir = new File('../node_modules/.pnpm/@capacitor+browser@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/browser/android')
include ':capacitor-clipboard'
project(':capacitor-clipboard').projectDir = new File('../node_modules/.pnpm/@capacitor+clipboard@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/clipboard/android')

View File

@@ -45,6 +45,7 @@
"dependencies": {
"@capacitor/android": "^5.2.2",
"@capacitor/app": "^5.0.6",
"@capacitor/browser": "^5.0.6",
"@capacitor/clipboard": "^5.0.6",
"@capacitor/core": "^5.2.2",
"@capacitor/filesystem": "^5.1.1",

105
pnpm-lock.yaml generated
View File

@@ -10,6 +10,9 @@ importers:
'@capacitor/app':
specifier: ^5.0.6
version: 5.0.6(@capacitor/core@5.2.2)
'@capacitor/browser':
specifier: ^5.0.6
version: 5.0.6(@capacitor/core@5.2.2)
'@capacitor/clipboard':
specifier: ^5.0.6
version: 5.0.6(@capacitor/core@5.2.2)
@@ -1513,6 +1516,14 @@ packages:
- typescript
dev: true
/@capacitor/browser@5.0.6(@capacitor/core@5.2.2):
resolution: {integrity: sha512-wEI7Na6PVzSP/00ud7pjbBwXwVG7HywCdy2fJT/hzF6yuHn4tDirbOvbr1JKd9LZqKs2Xn+TapV38JhBRhX6YA==}
peerDependencies:
'@capacitor/core': ^5.0.0
dependencies:
'@capacitor/core': 5.2.2
dev: false
/@capacitor/cli@3.9.0:
resolution: {integrity: sha512-NkbVZhYb0oPdh/XArE2ZmOwPFJbla5meShGhv3DxKCXeKn1rt92ile+2xOgtB/j+mL7f9cqQzTQM/11sGQzMAg==}
engines: {node: '>=12.4.0'}
@@ -3895,8 +3906,8 @@ packages:
- supports-color
dev: true
/@storybook/builder-vite@7.4.0-alpha.0(typescript@4.9.5)(vite@4.4.7):
resolution: {integrity: sha512-YklpIqtFfRLieOtYYbV4bImrbNNEFN8h3dIKM7ekshOkTw5+Ag2xx5zwwxJ1IgHu9C/Tbmgeeo+CO7YaGO56cA==}
/@storybook/builder-vite@7.4.0-alpha.1(typescript@4.9.5)(vite@4.4.7):
resolution: {integrity: sha512-BgQkoT6QlF8cRQIv/wk+utGVu9urhoMQmiv0fule/iZdiOL1oF/3CXxcWPk8FouV6fkOjvOCvO1kTvLzcwlm6g==}
peerDependencies:
'@preact/preset-vite': '*'
typescript: '>= 4.3.x'
@@ -3910,15 +3921,15 @@ packages:
vite-plugin-glimmerx:
optional: true
dependencies:
'@storybook/channels': 7.4.0-alpha.0
'@storybook/client-logger': 7.4.0-alpha.0
'@storybook/core-common': 7.4.0-alpha.0
'@storybook/csf-plugin': 7.4.0-alpha.0
'@storybook/channels': 7.4.0-alpha.1
'@storybook/client-logger': 7.4.0-alpha.1
'@storybook/core-common': 7.4.0-alpha.1
'@storybook/csf-plugin': 7.4.0-alpha.1
'@storybook/mdx2-csf': 1.1.0
'@storybook/node-logger': 7.4.0-alpha.0
'@storybook/preview': 7.4.0-alpha.0
'@storybook/preview-api': 7.4.0-alpha.0
'@storybook/types': 7.4.0-alpha.0
'@storybook/node-logger': 7.4.0-alpha.1
'@storybook/preview': 7.4.0-alpha.1
'@storybook/preview-api': 7.4.0-alpha.1
'@storybook/types': 7.4.0-alpha.1
'@types/find-cache-dir': 3.2.1
browser-assert: 1.2.1
es-module-lexer: 0.9.3
@@ -3958,14 +3969,14 @@ packages:
tiny-invariant: 1.3.1
dev: true
/@storybook/channels@7.4.0-alpha.0:
resolution: {integrity: sha512-Mh07rR524vtpDTG1BRNpHqH+BfH95DDqVIeG9YZI/z093B+MTwTAaVOcr5iReY4de01VeeC3mPDkPfvvHfqnSQ==}
/@storybook/channels@7.4.0-alpha.1:
resolution: {integrity: sha512-G02qB9LshnqIA6XLF6CWCr/CRcJHf/IAnWBCmsTFKKH4D+v7bYiVFdFf/fYAzDlfWhWkAnVL2DOXS+86XqF37A==}
dependencies:
'@storybook/client-logger': 7.4.0-alpha.0
'@storybook/core-events': 7.4.0-alpha.0
'@storybook/client-logger': 7.4.0-alpha.1
'@storybook/core-events': 7.4.0-alpha.1
'@storybook/global': 5.0.0
qs: 6.11.2
telejson: 7.1.0
telejson: 7.2.0
tiny-invariant: 1.3.1
dev: true
@@ -4032,8 +4043,8 @@ packages:
'@storybook/global': 5.0.0
dev: true
/@storybook/client-logger@7.4.0-alpha.0:
resolution: {integrity: sha512-gQTfh3gFVHpmw2CGZSWsk9qZDZuY/AdZZYC+uvyXtXLF1+TrmBkmJTWBEjk93Hm9LJu83Q+z1scCqIUyu6aGHQ==}
/@storybook/client-logger@7.4.0-alpha.1:
resolution: {integrity: sha512-/HsN2qRqlqiVkZBYKQcO+Ddr57XaQEx1TyACZ/TlT69LZoOpq5FbufvuLVOc2BO9Y3otH1IDLVc0IIV9q7U4HA==}
dependencies:
'@storybook/global': 5.0.0
dev: true
@@ -4166,11 +4177,11 @@ packages:
- supports-color
dev: true
/@storybook/core-common@7.4.0-alpha.0:
resolution: {integrity: sha512-kSxx/S8KeG7qcUm/0OCvNiDlZyge21A2kiwmLrG+EorQc6uCfKXUne8MgDT6tpjddWLu9Ek/so2HFSqBRfwBsA==}
/@storybook/core-common@7.4.0-alpha.1:
resolution: {integrity: sha512-FHCZAWuy9c1/CXULBc9V82dHyK+NQ2DqVR3lM7HRSklp7TzmdPQJo/lz/Tgr5eq9iQ988WR3UDtmye4lLG6RYQ==}
dependencies:
'@storybook/node-logger': 7.4.0-alpha.0
'@storybook/types': 7.4.0-alpha.0
'@storybook/node-logger': 7.4.0-alpha.1
'@storybook/types': 7.4.0-alpha.1
'@types/find-cache-dir': 3.2.1
'@types/node': 16.18.40
'@types/node-fetch': 2.6.4
@@ -4204,8 +4215,10 @@ packages:
resolution: {integrity: sha512-7Pkgwmj/9B7Z3NNSn2swnviBrg9L1VeYSFw6JJKxtQskt8QoY8LxAsPzVMlHjqRmO6sO7lHo9FgpzIFxdmFaAA==}
dev: true
/@storybook/core-events@7.4.0-alpha.0:
resolution: {integrity: sha512-+W41+PQVGruVKRqBmyubl88QmUj88fKefKU8qObNaJZnAicHa6ohb7cgBTD+qGuPwt1S4oWwAXFLvZxU21K6mQ==}
/@storybook/core-events@7.4.0-alpha.1:
resolution: {integrity: sha512-0nTJzu8Gr81L3kGpoYnUZPdKzsHkoVa3nMR9W0pnEY4KyJF3UAYST/qjZaMc62PfJinDz39zH3iXaF9+0tN4zA==}
dependencies:
ts-dedent: 2.2.0
dev: true
/@storybook/core-server@7.2.2:
@@ -4269,10 +4282,10 @@ packages:
- supports-color
dev: true
/@storybook/csf-plugin@7.4.0-alpha.0:
resolution: {integrity: sha512-qESMMYLm3d8kUgqdvnr3r6AJAaUmlC5fxHhcoAXz8+0PMmY2WOaP8N/k14kI+lbF9OhdNajfUkQSpB63xgWq0g==}
/@storybook/csf-plugin@7.4.0-alpha.1:
resolution: {integrity: sha512-QeDQLNRQi0co45AFV7bqf3JWf0/mKFLQDcKsMFfdN2TrazUoVu4sV4CMF1XEPpY4AF5pcfWOvAOzQ7NvbyvPOg==}
dependencies:
'@storybook/csf-tools': 7.4.0-alpha.0
'@storybook/csf-tools': 7.4.0-alpha.1
unplugin: 1.4.0
transitivePeerDependencies:
- supports-color
@@ -4294,15 +4307,15 @@ packages:
- supports-color
dev: true
/@storybook/csf-tools@7.4.0-alpha.0:
resolution: {integrity: sha512-ylzTMRRNRho8GfUz6u+SbFWmOMkyX5+XlceA4TbHz/uMQ6tjxf9S/ofDrEdkRuUCOHGnYwelLAn7DT77yBKohQ==}
/@storybook/csf-tools@7.4.0-alpha.1:
resolution: {integrity: sha512-dDExFS6JrBBPO7DMF1nLialzzcnZwdOd9tNrPBuJuwdT+BLbDNQVID4txReqMFD/gmL+YebN7Nv7JNuJubBtKA==}
dependencies:
'@babel/generator': 7.22.10
'@babel/parser': 7.22.10
'@babel/traverse': 7.22.10
'@babel/types': 7.22.10
'@storybook/csf': 0.1.1
'@storybook/types': 7.4.0-alpha.0
'@storybook/types': 7.4.0-alpha.1
fs-extra: 11.1.1
recast: 0.23.4
ts-dedent: 2.2.0
@@ -4425,8 +4438,8 @@ packages:
resolution: {integrity: sha512-UVjXJ3nRsGI+yyVFCDKFCjkzrQsUSAMORSlo5vOqypO3PjSahGQBgKjlKnZGXwvdGKB2FW56PbKnb/sPBI/kPg==}
dev: true
/@storybook/node-logger@7.4.0-alpha.0:
resolution: {integrity: sha512-oZHtVYuC2+VEhfbCWWSmra6bsMicqgyEIQY5296N9+pmQPOGoEQvkt75i1NXkJF/lTPnxRAJu699F3OYsTSQgQ==}
/@storybook/node-logger@7.4.0-alpha.1:
resolution: {integrity: sha512-GoVM7h5uRzjKZHEA7VjadLqL7J3ucAMIdHoArSsFDItvJI2Y7w29dzGm7AI0gK4C6ctBoB0Jd7uaC0tXaF1abQ==}
dev: true
/@storybook/postinstall@7.2.2:
@@ -4471,15 +4484,15 @@ packages:
util-deprecate: 1.0.2
dev: true
/@storybook/preview-api@7.4.0-alpha.0:
resolution: {integrity: sha512-iZVBsRtNST8MqQ1Sj/6nPszuL1qFKlw6ByQfWsoCgYPLAgb3nh3/vckapszAWud+YyW8QZbm53fL/Yg+aN4YUg==}
/@storybook/preview-api@7.4.0-alpha.1:
resolution: {integrity: sha512-aLidk1Y1/yGWreQcYB38Y9fr1IF1rplD6RsFg7Pg8IbCjibhrOQ1o92HqCN/cRw20hYTZ/FEpvzPCnt1druDTw==}
dependencies:
'@storybook/channels': 7.4.0-alpha.0
'@storybook/client-logger': 7.4.0-alpha.0
'@storybook/core-events': 7.4.0-alpha.0
'@storybook/channels': 7.4.0-alpha.1
'@storybook/client-logger': 7.4.0-alpha.1
'@storybook/core-events': 7.4.0-alpha.1
'@storybook/csf': 0.1.1
'@storybook/global': 5.0.0
'@storybook/types': 7.4.0-alpha.0
'@storybook/types': 7.4.0-alpha.1
'@types/qs': 6.9.7
dequal: 2.0.3
lodash: 4.17.21
@@ -4490,8 +4503,8 @@ packages:
util-deprecate: 1.0.2
dev: true
/@storybook/preview@7.4.0-alpha.0:
resolution: {integrity: sha512-qAcXK2y1/aqJWYibbNFrOgntl2RdinwmuVB2LAyzqiZK3KWu/qZQuC5fJZb8I2E4JEwzdT+V2/Upss7DMyCRAA==}
/@storybook/preview@7.4.0-alpha.1:
resolution: {integrity: sha512-LRfgOqyYiMaQBpaTgClsd0NCNClysdTwF6VTYWnEpisxtzI0HdcRwBTFLzppwnHGnZAg+zvpNjNEPjmuMvI1aw==}
dev: true
/@storybook/react-dom-shim@7.2.2(react-dom@18.2.0)(react@18.2.0):
@@ -4600,10 +4613,10 @@ packages:
file-system-cache: 2.3.0
dev: true
/@storybook/types@7.4.0-alpha.0:
resolution: {integrity: sha512-fUkrqGaI3kn8lfvcshmaNmQXs2LXG91iZSts2Sbmbk/Gnr1CnqV/NxIFc+qOAiesOwaLf9a7zD8+/izsLQUtQw==}
/@storybook/types@7.4.0-alpha.1:
resolution: {integrity: sha512-YHLi1ypmS82xpkuSCFeAfmRf4xjJl6+QdO+a4JfIbqjuKApyDl78czawXkrjT638fbw7Rz0gbySrLYUju7WntA==}
dependencies:
'@storybook/channels': 7.4.0-alpha.0
'@storybook/channels': 7.4.0-alpha.1
'@types/babel__core': 7.20.1
'@types/express': 4.17.17
file-system-cache: 2.3.0
@@ -11980,7 +11993,7 @@ packages:
resolution: {integrity: sha512-dD+VMYC5fBBQNesVb+mjB0LOkZIf100SQFbjAt9/sDstNUvc5ce3yZwLYXzgcOc7jcSMkrBu/cZNRzEM4YIAyw==}
engines: {node: ^14.18 || >=16}
dependencies:
'@storybook/builder-vite': 7.4.0-alpha.0(typescript@4.9.5)(vite@4.4.7)
'@storybook/builder-vite': 7.4.0-alpha.1(typescript@4.9.5)(vite@4.4.7)
transitivePeerDependencies:
- '@preact/preset-vite'
- encoding
@@ -12279,6 +12292,12 @@ packages:
memoizerific: 1.11.3
dev: true
/telejson@7.2.0:
resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==}
dependencies:
memoizerific: 1.11.3
dev: true
/temp-dir@2.0.0:
resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
engines: {node: '>=8'}

View File

@@ -0,0 +1,9 @@
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.mutinywallet.mutinywallet",
"sha256_cert_fingerprints":
["F8:20:46:16:9B:79:AA:D1:2A:77:CA:D6:1F:0E:F6:A4:D7:72:3A:5E:DE:C6:70:18:E9:9E:B4:DA:0B:44:17:60"]
}
}]

View File

@@ -1,6 +1,4 @@
import { App as CapacitorApp } from "@capacitor/app";
import { Capacitor } from "@capacitor/core";
import { Match, onCleanup, Show, Suspense, Switch } from "solid-js";
import { Match, Show, Suspense, Switch } from "solid-js";
import { A } from "solid-start";
import settings from "~/assets/icons/settings.svg";
@@ -30,25 +28,6 @@ export function App() {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
// Check if the platform is Android to handle back
if (Capacitor.getPlatform() === "android") {
const { remove } = CapacitorApp.addListener(
"backButton",
({ canGoBack }) => {
if (!canGoBack) {
CapacitorApp.exitApp();
} else {
window.history.back();
}
}
);
// Ensure the listener is cleaned up when the component is destroyed
onCleanup(() => {
remove();
});
}
return (
<SafeArea>
<DefaultMain>

View File

@@ -1,5 +1,5 @@
// @refresh reload
import { Suspense } from "solid-js";
import { onCleanup, Suspense } from "solid-js";
import {
Body,
ErrorBoundary,
@@ -10,15 +10,62 @@ import {
Meta,
Routes,
Scripts,
Title
Title,
useNavigate
} from "solid-start";
// eslint-disable-next-line
import "@mutinywallet/ui/style.css";
import { App as CapacitorApp } from "@capacitor/app";
import { Capacitor } from "@capacitor/core";
import { ErrorDisplay, I18nProvider, Toaster } from "~/components";
import { Provider as MegaStoreProvider } from "~/state/megaStore";
function GlobalListeners() {
// listeners for native navigation handling
// Check if the platform is Android to handle back
if (Capacitor.getPlatform() === "android") {
const { remove } = CapacitorApp.addListener(
"backButton",
({ canGoBack }) => {
if (!canGoBack) {
CapacitorApp.exitApp();
} else {
window.history.back();
}
}
);
// Ensure the listener is cleaned up when the component is destroyed
onCleanup(() => {
console.debug("cleaning up backButton listener");
remove();
});
}
// Handle app links on native platforms
if (Capacitor.isNativePlatform()) {
const navigate = useNavigate();
const { remove } = CapacitorApp.addListener("appUrlOpen", (data) => {
const url = new URL(data.url);
const path = url.pathname;
const urlParams = new URLSearchParams(url.search);
console.log(`Navigating to ${path}?${urlParams.toString()}`);
navigate(`${path}?${urlParams.toString()}`);
});
onCleanup(() => {
console.debug("cleaning up appUrlOpen listener");
remove();
});
}
return null;
}
export default function Root() {
return (
<Html lang="en">
@@ -81,6 +128,7 @@ export default function Root() {
<I18nProvider>
<MegaStoreProvider>
<Routes>
<GlobalListeners />
<FileRoutes />
</Routes>
<Toaster />

View File

@@ -1,6 +1,8 @@
import { Browser } from "@capacitor/browser";
import { NwcProfile } from "@mutinywallet/mutiny-wasm";
import { createResource, createSignal, For, Show } from "solid-js";
import { QRCodeSVG } from "solid-qr-code";
import { useSearchParams } from "solid-start";
import {
BackLink,
@@ -40,8 +42,8 @@ function Nwc() {
}
});
const urlParams = new URLSearchParams(window.location.search);
const queryName = urlParams.get("name");
const [searchParams, setSearchParams] = useSearchParams();
const queryName = searchParams.name;
const [formName, setFormName] = createSignal(queryName || "");
const [dialogOpen, setDialogOpen] = createSignal(!!queryName);
const [createLoading, setCreateLoading] = createSignal(false);
@@ -69,14 +71,14 @@ function Nwc() {
refetch();
}
setFormName("");
setSearchParams({ name: "" });
setDialogOpen(false);
const callbackUriScheme = getCallbackQueryParam();
const callbackUriScheme = searchParams.callbackUri;
if (callbackUriScheme) {
const fullURI = profile.nwc_uri.replace(
"nostr+walletconnect://",
`${getCallbackQueryParam()}://`
`${callbackUriScheme}://`
);
setCallbackUri(fullURI);
setCallbackDialogOpen(true);
@@ -91,7 +93,8 @@ function Nwc() {
function openCallbackUri() {
if (callbackUri()) {
window.open(callbackUri() as string, "_blank");
Browser.open({ url: callbackUri() as string });
setSearchParams({ callbackUri: "" });
setCallbackDialogOpen(false);
}
}
@@ -115,17 +118,12 @@ function Nwc() {
}
function openInNostrClient(uri: string) {
window.open(uri, "_blank");
Browser.open({ url: uri });
}
function openInPrimal(uri: string) {
const connectString = uri.replace("nostr+walletconnect", "primal");
window.open(connectString, "_blank");
}
function getCallbackQueryParam() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get("callbackUri");
Browser.open({ url: connectString });
}
return (

View File

@@ -65,7 +65,8 @@ export default defineConfig({
"@capacitor/filesystem",
"@capacitor/toast",
"@mutinywallet/barcode-scanner",
"@capacitor/app"
"@capacitor/app",
"@capacitor/browser"
],
// This is necessary because otherwise `vite dev` can't find the wasm
exclude: ["@mutinywallet/mutiny-wasm", "@mutinywallet/waila-wasm"]