From 6eb7188ec652c981d2a9269596dbe85d7e2e3965 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Sun, 12 Nov 2023 17:10:36 +0100 Subject: [PATCH] Updated ionic-android --- .../android/app/capacitor.build.gradle | 5 +- .../app/src/main/AndroidManifest.xml.orig | 48 -- .../android/capacitor.settings.gradle | 13 +- openvidu-ionic/capacitor.config.ts | 21 +- openvidu-ionic/package-lock.json | 425 +++++++++++------- openvidu-ionic/package.json | 12 +- openvidu-ionic/scripts/mobile_run.sh | 49 ++ .../src/app/components/ov-video.component.ts | 4 +- openvidu-ionic/src/app/home/home.module.ts | 4 + openvidu-ionic/src/app/home/home.page.html | 67 +-- openvidu-ionic/src/app/home/home.page.scss | 35 +- openvidu-ionic/src/app/home/home.page.ts | 106 ++++- .../src/assets/images/video-poster.png | Bin 0 -> 15076 bytes .../src/environments/environment.prod.ts | 4 +- .../src/environments/environment.ts | 13 +- 15 files changed, 498 insertions(+), 308 deletions(-) delete mode 100644 openvidu-ionic/android/app/src/main/AndroidManifest.xml.orig create mode 100755 openvidu-ionic/scripts/mobile_run.sh create mode 100644 openvidu-ionic/src/assets/images/video-poster.png diff --git a/openvidu-ionic/android/app/capacitor.build.gradle b/openvidu-ionic/android/app/capacitor.build.gradle index 1c2a7a88..e552d498 100644 --- a/openvidu-ionic/android/app/capacitor.build.gradle +++ b/openvidu-ionic/android/app/capacitor.build.gradle @@ -9,10 +9,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { - implementation project(':capacitor-app') - implementation project(':capacitor-haptics') - implementation project(':capacitor-keyboard') - implementation project(':capacitor-status-bar') + implementation project(':jcesarmobile-ssl-skip') } diff --git a/openvidu-ionic/android/app/src/main/AndroidManifest.xml.orig b/openvidu-ionic/android/app/src/main/AndroidManifest.xml.orig deleted file mode 100644 index 99bdcb3f..00000000 --- a/openvidu-ionic/android/app/src/main/AndroidManifest.xml.orig +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/openvidu-ionic/android/capacitor.settings.gradle b/openvidu-ionic/android/capacitor.settings.gradle index bd358734..87763e6a 100644 --- a/openvidu-ionic/android/capacitor.settings.gradle +++ b/openvidu-ionic/android/capacitor.settings.gradle @@ -2,14 +2,5 @@ include ':capacitor-android' project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') -include ':capacitor-app' -project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') - -include ':capacitor-haptics' -project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android') - -include ':capacitor-keyboard' -project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android') - -include ':capacitor-status-bar' -project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') +include ':jcesarmobile-ssl-skip' +project(':jcesarmobile-ssl-skip').projectDir = new File('../node_modules/@jcesarmobile/ssl-skip/android') diff --git a/openvidu-ionic/capacitor.config.ts b/openvidu-ionic/capacitor.config.ts index 27adcfeb..ba3c9e5c 100644 --- a/openvidu-ionic/capacitor.config.ts +++ b/openvidu-ionic/capacitor.config.ts @@ -1,12 +1,31 @@ import { CapacitorConfig } from '@capacitor/cli'; +import {environment} from './src/environments/environment'; const config: CapacitorConfig = { appId: 'io.ionic.starter', appName: 'openvidu-ionic', webDir: 'www', server: { - androidScheme: 'https' + androidScheme: 'http', + iosScheme: 'http', } }; +if (environment.externalIp) { + config.server = config.server || {}; + // config.server.hostname = 'localhost'; + config.server.cleartext = true; + config.includePlugins = config.includePlugins || []; + // '@jcesarmobile/ssl-skip' plugin is needed to allow serve the app over HTTPS + // with a self-signed certificate without installing the certificate in the device + config.includePlugins.push('@jcesarmobile/ssl-skip', 'cordova-plugin-android-permissions'); + + // Android configuration + config.android = config.android || {}; + config.android.allowMixedContent = true; + + // iOS configuration + +} + export default config; diff --git a/openvidu-ionic/package-lock.json b/openvidu-ionic/package-lock.json index 21458724..cff2e536 100644 --- a/openvidu-ionic/package-lock.json +++ b/openvidu-ionic/package-lock.json @@ -16,6 +16,8 @@ "@angular/platform-browser": "^16.0.0", "@angular/platform-browser-dynamic": "^16.0.0", "@angular/router": "^16.0.0", + "@awesome-cordova-plugins/android-permissions": "6.4.0", + "@awesome-cordova-plugins/core": "6.4.0", "@capacitor/android": "5.5.0", "@capacitor/app": "5.0.6", "@capacitor/core": "5.5.0", @@ -23,6 +25,7 @@ "@capacitor/keyboard": "5.0.6", "@capacitor/status-bar": "5.0.6", "@ionic/angular": "^7.0.0", + "cordova-plugin-android-permissions": "^1.1.5", "ionicons": "^7.0.0", "livekit-client": "^1.14.4", "rxjs": "~7.8.0", @@ -41,6 +44,7 @@ "@angular/language-service": "^16.0.0", "@capacitor/cli": "5.5.0", "@ionic/angular-toolkit": "^9.0.0", + "@jcesarmobile/ssl-skip": "^0.2.0", "@types/jasmine": "~4.3.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "5.3.0", @@ -84,12 +88,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1602.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.9.tgz", - "integrity": "sha512-U3vfb/e2sFfg0D9FyyRBXRPP7g4FBFtGK8Q3JPmvAVsHHwi5AUFRNR7YBChB/T5TMNY077HcTyEirVh2FeUpdA==", + "version": "0.1602.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.10.tgz", + "integrity": "sha512-FwemQXh3edqA/S6zPpsqKei5v7gt0R0WpjJoAJaz+FOpfDwij1fwnKr88XINY8xcefTcQaTDQxJZheJShA/hHw==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.9", + "@angular-devkit/core": "16.2.10", "rxjs": "7.8.1" }, "engines": { @@ -99,15 +103,15 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.9.tgz", - "integrity": "sha512-S1C4UYxRVyNt3C0wCxbT2jZ1dN5i37kS0mol3PQjbR8gQ0GQzHmzhjTBl1oImo8aouET9yhrk9etk65oat4mBQ==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.10.tgz", + "integrity": "sha512-msB/qjIsAOySDxdU5DpcX2sWGUEJOFIO03O9+HbtLwf3NDfe74mFfejxuKlHJXIJdgpM2Zc948M6+618QKpUYA==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1602.9", - "@angular-devkit/build-webpack": "0.1602.9", - "@angular-devkit/core": "16.2.9", + "@angular-devkit/architect": "0.1602.10", + "@angular-devkit/build-webpack": "0.1602.10", + "@angular-devkit/core": "16.2.10", "@babel/core": "7.22.9", "@babel/generator": "7.22.9", "@babel/helper-annotate-as-pure": "7.22.5", @@ -119,7 +123,7 @@ "@babel/runtime": "7.22.6", "@babel/template": "7.22.5", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "16.2.9", + "@ngtools/webpack": "16.2.10", "@vitejs/plugin-basic-ssl": "1.0.1", "ansi-colors": "4.1.3", "autoprefixer": "10.4.14", @@ -305,12 +309,12 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1602.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.9.tgz", - "integrity": "sha512-+3IxovfBPR2Vy730mGa0SVKkd5LQVom85gjXOs7WcnnnZmfc1q/BtFlqTgW1UWvTxP8IQdm7UYWVclQfL/WExw==", + "version": "0.1602.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.10.tgz", + "integrity": "sha512-H7HiFKbZl/xVxpr1RH05SGawTpA1417wvr2nFGRu2OiePd0lPr6pIhcq8F8gt7JcA8yZKKaqjn2gU+6um2MFLg==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1602.9", + "@angular-devkit/architect": "0.1602.10", "rxjs": "7.8.1" }, "engines": { @@ -324,9 +328,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.9.tgz", - "integrity": "sha512-dcHWjHBNGm3yCeNz19y8A1At4KgyC6XHNnbFL0y+nnZYiaESXjUoXJYKASedI6A+Bpl0HNq2URhH6bL6Af3+4w==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.10.tgz", + "integrity": "sha512-eo7suLDjyu5bSlEr4TluYkFm4v2PVLSAPgnau8XHHlN5Yg4P/BZ00ve7LA7C9S1gzRSCrxQhK5ki4rnoFTo5zg==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -351,12 +355,12 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.9.tgz", - "integrity": "sha512-lB51CGCILpcSI37CwKUAGDLxMqh7zmuRbiPo9s9mSkCM4ccqxFlaL+VFTq2/laneARD6aikpOHnkVm5myNzQPw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.10.tgz", + "integrity": "sha512-UCfPJKVNekb21bWRbzyx81tfHN3x8vU4ZMX/VA6xALg//QalMB7NOkkXBAssthnLastkyzkUtlvApTp2+R+EkQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.9", + "@angular-devkit/core": "16.2.10", "jsonc-parser": "3.2.0", "magic-string": "0.30.1", "ora": "5.4.1", @@ -369,9 +373,9 @@ } }, "node_modules/@angular-eslint/builder": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-16.2.0.tgz", - "integrity": "sha512-SZjXOi3YIjuX2CocuRsR2QH6k1ca9lRO6IMm0YIYMmBPFCRP2KFHkL6aQnXM6DSaymQNN2TXfpuvUd45NxhU1w==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-16.3.1.tgz", + "integrity": "sha512-PmIOnRwqdOW1bvZtpTGBTDcOq/Czm3D+IPC/k90yIMs1VsAtcxqUmUtELje+ylJeb2LPeEZavekSnEpcatM4HQ==", "dev": true, "dependencies": { "@nx/devkit": "16.5.1", @@ -383,18 +387,18 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-16.2.0.tgz", - "integrity": "sha512-ct9orDYxkMl2+uvM7UBfgV28Dq57V4dEs+Drh7cD673JIMa6sXbgmd0QEtm8W3cmyK/jcTzmuoufxbH7hOxd6g==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-16.3.1.tgz", + "integrity": "sha512-m4WP1xwS9XLcC/3n6lIcG5HZoai/5eb5W3xm48GVcv//0qE2p7S96RSgKPgGHvif5pF8O9xAqEWs3gDEG45+7A==", "dev": true }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-16.2.0.tgz", - "integrity": "sha512-zdiAIox1T+B71HL+A8m+1jWdU34nvPGLhCRw/uZNwHzknsF4tYzNQ9W7T/SC/g/2s1yT2yNosEVNJSGSFvunJg==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-16.3.1.tgz", + "integrity": "sha512-kSc8ESfoy8TUSthbq0Lpq9e17I+3Smy4rHoNpKCFEGuJgPs0+OssZMxB6a5EawGbv2EKTPEtrxzFm1WsLR0U9Q==", "dev": true, "dependencies": { - "@angular-eslint/utils": "16.2.0", + "@angular-eslint/utils": "16.3.1", "@typescript-eslint/utils": "5.62.0" }, "peerDependencies": { @@ -403,17 +407,17 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-16.2.0.tgz", - "integrity": "sha512-YFdQ6hHX6NlQj0lfogZwfyKjU8pqkJU+Zsk0ehjlXP8VfKFVmDeQT5/Xr6Df9C8pveC3hvq6Jgd8vo67S9Enxg==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-16.3.1.tgz", + "integrity": "sha512-+RcFEWqNiRt3+5jXvmlIDlXtP9+vjdmgmVL6tt8yDbqdjBOewtyMu4pE4YaR4sFboyxgME9PbO2WrOyPXh6xjg==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "16.2.0", - "@angular-eslint/utils": "16.2.0", + "@angular-eslint/bundled-angular-compiler": "16.3.1", + "@angular-eslint/utils": "16.3.1", "@typescript-eslint/type-utils": "5.62.0", "@typescript-eslint/utils": "5.62.0", "aria-query": "5.3.0", - "axobject-query": "3.2.1" + "axobject-query": "4.0.0" }, "peerDependencies": { "eslint": "^7.20.0 || ^8.0.0", @@ -421,13 +425,13 @@ } }, "node_modules/@angular-eslint/schematics": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-16.2.0.tgz", - "integrity": "sha512-2JUVR7hAKx37mgWeDjvyWEMH5uSeeksYuaQT5wwlgIzgrO4BNFuqs6Rgyp2jiYa7BFMX/qHULSa+bSq5J5ceEA==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-16.3.1.tgz", + "integrity": "sha512-cqrdobdtRY2XjLa6PhuGOQ7UhTRk2AvWS01sKeGjZ94nQJm5NUtEUyf6u3deIPYllW7gSAWjYzKUObKcTW/R+g==", "dev": true, "dependencies": { - "@angular-eslint/eslint-plugin": "16.2.0", - "@angular-eslint/eslint-plugin-template": "16.2.0", + "@angular-eslint/eslint-plugin": "16.3.1", + "@angular-eslint/eslint-plugin-template": "16.3.1", "@nx/devkit": "16.5.1", "ignore": "5.2.4", "nx": "16.5.1", @@ -439,12 +443,12 @@ } }, "node_modules/@angular-eslint/template-parser": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-16.2.0.tgz", - "integrity": "sha512-v2jVKTy2wN7iM9nHpBkxLn2wfL8jSl4IlPrXThIqj8No2VHtpLQZPKuXbGPUXQX05VS2Mj5feScQ36ZVGS8Rbw==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-16.3.1.tgz", + "integrity": "sha512-9+SxUtxB2iOnm0ldS2ow0stMxe02rB/TxeMIe8fxsLFHZdw8RQvs/p3HLvVHXzv6gUblMHebIb/ubUmwEVb2SA==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "16.2.0", + "@angular-eslint/bundled-angular-compiler": "16.3.1", "eslint-scope": "^7.0.0" }, "peerDependencies": { @@ -453,12 +457,12 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-16.2.0.tgz", - "integrity": "sha512-NxMRwnlIgzmbJQfWkfd9y3Sz0hzjFdK5LH44i+3D5NhpPdZ6SzwHAjMYWoYsmmNQX5tlDXoicYF9Mz9Wz8DJ/A==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-16.3.1.tgz", + "integrity": "sha512-tEBcce0rG+DmcPO8jhRffUFDioGw3G4cUAE15XlRctY1J3QzOBH9HdUOTDt0mMjBgpWCzh0YVT1Moh2bPXU9Xg==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "16.2.0", + "@angular-eslint/bundled-angular-compiler": "16.3.1", "@typescript-eslint/utils": "5.62.0" }, "peerDependencies": { @@ -481,15 +485,15 @@ } }, "node_modules/@angular/cli": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.9.tgz", - "integrity": "sha512-wkpV/Ni26LUeDmhee2TPXXEq3feEdZMSG8+nkfUK9kqIcxm0IjI1GLPeiVOX7aQobuKNe2cCAFNwsrXWjj+2og==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.10.tgz", + "integrity": "sha512-zDqlD+rXFuYZP169c2v35HkMbkchVCft5sS+VpoCCgYTk2rwxpeYkjJ8DQZztZJZRXQ+EMpkv/TubswmDro2zA==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1602.9", - "@angular-devkit/core": "16.2.9", - "@angular-devkit/schematics": "16.2.9", - "@schematics/angular": "16.2.9", + "@angular-devkit/architect": "0.1602.10", + "@angular-devkit/core": "16.2.10", + "@angular-devkit/schematics": "16.2.10", + "@schematics/angular": "16.2.10", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "ini": "4.1.1", @@ -752,6 +756,29 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", "dev": true }, + "node_modules/@awesome-cordova-plugins/android-permissions": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/android-permissions/-/android-permissions-6.4.0.tgz", + "integrity": "sha512-JmJW/WMM9yOeYl3G9C5OIucpgp7E+71qhN1aHPZQSIKivHGRFhZrX/K4lBjKqrIra4dVIV5swCqfQNwCHglLww==", + "dependencies": { + "@types/cordova": "latest" + }, + "peerDependencies": { + "@awesome-cordova-plugins/core": "^6.0.1", + "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0" + } + }, + "node_modules/@awesome-cordova-plugins/core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/core/-/core-6.4.0.tgz", + "integrity": "sha512-06I5JdTgAgKTby+VG+3sQF5+z4RNCEyVl6y7tz2rICx8MURL1biuh3oGGN1rCQQjsuMZcX5siMBr0NF/OHqxLQ==", + "dependencies": { + "@types/cordova": "latest" + }, + "peerDependencies": { + "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -3134,11 +3161,11 @@ "dev": true }, "node_modules/@ionic/angular": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-7.5.3.tgz", - "integrity": "sha512-LgTF8nNFj0B71E98gPEAP4cbSAnNfpDG82l/FGtozG83nto60wp13rWPtVuqG+xLSZqHgV6vN6d9W+2NfE46SQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-7.5.4.tgz", + "integrity": "sha512-TmySfb2HgFoZZaIvIyRKXhH68rHg8TMcrtVP/cTPv3NJiGDH3TGvbIdQmUtdjQ+WUqlgwCsuk7rNgSHtrC5F0w==", "dependencies": { - "@ionic/core": "7.5.3", + "@ionic/core": "7.5.4", "ionicons": "^7.0.0", "jsonc-parser": "^3.0.0", "tslib": "^2.3.0" @@ -3267,11 +3294,11 @@ } }, "node_modules/@ionic/core": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.3.tgz", - "integrity": "sha512-nd4NV6gSgohLvKHGmf+jAcgzI1387eKq3enjBfE574FK/ikl5CeIltv0MBVJr9lz0pNSL/y9m1tdaFg+syASuA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.4.tgz", + "integrity": "sha512-rZbKlcVucRTDOK2Woh4CWPePlsXiUt3G9dCUniduKD7WeiuAk0GzfmoM3WXBvcUpkVTUIOrvKHaqd3JJSWEIzg==", "dependencies": { - "@stencil/core": "^4.6.0", + "@stencil/core": "^4.7.1", "ionicons": "^7.2.1", "tslib": "^2.1.0" } @@ -3348,9 +3375,9 @@ } }, "node_modules/@ionic/utils-subprocess": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.13.tgz", - "integrity": "sha512-wguf0zfmca1UA1uF2kmbr/4jukrizlYlpIdtG1FdEaGrof8d5djdwg+g3jHe5dTR88urUQ3Gw68Yhs242dgGrg==", + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.14.tgz", + "integrity": "sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg==", "dev": true, "dependencies": { "@ionic/utils-array": "2.1.6", @@ -3507,6 +3534,12 @@ "node": ">=8" } }, + "node_modules/@jcesarmobile/ssl-skip": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@jcesarmobile/ssl-skip/-/ssl-skip-0.2.0.tgz", + "integrity": "sha512-hkhOM5+qPm5xzOYcbUODzYTZKyATEM3qixzUL1gJsJckZ5wAUQxyZJFM3hcUANv8fV8cONfX7A+d/DJpzVvM+Q==", + "dev": true + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -3572,9 +3605,9 @@ "dev": true }, "node_modules/@ngtools/webpack": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.9.tgz", - "integrity": "sha512-rOclD7FfT4OSwVA0nDnULbJS6TORJ0+sQiuT2ebaNFErYr3LOm6Zut05tnmzFw8q1cePrILbG+xpnbggNr9Pyw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.10.tgz", + "integrity": "sha512-XAVn59zP3ztuKDtw92Xc9+64RK4u4c9g8y5GgtjIWeOwgNXl8bYhAo3uTZzrSrOu96DFZGjsmghFab/7/C2pDg==", "dev": true, "engines": { "node": "^16.14.0 || >=18.10.0", @@ -4061,13 +4094,13 @@ } }, "node_modules/@schematics/angular": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.9.tgz", - "integrity": "sha512-uiU2YbZRVHgk1N1DDsek/5CKhfpZ8myJYNJk8eHV5LswnXOP3aqvH23VhneaAgOYwK5fISC7eMG0pLVKMvFfZQ==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.10.tgz", + "integrity": "sha512-PXmoswvN7qknTsXDmEvhZ9UG+awwWnQ/1Jd/eqqQx08iAaAT81OsXj1bN7eSs6tEGBKGjPb6q2xzuiECAdymzg==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.9", - "@angular-devkit/schematics": "16.2.9", + "@angular-devkit/core": "16.2.10", + "@angular-devkit/schematics": "16.2.10", "jsonc-parser": "3.2.0" }, "engines": { @@ -4325,6 +4358,11 @@ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", "dev": true }, + "node_modules/@types/cordova": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-11.0.3.tgz", + "integrity": "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg==" + }, "node_modules/@types/cors": { "version": "2.8.16", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.16.tgz", @@ -5656,9 +5694,9 @@ } }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -5681,9 +5719,9 @@ } }, "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", + "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", "dev": true, "dependencies": { "dequal": "^2.0.3" @@ -6631,6 +6669,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cordova-plugin-android-permissions": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/cordova-plugin-android-permissions/-/cordova-plugin-android-permissions-1.1.5.tgz", + "integrity": "sha512-oTTV9cCMBqXTCmU+nYRebsP2IQfrtdvl2vYXHjoJgv5NHCIDgY4KFg6kJTcwXQOiymeGXuw0+MTvJJOueAdleA==", + "engines": [ + { + "name": "cordova", + "version": ">=5.0.0" + } + ] + }, "node_modules/core-js-compat": { "version": "3.33.2", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.2.tgz", @@ -10171,9 +10220,9 @@ } }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.1.tgz", - "integrity": "sha512-opCrKqbthmq3SKZ10mFMQG9dk3fTa3quaOLD35kJa5ejwZHd9xAr+kLuziiZz2cG32s4lMZxNdmdcEQnTDP4+g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" @@ -17326,25 +17375,25 @@ } }, "@angular-devkit/architect": { - "version": "0.1602.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.9.tgz", - "integrity": "sha512-U3vfb/e2sFfg0D9FyyRBXRPP7g4FBFtGK8Q3JPmvAVsHHwi5AUFRNR7YBChB/T5TMNY077HcTyEirVh2FeUpdA==", + "version": "0.1602.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.10.tgz", + "integrity": "sha512-FwemQXh3edqA/S6zPpsqKei5v7gt0R0WpjJoAJaz+FOpfDwij1fwnKr88XINY8xcefTcQaTDQxJZheJShA/hHw==", "dev": true, "requires": { - "@angular-devkit/core": "16.2.9", + "@angular-devkit/core": "16.2.10", "rxjs": "7.8.1" } }, "@angular-devkit/build-angular": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.9.tgz", - "integrity": "sha512-S1C4UYxRVyNt3C0wCxbT2jZ1dN5i37kS0mol3PQjbR8gQ0GQzHmzhjTBl1oImo8aouET9yhrk9etk65oat4mBQ==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.10.tgz", + "integrity": "sha512-msB/qjIsAOySDxdU5DpcX2sWGUEJOFIO03O9+HbtLwf3NDfe74mFfejxuKlHJXIJdgpM2Zc948M6+618QKpUYA==", "dev": true, "requires": { "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1602.9", - "@angular-devkit/build-webpack": "0.1602.9", - "@angular-devkit/core": "16.2.9", + "@angular-devkit/architect": "0.1602.10", + "@angular-devkit/build-webpack": "0.1602.10", + "@angular-devkit/core": "16.2.10", "@babel/core": "7.22.9", "@babel/generator": "7.22.9", "@babel/helper-annotate-as-pure": "7.22.5", @@ -17356,7 +17405,7 @@ "@babel/runtime": "7.22.6", "@babel/template": "7.22.5", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "16.2.9", + "@ngtools/webpack": "16.2.10", "@vitejs/plugin-basic-ssl": "1.0.1", "ansi-colors": "4.1.3", "autoprefixer": "10.4.14", @@ -17447,19 +17496,19 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.1602.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.9.tgz", - "integrity": "sha512-+3IxovfBPR2Vy730mGa0SVKkd5LQVom85gjXOs7WcnnnZmfc1q/BtFlqTgW1UWvTxP8IQdm7UYWVclQfL/WExw==", + "version": "0.1602.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.10.tgz", + "integrity": "sha512-H7HiFKbZl/xVxpr1RH05SGawTpA1417wvr2nFGRu2OiePd0lPr6pIhcq8F8gt7JcA8yZKKaqjn2gU+6um2MFLg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1602.9", + "@angular-devkit/architect": "0.1602.10", "rxjs": "7.8.1" } }, "@angular-devkit/core": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.9.tgz", - "integrity": "sha512-dcHWjHBNGm3yCeNz19y8A1At4KgyC6XHNnbFL0y+nnZYiaESXjUoXJYKASedI6A+Bpl0HNq2URhH6bL6Af3+4w==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.10.tgz", + "integrity": "sha512-eo7suLDjyu5bSlEr4TluYkFm4v2PVLSAPgnau8XHHlN5Yg4P/BZ00ve7LA7C9S1gzRSCrxQhK5ki4rnoFTo5zg==", "dev": true, "requires": { "ajv": "8.12.0", @@ -17471,12 +17520,12 @@ } }, "@angular-devkit/schematics": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.9.tgz", - "integrity": "sha512-lB51CGCILpcSI37CwKUAGDLxMqh7zmuRbiPo9s9mSkCM4ccqxFlaL+VFTq2/laneARD6aikpOHnkVm5myNzQPw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.10.tgz", + "integrity": "sha512-UCfPJKVNekb21bWRbzyx81tfHN3x8vU4ZMX/VA6xALg//QalMB7NOkkXBAssthnLastkyzkUtlvApTp2+R+EkQ==", "dev": true, "requires": { - "@angular-devkit/core": "16.2.9", + "@angular-devkit/core": "16.2.10", "jsonc-parser": "3.2.0", "magic-string": "0.30.1", "ora": "5.4.1", @@ -17484,9 +17533,9 @@ } }, "@angular-eslint/builder": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-16.2.0.tgz", - "integrity": "sha512-SZjXOi3YIjuX2CocuRsR2QH6k1ca9lRO6IMm0YIYMmBPFCRP2KFHkL6aQnXM6DSaymQNN2TXfpuvUd45NxhU1w==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-16.3.1.tgz", + "integrity": "sha512-PmIOnRwqdOW1bvZtpTGBTDcOq/Czm3D+IPC/k90yIMs1VsAtcxqUmUtELje+ylJeb2LPeEZavekSnEpcatM4HQ==", "dev": true, "requires": { "@nx/devkit": "16.5.1", @@ -17494,43 +17543,43 @@ } }, "@angular-eslint/bundled-angular-compiler": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-16.2.0.tgz", - "integrity": "sha512-ct9orDYxkMl2+uvM7UBfgV28Dq57V4dEs+Drh7cD673JIMa6sXbgmd0QEtm8W3cmyK/jcTzmuoufxbH7hOxd6g==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-16.3.1.tgz", + "integrity": "sha512-m4WP1xwS9XLcC/3n6lIcG5HZoai/5eb5W3xm48GVcv//0qE2p7S96RSgKPgGHvif5pF8O9xAqEWs3gDEG45+7A==", "dev": true }, "@angular-eslint/eslint-plugin": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-16.2.0.tgz", - "integrity": "sha512-zdiAIox1T+B71HL+A8m+1jWdU34nvPGLhCRw/uZNwHzknsF4tYzNQ9W7T/SC/g/2s1yT2yNosEVNJSGSFvunJg==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-16.3.1.tgz", + "integrity": "sha512-kSc8ESfoy8TUSthbq0Lpq9e17I+3Smy4rHoNpKCFEGuJgPs0+OssZMxB6a5EawGbv2EKTPEtrxzFm1WsLR0U9Q==", "dev": true, "requires": { - "@angular-eslint/utils": "16.2.0", + "@angular-eslint/utils": "16.3.1", "@typescript-eslint/utils": "5.62.0" } }, "@angular-eslint/eslint-plugin-template": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-16.2.0.tgz", - "integrity": "sha512-YFdQ6hHX6NlQj0lfogZwfyKjU8pqkJU+Zsk0ehjlXP8VfKFVmDeQT5/Xr6Df9C8pveC3hvq6Jgd8vo67S9Enxg==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-16.3.1.tgz", + "integrity": "sha512-+RcFEWqNiRt3+5jXvmlIDlXtP9+vjdmgmVL6tt8yDbqdjBOewtyMu4pE4YaR4sFboyxgME9PbO2WrOyPXh6xjg==", "dev": true, "requires": { - "@angular-eslint/bundled-angular-compiler": "16.2.0", - "@angular-eslint/utils": "16.2.0", + "@angular-eslint/bundled-angular-compiler": "16.3.1", + "@angular-eslint/utils": "16.3.1", "@typescript-eslint/type-utils": "5.62.0", "@typescript-eslint/utils": "5.62.0", "aria-query": "5.3.0", - "axobject-query": "3.2.1" + "axobject-query": "4.0.0" } }, "@angular-eslint/schematics": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-16.2.0.tgz", - "integrity": "sha512-2JUVR7hAKx37mgWeDjvyWEMH5uSeeksYuaQT5wwlgIzgrO4BNFuqs6Rgyp2jiYa7BFMX/qHULSa+bSq5J5ceEA==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-16.3.1.tgz", + "integrity": "sha512-cqrdobdtRY2XjLa6PhuGOQ7UhTRk2AvWS01sKeGjZ94nQJm5NUtEUyf6u3deIPYllW7gSAWjYzKUObKcTW/R+g==", "dev": true, "requires": { - "@angular-eslint/eslint-plugin": "16.2.0", - "@angular-eslint/eslint-plugin-template": "16.2.0", + "@angular-eslint/eslint-plugin": "16.3.1", + "@angular-eslint/eslint-plugin-template": "16.3.1", "@nx/devkit": "16.5.1", "ignore": "5.2.4", "nx": "16.5.1", @@ -17539,22 +17588,22 @@ } }, "@angular-eslint/template-parser": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-16.2.0.tgz", - "integrity": "sha512-v2jVKTy2wN7iM9nHpBkxLn2wfL8jSl4IlPrXThIqj8No2VHtpLQZPKuXbGPUXQX05VS2Mj5feScQ36ZVGS8Rbw==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-16.3.1.tgz", + "integrity": "sha512-9+SxUtxB2iOnm0ldS2ow0stMxe02rB/TxeMIe8fxsLFHZdw8RQvs/p3HLvVHXzv6gUblMHebIb/ubUmwEVb2SA==", "dev": true, "requires": { - "@angular-eslint/bundled-angular-compiler": "16.2.0", + "@angular-eslint/bundled-angular-compiler": "16.3.1", "eslint-scope": "^7.0.0" } }, "@angular-eslint/utils": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-16.2.0.tgz", - "integrity": "sha512-NxMRwnlIgzmbJQfWkfd9y3Sz0hzjFdK5LH44i+3D5NhpPdZ6SzwHAjMYWoYsmmNQX5tlDXoicYF9Mz9Wz8DJ/A==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-16.3.1.tgz", + "integrity": "sha512-tEBcce0rG+DmcPO8jhRffUFDioGw3G4cUAE15XlRctY1J3QzOBH9HdUOTDt0mMjBgpWCzh0YVT1Moh2bPXU9Xg==", "dev": true, "requires": { - "@angular-eslint/bundled-angular-compiler": "16.2.0", + "@angular-eslint/bundled-angular-compiler": "16.3.1", "@typescript-eslint/utils": "5.62.0" } }, @@ -17567,15 +17616,15 @@ } }, "@angular/cli": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.9.tgz", - "integrity": "sha512-wkpV/Ni26LUeDmhee2TPXXEq3feEdZMSG8+nkfUK9kqIcxm0IjI1GLPeiVOX7aQobuKNe2cCAFNwsrXWjj+2og==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.10.tgz", + "integrity": "sha512-zDqlD+rXFuYZP169c2v35HkMbkchVCft5sS+VpoCCgYTk2rwxpeYkjJ8DQZztZJZRXQ+EMpkv/TubswmDro2zA==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1602.9", - "@angular-devkit/core": "16.2.9", - "@angular-devkit/schematics": "16.2.9", - "@schematics/angular": "16.2.9", + "@angular-devkit/architect": "0.1602.10", + "@angular-devkit/core": "16.2.10", + "@angular-devkit/schematics": "16.2.10", + "@schematics/angular": "16.2.10", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "ini": "4.1.1", @@ -17738,6 +17787,22 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", "dev": true }, + "@awesome-cordova-plugins/android-permissions": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/android-permissions/-/android-permissions-6.4.0.tgz", + "integrity": "sha512-JmJW/WMM9yOeYl3G9C5OIucpgp7E+71qhN1aHPZQSIKivHGRFhZrX/K4lBjKqrIra4dVIV5swCqfQNwCHglLww==", + "requires": { + "@types/cordova": "latest" + } + }, + "@awesome-cordova-plugins/core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/core/-/core-6.4.0.tgz", + "integrity": "sha512-06I5JdTgAgKTby+VG+3sQF5+z4RNCEyVl6y7tz2rICx8MURL1biuh3oGGN1rCQQjsuMZcX5siMBr0NF/OHqxLQ==", + "requires": { + "@types/cordova": "latest" + } + }, "@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -19330,11 +19395,11 @@ "dev": true }, "@ionic/angular": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-7.5.3.tgz", - "integrity": "sha512-LgTF8nNFj0B71E98gPEAP4cbSAnNfpDG82l/FGtozG83nto60wp13rWPtVuqG+xLSZqHgV6vN6d9W+2NfE46SQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-7.5.4.tgz", + "integrity": "sha512-TmySfb2HgFoZZaIvIyRKXhH68rHg8TMcrtVP/cTPv3NJiGDH3TGvbIdQmUtdjQ+WUqlgwCsuk7rNgSHtrC5F0w==", "requires": { - "@ionic/core": "7.5.3", + "@ionic/core": "7.5.4", "ionicons": "^7.0.0", "jsonc-parser": "^3.0.0", "tslib": "^2.3.0" @@ -19426,11 +19491,11 @@ } }, "@ionic/core": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.3.tgz", - "integrity": "sha512-nd4NV6gSgohLvKHGmf+jAcgzI1387eKq3enjBfE574FK/ikl5CeIltv0MBVJr9lz0pNSL/y9m1tdaFg+syASuA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.4.tgz", + "integrity": "sha512-rZbKlcVucRTDOK2Woh4CWPePlsXiUt3G9dCUniduKD7WeiuAk0GzfmoM3WXBvcUpkVTUIOrvKHaqd3JJSWEIzg==", "requires": { - "@stencil/core": "^4.6.0", + "@stencil/core": "^4.7.1", "ionicons": "^7.2.1", "tslib": "^2.1.0" } @@ -19492,9 +19557,9 @@ } }, "@ionic/utils-subprocess": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.13.tgz", - "integrity": "sha512-wguf0zfmca1UA1uF2kmbr/4jukrizlYlpIdtG1FdEaGrof8d5djdwg+g3jHe5dTR88urUQ3Gw68Yhs242dgGrg==", + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.14.tgz", + "integrity": "sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg==", "dev": true, "requires": { "@ionic/utils-array": "2.1.6", @@ -19608,6 +19673,12 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@jcesarmobile/ssl-skip": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@jcesarmobile/ssl-skip/-/ssl-skip-0.2.0.tgz", + "integrity": "sha512-hkhOM5+qPm5xzOYcbUODzYTZKyATEM3qixzUL1gJsJckZ5wAUQxyZJFM3hcUANv8fV8cONfX7A+d/DJpzVvM+Q==", + "dev": true + }, "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -19664,9 +19735,9 @@ "dev": true }, "@ngtools/webpack": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.9.tgz", - "integrity": "sha512-rOclD7FfT4OSwVA0nDnULbJS6TORJ0+sQiuT2ebaNFErYr3LOm6Zut05tnmzFw8q1cePrILbG+xpnbggNr9Pyw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.10.tgz", + "integrity": "sha512-XAVn59zP3ztuKDtw92Xc9+64RK4u4c9g8y5GgtjIWeOwgNXl8bYhAo3uTZzrSrOu96DFZGjsmghFab/7/C2pDg==", "dev": true, "requires": {} }, @@ -19971,13 +20042,13 @@ "optional": true }, "@schematics/angular": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.9.tgz", - "integrity": "sha512-uiU2YbZRVHgk1N1DDsek/5CKhfpZ8myJYNJk8eHV5LswnXOP3aqvH23VhneaAgOYwK5fISC7eMG0pLVKMvFfZQ==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.10.tgz", + "integrity": "sha512-PXmoswvN7qknTsXDmEvhZ9UG+awwWnQ/1Jd/eqqQx08iAaAT81OsXj1bN7eSs6tEGBKGjPb6q2xzuiECAdymzg==", "dev": true, "requires": { - "@angular-devkit/core": "16.2.9", - "@angular-devkit/schematics": "16.2.9", + "@angular-devkit/core": "16.2.10", + "@angular-devkit/schematics": "16.2.10", "jsonc-parser": "3.2.0" } }, @@ -20182,6 +20253,11 @@ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", "dev": true }, + "@types/cordova": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-11.0.3.tgz", + "integrity": "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg==" + }, "@types/cors": { "version": "2.8.16", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.16.tgz", @@ -21188,9 +21264,9 @@ "dev": true }, "axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "dev": true, "requires": { "follow-redirects": "^1.15.0", @@ -21212,9 +21288,9 @@ } }, "axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", + "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", "dev": true, "requires": { "dequal": "^2.0.3" @@ -21918,6 +21994,11 @@ } } }, + "cordova-plugin-android-permissions": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/cordova-plugin-android-permissions/-/cordova-plugin-android-permissions-1.1.5.tgz", + "integrity": "sha512-oTTV9cCMBqXTCmU+nYRebsP2IQfrtdvl2vYXHjoJgv5NHCIDgY4KFg6kJTcwXQOiymeGXuw0+MTvJJOueAdleA==" + }, "core-js-compat": { "version": "3.33.2", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.2.tgz", @@ -24580,9 +24661,9 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.1.tgz", - "integrity": "sha512-opCrKqbthmq3SKZ10mFMQG9dk3fTa3quaOLD35kJa5ejwZHd9xAr+kLuziiZz2cG32s4lMZxNdmdcEQnTDP4+g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true }, "istanbul-lib-instrument": { diff --git a/openvidu-ionic/package.json b/openvidu-ionic/package.json index 65136ec6..93beb762 100644 --- a/openvidu-ionic/package.json +++ b/openvidu-ionic/package.json @@ -4,8 +4,7 @@ "author": "Ionic Framework", "homepage": "https://ionicframework.com/", "scripts": { - "ng": "ng", - "start": "ng serve", + "start": "ionic serve", "build": "ng build", "watch": "ng build --watch --configuration development", "android": "npx ionic capacitor run android", @@ -14,7 +13,8 @@ "build:full": "npm run trapeze && npx ionic capacitor sync", "build:android": "npm run build:full && npx ionic capacitor build android --no-open && cd android && ./gradlew assembleDebug && ./gradlew assembleRelease", "copy:android": "cp ./android/app/build/outputs/apk/debug/app-debug.apk /opt/openvidu/android/openvidu-ionic.apk", - "dev:mobile": "npx ionic cap run android -l --external" + "dev:android": "./scripts/mobile_run.sh android", + "dev:ios": "./scripts/mobile_run.sh ios" }, "private": true, "dependencies": { @@ -26,13 +26,16 @@ "@angular/platform-browser": "^16.0.0", "@angular/platform-browser-dynamic": "^16.0.0", "@angular/router": "^16.0.0", - "@capacitor/android": "5.5.0", + "@awesome-cordova-plugins/android-permissions": "6.4.0", + "@awesome-cordova-plugins/core": "6.4.0", + "@capacitor/android": "5.5.1", "@capacitor/app": "5.0.6", "@capacitor/core": "5.5.0", "@capacitor/haptics": "5.0.6", "@capacitor/keyboard": "5.0.6", "@capacitor/status-bar": "5.0.6", "@ionic/angular": "^7.0.0", + "cordova-plugin-android-permissions": "^1.1.5", "ionicons": "^7.0.0", "livekit-client": "^1.14.4", "rxjs": "~7.8.0", @@ -51,6 +54,7 @@ "@angular/language-service": "^16.0.0", "@capacitor/cli": "5.5.0", "@ionic/angular-toolkit": "^9.0.0", + "@jcesarmobile/ssl-skip": "^0.2.0", "@types/jasmine": "~4.3.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "5.3.0", diff --git a/openvidu-ionic/scripts/mobile_run.sh b/openvidu-ionic/scripts/mobile_run.sh new file mode 100755 index 00000000..f664487f --- /dev/null +++ b/openvidu-ionic/scripts/mobile_run.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Check if an argument is provided +if [ -z "$1" ]; then + echo "Please provide the PLATFORM name." + echo "Example: ./android_run.sh " + exit 1 +fi + +PLATFORM=$1 + +# Getting the operating system +os_name=$(uname) + +case "$os_name" in + "Linux") + # Commands for Linux + LOCAL_IP=$(hostname -I | awk '{print $1}') + ;; + "Darwin") + # Commands for macOS + LOCAL_IP=$(ifconfig | grep "inet " | grep -Fv) + ;; + CYGWIN*|"MINGW32_NT"|"MINGW64_NT") + # Commands for Windows (Cygwin or MinGW) + LOCAL_IP=$(ipconfig | grep "IPv4 Address" | awk '{print $NF}') + ;; + *) + # Unsupported/Unknown OS + echo "Unsupported operating system: $os_name" + exit 1 + ;; +esac + +if [ -z "$LOCAL_IP" ]; then + echo "Cannot get your local IP address." + exit 1 +fi + +echo "Your local ip is $LOCAL_IP" +echo "Setting up your movile environment for developing OpenVidu..." + +# Set environment variables +sed -i "s/\(externalIp:\s*'\)[^']*/\1$LOCAL_IP/" src/environments/environment.ts + +npx ionic cap run $PLATFORM -l --external --disable-host-check --public-host=$LOCAL_IP --ssl + + + diff --git a/openvidu-ionic/src/app/components/ov-video.component.ts b/openvidu-ionic/src/app/components/ov-video.component.ts index ed329ddd..95082cb1 100644 --- a/openvidu-ionic/src/app/components/ov-video.component.ts +++ b/openvidu-ionic/src/app/components/ov-video.component.ts @@ -9,10 +9,10 @@ import { LocalVideoTrack, RemoteVideoTrack } from 'livekit-client'; @Component({ selector: 'ov-video', - template: '', + template: '', }) export class OpenViduVideoComponent implements AfterViewInit { - @ViewChild('videoElement') elementRef!: ElementRef; + @ViewChild('videoElement') elementRef!: ElementRef; _track!: LocalVideoTrack | RemoteVideoTrack; diff --git a/openvidu-ionic/src/app/home/home.module.ts b/openvidu-ionic/src/app/home/home.module.ts index e1de79ed..9788f3a8 100644 --- a/openvidu-ionic/src/app/home/home.module.ts +++ b/openvidu-ionic/src/app/home/home.module.ts @@ -8,6 +8,7 @@ import { HomePageRoutingModule } from './home-routing.module'; import { HttpClientModule } from '@angular/common/http'; import { OpenViduVideoComponent } from '../components/ov-video.component'; import { OpenViduAudioComponent } from '../components/ov-audio.component'; +import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; @NgModule({ @@ -19,5 +20,8 @@ import { OpenViduAudioComponent } from '../components/ov-audio.component'; HttpClientModule, ], declarations: [HomePage, OpenViduVideoComponent, OpenViduAudioComponent], + providers: [ + AndroidPermissions, + ], }) export class HomePageModule {} diff --git a/openvidu-ionic/src/app/home/home.page.html b/openvidu-ionic/src/app/home/home.page.html index 4022d4f9..10b0d150 100644 --- a/openvidu-ionic/src/app/home/home.page.html +++ b/openvidu-ionic/src/app/home/home.page.html @@ -59,37 +59,46 @@ - - - - - {{ getParticipantName(localPublication.trackSid) }} - - - - - - - {{ getParticipantName(publication.trackSid) }} - - - - + + + {{ myRoomName }} + + + + + + {{ getParticipantName(localPublication.trackSid) }} + + + + + + + + {{ getParticipantName(publication.trackSid) }} + + + + + + + p { background-color: #d8ffbd; } +::ng-deep video { + width: 100%; + height: 100%; + object-fit: cover; +} + .hidden { display: none; } + + +/* Hide the default video controls */ +::ng-deep video::-webkit-media-controls { + display: none; /* For Safari and Chrome */ +} + +::ng-deep video::-webkit-media-controls-panel { + display: none; /* For newer versions of Safari */ +} + +::ng-deep video::-webkit-media-controls-play-button { + display: none; /* For Safari Play button */ +} + +::ng-deep video::-webkit-media-controls-start-playback-button { + display: none; /* For Safari Start Playback button */ +} diff --git a/openvidu-ionic/src/app/home/home.page.ts b/openvidu-ionic/src/app/home/home.page.ts index 8a411fc1..c1616e37 100644 --- a/openvidu-ionic/src/app/home/home.page.ts +++ b/openvidu-ionic/src/app/home/home.page.ts @@ -6,10 +6,13 @@ import { RemoteTrackPublication, Room, RoomEvent, + TrackPublishOptions, } from 'livekit-client'; import { HttpClient } from '@angular/common/http'; import { AlertController, Platform } from '@ionic/angular'; import { lastValueFrom, take } from 'rxjs'; +import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; +import { environment } from 'src/environments/environment'; @Component({ selector: 'app-home', @@ -36,25 +39,32 @@ export class HomePage { private cameraSelected!: MediaDeviceInfo; private isFrontCamera: boolean = false; + ANDROID_PERMISSIONS = [ + this.androidPermissions.PERMISSION.CAMERA, + this.androidPermissions.PERMISSION.RECORD_AUDIO, + this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS, + ]; + constructor( private httpClient: HttpClient, private alertController: AlertController, - private platform: Platform + private platform: Platform, + private androidPermissions: AndroidPermissions ) { this.generateParticipantInfo(); - // WARNING!! To make easier first steps with mobile devices, this code allows - // using the demos OpenVidu deployment when no custom deployment is provided - // if ( - // this.platform.is('hybrid') && - // this.APPLICATION_SERVER_URL === 'http://localhost:5000/' - // ) { - // /** - // * WARNING: this APPLICATION_SERVER_URL is not secure and is only meant for a first quick test. - // * Anyone could access your video rooms. You should modify the APPLICATION_SERVER_URL to a custom private one. - // */ - // this.APPLICATION_SERVER_URL = 'https://call.next.openvidu.io/'; - // } + // WARNING!! To make the mobile development easier, this code allows + // using your local IP address for communicating with the backend. + // For production uses, the server should be accessible from the Internet + if (this.platform.is('hybrid')) { + if (environment.externalIp) { + console.warn('Your local IP address: ', environment.externalIp); + this.APPLICATION_SERVER_URL = this.APPLICATION_SERVER_URL.replace( + 'localhost', + environment.externalIp + ); + } + } } @HostListener('window:beforeunload') @@ -103,17 +113,29 @@ export class HomePage { this.myRoomName, this.myParticipantName ); - const livekitUrl = this.getLivekitUrlFromMetadata(token); + + let livekitUrl = this.getLivekitUrlFromMetadata(token); + + if (environment.externalIp) { + // WARNING!! To make the mobile development easier, this code allows + // using your local IP address for communicating with the backend. + // For production uses, the server should be accessible from the Internet + livekitUrl = livekitUrl.replace('localhost', environment.externalIp); + } + // First param is the LiveKit server URL. Second param is the access token await this.room.connect(livekitUrl, token); // --- 5) Requesting and Checking Android Permissions if (this.platform.is('hybrid') && this.platform.is('android')) { - console.log('Ionic Android platform'); await this.checkAndroidPermissions(); } + const publishOptions: TrackPublishOptions = { + stream: 'test', + }; + const [audioPublication, videoPublication] = await Promise.all([ this.room.localParticipant.setMicrophoneEnabled(true), this.room.localParticipant.setCameraEnabled(true), @@ -121,6 +143,7 @@ export class HomePage { // Set the main video in the page to display our webcam and store our localPublication this.localPublication = videoPublication; + this.refreshVideos(); await this.initDevices(); } catch (error: any) { console.log( @@ -185,7 +208,13 @@ export class HomePage { async toggleCamera() { if (this.room) { const enabled = !this.room.localParticipant.isCameraEnabled; + // const options: VideoCaptureOptions = {}; + // const publishOptions: TrackPublishOptions = { + // stream: 'test', + // }; + await this.room.localParticipant.setCameraEnabled(enabled); + this.refreshVideos(); this.cameraIcon = enabled ? 'videocam' : 'eye-off'; } } @@ -193,7 +222,14 @@ export class HomePage { async toggleMicrophone() { if (this.room) { const enabled = !this.room.localParticipant.isMicrophoneEnabled; - await this.room.localParticipant.setMicrophoneEnabled(enabled); + const publishOptions: TrackPublishOptions = { + stream: 'test', + }; + await this.room.localParticipant.setMicrophoneEnabled( + enabled, + undefined, + publishOptions + ); this.microphoneIcon = enabled ? 'mic' : 'mic-off'; } } @@ -207,6 +243,8 @@ export class HomePage { if (newCamera && this.room) { await this.room.switchActiveDevice('videoinput', newCamera.deviceId); this.cameraSelected = newCamera; + + this.refreshVideos(); } } catch (error) { console.error(error); @@ -245,9 +283,43 @@ export class HomePage { await alert.present(); } + private refreshVideos() { + // setTimeout(() => { + // console.warn('track restarted: UPDATED DOM'); + // const refreshedElement = document.getElementById('refreshed-workaround'); + // if (refreshedElement) { + // refreshedElement.remove(); + // } else { + // const p = document.createElement('p'); + // p.id = 'refreshed-workaround'; + // document.getElementById('room')?.appendChild(p); + // } + // }, 200); + } + private async checkAndroidPermissions(): Promise { await this.platform.ready(); - //TODO: Implement Android permissions + try { + await this.androidPermissions.requestPermissions( + this.ANDROID_PERMISSIONS + ); + const promisesArray: Promise[] = []; + this.ANDROID_PERMISSIONS.forEach((permission) => { + console.log('Checking ', permission); + promisesArray.push(this.androidPermissions.checkPermission(permission)); + }); + const responses = await Promise.all(promisesArray); + let allHasPermissions = true; + responses.forEach((response, i) => { + allHasPermissions = response.hasPermission; + if (!allHasPermissions) { + throw new Error('Permissions denied: ' + this.ANDROID_PERMISSIONS[i]); + } + }); + } catch (error) { + console.error('Error requesting or checking permissions: ', error); + throw error; + } } private async initDevices() { diff --git a/openvidu-ionic/src/assets/images/video-poster.png b/openvidu-ionic/src/assets/images/video-poster.png new file mode 100644 index 0000000000000000000000000000000000000000..60de08e47575a67f3989891c74ac22da63233689 GIT binary patch literal 15076 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 ZubHW)@<$r*X(
- {{ getParticipantName(localPublication.trackSid) }} -
- {{ getParticipantName(publication.trackSid) }} -
+ {{ getParticipantName(localPublication.trackSid) }} +
+ {{ getParticipantName(publication.trackSid) }} +