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

184 lines
5.7 KiB
JavaScript
Raw Normal View History

// @flow
import React from 'react';
import '../../analytics';
2017-09-18 07:09:43 +00:00
import '../../authentication';
2019-01-22 10:32:18 +00:00
import { setColorScheme } from '../../base/color-scheme';
import { DialogContainer } from '../../base/dialog';
import { CALL_INTEGRATION_ENABLED, updateFlags } from '../../base/flags';
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 '../../base/responsive-ui';
import { updateSettings } from '../../base/settings';
2018-08-07 00:30:32 +00:00
import '../../google-api';
import '../../mobile/audio-mode';
2019-07-11 11:32:17 +00:00
import '../../mobile/back-button';
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 '../../mobile/watchos';
import logger from '../logger';
import { AbstractApp } from './AbstractApp';
import type { Props as AbstractAppProps } from './AbstractApp';
declare var __DEV__;
/**
* The type of React {@code Component} props of {@link App}.
*/
type Props = AbstractAppProps & {
2019-01-22 10:32:18 +00:00
/**
* An object of colors that override the default colors of the app/sdk.
*/
2019-03-05 11:06:38 +00:00
colorScheme: ?Object,
/**
* Identifier for this app on the native side.
*/
externalAPIScope: string,
2019-01-22 10:32:18 +00:00
/**
* An object with the feature flags.
*/
flags: Object,
/**
* An object with user information (display name, email, avatar URL).
*/
userInfo: ?Object
};
/**
* Root app {@code Component} on mobile/React Native.
*
* @extends AbstractApp
*/
export class App extends AbstractApp {
2019-01-22 10:32:18 +00:00
_init: Promise<*>;
/**
* 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);
// 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();
}
/**
* Initializes the color scheme.
*
* @inheritdoc
*
* @returns {void}
*/
componentDidMount() {
super.componentDidMount();
2019-01-22 10:32:18 +00:00
this._init.then(() => {
// We set these early enough so then we avoid any unnecessary re-renders.
const { dispatch } = this.state.store;
dispatch(setColorScheme(this.props.colorScheme));
dispatch(updateFlags(this.props.flags));
dispatch(updateSettings(this.props.userInfo || {}));
// Update settings with feature-flag.
const callIntegrationEnabled = this.props.flags[CALL_INTEGRATION_ENABLED];
if (typeof callIntegrationEnabled !== 'undefined') {
dispatch(updateSettings({ disableCallIntegration: !callIntegrationEnabled }));
}
2019-01-22 10:32:18 +00:00
});
}
/**
* 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);
}
}
/**
* 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);
}
}