feat(rn) drop incoming call handling

This "feature" has been dead (and most likely buggy) for years. The
recommended way is for apps to implement their own incoming call
handling and then call into the JitsiMeetActivity. We did not have those
APIs back then.
This commit is contained in:
Saúl Ibarra Corretgé 2022-02-22 11:36:16 +01:00 committed by Saúl Ibarra Corretgé
parent e61ccc956f
commit c84e6eecdd
17 changed files with 0 additions and 766 deletions

View File

@ -1,72 +0,0 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* 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.
*/
package org.jitsi.meet.sdk.incoming_call;
import androidx.annotation.NonNull;
public class IncomingCallInfo {
/**
* URL for the caller avatar.
*/
private final String callerAvatarURL;
/**
* Caller's name.
*/
private final String callerName;
/**
* Whether this is a regular call or a video call.
*/
private final boolean hasVideo;
public IncomingCallInfo(
@NonNull String callerName,
@NonNull String callerAvatarURL,
boolean hasVideo) {
this.callerName = callerName;
this.callerAvatarURL = callerAvatarURL;
this.hasVideo = hasVideo;
}
/**
* Gets the caller's avatar URL.
*
* @return - The URL as a string.
*/
public String getCallerAvatarURL() {
return callerAvatarURL;
}
/**
* Gets the caller's name.
*
* @return - The caller's name.
*/
public String getCallerName() {
return callerName;
}
/**
* Gets whether the call is a video call or not.
*
* @return - {@code true} if this call has video; {@code false}, otherwise.
*/
public boolean hasVideo() {
return hasVideo;
}
}

View File

@ -1,73 +0,0 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* 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.
*/
package org.jitsi.meet.sdk.incoming_call;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReadableMap;
import org.jitsi.meet.sdk.BaseReactView;
import org.jitsi.meet.sdk.ListenerUtils;
import java.lang.reflect.Method;
import java.util.Map;
public class IncomingCallView
extends BaseReactView<IncomingCallViewListener> {
/**
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* redux action types.
*/
private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.mapListenerMethods(IncomingCallViewListener.class);
public IncomingCallView(@NonNull Context context) {
super(context);
}
/**
* Handler for {@link ExternalAPIModule} events.
*
* @param name The name of the event.
* @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
*/
@Override
protected void onExternalAPIEvent(String name, ReadableMap data) {
onExternalAPIEvent(LISTENER_METHODS, name, data);
}
/**
* Sets the information for the incoming call this {@code IncomingCallView}
* represents.
*
* @param callInfo - {@link IncomingCallInfo} object representing the caller
* information.
*/
public void setIncomingCallInfo(IncomingCallInfo callInfo) {
Bundle props = new Bundle();
props.putString("callerAvatarURL", callInfo.getCallerAvatarURL());
props.putString("callerName", callInfo.getCallerName());
props.putBoolean("hasVideo", callInfo.hasVideo());
createReactRootView("IncomingCallApp", props);
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* 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.
*/
package org.jitsi.meet.sdk.incoming_call;
import java.util.Map;
/**
* Interface for listening to events coming from Jitsi Meet, related to
* {@link IncomingCallView}.
*/
public interface IncomingCallViewListener {
/**
* Called when the user presses the "Answer" button on the
* {@link IncomingCallView}.
*
* @param data - Unused at the moment.
*/
void onIncomingCallAnswered(Map<String, Object> data);
/**
* Called when the user presses the "Decline" button on the
* {@link IncomingCallView}.
*
* @param data - Unused at the moment.
*/
void onIncomingCallDeclined(Map<String, Object> data);
}

View File

@ -7,7 +7,6 @@ import '../mobile/background/middleware';
import '../mobile/call-integration/middleware';
import '../mobile/external-api/middleware';
import '../mobile/full-screen/middleware';
import '../mobile/incoming-call/middleware';
import '../mobile/navigation/middleware';
import '../mobile/permissions/middleware';
import '../mobile/proximity/middleware';

View File

@ -5,7 +5,6 @@ import '../mobile/background/reducer';
import '../mobile/call-integration/reducer';
import '../mobile/external-api/reducer';
import '../mobile/full-screen/reducer';
import '../mobile/incoming-call/reducer';
import '../mobile/watchos/reducer';
import '../shared-video/reducer';

View File

@ -1,27 +0,0 @@
/**
* The type of redux action to answer an incoming call.
*
* {
* type: INCOMING_CALL_ANSWERED
* }
*/
export const INCOMING_CALL_ANSWERED = 'INCOMING_CALL_ANSWERED';
/**
* The type of redux action to decline an incoming call.
*
* {
* type: INCOMING_CALL_DECLINED
* }
*/
export const INCOMING_CALL_DECLINED = 'INCOMING_CALL_DECLINED';
/**
* The type of redux action to receive an incoming call.
*
* {
* type: INCOMING_CALL_RECEIVED,
* caller: Object
* }
*/
export const INCOMING_CALL_RECEIVED = 'INCOMING_CALL_RECEIVED';

View File

@ -1,49 +0,0 @@
// @flow
import {
INCOMING_CALL_ANSWERED,
INCOMING_CALL_DECLINED,
INCOMING_CALL_RECEIVED
} from './actionTypes';
/**
* Answers a received incoming call.
*
* @returns {{
* type: INCOMING_CALL_ANSWERED
* }}
*/
export function incomingCallAnswered() {
return {
type: INCOMING_CALL_ANSWERED
};
}
/**
* Declines a received incoming call.
*
* @returns {{
* type: INCOMING_CALL_DECLINED
* }}
*/
export function incomingCallDeclined() {
return {
type: INCOMING_CALL_DECLINED
};
}
/**
* Shows a received incoming call.
*
* @param {Object} caller - The caller of an incoming call.
* @returns {{
* type: INCOMING_CALL_RECEIVED,
* caller: Object
* }}
*/
export function incomingCallReceived(caller: Object) {
return {
type: INCOMING_CALL_RECEIVED,
caller
};
}

View File

@ -1,39 +0,0 @@
// @flow
import { translate } from '../../../base/i18n';
import { IconHangup } from '../../../base/icons';
import { connect } from '../../../base/redux';
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
import { incomingCallAnswered } from '../actions';
/**
* The type of the React {@code Component} props of {@link AnswerButton}.
*/
type Props = AbstractButtonProps & {
/**
* The redux {@code dispatch} function.
*/
dispatch: Function
};
/**
* An implementation of a button which accepts/answers an incoming call.
*/
class AnswerButton extends AbstractButton<Props, *> {
accessibilityLabel = 'incomingCall.answer';
icon = IconHangup;
label = 'incomingCall.answer';
/**
* Handles clicking / pressing the button, and answers the incoming call.
*
* @protected
* @returns {void}
*/
_handleClick() {
this.props.dispatch(incomingCallAnswered());
}
}
export default translate(connect()(AnswerButton));

View File

@ -1,39 +0,0 @@
// @flow
import { translate } from '../../../base/i18n';
import { IconHangup } from '../../../base/icons';
import { connect } from '../../../base/redux';
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
import { incomingCallDeclined } from '../actions';
/**
* The type of the React {@code Component} props of {@link DeclineButton}.
*/
type Props = AbstractButtonProps & {
/**
* The redux {@code dispatch} function.
*/
dispatch: Function
};
/**
* An implementation of a button which declines/rejects an incoming call.
*/
class DeclineButton extends AbstractButton<Props, *> {
accessibilityLabel = 'incomingCall.decline';
icon = IconHangup;
label = 'incomingCall.decline';
/**
* Handles clicking / pressing the button, and declines the incoming call.
*
* @protected
* @returns {void}
*/
_handleClick() {
this.props.dispatch(incomingCallDeclined());
}
}
export default translate(connect()(DeclineButton));

View File

@ -1,64 +0,0 @@
// @flow
import { BaseApp } from '../../../base/app';
import { incomingCallReceived } from '../actions';
import IncomingCallPage from './IncomingCallPage';
/**
* The type of the React {@code Component} props of {@link IncomingCallApp}.
*/
type Props = {
/**
* URL of the avatar for the caller.
*/
callerAvatarURL: string,
/**
* Name of the caller.
*/
callerName: string,
/**
* Whether this is a video call or not.
*/
hasVideo: boolean
};
/**
* Root application component for incoming call.
*
* @augments BaseApp
*/
export default class IncomingCallApp extends BaseApp<Props> {
_init: Promise<*>;
/**
* Navigates to {@link IncomingCallPage} upon mount.
*
* NOTE: This was implemented here instead of in a middleware for the
* {@link APP_WILL_MOUNT} action because that would run also for
* {@link App}.
*
* @returns {void}
*/
async componentDidMount() {
await super.componentDidMount();
const { dispatch } = this.state.store;
const {
callerAvatarURL: avatarUrl,
callerName: name,
hasVideo
} = this.props;
dispatch(incomingCallReceived({
avatarUrl,
hasVideo,
name
}));
super._navigate({ component: IncomingCallPage });
}
}

View File

@ -1,171 +0,0 @@
// @flow
import React, { Component } from 'react';
import { Image, Text, View } from 'react-native';
import { Avatar } from '../../../base/avatar';
import { translate } from '../../../base/i18n';
import { connect } from '../../../base/redux';
import AnswerButton from './AnswerButton';
import DeclineButton from './DeclineButton';
import styles, { CALLER_AVATAR_SIZE } from './styles';
/**
* The type of React {@code Component} props of {@link IncomingCallPage}.
*/
type Props = {
/**
* Caller's avatar URL.
*/
_callerAvatarURL: string,
/**
* Caller's name.
*/
_callerName: string,
/**
* Whether the call has video or not.
*/
_hasVideo: boolean,
/**
* Helper for translating strings.
*/
t: Function
};
/**
* The React {@code Component} displays an incoming call screen.
*/
class IncomingCallPage extends Component<Props> {
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { t, _callerName, _hasVideo } = this.props;
const callTitle
= _hasVideo
? t('incomingCall.videoCallTitle')
: t('incomingCall.audioCallTitle');
return (
<View style = { styles.pageContainer }>
<View style = { styles.backgroundAvatar }>
<Image
source = {{ uri: this.props._callerAvatarURL }}
style = { styles.backgroundAvatarImage } />
</View>
<Text style = { styles.title }>
{ callTitle }
</Text>
<Text
numberOfLines = { 6 }
style = { styles.callerName } >
{ _callerName }
</Text>
<Text style = { styles.productLabel }>
{ t('incomingCall.productLabel') }
</Text>
{ this._renderCallerAvatar() }
{ this._renderButtons() }
</View>
);
}
/**
* Renders buttons.
*
* @private
* @returns {React$Node}
*/
_renderButtons() {
const { t } = this.props;
return (
<View style = { styles.buttonsContainer }>
<View style = { styles.buttonWrapper } >
<DeclineButton
styles = { styles.declineButtonStyles } />
<Text style = { styles.buttonText }>
{ t('incomingCall.decline') }
</Text>
</View>
<View style = { styles.buttonWrapper }>
<AnswerButton
styles = { styles.answerButtonStyles } />
<Text style = { styles.buttonText }>
{ t('incomingCall.answer') }
</Text>
</View>
</View>
);
}
/**
* Renders caller avatar.
*
* @private
* @returns {React$Node}
*/
_renderCallerAvatar() {
return (
<View style = { styles.avatarContainer }>
<View style = { styles.avatar }>
<Avatar
size = { CALLER_AVATAR_SIZE }
url = { this.props._callerAvatarURL } />
</View>
</View>
);
}
}
/**
* Maps (parts of) the redux state to the component's props.
*
* @param {Object} state - The redux state.
* @param {Object} ownProps - The component's own props.
* @private
* @returns {{
* _callerAvatarURL: string,
* _callerName: string,
* _hasVideo: boolean
* }}
*/
function _mapStateToProps(state) {
const { caller } = state['features/mobile/incoming-call'] || {};
return {
/**
* The caller's avatar url.
*
* @private
* @type {string}
*/
_callerAvatarURL: caller.avatarUrl,
/**
* The caller's name.
*
* @private
* @type {string}
*/
_callerName: caller.name,
/**
* Whether the call has video or not.
*
* @private
* @type {boolean}
*/
_hasVideo: caller.hasVideo
};
}
export default translate(connect(_mapStateToProps)(IncomingCallPage));

View File

@ -1,2 +0,0 @@
export { default as IncomingCallApp } from './IncomingCallApp';
export { default as IncomingCallPage } from './IncomingCallPage';

View File

@ -1,133 +0,0 @@
import { ColorPalette, createStyleSheet } from '../../../base/styles';
const BUTTON_SIZE = 56;
const CALLER_AVATAR_BORDER_WIDTH = 3;
export const CALLER_AVATAR_SIZE = 128;
const CALLER_AVATAR_CIRCLE_SIZE
= CALLER_AVATAR_SIZE + (2 * CALLER_AVATAR_BORDER_WIDTH);
const LINE_SPACING = 8;
const PAGE_PADDING = 48;
const _icon = {
alignSelf: 'center',
color: ColorPalette.white,
fontSize: 32
};
const _responseButton = {
alignSelf: 'center',
borderRadius: BUTTON_SIZE / 2,
borderWidth: 0,
flex: 0,
flexDirection: 'row',
height: BUTTON_SIZE,
justifyContent: 'center',
width: BUTTON_SIZE
};
const _text = {
color: ColorPalette.white,
fontSize: 16
};
export default createStyleSheet({
answerButtonStyles: {
iconStyle: {
..._icon,
transform: [
{ rotateZ: '130deg' }
]
},
style: {
..._responseButton,
backgroundColor: ColorPalette.green
},
underlayColor: ColorPalette.buttonUnderlay
},
avatar: {
marginLeft: CALLER_AVATAR_BORDER_WIDTH,
marginTop: CALLER_AVATAR_BORDER_WIDTH,
position: 'absolute'
},
avatarBorder: {
borderRadius: CALLER_AVATAR_CIRCLE_SIZE / 2,
height: CALLER_AVATAR_CIRCLE_SIZE,
position: 'absolute',
width: CALLER_AVATAR_CIRCLE_SIZE
},
avatarContainer: {
height: CALLER_AVATAR_CIRCLE_SIZE,
marginTop: LINE_SPACING * 4,
width: CALLER_AVATAR_CIRCLE_SIZE
},
backgroundAvatar: {
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
top: 0
},
backgroundAvatarImage: {
flex: 1
},
buttonsContainer: {
alignItems: 'flex-end',
flex: 1,
flexDirection: 'row'
},
buttonText: {
..._text,
alignSelf: 'center',
marginTop: 1.5 * LINE_SPACING
},
buttonWrapper: {
flex: 1
},
callerName: {
..._text,
fontSize: 36,
marginBottom: LINE_SPACING,
marginLeft: PAGE_PADDING,
marginRight: PAGE_PADDING,
marginTop: LINE_SPACING,
textAlign: 'center'
},
declineButtonStyles: {
iconStyle: _icon,
style: {
..._responseButton,
backgroundColor: ColorPalette.red
},
underlayColor: ColorPalette.buttonUnderlay
},
pageContainer: {
alignItems: 'center',
flex: 1,
paddingBottom: PAGE_PADDING,
paddingTop: PAGE_PADDING
},
productLabel: {
..._text
},
title: {
..._text
}
});

View File

@ -1 +0,0 @@
export * from './components';

View File

@ -1,26 +0,0 @@
// @flow
import { MiddlewareRegistry } from '../../base/redux';
import { sendEvent } from '../external-api';
import { INCOMING_CALL_ANSWERED, INCOMING_CALL_DECLINED } from './actionTypes';
/**
* Middleware that captures redux actions and uses the ExternalAPI module to
* turn them into native events so the app knows about them.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
const result = next(action);
switch (action.type) {
case INCOMING_CALL_ANSWERED:
case INCOMING_CALL_DECLINED:
sendEvent(store, action.type, /* data */ {});
break;
}
return result;
});

View File

@ -1,23 +0,0 @@
// @flow
import { ReducerRegistry, set } from '../../base/redux';
import {
INCOMING_CALL_ANSWERED,
INCOMING_CALL_DECLINED,
INCOMING_CALL_RECEIVED
} from './actionTypes';
ReducerRegistry.register(
'features/mobile/incoming-call', (state = {}, action) => {
switch (action.type) {
case INCOMING_CALL_ANSWERED:
case INCOMING_CALL_DECLINED:
return set(state, 'caller', undefined);
case INCOMING_CALL_RECEIVED:
return set(state, 'caller', action.caller);
}
return state;
});

View File

@ -15,7 +15,6 @@ import { App } from './features/app/components';
import { _initLogging } from './features/base/logging/functions';
import JitsiThemePaperProvider
from './features/base/ui/components/JitsiThemeProvider';
import { IncomingCallApp } from './features/mobile/incoming-call';
declare var __DEV__;
@ -79,6 +78,3 @@ if (!__DEV__) {
// Register the main/root Component of JitsiMeetView.
AppRegistry.registerComponent('App', () => Root);
// Register the main/root Component of IncomingCallView.
AppRegistry.registerComponent('IncomingCallApp', () => IncomingCallApp);