From a8477e1cf527a20163d46fa68377ed87d8c87317 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 9 Jan 2018 08:51:44 +0100 Subject: [PATCH] MOBILE-2312 uploader: Implement delegate and the handlers --- src/app/app.scss | 19 ++ .../fileuploader/providers/album-handler.ts | 71 ++++ .../fileuploader/providers/audio-handler.ts | 88 +++++ .../fileuploader/providers/camera-handler.ts | 71 ++++ src/core/fileuploader/providers/delegate.ts | 303 ++++++++++++++++++ .../fileuploader/providers/file-handler.ts | 118 +++++++ .../fileuploader/providers/video-handler.ts | 88 +++++ 7 files changed, 758 insertions(+) create mode 100644 src/core/fileuploader/providers/album-handler.ts create mode 100644 src/core/fileuploader/providers/audio-handler.ts create mode 100644 src/core/fileuploader/providers/camera-handler.ts create mode 100644 src/core/fileuploader/providers/delegate.ts create mode 100644 src/core/fileuploader/providers/file-handler.ts create mode 100644 src/core/fileuploader/providers/video-handler.ts diff --git a/src/app/app.scss b/src/app/app.scss index 3b5ab9aad..8c06237ba 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -274,3 +274,22 @@ ion-select { } } } + +// File uploader. +// ------------------------- + +.core-fileuploader-file-handler { + position: relative; + + input { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + opacity: 0; + outline: none; + z-index: 100; + cursor: pointer; + } +} diff --git a/src/core/fileuploader/providers/album-handler.ts b/src/core/fileuploader/providers/album-handler.ts new file mode 100644 index 000000000..3e78e3e2d --- /dev/null +++ b/src/core/fileuploader/providers/album-handler.ts @@ -0,0 +1,71 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { CoreAppProvider } from '../../../providers/app'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from './delegate'; +import { CoreFileUploaderHelperProvider } from './helper'; +/** + * Handler to upload files from the album. + */ +@Injectable() +export class CoreFileUploaderAlbumHandler implements CoreFileUploaderHandler { + name = 'CoreFileUploaderAlbum'; + priority = 2000; + + constructor(private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, + private uploaderHelper: CoreFileUploaderHelperProvider) {} + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean|Promise { + return this.appProvider.isMobile(); + } + + /** + * Given a list of mimetypes, return the ones that are supported by the handler. + * + * @param {string[]} [mimetypes] List of mimetypes. + * @return {string[]} Supported mimetypes. + */ + getSupportedMimetypes(mimetypes: string[]) : string[] { + // Album allows picking images and videos. + return this.utils.filterByRegexp(mimetypes, /^(image|video)\//); + } + + /** + * Get the data to display the handler. + * + * @return {CoreFileUploaderHandlerData} Data. + */ + getData() : CoreFileUploaderHandlerData { + return { + title: 'core.fileuploader.photoalbums', + class: 'core-fileuploader-album-handler', + icon: 'images', + action: (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]) => { + return this.uploaderHelper.uploadImage(true, maxSize, upload, mimetypes).then((result) => { + return { + treated: true, + result: result + }; + }); + } + }; + } +} diff --git a/src/core/fileuploader/providers/audio-handler.ts b/src/core/fileuploader/providers/audio-handler.ts new file mode 100644 index 000000000..332e8d0ee --- /dev/null +++ b/src/core/fileuploader/providers/audio-handler.ts @@ -0,0 +1,88 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { Platform } from 'ionic-angular'; +import { CoreAppProvider } from '../../../providers/app'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from './delegate'; +import { CoreFileUploaderHelperProvider } from './helper'; +/** + * Handler to record an audio to upload it. + */ +@Injectable() +export class CoreFileUploaderAudioHandler implements CoreFileUploaderHandler { + name = 'CoreFileUploaderAudio'; + priority = 1600; + + constructor(private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private platform: Platform, + private uploaderHelper: CoreFileUploaderHelperProvider) {} + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean|Promise { + return this.appProvider.isMobile() || (this.appProvider.canGetUserMedia() && this.appProvider.canRecordMedia()); + } + + /** + * Given a list of mimetypes, return the ones that are supported by the handler. + * + * @param {string[]} [mimetypes] List of mimetypes. + * @return {string[]} Supported mimetypes. + */ + getSupportedMimetypes(mimetypes: string[]) : string[] { + if (this.platform.is('ios')) { + // iOS records as WAV. + return this.utils.filterByRegexp(mimetypes, /^audio\/wav$/); + } else if (this.platform.is('android')) { + // In Android we don't know the format the audio will be recorded, so accept any audio mimetype. + return this.utils.filterByRegexp(mimetypes, /^audio\//); + } else { + // In desktop, support audio formats that are supported by MediaRecorder. + let mediaRecorder = (window).MediaRecorder; + if (mediaRecorder) { + return mimetypes.filter((type) => { + let matches = type.match(/^audio\//); + return matches && matches.length && mediaRecorder.isTypeSupported(type); + }); + } + } + + return []; + } + + /** + * Get the data to display the handler. + * + * @return {CoreFileUploaderHandlerData} Data. + */ + getData() : CoreFileUploaderHandlerData { + return { + title: 'core.fileuploader.audio', + class: 'core-fileuploader-audio-handler', + icon: 'microphone', + action: (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]) => { + return this.uploaderHelper.uploadAudioOrVideo(true, maxSize, upload, mimetypes).then((result) => { + return { + treated: true, + result: result + }; + }); + } + }; + } +} diff --git a/src/core/fileuploader/providers/camera-handler.ts b/src/core/fileuploader/providers/camera-handler.ts new file mode 100644 index 000000000..e72adb6f4 --- /dev/null +++ b/src/core/fileuploader/providers/camera-handler.ts @@ -0,0 +1,71 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { CoreAppProvider } from '../../../providers/app'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from './delegate'; +import { CoreFileUploaderHelperProvider } from './helper'; +/** + * Handler to take a picture to upload it. + */ +@Injectable() +export class CoreFileUploaderCameraHandler implements CoreFileUploaderHandler { + name = 'CoreFileUploaderCamera'; + priority = 1800; + + constructor(private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, + private uploaderHelper: CoreFileUploaderHelperProvider) {} + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean|Promise { + return this.appProvider.isMobile() || this.appProvider.canGetUserMedia(); + } + + /** + * Given a list of mimetypes, return the ones that are supported by the handler. + * + * @param {string[]} [mimetypes] List of mimetypes. + * @return {string[]} Supported mimetypes. + */ + getSupportedMimetypes(mimetypes: string[]) : string[] { + // Camera only supports JPEG and PNG. + return this.utils.filterByRegexp(mimetypes, /^image\/(jpeg|png)$/); + } + + /** + * Get the data to display the handler. + * + * @return {CoreFileUploaderHandlerData} Data. + */ + getData() : CoreFileUploaderHandlerData { + return { + title: 'core.fileuploader.camera', + class: 'core-fileuploader-camera-handler', + icon: 'camera', + action: (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]) => { + return this.uploaderHelper.uploadImage(false, maxSize, upload, mimetypes).then((result) => { + return { + treated: true, + result: result + }; + }); + } + }; + } +} diff --git a/src/core/fileuploader/providers/delegate.ts b/src/core/fileuploader/providers/delegate.ts new file mode 100644 index 000000000..7cedfcb50 --- /dev/null +++ b/src/core/fileuploader/providers/delegate.ts @@ -0,0 +1,303 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { CoreEventsProvider } from '../../../providers/events'; +import { CoreLoggerProvider } from '../../../providers/logger'; +import { CoreSitesProvider } from '../../../providers/sites'; + +/** + * Interface that all handlers must implement. + */ +export interface CoreFileUploaderHandler { + /** + * A name to identify the addon. + * @type {string} + */ + name: string; + + /** + * Handler's priority. The highest priority, the highest position. + * @type {string} + */ + priority?: number; + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean|Promise; + + /** + * Given a list of mimetypes, return the ones that are supported by the handler. + * + * @param {string[]} [mimetypes] List of mimetypes. + * @return {string[]} Supported mimetypes. + */ + getSupportedMimetypes(mimetypes: string[]) : string[]; + + /** + * Get the data to display the handler. + * + * @return {CoreFileUploaderHandlerData} Data. + */ + getData() : CoreFileUploaderHandlerData; +}; + +/** + * Data needed to render the handler in the file picker. It must be returned by the handler. + */ +export interface CoreFileUploaderHandlerData { + /** + * The title to display in the handler. + * @type {string} + */ + title: string; + + /** + * The icon to display in the handler. + * @type {string} + */ + icon?: string; + + /** + * The class to assign to the handler item. + * @type {string} + */ + class?: string; + + /** + * Action to perform when the handler is clicked. + * + * @param {number} [maxSize] Max size of the file. If not defined or -1, no max size. + * @param {boolean} [upload] Whether the file should be uploaded. + * @param {boolean} [allowOffline] True to allow selecting in offline, false to require connection. + * @param {string[]} [mimetypes] List of supported mimetypes. If undefined, all mimetypes supported. + * @return {Promise} Promise resolved with the result of picking/uploading the file. + */ + action?(maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]) + : Promise; + + /** + * Function called after the handler is rendered. + * + * @param {number} [maxSize] Max size of the file. If not defined or -1, no max size. + * @param {boolean} [upload] Whether the file should be uploaded. + * @param {boolean} [allowOffline] True to allow selecting in offline, false to require connection. + * @param {string[]} [mimetypes] List of supported mimetypes. If undefined, all mimetypes supported. + */ + afterRender?(maxSize: number, upload: boolean, allowOffline: boolean, mimetypes: string[]) : void; +}; + +/** + * The result of clicking a handler. + */ +export interface CoreFileUploaderHandlerResult { + /** + * Whether the file was treated (uploaded or copied to tmp folder). + * @type {boolean} + */ + treated: boolean; + + /** + * The path of the file picked. Required if treated=false and fileEntry is not set. + * @type {string} + */ + path?: string; + + /** + * The fileEntry of the file picked. Required if treated=false and path is not set. + * @type {any} + */ + fileEntry?: any; + + /** + * Whether the file should be deleted after the upload. Ignored if treated=true. + * @type {boolean} + */ + delete?: boolean; + + /** + * The result of picking/uploading the file. Ignored if treated=false. + * @type {any} + */ + result?: any; +}; + +/** + * Data returned by the delegate for each handler. + */ +export interface CoreFileUploaderHandlerDataToReturn extends CoreFileUploaderHandlerData { + /** + * Handler's priority. + * @type {number} + */ + priority?: number; + + + /** + * Supported mimetypes. + * @type {string[]} + */ + mimetypes?: string[]; +}; + +/** + * Delegate to register handlers to be shown in the file picker. + */ +@Injectable() +export class CoreFileUploaderDelegate { + protected logger; + protected handlers: {[s: string]: CoreFileUploaderHandler} = {}; // All registered handlers. + protected enabledHandlers: {[s: string]: CoreFileUploaderHandler} = {}; // Handlers enabled for the current site. + protected lastUpdateHandlersStart: number; + + constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider) { + this.logger = logger.getInstance('CoreCourseModuleDelegate'); + + eventsProvider.on(CoreEventsProvider.LOGIN, this.updateHandlers.bind(this)); + eventsProvider.on(CoreEventsProvider.SITE_UPDATED, this.updateHandlers.bind(this)); + eventsProvider.on(CoreEventsProvider.REMOTE_ADDONS_LOADED, this.updateHandlers.bind(this)); + eventsProvider.on(CoreEventsProvider.LOGOUT, this.clearSiteHandlers.bind(this)); + } + + /** + * Clear current site handlers. Reserved for core use. + */ + protected clearSiteHandlers() : void { + this.enabledHandlers = {}; + } + + /** + * Get the handlers for the current site. + * + * @param {string[]} [mimetypes] List of supported mimetypes. If undefined, all mimetypes supported. + * @return {CoreFileUploaderHandlerDataToReturn[]} List of handlers data. + */ + getHandlers(mimetypes: string[]) : CoreFileUploaderHandlerDataToReturn[] { + let handlers = []; + + for (let name in this.enabledHandlers) { + let handler = this.enabledHandlers[name], + supportedMimetypes; + + if (mimetypes) { + if (!handler.getSupportedMimetypes) { + // Handler doesn't implement a required function, don't add it. + return; + } + + supportedMimetypes = handler.getSupportedMimetypes(mimetypes); + + if (!supportedMimetypes.length) { + // Handler doesn't support any mimetype, don't add it. + return; + } + } + + let data : CoreFileUploaderHandlerDataToReturn = handler.getData(); + data.priority = handler.priority; + data.mimetypes = supportedMimetypes; + handlers.push(data); + } + + return handlers; + } + + /** + * Check if a time belongs to the last update handlers call. + * This is to handle the cases where updateHandlers don't finish in the same order as they're called. + * + * @param {number} time Time to check. + * @return {boolean} Whether it's the last call. + */ + isLastUpdateCall(time: number) : boolean { + if (!this.lastUpdateHandlersStart) { + return true; + } + return time == this.lastUpdateHandlersStart; + } + + /** + * Register a handler. + * + * @param {CoreFileUploaderHandler} handler The handler to register. + * @return {boolean} True if registered successfully, false otherwise. + */ + registerHandler(handler: CoreFileUploaderHandler) : boolean { + if (typeof this.handlers[handler.name] !== 'undefined') { + this.logger.log(`Addon '${handler.name}' already registered`); + return false; + } + this.logger.log(`Registered addon '${handler.name}'`); + this.handlers[handler.name] = handler; + return true; + } + + /** + * Update the handler for the current site. + * + * @param {CoreFileUploaderHandler} handler The handler to check. + * @param {number} time Time this update process started. + * @return {Promise} Resolved when done. + */ + protected updateHandler(handler: CoreFileUploaderHandler, time: number) : Promise { + let promise, + siteId = this.sitesProvider.getCurrentSiteId(); + + if (!this.sitesProvider.isLoggedIn()) { + promise = Promise.reject(null); + } else { + promise = Promise.resolve(handler.isEnabled()); + } + + // Checks if the handler is enabled. + return promise.catch(() => { + return false; + }).then((enabled: boolean) => { + // Verify that this call is the last one that was started. + if (this.isLastUpdateCall(time) && this.sitesProvider.getCurrentSiteId() === siteId) { + if (enabled) { + this.enabledHandlers[handler.name] = handler; + } else { + delete this.enabledHandlers[handler.name]; + } + } + }); + } + + /** + * Update the handlers for the current site. + * + * @return {Promise} Resolved when done. + */ + protected updateHandlers() : Promise { + let promises = [], + now = Date.now(); + + this.logger.debug('Updating handlers for current site.'); + + this.lastUpdateHandlersStart = now; + + // Loop over all the handlers. + for (let name in this.handlers) { + promises.push(this.updateHandler(this.handlers[name], now)); + } + + return Promise.all(promises).catch(() => { + // Never reject. + }); + } +} diff --git a/src/core/fileuploader/providers/file-handler.ts b/src/core/fileuploader/providers/file-handler.ts new file mode 100644 index 000000000..8240c991b --- /dev/null +++ b/src/core/fileuploader/providers/file-handler.ts @@ -0,0 +1,118 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { Platform } from 'ionic-angular'; +import { CoreAppProvider } from '../../../providers/app'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreTimeUtilsProvider } from '../../../providers/utils/time'; +import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from './delegate'; +import { CoreFileUploaderHelperProvider } from './helper'; +import { CoreFileUploaderProvider } from './fileuploader'; +/** + * Handler to upload any type of file. + */ +@Injectable() +export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler { + name = 'CoreFileUploaderFile'; + priority = 1200; + + constructor(private appProvider: CoreAppProvider, private platform: Platform, private timeUtils: CoreTimeUtilsProvider, + private uploaderHelper: CoreFileUploaderHelperProvider, private uploaderProvider: CoreFileUploaderProvider, + private domUtils: CoreDomUtilsProvider) {} + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean|Promise { + return this.platform.is('android') || !this.appProvider.isMobile() || + (this.platform.is('ios') && this.platform.version().major >= 9); + } + + /** + * Given a list of mimetypes, return the ones that are supported by the handler. + * + * @param {string[]} [mimetypes] List of mimetypes. + * @return {string[]} Supported mimetypes. + */ + getSupportedMimetypes(mimetypes: string[]) : string[] { + return mimetypes; + } + + /** + * Get the data to display the handler. + * + * @return {CoreFileUploaderHandlerData} Data. + */ + getData() : CoreFileUploaderHandlerData { + const isIOS = this.platform.is('ios'); + + return { + title: isIOS ? 'core.fileuploader.more' : 'core.fileuploader.file', + class: 'core-fileuploader-file-handler', + icon: isIOS ? 'more' : 'folder', + afterRender: (maxSize: number, upload: boolean, allowOffline: boolean, mimetypes: string[]) => { + // Add an invisible file input in the file handler. + // It needs to be done like this because the action sheet items don't accept inputs. + const element = document.querySelector('.core-fileuploader-file-handler'); + if (element) { + const input = document.createElement('input'); + input.setAttribute('type', 'file'); + if (mimetypes && mimetypes.length && (!this.platform.is('android') || mimetypes.length == 1)) { + // Don't use accept attribute in Android with several mimetypes, it's not supported. + input.setAttribute('accept', mimetypes.join(', ')); + } + + input.addEventListener('change', (evt: Event) => { + let file = input.files[0], + fileName; + input.value = ''; // Unset input. + if (!file) { + return; + } + + // Verify that the mimetype of the file is supported, in case the accept attribute isn't supported. + const error = this.uploaderProvider.isInvalidMimetype(mimetypes, file.name, file.type); + if (error) { + this.domUtils.showErrorModal(error); + return; + } + + fileName = file.name; + if (isIOS) { + // Check the name of the file and add a timestamp if needed (take picture). + const matches = fileName.match(/image\.(jpe?g|png)/); + if (matches) { + fileName = 'image_' + this.timeUtils.readableTimestamp() + '.' + matches[1]; + } + } + + // Upload the picked file. + this.uploaderHelper.uploadFileObject(file, maxSize, upload, allowOffline, fileName).then((result) => { + this.uploaderHelper.fileUploaded(result); + }).catch((error) => { + if (error) { + this.domUtils.showErrorModal(error); + } + }); + }); + + element.appendChild(input); + } + } + }; + } +} diff --git a/src/core/fileuploader/providers/video-handler.ts b/src/core/fileuploader/providers/video-handler.ts new file mode 100644 index 000000000..d90cca555 --- /dev/null +++ b/src/core/fileuploader/providers/video-handler.ts @@ -0,0 +1,88 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { Platform } from 'ionic-angular'; +import { CoreAppProvider } from '../../../providers/app'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from './delegate'; +import { CoreFileUploaderHelperProvider } from './helper'; +/** + * Handler to record a video to upload it. + */ +@Injectable() +export class CoreFileUploaderVideoHandler implements CoreFileUploaderHandler { + name = 'CoreFileUploaderVideo'; + priority = 1400; + + constructor(private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private platform: Platform, + private uploaderHelper: CoreFileUploaderHelperProvider) {} + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean|Promise { + return this.appProvider.isMobile() || (this.appProvider.canGetUserMedia() && this.appProvider.canRecordMedia()); + } + + /** + * Given a list of mimetypes, return the ones that are supported by the handler. + * + * @param {string[]} [mimetypes] List of mimetypes. + * @return {string[]} Supported mimetypes. + */ + getSupportedMimetypes(mimetypes: string[]) : string[] { + if (this.platform.is('ios')) { + // iOS records as MOV. + return this.utils.filterByRegexp(mimetypes, /^video\/quicktime$/); + } else if (this.platform.is('android')) { + // In Android we don't know the format the video will be recorded, so accept any video mimetype. + return this.utils.filterByRegexp(mimetypes, /^video\//); + } else { + // In desktop, support video formats that are supported by MediaRecorder. + let mediaRecorder = (window).MediaRecorder; + if (mediaRecorder) { + return mimetypes.filter(function(type) { + let matches = type.match(/^video\//); + return matches && matches.length && mediaRecorder.isTypeSupported(type); + }); + } + } + + return []; + } + + /** + * Get the data to display the handler. + * + * @return {CoreFileUploaderHandlerData} Data. + */ + getData() : CoreFileUploaderHandlerData { + return { + title: 'core.fileuploader.video', + class: 'core-fileuploader-video-handler', + icon: 'videocam', + action: (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]) => { + return this.uploaderHelper.uploadAudioOrVideo(false, maxSize, upload, mimetypes).then((result) => { + return { + treated: true, + result: result + }; + }); + } + }; + } +}