MOBILE-2314 core: Remove cordova-plugin-media
parent
4013855ae9
commit
495d395beb
|
@ -196,11 +196,6 @@
|
||||||
<param name="android-package" value="com.adobe.phonegap.push.PushPlugin" />
|
<param name="android-package" value="com.adobe.phonegap.push.PushPlugin" />
|
||||||
</feature>
|
</feature>
|
||||||
</config-file>
|
</config-file>
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Media">
|
|
||||||
<param name="android-package" value="org.apache.cordova.media.AudioHandler" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="AndroidManifest.xml">
|
<config-file parent="/*" target="AndroidManifest.xml">
|
||||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
|
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
|
||||||
</config-file>
|
</config-file>
|
||||||
|
|
|
@ -4167,21 +4167,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ionic-native/media": {
|
|
||||||
"version": "5.36.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ionic-native/media/-/media-5.36.0.tgz",
|
|
||||||
"integrity": "sha512-WIDCeUlX7bCbse/x2Rr7mAIQJnLo18ZWcmsVgSTTBVS7ObU2DBl4ieqRx6y9PAAV+3tNZqMV4JAWDfMiFokpJg==",
|
|
||||||
"requires": {
|
|
||||||
"@types/cordova": "^0.0.34"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/cordova": {
|
|
||||||
"version": "0.0.34",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
|
|
||||||
"integrity": "sha512-rkiiTuf/z2wTd4RxFOb+clE7PF4AEJU0hsczbUdkHHBtkUmpWQpEddynNfJYKYtZFJKbq4F+brfekt1kx85IZA=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@ionic-native/media-capture": {
|
"@ionic-native/media-capture": {
|
||||||
"version": "5.36.0",
|
"version": "5.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ionic-native/media-capture/-/media-capture-5.36.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ionic-native/media-capture/-/media-capture-5.36.0.tgz",
|
||||||
|
@ -14340,11 +14325,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.2.0.tgz",
|
||||||
"integrity": "sha512-yDUG+9ieKVRitq5mGlNxjaZh/MgEhFFIgTIPhqSbUaQ8UuZbawy5mhJAVClqY97q8/rcQtL6dCDa7x2sEtCLcA=="
|
"integrity": "sha512-yDUG+9ieKVRitq5mGlNxjaZh/MgEhFFIgTIPhqSbUaQ8UuZbawy5mhJAVClqY97q8/rcQtL6dCDa7x2sEtCLcA=="
|
||||||
},
|
},
|
||||||
"cordova-plugin-media": {
|
|
||||||
"version": "5.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/cordova-plugin-media/-/cordova-plugin-media-5.0.4.tgz",
|
|
||||||
"integrity": "sha512-mAqincYqOT5gu5LWyfgJu3qmOq+lhLAKhnOZULpG622FvYiHjjfsoJ/fkI55WwI3FIcHeeyhToGvHXBCNJePZg=="
|
|
||||||
},
|
|
||||||
"cordova-plugin-media-capture": {
|
"cordova-plugin-media-capture": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cordova-plugin-media-capture/-/cordova-plugin-media-capture-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cordova-plugin-media-capture/-/cordova-plugin-media-capture-3.0.3.tgz",
|
||||||
|
|
|
@ -63,7 +63,6 @@
|
||||||
"@ionic-native/ionic-webview": "5.36.0",
|
"@ionic-native/ionic-webview": "5.36.0",
|
||||||
"@ionic-native/keyboard": "5.36.0",
|
"@ionic-native/keyboard": "5.36.0",
|
||||||
"@ionic-native/local-notifications": "5.36.0",
|
"@ionic-native/local-notifications": "5.36.0",
|
||||||
"@ionic-native/media": "5.36.0",
|
|
||||||
"@ionic-native/media-capture": "5.36.0",
|
"@ionic-native/media-capture": "5.36.0",
|
||||||
"@ionic-native/network": "5.36.0",
|
"@ionic-native/network": "5.36.0",
|
||||||
"@ionic-native/push": "5.36.0",
|
"@ionic-native/push": "5.36.0",
|
||||||
|
@ -104,7 +103,6 @@
|
||||||
"cordova-plugin-file": "6.0.2",
|
"cordova-plugin-file": "6.0.2",
|
||||||
"cordova-plugin-geolocation": "4.1.0",
|
"cordova-plugin-geolocation": "4.1.0",
|
||||||
"cordova-plugin-ionic-keyboard": "2.2.0",
|
"cordova-plugin-ionic-keyboard": "2.2.0",
|
||||||
"cordova-plugin-media": "5.0.4",
|
|
||||||
"cordova-plugin-media-capture": "3.0.3",
|
"cordova-plugin-media-capture": "3.0.3",
|
||||||
"cordova-plugin-network-information": "3.0.0",
|
"cordova-plugin-network-information": "3.0.0",
|
||||||
"cordova-plugin-prevent-override": "1.0.1",
|
"cordova-plugin-prevent-override": "1.0.1",
|
||||||
|
@ -225,9 +223,6 @@
|
||||||
"ANDROID_SUPPORT_V4_VERSION": "26.+"
|
"ANDROID_SUPPORT_V4_VERSION": "26.+"
|
||||||
},
|
},
|
||||||
"cordova-plugin-media-capture": {},
|
"cordova-plugin-media-capture": {},
|
||||||
"cordova-plugin-media": {
|
|
||||||
"KEEP_AVAUDIOSESSION_ALWAYS_ACTIVE": "NO"
|
|
||||||
},
|
|
||||||
"cordova-plugin-network-information": {},
|
"cordova-plugin-network-information": {},
|
||||||
"@moodlehq/cordova-plugin-qrscanner": {},
|
"@moodlehq/cordova-plugin-qrscanner": {},
|
||||||
"cordova-plugin-splashscreen": {},
|
"cordova-plugin-splashscreen": {},
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<core-loading [hideUntil]="readyToCapture">
|
<core-loading [hideUntil]="readyToCapture">
|
||||||
<div class="core-av-wrapper">
|
<div class="core-av-wrapper">
|
||||||
<!-- Video stream for image and video. -->
|
<!-- Video stream for image and video. -->
|
||||||
<video *ngIf="!isAudio" [hidden]="hasCaptured" class="core-webcam-stream" autoplay #streamVideo></video>
|
<video [hidden]="hasCaptured" class="core-webcam-stream" autoplay #streamVideo></video>
|
||||||
|
|
||||||
<!-- For video recording, use 2 videos and show/hide them because a CSS rule caused problems with the controls. -->
|
<!-- For video recording, use 2 videos and show/hide them because a CSS rule caused problems with the controls. -->
|
||||||
<video *ngIf="isVideo" [hidden]="!hasCaptured" class="core-webcam-video-captured" controls #previewVideo
|
<video *ngIf="isVideo" [hidden]="!hasCaptured" class="core-webcam-video-captured" controls #previewVideo
|
||||||
|
@ -25,21 +25,6 @@
|
||||||
<canvas *ngIf="isImage" class="core-webcam-image-canvas" #imgCanvas></canvas>
|
<canvas *ngIf="isImage" class="core-webcam-image-canvas" #imgCanvas></canvas>
|
||||||
<img *ngIf="isImage" [hidden]="!hasCaptured" class="core-webcam-image" alt="{{ 'core.capturedimage' | translate }}"
|
<img *ngIf="isImage" [hidden]="!hasCaptured" class="core-webcam-image" alt="{{ 'core.capturedimage' | translate }}"
|
||||||
#previewImage>
|
#previewImage>
|
||||||
|
|
||||||
<!-- Recording audio. -->
|
|
||||||
<div *ngIf="isAudio" class="core-audio-record-container">
|
|
||||||
<!-- Canvas to show audio waves when recording audio in browser. -->
|
|
||||||
<canvas [hidden]="hasCaptured || isCordovaAudioCapture" class="core-audio-canvas" #streamAudio></canvas>
|
|
||||||
|
|
||||||
<!-- Button to start/stop in mobile devices. -->
|
|
||||||
<ion-button fill="clear" *ngIf="!hasCaptured && isCordovaAudioCapture" (click)="actionClicked()" [attr.aria-label]="title">
|
|
||||||
<ion-icon *ngIf="!isCapturing" name="fas-microphone" slot="icon-only" aria-hidden="true"></ion-icon>
|
|
||||||
<ion-icon *ngIf="isCapturing" name="fas-square" slot="icon-only" aria-hidden="true"></ion-icon>
|
|
||||||
</ion-button>
|
|
||||||
|
|
||||||
<!-- Audio player to listen to the result. -->
|
|
||||||
<audio [hidden]="!hasCaptured" class="core-audio-captured" controls #previewAudio controlsList="nodownload"></audio>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
@ -48,8 +33,7 @@
|
||||||
<ion-row>
|
<ion-row>
|
||||||
<ion-col></ion-col>
|
<ion-col></ion-col>
|
||||||
<ion-col class="ion-text-center">
|
<ion-col class="ion-text-center">
|
||||||
<ion-button fill="clear" *ngIf="!hasCaptured && !isCordovaAudioCapture" (click)="actionClicked()" [attr.aria-label]="title">
|
<ion-button fill="clear" *ngIf="!hasCaptured" (click)="actionClicked()" [attr.aria-label]="title">
|
||||||
<ion-icon *ngIf="!isCapturing && isAudio" name="fas-microphone" slot="icon-only" aria-hidden="true"></ion-icon>
|
|
||||||
<ion-icon *ngIf="!isCapturing && isVideo" name="fas-video" slot="icon-only" aria-hidden="true"></ion-icon>
|
<ion-icon *ngIf="!isCapturing && isVideo" name="fas-video" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||||
<ion-icon *ngIf="isImage" name="fas-camera" slot="icon-only" aria-hidden="true"></ion-icon>
|
<ion-icon *ngIf="isImage" name="fas-camera" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||||
<ion-icon *ngIf="isCapturing" name="fas-square" slot="icon-only" aria-hidden="true"></ion-icon>
|
<ion-icon *ngIf="isCapturing" name="fas-square" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||||
|
|
|
@ -21,30 +21,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
height: 120px;
|
|
||||||
width: 120px;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
font-size: 120px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
audio {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
video, img {
|
video, img {
|
||||||
|
|
|
@ -13,23 +13,20 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnInit, OnDestroy, ViewChild, ElementRef, ChangeDetectorRef, Input } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ViewChild, ElementRef, ChangeDetectorRef, Input } from '@angular/core';
|
||||||
import { MediaObject } from '@ionic-native/media/ngx';
|
|
||||||
import { FileEntry } from '@ionic-native/file/ngx';
|
|
||||||
import { MediaFile } from '@ionic-native/media-capture/ngx';
|
import { MediaFile } from '@ionic-native/media-capture/ngx';
|
||||||
|
|
||||||
import { CoreFile, CoreFileProvider } from '@services/file';
|
import { CoreFile, CoreFileProvider } from '@services/file';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { ModalController, Media, Translate } from '@singletons';
|
import { ModalController, Translate } from '@singletons';
|
||||||
import { CoreError } from '@classes/errors/error';
|
import { CoreError } from '@classes/errors/error';
|
||||||
import { CoreCaptureError } from '@classes/errors/captureerror';
|
import { CoreCaptureError } from '@classes/errors/captureerror';
|
||||||
import { CoreCanceledError } from '@classes/errors/cancelederror';
|
import { CoreCanceledError } from '@classes/errors/cancelederror';
|
||||||
import { CorePath } from '@singletons/path';
|
import { CorePath } from '@singletons/path';
|
||||||
import { CorePlatform } from '@services/platform';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page to capture media in browser, or to capture audio in mobile devices.
|
* Page to capture media in browser.
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'core-emulator-capture-media',
|
selector: 'core-emulator-capture-media',
|
||||||
|
@ -38,7 +35,7 @@ import { CorePlatform } from '@services/platform';
|
||||||
})
|
})
|
||||||
export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@Input() type?: 'audio' | 'video' | 'image' | 'captureimage';
|
@Input() type?: 'video' | 'image' | 'captureimage';
|
||||||
@Input() maxTime?: number; // Max time to capture.
|
@Input() maxTime?: number; // Max time to capture.
|
||||||
@Input() facingMode?: string; // Camera facing mode.
|
@Input() facingMode?: string; // Camera facing mode.
|
||||||
@Input() mimetype?: string;
|
@Input() mimetype?: string;
|
||||||
|
@ -50,30 +47,20 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild('previewVideo') previewVideo?: ElementRef;
|
@ViewChild('previewVideo') previewVideo?: ElementRef;
|
||||||
@ViewChild('imgCanvas') imgCanvas?: ElementRef;
|
@ViewChild('imgCanvas') imgCanvas?: ElementRef;
|
||||||
@ViewChild('previewImage') previewImage?: ElementRef;
|
@ViewChild('previewImage') previewImage?: ElementRef;
|
||||||
@ViewChild('streamAudio') streamAudio?: ElementRef;
|
|
||||||
@ViewChild('previewAudio') previewAudio?: ElementRef;
|
|
||||||
|
|
||||||
title?: string; // The title of the page.
|
title?: string; // The title of the page.
|
||||||
isAudio?: boolean; // Whether it should capture audio.
|
|
||||||
isVideo?: boolean; // Whether it should capture video.
|
isVideo?: boolean; // Whether it should capture video.
|
||||||
isImage?: boolean; // Whether it should capture image.
|
isImage?: boolean; // Whether it should capture image.
|
||||||
readyToCapture?: boolean; // Whether it's ready to capture.
|
readyToCapture?: boolean; // Whether it's ready to capture.
|
||||||
hasCaptured?: boolean; // Whether it has captured something.
|
hasCaptured?: boolean; // Whether it has captured something.
|
||||||
isCapturing?: boolean; // Whether it's capturing.
|
isCapturing?: boolean; // Whether it's capturing.
|
||||||
resetChrono?: boolean; // Boolean to reset the chrono.
|
resetChrono?: boolean; // Boolean to reset the chrono.
|
||||||
isCordovaAudioCapture?: boolean; // Whether it's capturing audio using Cordova plugin.
|
|
||||||
|
|
||||||
protected isCaptureImage?: boolean; // To identify if it's capturing an image using media capture plugin (instead of camera).
|
protected isCaptureImage?: boolean; // To identify if it's capturing an image using media capture plugin (instead of camera).
|
||||||
protected mediaRecorder?: MediaRecorder; // To record video/audio.
|
protected mediaRecorder?: MediaRecorder; // To record video.
|
||||||
protected previewMedia?: HTMLAudioElement | HTMLVideoElement; // The element to preview the audio/video captured.
|
protected previewMedia?: HTMLVideoElement; // The element to preview the video captured.
|
||||||
protected mediaBlob?: Blob; // A Blob where the captured data is stored.
|
protected mediaBlob?: Blob; // A Blob where the captured data is stored.
|
||||||
protected localMediaStream?: MediaStream;
|
protected localMediaStream?: MediaStream;
|
||||||
protected audioDrawer?: {start: () => void; stop: () => void }; // To start/stop the display of audio sound.
|
|
||||||
|
|
||||||
// Variables for Cordova Media capture.
|
|
||||||
protected mediaFile?: MediaObject;
|
|
||||||
protected filePath?: string;
|
|
||||||
protected fileEntry?: FileEntry;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected changeDetectorRef: ChangeDetectorRef,
|
protected changeDetectorRef: ChangeDetectorRef,
|
||||||
|
@ -84,12 +71,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.initVariables();
|
this.initVariables();
|
||||||
|
this.initHtmlCapture();
|
||||||
if (this.isCordovaAudioCapture) {
|
|
||||||
this.initCordovaMediaPlugin();
|
|
||||||
} else {
|
|
||||||
this.initHtmlCapture();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,71 +90,10 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
if (this.type == 'video') {
|
if (this.type == 'video') {
|
||||||
this.isVideo = true;
|
this.isVideo = true;
|
||||||
this.title = 'core.capturevideo';
|
this.title = 'core.capturevideo';
|
||||||
} else if (this.type == 'audio') {
|
|
||||||
this.isAudio = true;
|
|
||||||
this.title = 'core.captureaudio';
|
|
||||||
} else if (this.type == 'image') {
|
} else if (this.type == 'image') {
|
||||||
this.isImage = true;
|
this.isImage = true;
|
||||||
this.title = 'core.captureimage';
|
this.title = 'core.captureimage';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isCordovaAudioCapture = CorePlatform.isMobile() && this.isAudio;
|
|
||||||
|
|
||||||
if (this.isCordovaAudioCapture) {
|
|
||||||
this.extension = CorePlatform.is('ios') ? 'wav' : 'aac';
|
|
||||||
this.returnDataUrl = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init recording with Cordova media plugin.
|
|
||||||
*
|
|
||||||
* @returns Promise resolved when ready.
|
|
||||||
*/
|
|
||||||
protected async initCordovaMediaPlugin(): Promise<void> {
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.createFileAndMediaInstance();
|
|
||||||
|
|
||||||
this.readyToCapture = true;
|
|
||||||
this.previewMedia = this.previewAudio?.nativeElement;
|
|
||||||
} catch (error) {
|
|
||||||
this.dismissWithError(-1, error.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a file entry and the cordova media instance.
|
|
||||||
*/
|
|
||||||
protected async createFileAndMediaInstance(): Promise<void> {
|
|
||||||
this.filePath = this.getFilePath();
|
|
||||||
|
|
||||||
// First create the file.
|
|
||||||
this.fileEntry = await CoreFile.createFile(this.filePath);
|
|
||||||
|
|
||||||
// Now create the media instance.
|
|
||||||
let absolutePath = CorePath.concatenatePaths(CoreFile.getBasePathInstant(), this.filePath);
|
|
||||||
|
|
||||||
if (CorePlatform.is('ios')) {
|
|
||||||
// In iOS we need to remove the file:// part.
|
|
||||||
absolutePath = absolutePath.replace(/^file:\/\//, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mediaFile = Media.create(absolutePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the file and the cordova media instance.
|
|
||||||
*/
|
|
||||||
protected async resetCordovaMediaCapture(): Promise<void> {
|
|
||||||
if (this.filePath) {
|
|
||||||
// Remove old file, don't block the user for this.
|
|
||||||
CoreFile.removeFile(this.filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mediaFile?.release();
|
|
||||||
|
|
||||||
await this.createFileAndMediaInstance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -183,8 +104,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
protected async initHtmlCapture(): Promise<void> {
|
protected async initHtmlCapture(): Promise<void> {
|
||||||
const constraints = {
|
const constraints = {
|
||||||
video: this.isAudio ? false : { facingMode: this.facingMode },
|
video: { facingMode: this.facingMode },
|
||||||
audio: !this.isImage,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -196,22 +116,18 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
if (!this.isImage) {
|
if (!this.isImage) {
|
||||||
if (this.isVideo) {
|
if (this.isVideo) {
|
||||||
this.previewMedia = this.previewVideo?.nativeElement;
|
this.previewMedia = this.previewVideo?.nativeElement;
|
||||||
} else {
|
|
||||||
this.previewMedia = this.previewAudio?.nativeElement;
|
|
||||||
this.initAudioDrawer(this.localMediaStream);
|
|
||||||
this.audioDrawer?.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mediaRecorder = new MediaRecorder(this.localMediaStream, { mimeType: this.mimetype });
|
this.mediaRecorder = new MediaRecorder(this.localMediaStream, { mimeType: this.mimetype });
|
||||||
|
|
||||||
// When video or audio is recorded, add it to the list of chunks.
|
// When video is recorded, add it to the list of chunks.
|
||||||
this.mediaRecorder.ondataavailable = (e): void => {
|
this.mediaRecorder.ondataavailable = (e): void => {
|
||||||
if (e.data.size > 0) {
|
if (e.data.size > 0) {
|
||||||
chunks.push(e.data);
|
chunks.push(e.data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// When recording stops, create a Blob element with the recording and set it to the video or audio.
|
// When recording stops, create a Blob element with the recording and set it to the video.
|
||||||
this.mediaRecorder.onstop = (): void => {
|
this.mediaRecorder.onstop = (): void => {
|
||||||
this.mediaBlob = new Blob(chunks);
|
this.mediaBlob = new Blob(chunks);
|
||||||
chunks = [];
|
chunks = [];
|
||||||
|
@ -267,91 +183,6 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the audio drawer. This code has been extracted from MDN's example on MediaStream Recording:
|
|
||||||
* https://github.com/mdn/web-dictaphone
|
|
||||||
*
|
|
||||||
* @param stream Stream returned by getUserMedia.
|
|
||||||
*/
|
|
||||||
protected initAudioDrawer(stream: MediaStream): void {
|
|
||||||
if (!this.streamAudio) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let skip = true;
|
|
||||||
let running = false;
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const audioCtx = new (window.AudioContext || (<any> window).webkitAudioContext)();
|
|
||||||
const canvasCtx = this.streamAudio.nativeElement.getContext('2d');
|
|
||||||
const source = audioCtx.createMediaStreamSource(stream);
|
|
||||||
const analyser = audioCtx.createAnalyser();
|
|
||||||
const bufferLength = analyser.frequencyBinCount;
|
|
||||||
const dataArray = new Uint8Array(bufferLength);
|
|
||||||
const width = this.streamAudio.nativeElement.width;
|
|
||||||
const height = this.streamAudio.nativeElement.height;
|
|
||||||
const drawAudio = (): void => {
|
|
||||||
if (!running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the draw every animation frame.
|
|
||||||
requestAnimationFrame(drawAudio);
|
|
||||||
|
|
||||||
// Skip half of the frames to improve performance, shouldn't affect the smoothness.
|
|
||||||
skip = !skip;
|
|
||||||
if (skip) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sliceWidth = width / bufferLength;
|
|
||||||
let x = 0;
|
|
||||||
|
|
||||||
analyser.getByteTimeDomainData(dataArray);
|
|
||||||
|
|
||||||
canvasCtx.fillStyle = 'rgb(200, 200, 200)';
|
|
||||||
canvasCtx.fillRect(0, 0, width, height);
|
|
||||||
|
|
||||||
canvasCtx.lineWidth = 1;
|
|
||||||
canvasCtx.strokeStyle = 'rgb(0, 0, 0)';
|
|
||||||
|
|
||||||
canvasCtx.beginPath();
|
|
||||||
|
|
||||||
for (let i = 0; i < bufferLength; i++) {
|
|
||||||
const v = dataArray[i] / 128.0;
|
|
||||||
const y = v * height / 2;
|
|
||||||
|
|
||||||
if (i === 0) {
|
|
||||||
canvasCtx.moveTo(x, y);
|
|
||||||
} else {
|
|
||||||
canvasCtx.lineTo(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
x += sliceWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvasCtx.lineTo(width, height / 2);
|
|
||||||
canvasCtx.stroke();
|
|
||||||
};
|
|
||||||
|
|
||||||
analyser.fftSize = 2048;
|
|
||||||
source.connect(analyser);
|
|
||||||
|
|
||||||
this.audioDrawer = {
|
|
||||||
start: (): void => {
|
|
||||||
if (running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
running = true;
|
|
||||||
drawAudio();
|
|
||||||
},
|
|
||||||
stop: (): void => {
|
|
||||||
running = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main action clicked: record or stop recording.
|
* Main action clicked: record or stop recording.
|
||||||
*/
|
*/
|
||||||
|
@ -369,15 +200,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
this.isCapturing = true;
|
this.isCapturing = true;
|
||||||
this.resetChrono = false;
|
this.resetChrono = false;
|
||||||
|
|
||||||
if (this.isCordovaAudioCapture) {
|
this.mediaRecorder?.start();
|
||||||
this.mediaFile?.startRecord();
|
|
||||||
if (this.previewMedia) {
|
|
||||||
this.previewMedia.src = '';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.mediaRecorder?.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
} else {
|
} else {
|
||||||
if (!this.imgCanvas) {
|
if (!this.imgCanvas) {
|
||||||
|
@ -419,11 +242,6 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
// Send a "cancelled" error like the Cordova plugin does.
|
// Send a "cancelled" error like the Cordova plugin does.
|
||||||
this.dismissWithCanceledError('Canceled.', 'Camera cancelled');
|
this.dismissWithCanceledError('Canceled.', 'Camera cancelled');
|
||||||
|
|
||||||
if (this.isCordovaAudioCapture && this.filePath) {
|
|
||||||
// Delete the tmp file.
|
|
||||||
CoreFile.removeFile(this.filePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -432,11 +250,6 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
async discard(): Promise<void> {
|
async discard(): Promise<void> {
|
||||||
this.previewMedia?.pause();
|
this.previewMedia?.pause();
|
||||||
this.streamVideo?.nativeElement.play();
|
this.streamVideo?.nativeElement.play();
|
||||||
this.audioDrawer?.start();
|
|
||||||
|
|
||||||
if (this.isCordovaAudioCapture) {
|
|
||||||
await this.resetCordovaMediaCapture();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hasCaptured = false;
|
this.hasCaptured = false;
|
||||||
this.isCapturing = false;
|
this.isCapturing = false;
|
||||||
|
@ -492,30 +305,23 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.mediaBlob && !this.isCordovaAudioCapture) {
|
if (!this.mediaBlob) {
|
||||||
// Shouldn't happen.
|
// Shouldn't happen.
|
||||||
CoreDomUtils.showErrorModal('Please capture the media first.');
|
CoreDomUtils.showErrorModal('Please capture the media first.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileEntry = this.fileEntry;
|
|
||||||
const loadingModal = await CoreDomUtils.showModalLoading();
|
const loadingModal = await CoreDomUtils.showModalLoading();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!this.isCordovaAudioCapture) {
|
// Capturing in browser. Write the blob in a file.
|
||||||
// Capturing in browser. Write the blob in a file.
|
if (!this.mediaBlob) {
|
||||||
if (!this.mediaBlob) {
|
// Shouldn't happen.
|
||||||
// Shouldn't happen.
|
throw new Error('Please capture the media first.');
|
||||||
throw new Error('Please capture the media first.');
|
|
||||||
}
|
|
||||||
|
|
||||||
fileEntry = await CoreFile.writeFile(this.getFilePath(), this.mediaBlob);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileEntry) {
|
const fileEntry = await CoreFile.writeFile(this.getFilePath(), this.mediaBlob);
|
||||||
throw new CoreError('File not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isImage && !this.isCaptureImage) {
|
if (this.isImage && !this.isCaptureImage) {
|
||||||
this.dismissWithData(fileEntry.toURL());
|
this.dismissWithData(fileEntry.toURL());
|
||||||
|
@ -560,30 +366,20 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop capturing. Only for video and audio.
|
* Stop capturing. Only for video.
|
||||||
*/
|
*/
|
||||||
stopCapturing(): void {
|
stopCapturing(): void {
|
||||||
this.isCapturing = false;
|
this.isCapturing = false;
|
||||||
this.hasCaptured = true;
|
this.hasCaptured = true;
|
||||||
|
|
||||||
if (this.isCordovaAudioCapture) {
|
this.streamVideo && this.streamVideo.nativeElement.pause();
|
||||||
this.mediaFile?.stopRecord();
|
this.mediaRecorder && this.mediaRecorder.stop();
|
||||||
if (this.previewMedia && this.fileEntry) {
|
|
||||||
this.previewMedia.src = CoreFile.convertFileSrc(this.fileEntry.toURL());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.streamVideo && this.streamVideo.nativeElement.pause();
|
|
||||||
this.audioDrawer && this.audioDrawer.stop();
|
|
||||||
this.mediaRecorder && this.mediaRecorder.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page destroyed.
|
* Page destroyed.
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.mediaFile?.release();
|
|
||||||
|
|
||||||
if (this.localMediaStream) {
|
if (this.localMediaStream) {
|
||||||
const tracks = this.localMediaStream.getTracks();
|
const tracks = this.localMediaStream.getTracks();
|
||||||
tracks.forEach((track) => {
|
tracks.forEach((track) => {
|
||||||
|
@ -592,14 +388,13 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
this.streamVideo?.nativeElement.pause();
|
this.streamVideo?.nativeElement.pause();
|
||||||
this.previewMedia?.pause();
|
this.previewMedia?.pause();
|
||||||
this.audioDrawer?.stop();
|
|
||||||
delete this.mediaBlob;
|
delete this.mediaBlob;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CaptureMediaComponentInputs = {
|
export type CaptureMediaComponentInputs = {
|
||||||
type: 'audio' | 'video' | 'image' | 'captureimage';
|
type: 'video' | 'image' | 'captureimage';
|
||||||
maxTime?: number; // Max time to capture.
|
maxTime?: number; // Max time to capture.
|
||||||
facingMode?: string; // Camera facing mode.
|
facingMode?: string; // Camera facing mode.
|
||||||
mimetype?: string;
|
mimetype?: string;
|
||||||
|
|
|
@ -14,23 +14,18 @@
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CameraOptions } from '@ionic-native/camera/ngx';
|
import { CameraOptions } from '@ionic-native/camera/ngx';
|
||||||
import { CaptureAudioOptions, CaptureImageOptions, CaptureVideoOptions, MediaFile } from '@ionic-native/media-capture/ngx';
|
import { CaptureImageOptions, CaptureVideoOptions, MediaFile } from '@ionic-native/media-capture/ngx';
|
||||||
|
|
||||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
import { makeSingleton, ModalController } from '@singletons';
|
import { makeSingleton, ModalController } from '@singletons';
|
||||||
import { CaptureMediaComponentInputs, CoreEmulatorCaptureMediaComponent } from '../components/capture-media/capture-media';
|
import { CaptureMediaComponentInputs, CoreEmulatorCaptureMediaComponent } from '../components/capture-media/capture-media';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper service with some features to capture media (image, audio, video).
|
* Helper service with some features to capture media (image, video).
|
||||||
*/
|
*/
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class CoreEmulatorCaptureHelperProvider {
|
export class CoreEmulatorCaptureHelperProvider {
|
||||||
|
|
||||||
protected possibleAudioMimeTypes = {
|
|
||||||
'audio/webm': 'weba',
|
|
||||||
'audio/ogg': 'ogg',
|
|
||||||
};
|
|
||||||
|
|
||||||
protected possibleVideoMimeTypes = {
|
protected possibleVideoMimeTypes = {
|
||||||
'video/webm;codecs=vp9': 'webm',
|
'video/webm;codecs=vp9': 'webm',
|
||||||
'video/webm;codecs=vp8': 'webm',
|
'video/webm;codecs=vp8': 'webm',
|
||||||
|
@ -38,22 +33,20 @@ export class CoreEmulatorCaptureHelperProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
videoMimeType?: string;
|
videoMimeType?: string;
|
||||||
audioMimeType?: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture media (image, audio, video).
|
* Capture media (image, video).
|
||||||
*
|
*
|
||||||
* @param type Type of media: image, audio, video.
|
* @param type Type of media: image, video.
|
||||||
* @param options Optional options.
|
* @param options Optional options.
|
||||||
* @returns Promise resolved when captured, rejected if error.
|
* @returns Promise resolved when captured, rejected if error.
|
||||||
*/
|
*/
|
||||||
captureMedia(type: 'image', options?: MockCameraOptions): Promise<string>;
|
captureMedia(type: 'image', options?: MockCameraOptions): Promise<string>;
|
||||||
captureMedia(type: 'captureimage', options?: MockCaptureImageOptions): Promise<MediaFile[]>;
|
captureMedia(type: 'captureimage', options?: MockCaptureImageOptions): Promise<MediaFile[]>;
|
||||||
captureMedia(type: 'audio', options?: MockCaptureAudioOptions): Promise<MediaFile[]>;
|
|
||||||
captureMedia(type: 'video', options?: MockCaptureVideoOptions): Promise<MediaFile[]>;
|
captureMedia(type: 'video', options?: MockCaptureVideoOptions): Promise<MediaFile[]>;
|
||||||
async captureMedia(
|
async captureMedia(
|
||||||
type: 'image' | 'captureimage' | 'audio' | 'video',
|
type: 'image' | 'captureimage' | 'video',
|
||||||
options?: MockCameraOptions | MockCaptureImageOptions | MockCaptureAudioOptions | MockCaptureVideoOptions,
|
options?: MockCameraOptions | MockCaptureImageOptions | MockCaptureVideoOptions,
|
||||||
): Promise<MediaFile[] | string> {
|
): Promise<MediaFile[] | string> {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
|
@ -67,10 +60,6 @@ export class CoreEmulatorCaptureHelperProvider {
|
||||||
const mimeAndExt = this.getMimeTypeAndExtension(type, options.mimetypes);
|
const mimeAndExt = this.getMimeTypeAndExtension(type, options.mimetypes);
|
||||||
params.mimetype = mimeAndExt.mimetype;
|
params.mimetype = mimeAndExt.mimetype;
|
||||||
params.extension = mimeAndExt.extension;
|
params.extension = mimeAndExt.extension;
|
||||||
} else if (type == 'audio') {
|
|
||||||
const mimeAndExt = this.getMimeTypeAndExtension(type, options.mimetypes);
|
|
||||||
params.mimetype = mimeAndExt.mimetype;
|
|
||||||
params.extension = mimeAndExt.extension;
|
|
||||||
} else if (type == 'image') {
|
} else if (type == 'image') {
|
||||||
if ('sourceType' in options && options.sourceType !== undefined && options.sourceType != 1) {
|
if ('sourceType' in options && options.sourceType !== undefined && options.sourceType != 1) {
|
||||||
return Promise.reject('This source type is not supported in browser.');
|
return Promise.reject('This source type is not supported in browser.');
|
||||||
|
@ -121,7 +110,7 @@ export class CoreEmulatorCaptureHelperProvider {
|
||||||
/**
|
/**
|
||||||
* Get the mimetype and extension to capture media.
|
* Get the mimetype and extension to capture media.
|
||||||
*
|
*
|
||||||
* @param type Type of media: image, audio, video.
|
* @param type Type of media: image, video.
|
||||||
* @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
|
* @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
|
||||||
* @returns An object with mimetype and extension to use.
|
* @returns An object with mimetype and extension to use.
|
||||||
*/
|
*/
|
||||||
|
@ -148,10 +137,6 @@ export class CoreEmulatorCaptureHelperProvider {
|
||||||
// No mimetype found, use default extension.
|
// No mimetype found, use default extension.
|
||||||
result.mimetype = this.videoMimeType;
|
result.mimetype = this.videoMimeType;
|
||||||
result.extension = this.possibleVideoMimeTypes[result.mimetype!];
|
result.extension = this.possibleVideoMimeTypes[result.mimetype!];
|
||||||
} else if (type == 'audio') {
|
|
||||||
// No mimetype found, use default extension.
|
|
||||||
result.mimetype = this.audioMimeType;
|
|
||||||
result.extension = this.possibleAudioMimeTypes[result.mimetype!];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -170,20 +155,12 @@ export class CoreEmulatorCaptureHelperProvider {
|
||||||
* Initialize the mimetypes to use when capturing.
|
* Initialize the mimetypes to use when capturing.
|
||||||
*/
|
*/
|
||||||
protected initMimeTypes(): void {
|
protected initMimeTypes(): void {
|
||||||
// Determine video and audio mimetype to use.
|
|
||||||
for (const mimeType in this.possibleVideoMimeTypes) {
|
for (const mimeType in this.possibleVideoMimeTypes) {
|
||||||
if (window.MediaRecorder.isTypeSupported(mimeType)) {
|
if (window.MediaRecorder.isTypeSupported(mimeType)) {
|
||||||
this.videoMimeType = mimeType;
|
this.videoMimeType = mimeType;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const mimeType in this.possibleAudioMimeTypes) {
|
|
||||||
if (window.MediaRecorder.isTypeSupported(mimeType)) {
|
|
||||||
this.audioMimeType = mimeType;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,9 +186,6 @@ export interface MockCameraOptions extends CameraOptions {
|
||||||
export interface MockCaptureImageOptions extends CaptureImageOptions {
|
export interface MockCaptureImageOptions extends CaptureImageOptions {
|
||||||
mimetypes?: string[]; // Allowed mimetypes.
|
mimetypes?: string[]; // Allowed mimetypes.
|
||||||
}
|
}
|
||||||
export interface MockCaptureAudioOptions extends CaptureAudioOptions {
|
|
||||||
mimetypes?: string[]; // Allowed mimetypes.
|
|
||||||
}
|
|
||||||
export interface MockCaptureVideoOptions extends CaptureVideoOptions {
|
export interface MockCaptureVideoOptions extends CaptureVideoOptions {
|
||||||
mimetypes?: string[]; // Allowed mimetypes.
|
mimetypes?: string[]; // Allowed mimetypes.
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
MediaCapture,
|
MediaCapture,
|
||||||
CaptureAudioOptions,
|
|
||||||
CaptureImageOptions,
|
CaptureImageOptions,
|
||||||
CaptureVideoOptions,
|
CaptureVideoOptions,
|
||||||
MediaFile,
|
MediaFile,
|
||||||
|
@ -29,16 +28,6 @@ import { CoreEmulatorCaptureHelper } from './capture-helper';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MediaCaptureMock extends MediaCapture {
|
export class MediaCaptureMock extends MediaCapture {
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the audio recorder application and return information about captured audio clip files.
|
|
||||||
*
|
|
||||||
* @param options Options.
|
|
||||||
* @returns Promise resolved when captured.
|
|
||||||
*/
|
|
||||||
captureAudio(options: CaptureAudioOptions): Promise<MediaFile[]> {
|
|
||||||
return CoreEmulatorCaptureHelper.captureMedia('audio', options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the camera application and return information about captured image files.
|
* Start the camera application and return information about captured image files.
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,7 +29,6 @@ import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
|
||||||
import { WebView } from '@ionic-native/ionic-webview/ngx';
|
import { WebView } from '@ionic-native/ionic-webview/ngx';
|
||||||
import { Keyboard } from '@ionic-native/keyboard/ngx';
|
import { Keyboard } from '@ionic-native/keyboard/ngx';
|
||||||
import { LocalNotifications } from '@ionic-native/local-notifications/ngx';
|
import { LocalNotifications } from '@ionic-native/local-notifications/ngx';
|
||||||
import { Media } from '@ionic-native/media/ngx';
|
|
||||||
import { MediaCapture } from '@ionic-native/media-capture/ngx';
|
import { MediaCapture } from '@ionic-native/media-capture/ngx';
|
||||||
import { Push } from '@ionic-native/push/ngx';
|
import { Push } from '@ionic-native/push/ngx';
|
||||||
import { QRScanner } from '@ionic-native/qr-scanner/ngx';
|
import { QRScanner } from '@ionic-native/qr-scanner/ngx';
|
||||||
|
@ -54,7 +53,6 @@ export const CORE_NATIVE_SERVICES = [
|
||||||
InAppBrowser,
|
InAppBrowser,
|
||||||
Keyboard,
|
Keyboard,
|
||||||
LocalNotifications,
|
LocalNotifications,
|
||||||
Media,
|
|
||||||
MediaCapture,
|
MediaCapture,
|
||||||
Push,
|
Push,
|
||||||
QRScanner,
|
QRScanner,
|
||||||
|
@ -82,7 +80,6 @@ export const CORE_NATIVE_SERVICES = [
|
||||||
InAppBrowser,
|
InAppBrowser,
|
||||||
Keyboard,
|
Keyboard,
|
||||||
LocalNotifications,
|
LocalNotifications,
|
||||||
Media,
|
|
||||||
MediaCapture,
|
MediaCapture,
|
||||||
Push,
|
Push,
|
||||||
QRScanner,
|
QRScanner,
|
||||||
|
|
|
@ -52,7 +52,6 @@ import { InAppBrowser as InAppBrowserService } from '@ionic-native/in-app-browse
|
||||||
import { WebView as WebViewService } from '@ionic-native/ionic-webview/ngx';
|
import { WebView as WebViewService } from '@ionic-native/ionic-webview/ngx';
|
||||||
import { Keyboard as KeyboardService } from '@ionic-native/keyboard/ngx';
|
import { Keyboard as KeyboardService } from '@ionic-native/keyboard/ngx';
|
||||||
import { LocalNotifications as LocalNotificationsService } from '@ionic-native/local-notifications/ngx';
|
import { LocalNotifications as LocalNotificationsService } from '@ionic-native/local-notifications/ngx';
|
||||||
import { Media as MediaService } from '@ionic-native/media/ngx';
|
|
||||||
import { MediaCapture as MediaCaptureService } from '@ionic-native/media-capture/ngx';
|
import { MediaCapture as MediaCaptureService } from '@ionic-native/media-capture/ngx';
|
||||||
import { Push as PushService } from '@ionic-native/push/ngx';
|
import { Push as PushService } from '@ionic-native/push/ngx';
|
||||||
import { QRScanner as QRScannerService } from '@ionic-native/qr-scanner/ngx';
|
import { QRScanner as QRScannerService } from '@ionic-native/qr-scanner/ngx';
|
||||||
|
@ -184,7 +183,6 @@ export const Geolocation = makeSingleton(GeolocationService);
|
||||||
export const InAppBrowser = makeSingleton(InAppBrowserService);
|
export const InAppBrowser = makeSingleton(InAppBrowserService);
|
||||||
export const Keyboard = makeSingleton(KeyboardService);
|
export const Keyboard = makeSingleton(KeyboardService);
|
||||||
export const LocalNotifications = makeSingleton(LocalNotificationsService);
|
export const LocalNotifications = makeSingleton(LocalNotificationsService);
|
||||||
export const Media = makeSingleton(MediaService);
|
|
||||||
export const MediaCapture = makeSingleton(MediaCaptureService);
|
export const MediaCapture = makeSingleton(MediaCaptureService);
|
||||||
export const NativeHttp = makeSingleton(HTTP);
|
export const NativeHttp = makeSingleton(HTTP);
|
||||||
export const Push = makeSingleton(PushService);
|
export const Push = makeSingleton(PushService);
|
||||||
|
|
Loading…
Reference in New Issue