diff --git a/apps/picsa-apps/extension-app-native/android/app/build.gradle b/apps/picsa-apps/extension-app-native/android/app/build.gradle index 852853727..2ddcd75d6 100644 --- a/apps/picsa-apps/extension-app-native/android/app/build.gradle +++ b/apps/picsa-apps/extension-app-native/android/app/build.gradle @@ -6,8 +6,8 @@ android { applicationId "io.picsa.extension" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 3011000 - versionName "3.11.0" + versionCode 3012000 + versionName "3.12.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/apps/picsa-apps/extension-app-native/android/app/src/main/AndroidManifest.xml b/apps/picsa-apps/extension-app-native/android/app/src/main/AndroidManifest.xml index 2c31450b9..f37c74112 100644 --- a/apps/picsa-apps/extension-app-native/android/app/src/main/AndroidManifest.xml +++ b/apps/picsa-apps/extension-app-native/android/app/src/main/AndroidManifest.xml @@ -23,6 +23,13 @@ + + + + + + + +

Open With...

+ +
+
PICSA
+

PICSA App

+ +
+
+
+ language +

Browser

+ +
+
+ + `, + styles: [ + ` + .picsa-app-icon { + background: #8a2644; + border-radius: 10px; + color: white; + padding: 4px; + line-height: 48px; + height: 48px; + width: 48px; + text-align: center; + font-size: 16px; + } + .open-option { + display: flex; + align-items: center; + margin-bottom: 1rem; + } + h3 { + flex: 1; + text-align: left; + margin-left: 1rem; + } + a { + text-decoration: none; + color: unset; + } + button { + width: 90px; + } + .open-icon { + font-size: 48px; + height: 48px; + width: 48px; + padding: 4px; + } + .spacer { + height: 1rem; + } + `, + ], +}) +export class AppOpenPromptComponent { + appDynamicLink: string; + constructor( + deepLinksService: DeepLinksService, + private bottomSheet: MatBottomSheet + ) { + this.appDynamicLink = deepLinksService.config.appDynamicLink; + } + + dismiss() { + this.bottomSheet.dismiss(); + } +} diff --git a/libs/shared/src/modules/deep-links/deep-links.module.ts b/libs/shared/src/modules/deep-links/deep-links.module.ts new file mode 100644 index 000000000..827d7e339 --- /dev/null +++ b/libs/shared/src/modules/deep-links/deep-links.module.ts @@ -0,0 +1,26 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { AppOpenPromptComponent } from './app-open-prompt.component'; +import { DeepLinksService, DeepLinksServiceConfig } from './deep-links.service'; + +@NgModule({ + imports: [MatIconModule, MatBottomSheetModule, MatButtonModule], + exports: [], + declarations: [AppOpenPromptComponent], +}) +export class PicsaDeepLinksModule { + constructor(deepLinksService: DeepLinksService) { + deepLinksService.init(); + } + // https://angular.io/guide/singleton-services#providing-a-singleton-service + static forRoot( + config: DeepLinksServiceConfig + ): ModuleWithProviders { + return { + ngModule: PicsaDeepLinksModule, + providers: [{ provide: DeepLinksServiceConfig, useValue: config }], + }; + } +} diff --git a/libs/shared/src/modules/deep-links/deep-links.service.ts b/libs/shared/src/modules/deep-links/deep-links.service.ts new file mode 100644 index 000000000..94e9aff64 --- /dev/null +++ b/libs/shared/src/modules/deep-links/deep-links.service.ts @@ -0,0 +1,62 @@ +import { Injectable, NgZone, Optional } from '@angular/core'; +import { MatBottomSheet } from '@angular/material/bottom-sheet'; +import { Router } from '@angular/router'; +import { App, URLOpenListenerEvent } from '@capacitor/app'; +import { Capacitor } from '@capacitor/core'; +import { AppOpenPromptComponent } from './app-open-prompt.component'; + +export class DeepLinksServiceConfig { + /** Web url associated with deep links */ + baseUrl: string; + /** E.g. firebase dynamic link */ + appDynamicLink: string; +} + +@Injectable({ providedIn: 'root' }) +/** + * Provide support for opening deep links within app + * https://capacitorjs.com/docs/guides/deep-links#angular + */ +export class DeepLinksService { + constructor( + @Optional() public config: DeepLinksServiceConfig, + private zone: NgZone, + private router: Router, + private bottomSheet: MatBottomSheet + ) {} + + public init() { + if (Capacitor.isNativePlatform()) { + const baseUrl = this.config?.baseUrl || location.origin; + App.addListener('appUrlOpen', (event: URLOpenListenerEvent) => { + this.zone.run(() => { + const slug = event.url.replace(`${baseUrl}/`, ''); + if (slug) { + this.router.navigateByUrl(slug); + } + }); + }); + } else { + if (this.isMobile()) { + // open prompt after slight delay to allow time for app to render + setTimeout(() => { + this.toggleAppOpenTargetSheet(); + }, 2500); + } + } + } + + private isMobile() { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent + ); + } + + /** + * Present a bottom sheet to encourage user to use native version of app if running + * on mobile + */ + private toggleAppOpenTargetSheet() { + this.bottomSheet.open(AppOpenPromptComponent); + } +} diff --git a/libs/shared/src/modules/index.ts b/libs/shared/src/modules/index.ts index 0a53d674d..d5965fc1c 100644 --- a/libs/shared/src/modules/index.ts +++ b/libs/shared/src/modules/index.ts @@ -1,5 +1,12 @@ -import {PicsaNativeModule} from './native'; -import {PicsaDbModule} from './db.module'; -import {PicsaTranslateModule, PicsaTranslateService} from './translate' +import { PicsaNativeModule } from './native'; +import { PicsaDbModule } from './db.module'; +import { PicsaTranslateModule, PicsaTranslateService } from './translate'; +import { PicsaDeepLinksModule } from './deep-links/deep-links.module'; -export {PicsaNativeModule, PicsaDbModule, PicsaTranslateModule, PicsaTranslateService} \ No newline at end of file +export { + PicsaNativeModule, + PicsaDbModule, + PicsaTranslateModule, + PicsaTranslateService, + PicsaDeepLinksModule, +}; diff --git a/package.json b/package.json index 03762cac3..c85c52ad9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "picsa-apps", - "version": "3.11.0", + "version": "3.12.0", "license": "See LICENSE", "scripts": { "ng": "nx",