jiti-meet/react/features/app/components/App.native.js

212 lines
6.5 KiB
JavaScript
Raw Normal View History

// @flow
import React from 'react';
import { Linking } from 'react-native';
import '../../analytics';
2017-09-18 07:09:43 +00:00
import '../../authentication';
import { DialogContainer } from '../../base/dialog';
2018-07-09 22:14:55 +00:00
import '../../base/jwt';
2017-02-10 16:13:39 +00:00
import { Platform } from '../../base/react';
import {
AspectRatioDetector,
ReducedUIDetector
} from '../../base/responsive-ui';
2018-08-07 00:30:32 +00:00
import '../../google-api';
import '../../mobile/audio-mode';
import '../../mobile/background';
android: add ConnectionService * feat(Android): implement ConnectionService Adds basic integration with Android's ConnectionService by implementing the outgoing call scenario. * ref(callkit): rename _SET_CALLKIT_SUBSCRIPTIONS * ref(callkit): move feature to call-integration directory * feat(ConnectionService): synchronize video state * ref(AudioMode): use ConnectionService on API >= 26 Not ready yet - few details left mentioned in the FIXMEs * feat(ConnectionService): add debug logs Adds logs to trace the calls. * fix(ConnectionService): leaking ConnectionImpl instances Turns out there is no callback fired back from the JavaScript side after the disconnect or abort event is sent from the native. The connection must be marked as disconnected and removed immediately. * feat(ConnectionService): handle onCreateOutgoingConnectionFailed * ref(ConnectionService): merge classes and move to the sdk package * feat(CallIntegration): show Alert if outgoing call fails * fix(ConnectionService): alternatively get call UUID from the account Some Android flavours (or versions ?) do copy over extras to the onCreateOutgoingConnectionFailed callback. But the call UUID is also set as the PhoneAccount's label, so eventually it should be available there. * ref(ConnectionService): use call UUID as PhoneAccount ID. The extra is not reliable on some custom Android flavours. It also makes sense to use unique id for the account instead of the URL given that it's created on the per call basis. * fix(ConnectionService): abort the call when hold is requested Turns out Android P can sometimes request HOLD even though there's no HOLD capability added to the connection (what!?), so just abort the call in that case. * fix(ConnectionService): unregister account on call failure Unregister the PhoneAccount onCreateOutgoingConnectionFailed. That's before the ConnectionImpl instance is created which is normally responsible for doing that. * fix(AudioModeModule): make package private and run on the audio thread * address other review comments
2019-01-31 16:20:54 +00:00
import '../../mobile/call-integration';
import '../../mobile/external-api';
import '../../mobile/full-screen';
import '../../mobile/permissions';
import '../../mobile/picture-in-picture';
import '../../mobile/proximity';
import '../../mobile/wake-lock';
import { AbstractApp } from './AbstractApp';
import type { Props as AbstractAppProps } from './AbstractApp';
declare var __DEV__;
2018-07-30 14:38:25 +00:00
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* The type of React {@code Component} props of {@link App}.
*/
type Props = AbstractAppProps & {
/**
* Whether Picture-in-Picture is enabled. If {@code true}, a toolbar button
* is rendered in the {@link Conference} view to afford entering
* Picture-in-Picture.
*/
pictureInPictureEnabled: boolean,
/**
* Whether the Welcome page is enabled. If {@code true}, the Welcome page is
* rendered when the {@link App} is not at a location (URL) identifying
* a Jitsi Meet conference/room.
*/
welcomePageEnabled: boolean
};
/**
* Root app {@code Component} on mobile/React Native.
*
* @extends AbstractApp
*/
export class App extends AbstractApp {
/**
* Initializes a new {@code App} instance.
*
* @param {Props} props - The read-only React {@code Component} props with
* which the new instance is to be initialized.
*/
constructor(props: Props) {
super(props);
// Bind event handlers so they are only bound once for every instance.
this._onLinkingURL = this._onLinkingURL.bind(this);
// In the Release configuration, React Native will (intentionally) throw
// an unhandled JavascriptException for an unhandled JavaScript error.
// This will effectively kill the app. In accord with the Web, do not
// kill the app.
this._maybeDisableExceptionsManager();
}
/**
* Subscribe to notifications about activating URLs registered to be handled
* by this app.
*
* @inheritdoc
* @returns {void}
2017-03-28 16:43:33 +00:00
* @see https://facebook.github.io/react-native/docs/linking.html
*/
componentDidMount() {
super.componentDidMount();
Linking.addEventListener('url', this._onLinkingURL);
}
/**
* Unsubscribe from notifications about activating URLs registered to be
* handled by this app.
*
* @inheritdoc
* @returns {void}
2017-03-28 16:43:33 +00:00
* @see https://facebook.github.io/react-native/docs/linking.html
*/
componentWillUnmount() {
Linking.removeEventListener('url', this._onLinkingURL);
super.componentWillUnmount();
}
/**
2017-11-07 14:28:08 +00:00
* Injects {@link AspectRatioDetector} in order to detect the aspect ratio
* of this {@code App}'s user interface and afford {@link AspectRatioAware}.
*
* @override
*/
_createMainElement(component, props) {
return (
<AspectRatioDetector>
<ReducedUIDetector>
{ super._createMainElement(component, props) }
</ReducedUIDetector>
2017-11-07 14:28:08 +00:00
</AspectRatioDetector>
);
}
/**
* Attempts to disable the use of React Native
* {@link ExceptionsManager#handleException} on platforms and in
* configurations on/in which the use of the method in questions has been
* determined to be undesirable. For example, React Native will
* (intentionally) throw an unhandled {@code JavascriptException} for an
* unhandled JavaScript error in the Release configuration. This will
* effectively kill the app. In accord with the Web, do not kill the app.
*
* @private
* @returns {void}
*/
_maybeDisableExceptionsManager() {
if (__DEV__) {
// As mentioned above, only the Release configuration was observed
// to suffer.
return;
}
if (Platform.OS !== 'android') {
// A solution based on RTCSetFatalHandler was implemented on iOS and
// it is preferred because it is at a later step of the
// error/exception handling and it is specific to fatal
// errors/exceptions which were observed to kill the app. The
// solution implemented bellow was tested on Android only so it is
// considered safest to use it there only.
return;
}
const oldHandler = global.ErrorUtils.getGlobalHandler();
const newHandler = _handleException;
if (!oldHandler || oldHandler !== newHandler) {
newHandler.next = oldHandler;
global.ErrorUtils.setGlobalHandler(newHandler);
}
}
_onLinkingURL: (*) => void;
/**
* Notified by React's Linking API that a specific URL registered to be
* handled by this app was activated.
*
* @param {Object} event - The details of the notification/event.
* @param {string} event.url - The URL registered to be handled by this app
* which was activated.
* @private
* @returns {void}
*/
2017-07-06 18:24:24 +00:00
_onLinkingURL({ url }) {
super._openURL(url);
}
/**
* Renders the platform specific dialog container.
*
* @returns {React$Element}
*/
_renderDialogContainer() {
return (
<DialogContainer />
);
}
}
/**
* Handles a (possibly unhandled) JavaScript error by preventing React Native
* from converting a fatal error into an unhandled native exception which will
* kill the app.
*
* @param {Error} error - The (possibly unhandled) JavaScript error to handle.
* @param {boolean} fatal - If the specified error is fatal, {@code true};
* otherwise, {@code false}.
* @private
* @returns {void}
*/
function _handleException(error, fatal) {
if (fatal) {
// In the Release configuration, React Native will (intentionally) throw
// an unhandled JavascriptException for an unhandled JavaScript error.
// This will effectively kill the app. In accord with the Web, do not
// kill the app.
2018-07-30 14:38:25 +00:00
logger.error(error);
} else {
// Forward to the next globalHandler of ErrorUtils.
const { next } = _handleException;
typeof next === 'function' && next(error, fatal);
}
}