Compare commits

..

1 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
cd48025d09 fix(doc) update README 2022-10-05 22:17:59 +02:00
1276 changed files with 20180 additions and 14150 deletions

View File

@@ -19,7 +19,7 @@ jobs:
run: npm run lang-sort
- name: Check if the git repository is clean
run: $(exit $(git status --porcelain --untracked-files=no | head -255 | wc -l)) || (echo "Dirty git tree"; git diff; exit 1)
- run: npm run lint:ci
- run: npm run lint
- run: for file in lang/*.json; do npx --yes jsonlint -q $file || exit 1; done
linux-build:
name: Build Frontend (Linux)

1
.gitignore vendored
View File

@@ -92,4 +92,3 @@ twa/*.apk
twa/*.aab
twa/assetlinks.json
tsconfig.json

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#17A0DB</color>
<color name="navigationBarColor">#161618</color>
<color name="colorPrimaryDark">#1081B2</color>
</resources>

View File

@@ -2,7 +2,7 @@
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:navigationBarColor">@color/navigationBarColor</item>
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
<item name="android:windowDisablePreview">true</item>
</style>
</resources>

View File

@@ -22,8 +22,6 @@ import android.os.Build;
import android.telecom.CallAudioState;
import androidx.annotation.RequiresApi;
import com.facebook.react.bridge.ReactContext;
import java.util.HashSet;
import java.util.Set;
@@ -51,8 +49,6 @@ class AudioDeviceHandlerConnectionService implements
*/
private AudioModeModule module;
private RNConnectionService rcs;
/**
* Converts any of the "DEVICE_" constants into the corresponding
* {@link android.telecom.CallAudioState} "ROUTE_" number.
@@ -145,8 +141,8 @@ class AudioDeviceHandlerConnectionService implements
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
module = audioModeModule;
rcs = module.getContext().getNativeModule(RNConnectionService.class);
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
if (rcs != null) {
rcs.setCallAudioStateListener(this);
} else {
@@ -156,9 +152,9 @@ class AudioDeviceHandlerConnectionService implements
@Override
public void stop() {
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
if (rcs != null) {
rcs.setCallAudioStateListener(null);
rcs = null;
} else {
JitsiMeetLogger.w(TAG + " Couldn't set call audio state listener, module is null");
}

View File

@@ -26,13 +26,10 @@ import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
@@ -199,7 +196,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
deviceInfo.putBoolean("selected", device.equals(selectedDevice));
data.pushMap(deviceInfo);
}
getContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(DEVICE_CHANGE_EVENT, data);
ReactInstanceManagerHolder.emitEvent(DEVICE_CHANGE_EVENT, data);
JitsiMeetLogger.i(TAG + " Updating audio device list");
}
});
@@ -215,10 +212,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
return NAME;
}
public ReactContext getContext(){
return this.getReactApplicationContext();
}
/**
* Initializes the audio device handler module. This function is called *after* all Catalyst
* modules have been created, and that's why we use it, because {@link AudioDeviceHandlerConnectionService}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.jitsi.meet.sdk;
import org.webrtc.VideoCodecInfo;
import java.util.Map;
import java.util.HashMap;
/** Container for static helper functions related to dealing with H264 codecs. */
class H264Utils {
public static final String H264_FMTP_PROFILE_LEVEL_ID = "profile-level-id";
public static final String H264_FMTP_LEVEL_ASYMMETRY_ALLOWED = "level-asymmetry-allowed";
public static final String H264_FMTP_PACKETIZATION_MODE = "packetization-mode";
public static final String H264_PROFILE_CONSTRAINED_BASELINE = "42e0";
public static final String H264_PROFILE_CONSTRAINED_HIGH = "640c";
public static final String H264_LEVEL_3_1 = "1f"; // 31 in hex.
public static final String H264_CONSTRAINED_HIGH_3_1 =
H264_PROFILE_CONSTRAINED_HIGH + H264_LEVEL_3_1;
public static final String H264_CONSTRAINED_BASELINE_3_1 =
H264_PROFILE_CONSTRAINED_BASELINE + H264_LEVEL_3_1;
public static Map<String, String> getDefaultH264Params(boolean isHighProfile) {
final Map<String, String> params = new HashMap<>();
params.put(VideoCodecInfo.H264_FMTP_LEVEL_ASYMMETRY_ALLOWED, "1");
params.put(VideoCodecInfo.H264_FMTP_PACKETIZATION_MODE, "1");
params.put(VideoCodecInfo.H264_FMTP_PROFILE_LEVEL_ID,
isHighProfile ? VideoCodecInfo.H264_CONSTRAINED_HIGH_3_1
: VideoCodecInfo.H264_CONSTRAINED_BASELINE_3_1);
return params;
}
public static VideoCodecInfo DEFAULT_H264_BASELINE_PROFILE_CODEC =
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ false));
public static VideoCodecInfo DEFAULT_H264_HIGH_PROFILE_CODEC =
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ true));
}

View File

@@ -15,7 +15,6 @@
*/
package org.jitsi.meet.sdk;
import android.app.Application;
import android.content.Context;
import android.util.Log;
@@ -23,7 +22,6 @@ import androidx.annotation.NonNull;
import androidx.startup.Initializer;
import com.facebook.soloader.SoLoader;
import org.wonday.orientation.OrientationActivityLifecycle;
import java.util.Collections;
import java.util.List;
@@ -39,10 +37,6 @@ public class JitsiInitializer implements Initializer<Boolean> {
// Register our uncaught exception handler.
JitsiMeetUncaughtExceptionHandler.register();
// Register activity lifecycle handler for the orientation locker module.
((Application) context).registerActivityLifecycleCallbacks(OrientationActivityLifecycle.getInstance());
return true;
}

View File

@@ -31,6 +31,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.facebook.react.modules.core.PermissionListener;
import org.wonday.orientation.OrientationActivityLifecycle;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
@@ -103,6 +104,7 @@ public class JitsiMeetActivity extends AppCompatActivity
this.jitsiView = findViewById(R.id.jitsiView);
registerForBroadcastMessages();
registerActivityLifecycleCallbacks(OrientationActivityLifecycle.getInstance());
if (!extraInitialize()) {
initialize();

View File

@@ -31,12 +31,12 @@ import com.facebook.react.common.LifecycleState;
import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.oney.WebRTCModule.EglUtils;
import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
import org.devio.rn.splashscreen.SplashScreenModule;
import org.webrtc.EglBase;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule;
@@ -89,10 +89,8 @@ class ReactInstanceManagerHolder {
.createAudioDeviceModule();
options.setAudioDeviceModule(adm);
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
options.setVideoDecoderFactory(new WebRTCVideoDecoderFactory(eglContext));
options.setVideoEncoderFactory(new WebRTCVideoEncoderFactory(eglContext));
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
nativeModules.add(new WebRTCModule(reactContext, options));

View File

@@ -1,19 +0,0 @@
package org.jitsi.meet.sdk;
/** Enumeration of supported video codec types. */
public enum VideoCodecMimeType {
VP8("video/x-vnd.on2.vp8"),
VP9("video/x-vnd.on2.vp9"),
H264("video/avc"),
AV1("video/av01");
private final String mimeType;
private VideoCodecMimeType(String mimeType) {
this.mimeType = mimeType;
}
String mimeType() {
return mimeType;
}
}

View File

@@ -1,52 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.HardwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoDecoder;
import org.webrtc.VideoDecoderFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* This is a custom video decoder factory for WebRTC which behaves similarly
* to the default one in iOS. It supports the following codecs:
*
* - In hardware: H.264 (baseline)
* - In software: VP8, VP9, AV1
*/
public class WebRTCVideoDecoderFactory implements VideoDecoderFactory {
private final VideoDecoderFactory hardwareVideoDecoderFactory;
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory();
public WebRTCVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext);
}
@Nullable
@Override
public VideoDecoder createDecoder(VideoCodecInfo codecInfo) {
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
return this.hardwareVideoDecoderFactory.createDecoder(codecInfo);
}
return this.softwareVideoDecoderFactory.createDecoder(codecInfo);
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
List<VideoCodecInfo> codecs = new ArrayList<>();
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
}
}

View File

@@ -1,53 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.HardwareVideoEncoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoEncoder;
import org.webrtc.VideoEncoderFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* This is a custom video encoder factory for WebRTC which behaves similarly
* to the default one in iOS. It supports the following codecs:
*
* - In hardware: H.264 (baseline)
* - In software: VP8, VP9, AV1
*/
public class WebRTCVideoEncoderFactory implements VideoEncoderFactory {
private final VideoEncoderFactory hardwareVideoEncoderFactory;
private final VideoEncoderFactory softwareVideoEncoderFactory = new SoftwareVideoEncoderFactory();
public WebRTCVideoEncoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoEncoderFactory =
new HardwareVideoEncoderFactory(eglContext, false, false);
}
@Nullable
@Override
public VideoEncoder createEncoder(VideoCodecInfo codecInfo) {
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
return this.hardwareVideoEncoderFactory.createEncoder(codecInfo);
}
return this.softwareVideoEncoderFactory.createEncoder(codecInfo);
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
List<VideoCodecInfo> codecs = new ArrayList<>();
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
}
}

View File

@@ -27,6 +27,7 @@ import {
} from './react/features/app/actions';
import { showModeratedNotification } from './react/features/av-moderation/actions';
import { shouldShowModeratedNotification } from './react/features/av-moderation/functions';
import { setAudioOnly } from './react/features/base/audio-only';
import {
AVATAR_URL_COMMAND,
CONFERENCE_LEAVE_REASONS,
@@ -61,14 +62,12 @@ import {
import {
checkAndNotifyForNewDevice,
getAvailableDevices,
getDefaultDeviceId,
notifyCameraError,
notifyMicError,
setAudioOutputDeviceId,
updateDeviceList
} from './react/features/base/devices/actions.web';
import {
getDefaultDeviceId,
setAudioOutputDeviceId
} from './react/features/base/devices/functions.web';
} from './react/features/base/devices';
import {
JitsiConferenceErrors,
JitsiConferenceEvents,
@@ -76,6 +75,7 @@ import {
JitsiConnectionEvents,
JitsiE2ePingEvents,
JitsiMediaDevicesEvents,
JitsiParticipantConnectionStatus,
JitsiTrackErrors,
JitsiTrackEvents,
browser
@@ -99,7 +99,9 @@ import {
getNormalizedDisplayName,
getVirtualScreenshareParticipantByOwnerId,
localParticipantAudioLevelChanged,
localParticipantConnectionStatusChanged,
localParticipantRoleChanged,
participantConnectionStatusChanged,
participantKicked,
participantMutedUs,
participantPresenceChanged,
@@ -108,19 +110,23 @@ import {
screenshareParticipantDisplayNameChanged,
updateRemoteParticipantFeatures
} from './react/features/base/participants';
import { updateSettings } from './react/features/base/settings';
import {
getUserSelectedCameraDeviceId,
updateSettings
} from './react/features/base/settings';
import {
addLocalTrack,
createLocalPresenterTrack,
createLocalTracksF,
destroyLocalTracks,
getLocalJitsiAudioTrack,
getLocalJitsiVideoTrack,
getLocalTracks,
getLocalVideoTrack,
isLocalCameraTrackMuted,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
replaceLocalTrack,
toggleScreensharing as toggleScreensharingA,
trackAdded,
trackRemoved
} from './react/features/base/tracks';
@@ -140,12 +146,18 @@ import {
} from './react/features/notifications';
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
import { suspendDetected } from './react/features/power-monitor';
import { initPrejoin, makePrecallTest, setJoiningInProgress } from './react/features/prejoin/actions';
import { isPrejoinPageVisible } from './react/features/prejoin/functions';
import {
initPrejoin,
isPrejoinPageVisible,
makePrecallTest,
setJoiningInProgress
} from './react/features/prejoin';
import { disableReceiver, stopReceiver } from './react/features/remote-control';
import { isScreenAudioShared, setScreenAudioShareState } from './react/features/screen-share/';
import { toggleScreenshotCaptureSummary } from './react/features/screenshot-capture';
import { isScreenshotCaptureEnabled } from './react/features/screenshot-capture/functions';
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
import { createPresenterEffect } from './react/features/stream-effects/presenter';
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
import { endpointMessageReceived } from './react/features/subtitles';
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
@@ -177,6 +189,15 @@ let _connectionPromise;
*/
let _onConnectionPromiseCreated;
/**
* This promise is used for chaining mutePresenterVideo calls in order to avoid calling GUM multiple times if it takes
* a while to finish.
*
* @type {Promise<void>}
* @private
*/
let _prevMutePresenterVideo = Promise.resolve();
/*
* Logic to open a desktop picker put on the window global for
* lib-jitsi-meet to detect and invoke
@@ -460,6 +481,12 @@ export default {
isSharingScreen: false,
/**
* The local presenter video track (if any).
* @type {JitsiLocalTrack|null}
*/
localPresenterVideo: null,
/**
* Returns an object containing a promise which resolves with the created tracks &
* the errors resulting from that process.
@@ -504,10 +531,22 @@ export default {
firePermissionPromptIsShownEvent: true
};
// Spot uses the _desktopSharingSourceDevice config option to use an external video input device label as
// screenshare and calls getUserMedia instead of getDisplayMedia for capturing the media.
if (options.startScreenSharing && config._desktopSharingSourceDevice) {
tryCreateLocalTracks = this._createDesktopTrack()
// FIXME is there any simpler way to rewrite this spaghetti below ?
if (options.startScreenSharing) {
// This option has been deprecated since it is no longer supported as per the w3c spec.
// https://w3c.github.io/mediacapture-screen-share/#dom-mediadevices-getdisplaymedia. If the user has not
// interacted with the webpage before the getDisplayMedia call, the promise will be rejected by the
// browser. This has already been implemented in Firefox and Safari and will be implemented in Chrome soon.
// https://bugs.chromium.org/p/chromium/issues/detail?id=1198918
// Please note that Spot uses the same config option to use an external video input device label as
// screenshare and calls getUserMedia instead of getDisplayMedia for capturing the media. Therefore it
// needs to be supported here if _desktopSharingSourceDevice is provided.
const errMessage = new Error('startScreenSharing config option is no longer supported for web browsers');
const desktopPromise = config._desktopSharingSourceDevice
? this._createDesktopTrack()
: Promise.reject(errMessage);
tryCreateLocalTracks = desktopPromise
.then(([ desktopStream ]) => {
if (!requestedAudio) {
return [ desktopStream ];
@@ -872,7 +911,8 @@ export default {
isLocalVideoMuted() {
// If the tracks are not ready, read from base/media state
return this._localTracksInitialized
? isLocalTrackMuted(APP.store.getState()['features/base/tracks'], MEDIA_TYPE.VIDEO)
? isLocalCameraTrackMuted(
APP.store.getState()['features/base/tracks'])
: isVideoMutedByUser(APP.store);
},
@@ -992,6 +1032,36 @@ export default {
this.muteAudio(!this.isLocalAudioMuted(), showUI);
},
/**
* Simulates toolbar button click for presenter video mute. Used by
* shortcuts and API.
* @param mute true for mute and false for unmute.
* @param {boolean} [showUI] when set to false will not display any error
* dialogs in case of media permissions error.
*/
async mutePresenter(mute, showUI = true) {
const maybeShowErrorDialog = error => {
showUI && APP.store.dispatch(notifyCameraError(error));
};
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
if (mute) {
try {
await localVideo.setEffect(undefined);
} catch (err) {
logger.error('Failed to remove the presenter effect', err);
maybeShowErrorDialog(err);
}
} else {
try {
await localVideo.setEffect(await this._createPresenterStreamEffect());
} catch (err) {
logger.error('Failed to apply the presenter effect', err);
maybeShowErrorDialog(err);
}
}
},
/**
* Simulates toolbar button click for video mute. Used by shortcuts and API.
* @param mute true for mute and false for unmute.
@@ -1000,6 +1070,8 @@ export default {
*/
muteVideo(mute, showUI = true) {
if (this.videoSwitchInProgress) {
// Turning the camera on while the screen sharing mode is being turned off is causing issues around
// the presenter mode handling. It should be okay for the user to click the button again once that's done.
console.warn('muteVideo - unable to perform operations while video switch is in progress');
return;
@@ -1012,6 +1084,13 @@ export default {
return;
}
if (this.isSharingScreen) {
// Chain _mutePresenterVideo calls
_prevMutePresenterVideo = _prevMutePresenterVideo.then(() => this._mutePresenterVideo(mute));
return;
}
// If not ready to modify track's state yet adjust the base/media
if (!this._localTracksInitialized) {
// This will only modify base/media.video.muted which is then synced
@@ -1323,6 +1402,8 @@ export default {
// Restore initial state.
this._localTracksInitialized = false;
this.isSharingScreen = false;
this.localPresenterVideo = null;
this.roomName = roomName;
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(options);
@@ -1454,6 +1535,33 @@ export default {
});
},
/**
* Sets `this.isSharingScreen` depending on provided video stream.
* In case new screen sharing status is not equal previous one
* it updates desktop sharing buttons in UI
* and notifies external application.
*
* @param {JitsiLocalTrack} [newStream] new stream to use or null
* @private
* @returns {void}
*/
_setSharingScreen(newStream) {
const wasSharingScreen = this.isSharingScreen;
this.isSharingScreen = newStream && newStream.videoType === 'desktop';
if (wasSharingScreen !== this.isSharingScreen) {
const details = {};
if (this.isSharingScreen) {
details.sourceType = newStream.sourceType;
}
APP.API.notifyScreenSharingStatusChanged(
this.isSharingScreen, details);
}
},
/**
* Start using provided audio stream.
* Stops previous audio stream.
@@ -1533,6 +1641,35 @@ export default {
const tracks = APP.store.getState()['features/base/tracks'];
const duration = getLocalVideoTrack(tracks)?.jitsiTrack.getDuration() ?? 0;
// It can happen that presenter GUM is in progress while screensharing is being turned off. Here it needs to
// wait for that GUM to be resolved in order to prevent leaking the presenter track(this.localPresenterVideo
// will be null when SS is being turned off, but it will initialize once GUM resolves).
let promise = _prevMutePresenterVideo = _prevMutePresenterVideo.then(() => {
// mute the presenter track if it exists.
if (this.localPresenterVideo) {
return (
this.localPresenterVideo.dispose().then(() => {
APP.store.dispatch(trackRemoved(this.localPresenterVideo));
this.localPresenterVideo = null;
})
.then(() => {
// This is needed only for setting the correct muted state in features/base/media.
// NOTE: It is important to be executed after we have disposed and removed the presenter track.
// This way all the side effects won't be executed and we won't start additional O/A cycle for
// replacing the track with video with the one without video. This O/A cycle is not needed since
// we are trying to destroy all tracks. Also due to the current async nature of muting the
// presenter, the final removal of the screen sharing track (see the code at the end of the
// function) can be executed between the removal of the stream with video and adding the
// original screen sharing stream to the peer connection. This will lead to a failure to remove
// the screen sharing track, compromising the screen sharing state in jitsi-meet and the user
// won't be able to turn off the screen sharing.
APP.store.dispatch(setVideoMuted(true, MEDIA_TYPE.PRESENTER));
})
);
}
});
// If system audio was also shared stop the AudioMixerEffect and dispose of the desktop audio track.
if (this._mixerEffect) {
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
@@ -1551,10 +1688,9 @@ export default {
}
APP.store.dispatch(setScreenAudioShareState(false));
let promise;
if (didHaveVideo && !ignoreDidHaveVideo) {
promise = createLocalTracksF({ devices: [ 'video' ] })
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
.then(([ stream ]) => {
logger.debug(`_turnScreenSharingOff using ${stream} for useVideoStream`);
@@ -1570,7 +1706,11 @@ export default {
);
});
} else {
promise = this.useVideoStream(null);
promise = promise.then(() => {
logger.debug('_turnScreenSharingOff using null for useVideoStream');
return this.useVideoStream(null);
});
}
return promise.then(
@@ -1588,6 +1728,54 @@ export default {
});
},
/**
* Toggles between screen sharing and camera video if the toggle parameter
* is not specified and starts the procedure for obtaining new screen
* sharing/video track otherwise.
*
* @param {boolean} [toggle] - If true - new screen sharing track will be
* obtained. If false - new video track will be obtain. If not specified -
* toggles between screen sharing and camera video.
* @param {Object} [options] - Screen sharing options that will be passed to
* createLocalTracks.
* @param {boolean} [options.audioOnly] - Whether or not audioOnly is enabled.
* @param {Array<string>} [options.desktopSharingSources] - Array with the
* sources that have to be displayed in the desktop picker window ('screen',
* 'window', etc.).
* @param {Object} [options.desktopStream] - An existing desktop stream to
* use instead of creating a new desktop stream.
* @param {boolean} ignoreDidHaveVideo - if true ignore if video was on when sharing started.
* @return {Promise.<T>}
*/
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}, ignoreDidHaveVideo) {
logger.debug(`toggleScreenSharing: ${toggle}`);
if (this.videoSwitchInProgress) {
return Promise.reject(`toggleScreenSharing: ${toggle} aborted - video switch in progress.`);
}
if (!JitsiMeetJS.isDesktopSharingEnabled()) {
return Promise.reject('Cannot toggle screen sharing: not supported.');
}
if (toggle) {
try {
await this._switchToScreenSharing(options);
if (this.isAudioOnly()) {
APP.store.dispatch(setAudioOnly(false));
}
return;
} catch (err) {
logger.error('Failed to switch to screensharing', err);
return;
}
}
return this._untoggleScreenSharing
? this._untoggleScreenSharing(ignoreDidHaveVideo)
: Promise.resolve();
},
/**
* Creates desktop (screensharing) {@link JitsiLocalTrack}
*
@@ -1660,6 +1848,228 @@ export default {
});
},
/**
* Creates a new instance of presenter effect. A new video track is created
* using the new set of constraints that are calculated based on
* the height of the desktop that is being currently shared.
*
* @param {number} height - The height of the desktop stream that is being
* currently shared.
* @param {string} cameraDeviceId - The device id of the camera to be used.
* @return {Promise<JitsiStreamPresenterEffect>} - A promise resolved with
* {@link JitsiStreamPresenterEffect} if it succeeds.
*/
async _createPresenterStreamEffect(height = null, cameraDeviceId = null) {
if (!this.localPresenterVideo) {
const camera = cameraDeviceId ?? getUserSelectedCameraDeviceId(APP.store.getState());
try {
this.localPresenterVideo = await createLocalPresenterTrack({ cameraDeviceId: camera }, height);
} catch (err) {
logger.error('Failed to create a camera track for presenter', err);
return;
}
APP.store.dispatch(trackAdded(this.localPresenterVideo));
}
try {
const effect = await createPresenterEffect(this.localPresenterVideo.stream);
return effect;
} catch (err) {
logger.error('Failed to create the presenter effect', err);
}
},
/**
* Tries to turn the presenter video track on or off. If a presenter track
* doesn't exist, a new video track is created.
*
* @param mute - true for mute and false for unmute.
*
* @private
*/
async _mutePresenterVideo(mute) {
const maybeShowErrorDialog = error => {
APP.store.dispatch(notifyCameraError(error));
};
// Check for NO-OP
if (mute && (!this.localPresenterVideo || this.localPresenterVideo.isMuted())) {
return;
} else if (!mute && this.localPresenterVideo && !this.localPresenterVideo.isMuted()) {
return;
}
// Create a new presenter track and apply the presenter effect.
if (!this.localPresenterVideo && !mute) {
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
const { height, width } = localVideo.track.getSettings() ?? localVideo.track.getConstraints();
const isPortrait = height >= width;
const DESKTOP_STREAM_CAP = 720;
const highResolutionTrack
= (isPortrait && width > DESKTOP_STREAM_CAP) || (!isPortrait && height > DESKTOP_STREAM_CAP);
// Resizing the desktop track for presenter is causing blurriness of the desktop share on chrome.
// Disable resizing by default, enable it only when config.js setting is enabled.
const resizeDesktopStream = highResolutionTrack && config.videoQuality?.resizeDesktopForPresenter;
if (resizeDesktopStream) {
let desktopResizeConstraints = {};
if (height && width) {
const advancedConstraints = [ { aspectRatio: (width / height).toPrecision(4) } ];
const constraint = isPortrait ? { width: DESKTOP_STREAM_CAP } : { height: DESKTOP_STREAM_CAP };
advancedConstraints.push(constraint);
desktopResizeConstraints.advanced = advancedConstraints;
} else {
desktopResizeConstraints = {
width: 1280,
height: 720
};
}
// Apply the constraints on the desktop track.
try {
await localVideo.track.applyConstraints(desktopResizeConstraints);
} catch (err) {
logger.error('Failed to apply constraints on the desktop stream for presenter mode', err);
return;
}
}
const trackHeight = resizeDesktopStream
? localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP
: height;
let effect;
try {
effect = await this._createPresenterStreamEffect(trackHeight);
} catch (err) {
logger.error('Failed to unmute Presenter Video', err);
maybeShowErrorDialog(err);
return;
}
// Replace the desktop track on the peerconnection.
try {
await localVideo.setEffect(effect);
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
this.setVideoMuteStatus();
} catch (err) {
logger.error('Failed to apply the Presenter effect', err);
}
} else {
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
}
},
/**
* Tries to switch to the screensharing mode by disposing camera stream and
* replacing it with a desktop one.
*
* @param {Object} [options] - Screen sharing options that will be passed to
* createLocalTracks.
*
* @return {Promise} - A Promise resolved if the operation succeeds or
* rejected with some unknown type of error in case it fails. Promise will
* be rejected immediately if {@link videoSwitchInProgress} is true.
*
* @private
*/
_switchToScreenSharing(options = {}) {
if (this.videoSwitchInProgress) {
return Promise.reject('Switch in progress.');
}
this.videoSwitchInProgress = true;
return this._createDesktopTrack(options)
.then(async streams => {
let desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
const { audioOnly = false } = options;
// If we're in audio only mode dispose of the video track otherwise the screensharing state will be
// inconsistent.
if (audioOnly) {
desktopVideoStream.dispose();
desktopVideoStream = undefined;
if (!this._desktopAudioStream) {
return Promise.reject(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK);
}
}
if (desktopVideoStream) {
logger.debug(`_switchToScreenSharing is using ${desktopVideoStream} for useVideoStream`);
await this.useVideoStream(desktopVideoStream);
}
if (this._desktopAudioStream) {
// Noise suppression doesn't work with desktop audio because we can't chain
// track effects yet, disable it first.
// We need to to wait for the effect to clear first or it might interfere with the audio mixer.
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
// If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
// api.
if (localAudio) {
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
logger.debug(`_switchToScreenSharing is mixing ${this._desktopAudioStream} and ${localAudio}`
+ ' as a single audio stream');
await localAudio.setEffect(this._mixerEffect);
} else {
// If no local stream is present ( i.e. no input audio devices) we use the screen share audio
// stream as we would use a regular stream.
logger.debug(`_switchToScreenSharing is using ${this._desktopAudioStream} for replacing it as`
+ ' the only audio track on the conference');
await room.replaceTrack(null, this._desktopAudioStream);
}
APP.store.dispatch(setScreenAudioShareState(true));
}
})
.then(() => {
this.videoSwitchInProgress = false;
if (isScreenshotCaptureEnabled(APP.store.getState(), false, true)) {
APP.store.dispatch(toggleScreenshotCaptureSummary(true));
}
sendAnalytics(createScreenSharingEvent('started'));
logger.log('Screen sharing started');
})
.catch(error => {
this.videoSwitchInProgress = false;
// Pawel: With this call I'm trying to preserve the original
// behaviour although it is not clear why would we "untoggle"
// on failure. I suppose it was to restore video in case there
// was some problem during "this.useVideoStream(desktopStream)".
// It's important to note that the handler will not be available
// if we fail early on trying to get desktop media (which makes
// sense, because the camera video is still being used, so
// nothing to "untoggle").
if (this._untoggleScreenSharing) {
this._untoggleScreenSharing();
}
// FIXME the code inside of _handleScreenSharingError is
// asynchronous, but does not return a Promise and is not part
// of the current Promise chain.
this._handleScreenSharingError(error);
return Promise.reject(error);
});
},
/**
* Handles {@link JitsiTrackError} returned by the lib-jitsi-meet when
* trying to create screensharing track. It will either do nothing if
@@ -1864,6 +2274,11 @@ export default {
(jitsiConference, p2p) =>
APP.store.dispatch(p2pStatusChanged(p2p)));
room.on(
JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
(id, connectionStatus) => APP.store.dispatch(
participantConnectionStatusChanged(id, connectionStatus)));
room.on(
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
(dominant, previous, silence) => {
@@ -1874,6 +2289,16 @@ export default {
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
conferenceTimestamp => APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp)));
room.on(JitsiConferenceEvents.CONNECTION_INTERRUPTED, () => {
APP.store.dispatch(localParticipantConnectionStatusChanged(
JitsiParticipantConnectionStatus.INTERRUPTED));
});
room.on(JitsiConferenceEvents.CONNECTION_RESTORED, () => {
APP.store.dispatch(localParticipantConnectionStatusChanged(
JitsiParticipantConnectionStatus.ACTIVE));
});
room.on(
JitsiConferenceEvents.DISPLAY_NAME_CHANGED,
(id, displayName) => {
@@ -2093,38 +2518,77 @@ export default {
APP.UI.addListener(
UIEvents.VIDEO_DEVICE_CHANGED,
cameraDeviceId => {
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
const videoWasMuted = this.isLocalVideoMuted();
sendAnalytics(createDeviceChangedEvent('video', 'input'));
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
// If both screenshare and video are in progress, restart the
// presenter mode with the new camera device.
if (this.isSharingScreen && !videoWasMuted) {
const { height } = localVideo.track.getSettings();
return stream;
})
.then(stream => {
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
// dispose the existing presenter track and create a new
// camera track.
// FIXME JitsiLocalTrack.dispose is async and should be waited for
this.localPresenterVideo && this.localPresenterVideo.dispose();
this.localPresenterVideo = null;
return this._createPresenterStreamEffect(height, cameraDeviceId)
.then(effect => localVideo.setEffect(effect))
.then(() => {
this.setVideoMuteStatus();
logger.log('Switched local video device while screen sharing and the video is unmuted');
this._updateVideoDeviceId();
})
.catch(err => APP.store.dispatch(notifyCameraError(err)));
// If screenshare is in progress but video is muted, update the default device
// id for video, dispose the existing presenter track and create a new effect
// that can be applied on un-mute.
} else if (this.isSharingScreen && videoWasMuted) {
logger.log('Switched local video device: while screen sharing and the video is muted');
const { height } = localVideo.track.getSettings();
return this.useVideoStream(stream);
})
.then(() => {
logger.info(`Switched local video device to ${cameraDeviceId}.`);
this._updateVideoDeviceId();
})
.catch(error => {
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
return APP.store.dispatch(notifyCameraError(error));
});
// FIXME JitsiLocalTrack.dispose is async and should be waited for
this.localPresenterVideo && this.localPresenterVideo.dispose();
this.localPresenterVideo = null;
this._createPresenterStreamEffect(height, cameraDeviceId);
// if there is only video, switch to the new camera stream.
} else {
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId,
micDeviceId: null
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(stream => {
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
return this.useVideoStream(stream);
})
.then(() => {
logger.info(`Switched local video device to ${cameraDeviceId}.`);
this._updateVideoDeviceId();
})
.catch(error => {
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
return APP.store.dispatch(notifyCameraError(error));
});
}
}
);
@@ -2148,6 +2612,7 @@ export default {
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
createLocalTracksF({
devices: [ 'audio' ],
cameraDeviceId: null,
micDeviceId: selectedDeviceId
})
.then(([ stream ]) => {
@@ -2192,6 +2657,12 @@ export default {
APP.UI.updateLargeVideo(displayedUserId, true);
}
});
APP.UI.addListener(
UIEvents.TOGGLE_SCREENSHARING, ({ enabled, audioOnly, ignoreDidHaveVideo }) => {
this.toggleScreenSharing(enabled, { audioOnly }, ignoreDidHaveVideo);
}
);
},
/**
@@ -2290,6 +2761,13 @@ export default {
cameraDeviceId: localVideo.getDeviceId()
}));
}
// If screenshare is in progress, get the device id from the presenter track.
if (this.localPresenterVideo) {
APP.store.dispatch(updateSettings({
cameraDeviceId: this.localPresenterVideo.getDeviceId()
}));
}
},
/**
@@ -2738,7 +3216,7 @@ export default {
return;
}
APP.store.dispatch(toggleScreensharingA(undefined, false, { desktopStream }));
this.toggleScreenSharing(undefined, { desktopStream });
}
});
}

View File

@@ -65,6 +65,10 @@ var config = {
// issues related to insertable streams.
// disableE2EE: false,
// Enables/disables thumbnail reordering in the filmstrip. It is enabled by default unless explicitly
// disabled by the below option.
// enableThumbnailReordering: true,
// Enables XMPP WebSocket (as opposed to BOSH) for the given amount of users.
// mobileXmppWsThreshold: 10, // enable XMPP WebSockets on mobile for 10% of the users
@@ -79,6 +83,15 @@ var config = {
// This is useful when the client runs on a host with limited resources.
// noAutoPlayVideo: false,
// Enable / disable 500 Kbps bitrate cap on desktop tracks. When enabled,
// simulcast is turned off for the desktop share. If presenter is turned
// on while screensharing is in progress, the max bitrate is automatically
// adjusted to 2.5 Mbps. This takes a value between 0 and 1 which determines
// the probability for this to be enabled. This setting has been deprecated.
// desktopSharingFrameRate.max now determines whether simulcast will be enabled
// or disabled for the screenshare.
// capScreenshareBitrate: 1, // 0 to disable - deprecated.
// Whether to use fake constraints (height: 99999, width: 99999) when calling getDisplayMedia on
// Chromium based browsers. This is intended as a workaround for
// https://bugs.chromium.org/p/chromium/issues/detail?id=1056311
@@ -90,6 +103,20 @@ var config = {
// callStatsThreshold: 5, // enable callstats for 5% of the users.
},
// Feature Flags.
flags: {
// Enables source names in the signaling.
// sourceNameSignaling: false,
// Enables sending multiple video streams, i.e., camera and desktop tracks can be shared in the conference
// separately as two different streams instead of one composite stream.
// sendMultipleVideoStreams: false,
// Signal that this client supports receiving multiple video streams. Without this flag jicofo will enable
// multi-stream backward compatibility.
// receiveMultipleVideoStreams: true,
},
// Disables moderator indicators.
// disableModeratorIndicator: false,
@@ -197,29 +224,9 @@ var config = {
// Specifies whether the raised hand will hide when someone becomes a dominant speaker or not
// disableRemoveRaisedHandOnFocus: false,
// speakerStats: {
// // Specifies whether the speaker stats is enable or not.
// disabled: false,
// // Specifies whether there will be a search field in speaker stats or not.
// disableSearch: false,
// // Specifies whether participants in speaker stats should be ordered or not, and with what priority.
// // 'role', <- Moderators on top.
// // 'name', <- Alphabetically by name.
// // 'hasLeft', <- The ones that have left in the bottom.
// order: [
// 'role',
// 'name',
// 'hasLeft',
// ],
// },
// DEPRECATED. Please use speakerStats.disableSearch instead.
// Specifies whether there will be a search field in speaker stats or not
// disableSpeakerStatsSearch: false,
// DEPRECATED. Please use speakerStats.order .
// Specifies whether participants in speaker stats should be ordered or not, and with what priority
// speakerStatsOrder: [
// 'role', <- Moderators on top
@@ -500,6 +507,9 @@ var config = {
// 720: 'high',
// },
//
// // Provides a way to resize the desktop track to 720p (if it is greater than 720p) before creating a canvas
// // for the presenter mode (camera picture-in-picture mode with screenshare).
// resizeDesktopForPresenter: false,
// },
// Notification timeouts
@@ -731,7 +741,6 @@ var config = {
// 'toggle-camera',
// 'undock-iframe',
// 'videoquality',
// 'whiteboard',
// ],
// Holds values related to toolbar visibility control.
@@ -767,13 +776,11 @@ var config = {
// 'desktop',
// 'download',
// 'embedmeeting',
// 'end-meeting',
// 'etherpad',
// 'feedback',
// 'filmstrip',
// 'fullscreen',
// 'hangup',
// 'hangup-menu',
// 'help',
// {
// key: 'invite',
@@ -806,7 +813,6 @@ var config = {
// key: 'add-passcode',
// preventExecution: false
// },
// 'whiteboard',
// ],
// List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons:
@@ -1481,8 +1487,6 @@ var config = {
// displayMode: 'all',
// // How long the GIF should be displayed on the tile (in milliseconds).
// tileTime: 5000,
// // Limit results by rating: g, pg, pg-13, r. Default value: g.
// rating: 'pg',
// },
// Logging

View File

@@ -10,7 +10,7 @@ import {
connectionEstablished,
connectionFailed,
constructOptions
} from './react/features/base/connection/actions.web';
} from './react/features/base/connection/actions';
import { openDialog } from './react/features/base/dialog/actions';
import { setJWT } from './react/features/base/jwt';
import {

View File

@@ -182,8 +182,3 @@ form {
background: rgba(0, 0, 0, .5);
border-radius: 4px;
}
/* Necessary for the new icons to work properly. */
.jitsi-icon svg path {
fill: inherit !important;
}

View File

@@ -24,6 +24,8 @@
}
.drawer-menu {
background: #242528;
border-radius: 16px 16px 0 0;
overflow-y: auto;
margin-bottom: env(safe-area-inset-bottom, 0);
width: 100%;

View File

@@ -25,7 +25,7 @@
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
border-radius: 3px;
cursor: pointer;
padding: 1px;
padding: 4px;
position: absolute;
right: -4px;
top: -3px;

View File

@@ -8,6 +8,7 @@ $hangupColor:#DD3849;
$hangupHoverColor: #F25363;
$hangupMenuButtonColor:#0056E0;;
$hangupMenuButtonHoverColor: #246FE5;
$hangupFontSize: 2em;
/**
* Size variables.
@@ -44,6 +45,7 @@ $newToolbarSizeMobile: 60px;
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
$toolbarTitleFontSize: 19px;
$overflowMenuItemColor: #fff;
$overflowMenuItemBackground: #36383C;
/**
@@ -86,7 +88,7 @@ $chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
$chatRemoteMessageBackgroundColor: #242528;
$sidebarWidth: 315px;
/**
/**
* Misc.
*/
$borderRadius: 4px;
@@ -101,6 +103,8 @@ $zindex0: 0;
$zindex1: 1;
$zindex2: 2;
$zindex3: 3;
$toolbarBackgroundZ: 4;
$labelsZ: 5;
$subtitlesZ: 7;
$popoverZ: 8;
$reloadZ: 20;
@@ -109,7 +113,10 @@ $ringingZ: 300;
$sideToolbarContainerZ: 300;
$toolbarZ: 250;
$drawerZ: 351;
$tooltipsZ: 401;
$dropdownMaskZ: 900;
$dropdownZ: 901;
$centeredVideoLabelZ: 1010;
$overlayZ: 1016;
// Place filmstrip videos over toolbar in order
// to make connection info visible.
@@ -243,6 +250,7 @@ $chromeExtensionBannerRightInMeeeting: 10px;
/**
* media type thresholds
*/
$smallScreen: 700px;
$verySmallScreen: 500px;
/**

View File

@@ -8,7 +8,6 @@
border-radius: 3px;
overflow: auto;
padding: 8px;
margin-bottom: 8px;
}
&-entry {

View File

@@ -38,7 +38,3 @@
margin-top: 2px;
display: block;
}
.dialog-bottom-margin {
margin-bottom: 5px;
}

View File

@@ -9,6 +9,7 @@
font-size: 15px;
margin-left: auto;
margin-top: 16px;
width: auto;
}
&-code {

View File

@@ -46,8 +46,6 @@
}
.feedback-dialog {
margin-bottom: 5px;
.details {
textarea {
min-height: 100px;

View File

@@ -84,6 +84,56 @@
}
}
&.icon-container {
display: none;
&.active {
display: flex;
width: calc(100% - 26px);
padding: 8px 8px 8px 16px;
background: #2A3A4B;
border: 1px solid #5E6D7A;
border-top: none;
border-radius: 0 0 3px 3px;
.copy-invite-icon, .provider-icon {
align-items: center;
cursor: pointer;
display: flex;
height: 40px;
place-content: center;
width: 40px;
}
&:hover > div:hover {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 4px;
}
& > :not(:last-child) {
margin-right: 16px;
}
.copy-invite-icon > div > svg > path {
fill: #A4B8D1;
}
}
}
&.dial-in-display {
.info-label {
color: #A4B8D1;
}
.dial-in-copy {
display: inline-block;
vertical-align: middle;
cursor: pointer;
height: 24px;
}
}
&.invite-buttons {
width: 100%;
text-align: right;

View File

@@ -1,7 +1,3 @@
.share-audio-dialog-container {
max-height: none;
}
.share-audio-dialog {
.share-audio-animation {
width: 100%;

View File

@@ -204,7 +204,3 @@
}
}
}
.lobby-button-margin {
margin-bottom: 16px;
}

View File

@@ -6,6 +6,8 @@ $baseLight: #FFFFFF;
/**
* Controls
*/
$controlBackground: $baseLight;
$controlColor: #333333;
$sliderTrackBackground: #474747;
$sliderThumbBackground: #3572b0;
@@ -58,21 +60,42 @@ $readOnlyInputColor: #a7a7a7;
$defaultDarkSelectionColor: #ccc;
$buttonFontWeight: 400;
$labelFontWeight: 400;
$hintFontSize: em(13, 14);
$linkFontColor: #3572b0;
$linkHoverFontColor: darken(#3572b0, 10%);
$dropdownColor: #333;
$errorColor: #c61600;
// Feedback colors
$feedbackCancelFontColor: #333;
// Popover colors
$popoverFontColor: #ffffff !important;
$popupSliderColor: #0376da;
// Toolbar
$secondaryToolbarBg: rgba(0, 0, 0, 0.5);
// TOFIX: Once moved to react rename to match the side panel class name.
$sideToolbarContainerBg: rgba(0, 0, 0, 0.75);
$toolbarBackground: rgba(0, 0, 0, 0.5);
$toolbarBadgeBackground: #165ECC;
$toolbarBadgeColor: #FFFFFF;
$toolbarButtonColor: #FFFFFF;
$toolbarSelectBackground: rgba(0, 0, 0, .6);
$toolbarTitleColor: #FFFFFF;
$toolbarToggleBackground: #12499C;
/**
* Forms
*/
$selectBg: $baseLight;
//dropdown
$selectFontColor: $controlColor;
$selectBg: $controlBackground;
$selectActiveBg: darken($controlBackground, 5%);
$selectActiveItemBg: darken($controlBackground, 20%);
/**
* TODO: Replace by themed component.
*/
$videoQualityActive: #57A0ff;

2
debian/control vendored
View File

@@ -20,7 +20,7 @@ Description: WebRTC JavaScript video conferences
Package: jitsi-meet-web-config
Architecture: all
Depends: openssl, nginx | nginx-full | nginx-extras | openresty | apache2, curl
Depends: openssl, nginx | nginx-full | nginx-extras | apache2, curl
Description: Configuration for web serving of Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.

View File

@@ -1,12 +1,12 @@
Template: jitsi-meet-prosody/jvb-hostname
Type: string
_Description: The domain of the current installation (e.g. meet.jitsi.com):
The value of the domain that is set in the Jitsi Videobridge installation.
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-videobridge/jvb-hostname
Type: string
_Description: The domain of the current installation (e.g. meet.jitsi.com):
The value of the domain that is set in the Jitsi Videobridge installation.
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-videobridge/jvbsecret
Type: password

View File

@@ -1,9 +1,9 @@
Template: jitsi-meet-turnserver/jvb-hostname
Type: string
_Description: The domain of the current installation (e.g. meet.jitsi.com):
The value of the domain that is set in the Jitsi Videobridge installation.
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-videobridge/jvb-hostname
Type: string
_Description: The domain of the current installation (e.g. meet.jitsi.com):
The value of the domain that is set in the Jitsi Videobridge installation.
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.

View File

@@ -57,10 +57,6 @@ case "$1" in
|| [ "$NGINX_EXTRAS_INSTALL_CHECK" = "unpacked" ] ; then
FORCE_NGINX="true"
fi
OPENRESTY_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'openresty' 2>/dev/null | awk '{print $3}' || true)"
if [ "$OPENRESTY_INSTALL_CHECK" = "installed" ] || [ "$OPENRESTY_INSTALL_CHECK" = "unpacked" ] ; then
FORCE_OPENRESTY="true"
fi
APACHE_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'apache2' 2>/dev/null | awk '{print $3}' || true)"
if [ "$APACHE_INSTALL_CHECK" = "installed" ] || [ "$APACHE_INSTALL_CHECK" = "unpacked" ] ; then
FORCE_APACHE="true"
@@ -175,52 +171,21 @@ case "$1" in
fi
fi
# Fixes multi-stream flags to workaround problem with mobile joining a multi-stream call with multi-stream disabled
if ! grep -q "^config.flags.sourceNameSignaling*" $JITSI_MEET_CONFIG; then
echo "config.flags.sourceNameSignaling = true;" >> $JITSI_MEET_CONFIG
fi
if ! grep -q "^config.flags.sendMultipleVideoStreams*" $JITSI_MEET_CONFIG; then
echo "config.flags.sendMultipleVideoStreams = true;" >> $JITSI_MEET_CONFIG
fi
if ! grep -q "^config.flags.receiveMultipleVideoStreams*" $JITSI_MEET_CONFIG; then
echo "config.flags.receiveMultipleVideoStreams = true;" >> $JITSI_MEET_CONFIG
fi
if [[ "$FORCE_OPENRESTY" = "true" ]]; then
NGX_COMMON_CONF_PATH="/usr/local/openresty/nginx/conf/$JVB_HOSTNAME.conf"
NGX_SVC_NAME=openresty
OPENRESTY_NGX_CONF="/usr/local/openresty/nginx/conf/nginx.conf"
else
NGX_COMMON_CONF_PATH="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
NGX_SVC_NAME=nginx
fi
if [[ ( "$FORCE_NGINX" = "true" || "$FORCE_OPENRESTY" = "true" ) && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
if [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
# this is a reconfigure, lets just delete old links
if [ "$RECONFIGURING" = "true" ] ; then
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME_OLD.conf
rm -f /etc/jitsi/meet/$JVB_HOSTNAME_OLD-config.js
if [[ "$FORCE_OPENRESTY" = "true" ]]; then
sed -i "/include.*$JVB_HOSTNAME_OLD/d" "$OPENRESTY_NGX_CONF"
fi
fi
# nginx conf
if [ ! -f "$NGX_COMMON_CONF_PATH" ] ; then
cp /usr/share/jitsi-meet-web-config/jitsi-meet.example "$NGX_COMMON_CONF_PATH"
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ] && ! [[ "$FORCE_OPENRESTY" = "true" ]] ; then
ln -s "$NGX_COMMON_CONF_PATH" /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
fi
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" "$NGX_COMMON_CONF_PATH"
if [[ "$FORCE_OPENRESTY" = "true" ]]; then
OPENRESTY_NGX_CONF_MD5_ORIG=$(dpkg-query -s openresty | sed -n '/\/nginx\.conf /{s@.* @@;p}')
OPENRESTY_NGX_CONF_MD5_USERS=$(md5sum "$OPENRESTY_NGX_CONF" | sed 's@ .*@@')
if [[ "$OPENRESTY_NGX_CONF_MD5_USERS" = "$OPENRESTY_NGX_CONF_MD5_ORIG" ]]; then
sed -i "/^http \x7b/,/^\x7d/s@^\x7d@\tinclude $NGX_COMMON_CONF_PATH;\n\x7d@" "$OPENRESTY_NGX_CONF"
fi
if [ ! -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf ] ; then
cp /usr/share/jitsi-meet-web-config/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ] ; then
ln -s /etc/nginx/sites-available/$JVB_HOSTNAME.conf /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
fi
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /etc/nginx/sites-available/$JVB_HOSTNAME.conf
fi
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
@@ -228,14 +193,14 @@ case "$1" in
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
sed -i "s/ssl_certificate_key\ \/etc\/jitsi\/meet\/.*key/ssl_certificate_key\ $CERT_KEY_ESC/g" \
"$NGX_COMMON_CONF_PATH"
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
sed -i "s/ssl_certificate\ \/etc\/jitsi\/meet\/.*crt/ssl_certificate\ $CERT_CRT_ESC/g" \
"$NGX_COMMON_CONF_PATH"
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
fi
invoke-rc.d $NGX_SVC_NAME reload || true
invoke-rc.d nginx reload || true
elif [[ "$FORCE_APACHE" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
# this is a reconfigure, lets just delete old links

View File

@@ -24,9 +24,6 @@ set -e
case "$1" in
remove)
if [ -x "/etc/init.d/openresty" ]; then
invoke-rc.d openresty reload || true
fi
if [ -x "/etc/init.d/nginx" ]; then
invoke-rc.d nginx reload || true
fi
@@ -41,7 +38,6 @@ case "$1" in
rm -f /etc/jitsi/meet/$JVB_HOSTNAME-config.js
rm -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
rm -f /usr/local/openresty/nginx/conf/$JVB_HOSTNAME.conf
rm -f /etc/apache2/sites-available/$JVB_HOSTNAME.conf
rm -f /etc/apache2/sites-enabled/$JVB_HOSTNAME.conf
rm -f /etc/jitsi/meet/$JVB_HOSTNAME.key

View File

@@ -23,8 +23,8 @@ _Description: Full local server path to the SSL certificate file:
Template: jitsi-meet/jvb-hostname
Type: string
_Description: The domain of the current installation (e.g. meet.jitsi.com):
The value of the domain that is set in the Jitsi Videobridge installation.
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-videobridge/jvb-hostname
Type: string
@@ -33,8 +33,8 @@ _Description: Hostname:
Template: jitsi-meet/jaas-choice
Type: boolean
_Description: Add telephony to your Jitsi meetings?
You can easily add dial-in support to your meetings. To allow this we would need your permission to create a free JaaS (Jitsi as a Service) account for you.
_Description: Interested in adding telephony to your Jitsi meetings?
You can easily add dialing in support to your meetings. You need to give us the permission to create a free JaaS (Jitsi as a Service) account for you.
Template: jitsi-meet/email
Type: string

View File

@@ -84,7 +84,7 @@ msgstr ""
#. Description
#: ../jitsi-meet-web-config.templates:4001
msgid ""
"The value of the domain that is set in the Jitsi Videobridge installation."
"The value for the hostname that is set in Jitsi Videobridge installation."
msgstr ""
#. Type: boolean

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Binary file not shown.

12
globals.d.ts vendored
View File

@@ -9,22 +9,12 @@ declare global {
UI: any;
API: any;
conference: any;
debugLogs: any;
keyboardshortcut: {
registerShortcut: Function;
unregisterShortcut: Function;
}
};
const interfaceConfig: any;
interface Window {
config: IConfig;
config?: IConfig;
JITSI_MEET_LITE_SDK?: boolean;
interfaceConfig?: any;
JitsiMeetJS?: any;
}
const config: IConfig;
const JitsiMeetJS: any;
}

43
globals.native.d.ts vendored
View File

@@ -1,43 +0,0 @@
import { IConfig } from "./react/features/base/config/configType";
export {};
interface ILocation extends URL {
assign(url: string);
replace(url: string);
reload();
};
interface IWindow {
JITSI_MEET_LITE_SDK: boolean;
JitsiMeetJS: any;
config: IConfig;
document: any;
innerHeight: number;
innerWidth: number;
interfaceConfig: any;
location: ILocation;
self: any;
top: any;
onerror: (event: string, source: any, lineno: any, colno: any, e: Error) => void;
onunhandledrejection: (event: any) => void;
setTimeout: typeof setTimeout;
clearTimeout: typeof clearTimeout;
setImmediate: typeof setImmediate;
clearImmediate: typeof clearImmediate;
addEventListener: Function;
}
interface INavigator {
product: string;
}
declare global {
const APP: any;
const document: any;
const interfaceConfig: any;
const navigator: INavigator;
const window: IWindow;
}

View File

@@ -13,14 +13,14 @@ PODS:
- CocoaLumberjack/Core (= 3.7.2)
- CocoaLumberjack/Core (3.7.2)
- DoubleConversion (1.1.6)
- FBLazyVector (0.68.4)
- FBReactNativeSpec (0.68.4):
- FBLazyVector (0.68.3)
- FBReactNativeSpec (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.68.4)
- RCTTypeSafety (= 0.68.4)
- React-Core (= 0.68.4)
- React-jsi (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- RCTRequired (= 0.68.3)
- RCTTypeSafety (= 0.68.3)
- React-Core (= 0.68.3)
- React-jsi (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- Firebase/Analytics (8.15.0):
- Firebase/Core
- Firebase/Core (8.15.0):
@@ -163,201 +163,201 @@ PODS:
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCTRequired (0.68.4)
- RCTTypeSafety (0.68.4):
- FBLazyVector (= 0.68.4)
- RCTRequired (0.68.3)
- RCTTypeSafety (0.68.3):
- FBLazyVector (= 0.68.3)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.68.4)
- React-Core (= 0.68.4)
- React (0.68.4):
- React-Core (= 0.68.4)
- React-Core/DevSupport (= 0.68.4)
- React-Core/RCTWebSocket (= 0.68.4)
- React-RCTActionSheet (= 0.68.4)
- React-RCTAnimation (= 0.68.4)
- React-RCTBlob (= 0.68.4)
- React-RCTImage (= 0.68.4)
- React-RCTLinking (= 0.68.4)
- React-RCTNetwork (= 0.68.4)
- React-RCTSettings (= 0.68.4)
- React-RCTText (= 0.68.4)
- React-RCTVibration (= 0.68.4)
- React-callinvoker (0.68.4)
- React-Codegen (0.68.4):
- FBReactNativeSpec (= 0.68.4)
- RCTRequired (= 0.68.3)
- React-Core (= 0.68.3)
- React (0.68.3):
- React-Core (= 0.68.3)
- React-Core/DevSupport (= 0.68.3)
- React-Core/RCTWebSocket (= 0.68.3)
- React-RCTActionSheet (= 0.68.3)
- React-RCTAnimation (= 0.68.3)
- React-RCTBlob (= 0.68.3)
- React-RCTImage (= 0.68.3)
- React-RCTLinking (= 0.68.3)
- React-RCTNetwork (= 0.68.3)
- React-RCTSettings (= 0.68.3)
- React-RCTText (= 0.68.3)
- React-RCTVibration (= 0.68.3)
- React-callinvoker (0.68.3)
- React-Codegen (0.68.3):
- FBReactNativeSpec (= 0.68.3)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.68.4)
- RCTTypeSafety (= 0.68.4)
- React-Core (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-Core (0.68.4):
- RCTRequired (= 0.68.3)
- RCTTypeSafety (= 0.68.3)
- React-Core (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-Core (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.68.4)
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-Core/Default (= 0.68.3)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/CoreModulesHeaders (0.68.4):
- React-Core/CoreModulesHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/Default (0.68.4):
- React-Core/Default (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/DevSupport (0.68.4):
- React-Core/DevSupport (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.68.4)
- React-Core/RCTWebSocket (= 0.68.4)
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-jsinspector (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-Core/Default (= 0.68.3)
- React-Core/RCTWebSocket (= 0.68.3)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-jsinspector (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTActionSheetHeaders (0.68.4):
- React-Core/RCTActionSheetHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTAnimationHeaders (0.68.4):
- React-Core/RCTAnimationHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTBlobHeaders (0.68.4):
- React-Core/RCTBlobHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTImageHeaders (0.68.4):
- React-Core/RCTImageHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTLinkingHeaders (0.68.4):
- React-Core/RCTLinkingHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTNetworkHeaders (0.68.4):
- React-Core/RCTNetworkHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTSettingsHeaders (0.68.4):
- React-Core/RCTSettingsHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTTextHeaders (0.68.4):
- React-Core/RCTTextHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTVibrationHeaders (0.68.4):
- React-Core/RCTVibrationHeaders (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-Core/RCTWebSocket (0.68.4):
- React-Core/RCTWebSocket (0.68.3):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.68.4)
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsiexecutor (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-Core/Default (= 0.68.3)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsiexecutor (= 0.68.3)
- React-perflogger (= 0.68.3)
- Yoga
- React-CoreModules (0.68.4):
- React-CoreModules (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.4)
- React-Codegen (= 0.68.4)
- React-Core/CoreModulesHeaders (= 0.68.4)
- React-jsi (= 0.68.4)
- React-RCTImage (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-cxxreact (0.68.4):
- RCTTypeSafety (= 0.68.3)
- React-Codegen (= 0.68.3)
- React-Core/CoreModulesHeaders (= 0.68.3)
- React-jsi (= 0.68.3)
- React-RCTImage (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-cxxreact (0.68.3):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-callinvoker (= 0.68.4)
- React-jsi (= 0.68.4)
- React-jsinspector (= 0.68.4)
- React-logger (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-runtimeexecutor (= 0.68.4)
- React-jsi (0.68.4):
- React-callinvoker (= 0.68.3)
- React-jsi (= 0.68.3)
- React-jsinspector (= 0.68.3)
- React-logger (= 0.68.3)
- React-perflogger (= 0.68.3)
- React-runtimeexecutor (= 0.68.3)
- React-jsi (0.68.3):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-jsi/Default (= 0.68.4)
- React-jsi/Default (0.68.4):
- React-jsi/Default (= 0.68.3)
- React-jsi/Default (0.68.3):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-jsiexecutor (0.68.4):
- React-jsiexecutor (0.68.3):
- DoubleConversion
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-jsinspector (0.68.4)
- React-logger (0.68.4):
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-perflogger (= 0.68.3)
- React-jsinspector (0.68.3)
- React-logger (0.68.3):
- glog
- react-native-background-timer (2.4.1):
- React-Core
@@ -390,74 +390,74 @@ PODS:
- React-Core
- react-native-webview (11.15.1):
- React-Core
- React-perflogger (0.68.4)
- React-RCTActionSheet (0.68.4):
- React-Core/RCTActionSheetHeaders (= 0.68.4)
- React-RCTAnimation (0.68.4):
- React-perflogger (0.68.3)
- React-RCTActionSheet (0.68.3):
- React-Core/RCTActionSheetHeaders (= 0.68.3)
- React-RCTAnimation (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.4)
- React-Codegen (= 0.68.4)
- React-Core/RCTAnimationHeaders (= 0.68.4)
- React-jsi (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-RCTBlob (0.68.4):
- RCTTypeSafety (= 0.68.3)
- React-Codegen (= 0.68.3)
- React-Core/RCTAnimationHeaders (= 0.68.3)
- React-jsi (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-RCTBlob (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- React-Codegen (= 0.68.4)
- React-Core/RCTBlobHeaders (= 0.68.4)
- React-Core/RCTWebSocket (= 0.68.4)
- React-jsi (= 0.68.4)
- React-RCTNetwork (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-RCTImage (0.68.4):
- React-Codegen (= 0.68.3)
- React-Core/RCTBlobHeaders (= 0.68.3)
- React-Core/RCTWebSocket (= 0.68.3)
- React-jsi (= 0.68.3)
- React-RCTNetwork (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-RCTImage (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.4)
- React-Codegen (= 0.68.4)
- React-Core/RCTImageHeaders (= 0.68.4)
- React-jsi (= 0.68.4)
- React-RCTNetwork (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-RCTLinking (0.68.4):
- React-Codegen (= 0.68.4)
- React-Core/RCTLinkingHeaders (= 0.68.4)
- React-jsi (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-RCTNetwork (0.68.4):
- RCTTypeSafety (= 0.68.3)
- React-Codegen (= 0.68.3)
- React-Core/RCTImageHeaders (= 0.68.3)
- React-jsi (= 0.68.3)
- React-RCTNetwork (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-RCTLinking (0.68.3):
- React-Codegen (= 0.68.3)
- React-Core/RCTLinkingHeaders (= 0.68.3)
- React-jsi (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-RCTNetwork (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.4)
- React-Codegen (= 0.68.4)
- React-Core/RCTNetworkHeaders (= 0.68.4)
- React-jsi (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-RCTSettings (0.68.4):
- RCTTypeSafety (= 0.68.3)
- React-Codegen (= 0.68.3)
- React-Core/RCTNetworkHeaders (= 0.68.3)
- React-jsi (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-RCTSettings (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.4)
- React-Codegen (= 0.68.4)
- React-Core/RCTSettingsHeaders (= 0.68.4)
- React-jsi (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-RCTText (0.68.4):
- React-Core/RCTTextHeaders (= 0.68.4)
- React-RCTVibration (0.68.4):
- RCTTypeSafety (= 0.68.3)
- React-Codegen (= 0.68.3)
- React-Core/RCTSettingsHeaders (= 0.68.3)
- React-jsi (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-RCTText (0.68.3):
- React-Core/RCTTextHeaders (= 0.68.3)
- React-RCTVibration (0.68.3):
- RCT-Folly (= 2021.06.28.00-v2)
- React-Codegen (= 0.68.4)
- React-Core/RCTVibrationHeaders (= 0.68.4)
- React-jsi (= 0.68.4)
- ReactCommon/turbomodule/core (= 0.68.4)
- React-runtimeexecutor (0.68.4):
- React-jsi (= 0.68.4)
- ReactCommon/turbomodule/core (0.68.4):
- React-Codegen (= 0.68.3)
- React-Core/RCTVibrationHeaders (= 0.68.3)
- React-jsi (= 0.68.3)
- ReactCommon/turbomodule/core (= 0.68.3)
- React-runtimeexecutor (0.68.3):
- React-jsi (= 0.68.3)
- ReactCommon/turbomodule/core (0.68.3):
- DoubleConversion
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-callinvoker (= 0.68.4)
- React-Core (= 0.68.4)
- React-cxxreact (= 0.68.4)
- React-jsi (= 0.68.4)
- React-logger (= 0.68.4)
- React-perflogger (= 0.68.4)
- React-callinvoker (= 0.68.3)
- React-Core (= 0.68.3)
- React-cxxreact (= 0.68.3)
- React-jsi (= 0.68.3)
- React-logger (= 0.68.3)
- React-perflogger (= 0.68.3)
- RNCalendarEvents (2.2.0):
- React
- RNCAsyncStorage (1.17.3):
- RNCAsyncStorage (1.15.14):
- React-Core
- RNCClipboard (1.5.1):
- React-Core
@@ -706,8 +706,8 @@ SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234
CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: 023a2028f218d648b588348bfa9261b4914b93db
FBReactNativeSpec: 9f4902cc009389d3704ff75de2aa513dee34d5c2
FBLazyVector: 34f7420274737b6fcf2e2d9fd42641e66b4436a3
FBReactNativeSpec: 68c23fb2cea9393190e0815b673d742fa33d2dab
Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d
FirebaseAnalytics: 7761cbadb00a717d8d0939363eb46041526474fa
FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1
@@ -732,18 +732,18 @@ SPEC CHECKSUMS:
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
PromisesSwift: 99fddfe4a0ec88a56486644c0da106694c92a604
RCT-Folly: 4d8508a426467c48885f1151029bc15fa5d7b3b8
RCTRequired: e6003505912d056f21f64465063cf4b79418f2b9
RCTTypeSafety: d7ef4745c8d9c9faa65c26b4b6230fc5cd4c4424
React: 6692c30fb74ab29078b25c31c9841d863e08cdd9
React-callinvoker: fe2b234fa518d8bb7600707c536ab0a3e1f5edba
React-Codegen: 9964bb2422c7014894182ac50068caae05f68551
React-Core: a07bcd2f15ff93cddc9ceb07eddeec3d2ff8d990
React-CoreModules: 7fb4ee0fc35ad2b7daf775f0ef6309efdd8d3d82
React-cxxreact: 51a8058a35a2f02ad4175334a7cd24aa5558ced4
React-jsi: 69b974b418d2658a3f1799903be7cbcb8ac59755
React-jsiexecutor: 4f35a29798ba9d0d892a84001d11f626688dbb8e
React-jsinspector: 6f75220cd4b6020976d340ab21c63458dd3cad9e
React-logger: 7013d2499df6346e6a72802d4084badaaa82543b
RCTRequired: b8caca023d386d43740dfb94c2cf68f695fa5e77
RCTTypeSafety: ec44ea1d6ad1e5cd6447b22159ff40c2ebbd23b1
React: 9f8c8afb9a9d61b7a1b01a1c6fb7f0d4f902988f
React-callinvoker: f813eee352cfd333208e8d67a72f584f5435769d
React-Codegen: 771562186fec8c7830897f97ca59de683abd3184
React-Core: 74670b4b715083e1c9003462f3f4fe32a70ba5c5
React-CoreModules: 34bd5b93e5322e60102a5ad78b992c882e558022
React-cxxreact: adc9fc6a9333ae779bd72effaf77173bd9f22bf7
React-jsi: ab91137ea7d92a86e48b6f15d3a5580bea471776
React-jsiexecutor: a5043e9e1e1bd13b80b58b228c6901b3721a4f54
React-jsinspector: 86e89b9f135787a2e8eb74b3fc00ec61e9a80ae1
React-logger: f790bd10f86b38012e108fb4b564023602702270
react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe
react-native-get-random-values: 30b3f74ca34e30e2e480de48e4add2706a40ac8f
react-native-keep-awake: afad8a51dfef9fe9655a6344771be32c8596d774
@@ -757,20 +757,20 @@ SPEC CHECKSUMS:
react-native-video: bb6f12a7198db53b261fefb5d609dc77417acc8b
react-native-webrtc: 4a4c31be61f88d1d3356526eebce72f462a6760e
react-native-webview: ea4899a1056c782afa96dd082179a66cbebf5504
React-perflogger: 0b0500685176e53ea582c45179a653aa82e4ae49
React-RCTActionSheet: 38469be9d20242f9c717e43c2983e8e3e6c640c4
React-RCTAnimation: 93774f3e8857e7c3c1cbbd277056d02be4496be1
React-RCTBlob: 6d0567d7a6561b62feb8c3b1cc33b3c591ba85ab
React-RCTImage: 1006a91318a6181a0256b89d2e321b6ea0e2e6e3
React-RCTLinking: 0b2300493c879c3bcac2d4c6b0178e8d0e5e2202
React-RCTNetwork: b9a33a95703651abed92490e50396d54b7270a17
React-RCTSettings: e6464123e5b5062fc23bb5adb51188a6061e9601
React-RCTText: 188d6f0ae20cd28891f59ecad41028ee2f793757
React-RCTVibration: a67beb7d2f3c73e9b74c4124ef61b84c601be649
React-runtimeexecutor: 088723cf020113e64736a709f52719dbb359c73e
ReactCommon: 1a4f19f3b4366feec03a98bdbb200b6085c5000f
React-perflogger: fa15d1d29ff7557ee25ea48f7f59e65896fb3215
React-RCTActionSheet: e83515333352a3cc19c146e3c7a63a8a9269da8f
React-RCTAnimation: 8032daa2846e3db7ac28c4c5a207d0bfb5e1e3ad
React-RCTBlob: fe40e206cebcb4f552e0ecdac3ef81b3baf3cb37
React-RCTImage: dfc0df14cbfec1ec54fdd4700b8fe3bf8127dde2
React-RCTLinking: ac9f65f0c8db738a6156ae7640ba92494b4770a5
React-RCTNetwork: cf289a0386a1bd057e5eabb8563dfe5ce0af868c
React-RCTSettings: 7889cfcf6c7d5738f3cb8889557a38eeea2f04ff
React-RCTText: fd249e1f8406fb6e35cc77a2b9ff66a3477bf41a
React-RCTVibration: f41f116aad644973f24653effb3db3de64fa0314
React-runtimeexecutor: 8cdd80915ed6dabf2221a689f1f7ddb50ea5e9f3
ReactCommon: 5b1b43a7d81a1ac4eec85f7c4db3283a14a3b13d
RNCalendarEvents: 7e65eb4a94f53c1744d1e275f7fafcfaa619f7a3
RNCAsyncStorage: 005c0e2f09575360f142d0d1f1f15e4ec575b1af
RNCAsyncStorage: ea6b5c280997b2b32a587793163b1f10e580c4f7
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd
RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31
@@ -781,7 +781,7 @@ SPEC CHECKSUMS:
RNSound: 27e8268bdb0a1f191f219a33267f7e0445e8d62f
RNSVG: f3b60aeeaa81960e2e0536c3a9eef50b667ef3a9
RNWatch: dae6c858a2051dbdcfb00b9a86cf4d90400263b4
Yoga: c926c8eec5c78a788b51e6c8a604825d00d694d7
Yoga: 2f6a78c58dcc2963bd8e34d96a4246d9dff2e3a7
PODFILE CHECKSUM: e671cdcdb80fab67e305861c36bfae8ed5a5b0ef

View File

@@ -1,11 +0,0 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,13 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,11 +0,0 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,12 +0,0 @@
{
"images" : [
{
"filename" : "end_call_button.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,12 +0,0 @@
{
"images" : [
{
"filename" : "microphone_off_button.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,12 +0,0 @@
{
"images" : [
{
"filename" : "microphone_on_button.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,101 +0,0 @@
//
// DarwinNotificationsObserver.swift
// WidgetsExtension
//
// Created by Alex Bumbu on 17.10.2022.
//
import Foundation
enum DarwinNotification: String {
case meetingMutedChanged = "iOS_MeetingMutedChanged"
}
extension DarwinNotification {
var name: String { rawValue }
}
class DarwinNotificationsObserver {
private static var observers = Array<ProxyObserver>()
private let queue = DispatchQueue(label: "org.jitsi.meet.darwinNotificationObserver", qos: .default, autoreleaseFrequency: .workItem)
private var notificationCenter: CFNotificationCenter
init() {
notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
}
func observe(notification: DarwinNotification, handler: @escaping () -> Void) {
let proxyObserver = ProxyObserver(observer: self, notificationName: notification.name, handler: handler)
queue.async {
DarwinNotificationsObserver.observers.append(proxyObserver)
}
let callback: CFNotificationCallback = { _, observer, name, _, _ in
guard let observer = observer else {
return
}
// Extract pointer to `observer` from void pointer:
let proxyObserver = Unmanaged<ProxyObserver>.fromOpaque(observer).takeUnretainedValue()
var observers = DarwinNotificationsObserver.observers
if !proxyObserver.forwardNotification(), let index = observers.firstIndex(of: proxyObserver) {
// cleanup if `forwardNotification` fails
observers.remove(at: index)
}
}
CFNotificationCenterAddObserver(notificationCenter,
Unmanaged.passUnretained(proxyObserver).toOpaque(),
callback,
notification.name as CFString,
nil,
.deliverImmediately)
}
func stopObserving(notification: DarwinNotification) {
queue.sync {
DarwinNotificationsObserver.observers.removeAll { $0.observer == nil }
}
if let index = DarwinNotificationsObserver.observers.firstIndex(where: { $0.observer === self && $0.notificationName == notification.name }) {
let proxyObserver = DarwinNotificationsObserver.observers[index]
CFNotificationCenterRemoveObserver(notificationCenter,
Unmanaged.passUnretained(proxyObserver).toOpaque(),
CFNotificationName(notification.name as CFString),
nil)
queue.async {
DarwinNotificationsObserver.observers.remove(at: index)
}
}
}
}
private class ProxyObserver: Equatable {
let notificationName: String
weak var observer: AnyObject?
private let handler: () -> (Void)
static func == (lhs: ProxyObserver, rhs: ProxyObserver) -> Bool {
lhs.observer === rhs.observer && lhs.notificationName == rhs.notificationName
}
init(observer: AnyObject? = nil, notificationName: String, handler: @escaping () -> Void) {
self.notificationName = notificationName
self.handler = handler
self.observer = observer
}
func forwardNotification() -> Bool {
guard observer != nil else {
return false
}
handler()
return true
}
}

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>

View File

@@ -1,54 +0,0 @@
//
// LockScreenLeaveMeetingWidget.swift
// WidgetsExtension
//
// Created by Alex Bumbu on 31.10.2022.
//
import SwiftUI
import WidgetKit
struct LockScreenLeaveMeetingWidget: Widget {
let kind: String = "LockScreenLeaveMeetingWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
WidgetsEntryView(entry: entry)
}
.configurationDisplayName("Leave Jitsi Meeting Widget")
.description("This is a lockscreen widget for leaving the ongoing Jitsi meeting.")
.supportedFamilies([.accessoryCircular])
}
}
//struct LockScreenLeaveMeetingWidget_Preview: PreviewProvider {
// static var previews: some View {
// let meetingState = MeetingState(audioMuted: true)
//
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
// .previewContext(WidgetPreviewContext(family: .accessoryCircular))
// .previewDisplayName("Circular")
// }
//}
private struct WidgetsEntryView: View {
@Environment(\.widgetFamily) var widgetFamily
var entry: Provider.Entry
var body: some View {
if entry.meetingState != nil, widgetFamily == .accessoryCircular {
AccessoryCircularWidgetView()
} else {
EmptyView()
}
}
}
private struct AccessoryCircularWidgetView: View {
var body: some View {
Image("leave_meeting")
.resizable()
.aspectRatio(contentMode: .fit)
.widgetURL(URL(string: "meet/leaveMeeting")!)
}
}

View File

@@ -1,89 +0,0 @@
//
// LockScreenMuteAudioWidget.swift
// WidgetsExtension
//
// Created by Alex Bumbu on 31.10.2022.
//
import SwiftUI
import WidgetKit
struct LockScreenMuteAudioWidget: Widget {
let kind: String = "LockScreenMuteAudioWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
WidgetsEntryView(entry: entry)
}
.configurationDisplayName("Mute Jitsi Audio Widget")
.description("This is a lockscreen widget for muting or unmuting the audio for the ongoing Jitsi meeting.")
.supportedFamilies([.accessoryCircular])
}
}
//struct LockScreenMuteAudioWidget_Preview: PreviewProvider {
// static var previews: some View {
// let meetingState = MeetingState(audioMuted: true)
//
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
// .previewContext(WidgetPreviewContext(family: .accessoryInline))
// .previewDisplayName("Inline")
//
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
// .previewContext(WidgetPreviewContext(family: .accessoryCircular))
// .previewDisplayName("Circular")
//
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
// .previewContext(WidgetPreviewContext(family: .accessoryRectangular))
// .previewDisplayName("Rectangular")
// }
//}
private struct WidgetsEntryView: View {
@Environment(\.widgetFamily) var widgetFamily
var entry: Provider.Entry
var body: some View {
if let meetingState = entry.meetingState {
switch widgetFamily {
case .accessoryInline:
Text("Some meeting name")
case .accessoryRectangular:
AccessoryCircularWidgetView(audioMuted: meetingState.audioMuted)
case .accessoryCircular:
AccessoryCircularWidgetView(audioMuted: meetingState.audioMuted)
default:
EmptyView()
}
} else {
EmptyView()
}
}
}
private struct AccessoryRectangularWidgetView: View {
var audioMuted: Bool
var body: some View {
let imageName: String = audioMuted ? "microphone_on" : "microphone_off"
let caption: String = audioMuted ? "Unmute \naudio" : "Mute \naudio"
HStack {
Image(imageName)
.resizable()
.aspectRatio(contentMode: .fit)
Text(caption)
}.widgetURL(URL(string: "meet/toggleAudioMute")!)
}
}
private struct AccessoryCircularWidgetView: View {
var audioMuted: Bool
var body: some View {
let imageName: String = audioMuted ? "microphone_on" : "microphone_off"
Image(imageName)
.resizable()
.aspectRatio(contentMode: .fit)
.widgetURL(URL(string: "meet/toggleAudioMute")!)
}
}

View File

@@ -1,30 +0,0 @@
//
// MeetingState.swift
// WidgetsExtension
//
// Created by Alex Bumbu on 28.10.2022.
//
import Foundation
struct MeetingState: Decodable {
var audioMuted: Bool
}
extension MeetingState {
private static var stateFileURL: URL? {
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.org.jitsi.meet.appgroup")?.appending(component: "widgetState")
}
static func load() -> MeetingState? {
guard
let stateFileURL = stateFileURL,
let data = try? Data(contentsOf: stateFileURL)
else {
return nil
}
let decoder = PropertyListDecoder()
return try? decoder.decode(MeetingState.self, from: data)
}
}

View File

@@ -1,44 +0,0 @@
//
// Provider.swift
// WidgetsExtension
//
// Created by Alex Bumbu on 31.10.2022.
//
import WidgetKit
import SwiftUI
struct CurrentMeetingEntry: TimelineEntry {
let date: Date
var meetingState: MeetingState?
}
class Provider: TimelineProvider {
private var currentMeetingState: MeetingState? {
return MeetingState.load()
}
func placeholder(in context: Context) -> CurrentMeetingEntry {
CurrentMeetingEntry(date: Date(),
meetingState: MeetingState(audioMuted: false))
}
func getSnapshot(in context: Context, completion: @escaping (CurrentMeetingEntry) -> ()) {
var meetingState = currentMeetingState
if context.isPreview {
meetingState = MeetingState(audioMuted: false)
}
let entry = CurrentMeetingEntry(date: Date(), meetingState: meetingState)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<CurrentMeetingEntry>) -> ()) {
var entries: [CurrentMeetingEntry] = []
let entry = CurrentMeetingEntry(date: Date(), meetingState: currentMeetingState)
entries.append(entry)
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}

View File

@@ -1,19 +0,0 @@
//
// Widgets.swift
// Widgets
//
// Created by Alex Bumbu on 17.10.2022.
// Copyright © 2022 Facebook. All rights reserved.
//
import WidgetKit
import SwiftUI
@main
struct Widgets: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
LockScreenMuteAudioWidget()
LockScreenLeaveMeetingWidget()
}
}

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.org.jitsi.meet.appgroup</string>
</array>
</dict>
</plist>

View File

@@ -23,26 +23,14 @@
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
4E46D952290FF39E00761DEF /* LockScreenMuteAudioWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E46D951290FF39E00761DEF /* LockScreenMuteAudioWidget.swift */; };
4E46D954290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E46D953290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift */; };
4E6920B828FD84D700645D9E /* DarwinNotificationsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6920B728FD84D700645D9E /* DarwinNotificationsObserver.swift */; };
4E6A3E17291024B900E6B0B5 /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6A3E16291024B900E6B0B5 /* Provider.swift */; };
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */; };
4E90F9402632D1AB001102D4 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E90F93F2632D1AB001102D4 /* Atomic.swift */; };
4EA73DA3290C1D6C00A16FF8 /* MeetingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA73DA2290C1D6C00A16FF8 /* MeetingState.swift */; };
4EB06024260E026600F524C5 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC49B8625BED71300E76218 /* ReplayKit.framework */; };
4EB06027260E026600F524C5 /* SampleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06026260E026600F524C5 /* SampleHandler.swift */; };
4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
4EB0603C260E09D000F524C5 /* SocketConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06039260E09D000F524C5 /* SocketConnection.swift */; };
4EB0603D260E09D000F524C5 /* DarwinNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */; };
4EB0603E260E09D000F524C5 /* SampleUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603B260E09D000F524C5 /* SampleUploader.swift */; };
4EBB458A28FFFD4100855769 /* RoutesHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBB458928FFFD4100855769 /* RoutesHandler.m */; };
4EBB458E2902E85B00855769 /* WidgetKitHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBB458D2902E85B00855769 /* WidgetKitHelper.swift */; };
4ECA496628FD590000085365 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ECA496528FD590000085365 /* WidgetKit.framework */; };
4ECA496828FD590000085365 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ECA496728FD590000085365 /* SwiftUI.framework */; };
4ECA496B28FD590000085365 /* Widgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECA496A28FD590000085365 /* Widgets.swift */; };
4ECA496D28FD590000085365 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4ECA496C28FD590000085365 /* Assets.xcassets */; };
4ECA497128FD590000085365 /* WidgetsExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4ECA496428FD590000085365 /* WidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
C2116A7673E01A1CCD5DC1F4 /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BF9FEBA4DEAB800AD735681 /* libPods-JitsiMeet.a */; };
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */; };
DEA9F289258A6EA800D4CD74 /* JitsiMeetSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; };
DEA9F28A258A6EA800D4CD74 /* JitsiMeetSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -76,13 +64,6 @@
remoteGlobalIDString = 4EB06022260E026600F524C5;
remoteInfo = "JitsiMeetBroadcast Extension";
};
4ECA496F28FD590000085365 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4ECA496328FD590000085365;
remoteInfo = WidgetsExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -127,7 +108,6 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
4ECA497128FD590000085365 /* WidgetsExtension.appex in Embed App Extensions */,
4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
@@ -154,7 +134,6 @@
0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationController.swift; sourceTree = "<group>"; };
0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0BEA5C3E1F7B8F73000D0AB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0BF9FEBA4DEAB800AD735681 /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -162,12 +141,8 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
4E46D951290FF39E00761DEF /* LockScreenMuteAudioWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenMuteAudioWidget.swift; sourceTree = "<group>"; };
4E46D953290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenLeaveMeetingWidget.swift; sourceTree = "<group>"; };
4E6920B728FD84D700645D9E /* DarwinNotificationsObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarwinNotificationsObserver.swift; sourceTree = "<group>"; };
4E6A3E16291024B900E6B0B5 /* Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Provider.swift; sourceTree = "<group>"; };
3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
4E90F93F2632D1AB001102D4 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
4EA73DA2290C1D6C00A16FF8 /* MeetingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingState.swift; sourceTree = "<group>"; };
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = JitsiMeetBroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
4EB06026260E026600F524C5 /* SampleHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleHandler.swift; sourceTree = "<group>"; };
4EB06028260E026600F524C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -175,21 +150,10 @@
4EB06039260E09D000F524C5 /* SocketConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketConnection.swift; sourceTree = "<group>"; };
4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarwinNotificationCenter.swift; sourceTree = "<group>"; };
4EB0603B260E09D000F524C5 /* SampleUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUploader.swift; sourceTree = "<group>"; };
4EBB458828FFFD4100855769 /* RoutesHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoutesHandler.h; sourceTree = "<group>"; };
4EBB458928FFFD4100855769 /* RoutesHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoutesHandler.m; sourceTree = "<group>"; };
4EBB458B2902A94700855769 /* WidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetsExtension.entitlements; sourceTree = "<group>"; };
4EBB458C2902E85B00855769 /* JitsiMeet-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeet-Bridging-Header.h"; sourceTree = "<group>"; };
4EBB458D2902E85B00855769 /* WidgetKitHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetKitHelper.swift; sourceTree = "<group>"; };
4EC49B8625BED71300E76218 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
4ECA496428FD590000085365 /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
4ECA496528FD590000085365 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
4ECA496728FD590000085365 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
4ECA496A28FD590000085365 /* Widgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widgets.swift; sourceTree = "<group>"; };
4ECA496C28FD590000085365 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
4ECA496E28FD590000085365 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7052390E12D7319D36D8E4CA /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
8CAA3C5A38E868335D1C1EC1 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DE050388256E904600DEE3A5 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/apple/WebRTC.xcframework"; sourceTree = "<group>"; };
DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRUtilities.m; sourceTree = "<group>"; };
DE4C456021DE1E4E00EA0709 /* FIRUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRUtilities.h; sourceTree = "<group>"; };
@@ -216,7 +180,7 @@
DED016F128ECBC9D009D5E8D /* WebRTC.xcframework in Frameworks */,
DEA9F289258A6EA800D4CD74 /* JitsiMeetSDK.framework in Frameworks */,
FD572B9827EDF32300A800FB /* GiphyUISDK.xcframework in Frameworks */,
C2116A7673E01A1CCD5DC1F4 /* libPods-JitsiMeet.a in Frameworks */,
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -235,15 +199,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
4ECA496128FD590000085365 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4ECA496828FD590000085365 /* SwiftUI.framework in Frameworks */,
4ECA496628FD590000085365 /* WidgetKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -258,9 +213,7 @@
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */,
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */,
4EC49B8625BED71300E76218 /* ReplayKit.framework */,
4ECA496528FD590000085365 /* WidgetKit.framework */,
4ECA496728FD590000085365 /* SwiftUI.framework */,
0BF9FEBA4DEAB800AD735681 /* libPods-JitsiMeet.a */,
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -308,10 +261,6 @@
0BBD021F212EB69D00CCB19F /* Types.h */,
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */,
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */,
4EBB458828FFFD4100855769 /* RoutesHandler.h */,
4EBB458928FFFD4100855769 /* RoutesHandler.m */,
4EBB458D2902E85B00855769 /* WidgetKitHelper.swift */,
4EBB458C2902E85B00855769 /* JitsiMeet-Bridging-Header.h */,
);
path = src;
sourceTree = "<group>";
@@ -333,25 +282,9 @@
sourceTree = "<group>";
tabWidth = 4;
};
4ECA496928FD590000085365 /* Widgets Extension */ = {
isa = PBXGroup;
children = (
4ECA496A28FD590000085365 /* Widgets.swift */,
4E46D951290FF39E00761DEF /* LockScreenMuteAudioWidget.swift */,
4E46D953290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift */,
4E6A3E16291024B900E6B0B5 /* Provider.swift */,
4EA73DA2290C1D6C00A16FF8 /* MeetingState.swift */,
4E6920B728FD84D700645D9E /* DarwinNotificationsObserver.swift */,
4ECA496C28FD590000085365 /* Assets.xcassets */,
4ECA496E28FD590000085365 /* Info.plist */,
);
path = "Widgets Extension";
sourceTree = "<group>";
};
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
4EBB458B2902A94700855769 /* WidgetsExtension.entitlements */,
B3B083EB1D4955FF0069CEE7 /* app.entitlements */,
0B26BE711EC5BC4D00EEFB41 /* Frameworks */,
83CBBA001A601CBA00E9B192 /* Products */,
@@ -359,12 +292,11 @@
0BEA5C261F7B8F73000D0AB4 /* Watch app */,
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
4ECA496928FD590000085365 /* Widgets Extension */,
BD4E28FA984EA7018FD927DF /* Pods */,
CDD71F5E1157E9F283DF92A8 /* Pods */,
);
indentWidth = 4;
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 4;
tabWidth = 2;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
@@ -373,16 +305,15 @@
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */,
4ECA496428FD590000085365 /* WidgetsExtension.appex */,
);
name = Products;
sourceTree = "<group>";
};
BD4E28FA984EA7018FD927DF /* Pods */ = {
CDD71F5E1157E9F283DF92A8 /* Pods */ = {
isa = PBXGroup;
children = (
7052390E12D7319D36D8E4CA /* Pods-JitsiMeet.debug.xcconfig */,
8CAA3C5A38E868335D1C1EC1 /* Pods-JitsiMeet.release.xcconfig */,
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */,
3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */,
);
name = Pods;
path = ../Pods;
@@ -430,7 +361,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
buildPhases = (
58E2CB346F2C2A873294F481 /* [CP] Check Pods Manifest.lock */,
69BC5020DBE393B56BD76636 /* [CP] Check Pods Manifest.lock */,
0BBA83C41EC9F7600075A103 /* Run React packager */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
@@ -449,7 +380,6 @@
dependencies = (
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
4EB0602A260E026600F524C5 /* PBXTargetDependency */,
4ECA497028FD590000085365 /* PBXTargetDependency */,
);
name = JitsiMeet;
productName = "Jitsi Meet";
@@ -473,32 +403,15 @@
productReference = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
4ECA496328FD590000085365 /* WidgetsExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4ECA497428FD590100085365 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */;
buildPhases = (
4ECA496028FD590000085365 /* Sources */,
4ECA496128FD590000085365 /* Frameworks */,
4ECA496228FD590000085365 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = WidgetsExtension;
productName = WidgetsExtension;
productReference = 4ECA496428FD590000085365 /* WidgetsExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1400;
LastSwiftUpdateCheck = 1240;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
0BEA5C241F7B8F73000D0AB4 = {
CreatedOnToolsVersion = 9.0;
@@ -511,7 +424,6 @@
ProvisioningStyle = Automatic;
};
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1400;
SystemCapabilities = {
com.apple.SafariKeychain = {
enabled = 1;
@@ -524,9 +436,6 @@
4EB06022260E026600F524C5 = {
CreatedOnToolsVersion = 12.4;
};
4ECA496328FD590000085365 = {
CreatedOnToolsVersion = 14.0.1;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
@@ -546,7 +455,6 @@
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */,
4ECA496328FD590000085365 /* WidgetsExtension */,
);
};
/* End PBXProject section */
@@ -586,14 +494,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
4ECA496228FD590000085365 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4ECA496D28FD590000085365 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -643,7 +543,7 @@
shellPath = /bin/sh;
shellScript = "if test \"$PRODUCT_BUNDLE_IDENTIFIER\" = \"com.atlassian.JitsiMeet.ios\"; then\n ENTITLEMENTS_PLIST=\"$PROJECT_DIR/app.entitlements\"\n \n /usr/libexec/PlistBuddy -c \"Add :com.apple.developer.avfoundation.multitasking-camera-access bool 1\" $ENTITLEMENTS_PLIST\nfi\n";
};
58E2CB346F2C2A873294F481 /* [CP] Check Pods Manifest.lock */ = {
69BC5020DBE393B56BD76636 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -742,10 +642,8 @@
files = (
0B412F1F1EDEE6E800B1A0A6 /* ViewController.m in Sources */,
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
4EBB458E2902E85B00855769 /* WidgetKitHelper.swift in Sources */,
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
4EBB458A28FFFD4100855769 /* RoutesHandler.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -761,19 +659,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
4ECA496028FD590000085365 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4ECA496B28FD590000085365 /* Widgets.swift in Sources */,
4E6920B828FD84D700645D9E /* DarwinNotificationsObserver.swift in Sources */,
4E46D954290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift in Sources */,
4EA73DA3290C1D6C00A16FF8 /* MeetingState.swift in Sources */,
4E6A3E17291024B900E6B0B5 /* Provider.swift in Sources */,
4E46D952290FF39E00761DEF /* LockScreenMuteAudioWidget.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -792,11 +677,6 @@
target = 4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */;
targetProxy = 4EB06029260E026600F524C5 /* PBXContainerItemProxy */;
};
4ECA497028FD590000085365 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4ECA496328FD590000085365 /* WidgetsExtension */;
targetProxy = 4ECA496F28FD590000085365 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -965,12 +845,11 @@
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7052390E12D7319D36D8E4CA /* Pods-JitsiMeet.debug.xcconfig */;
baseConfigurationReference = 756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDebug;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -992,20 +871,16 @@
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet;
PRODUCT_NAME = "jitsi-meet";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "src/JitsiMeet-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8CAA3C5A38E868335D1C1EC1 /* Pods-JitsiMeet.release.xcconfig */;
baseConfigurationReference = 3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRelease;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -1026,8 +901,6 @@
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet;
PRODUCT_NAME = "jitsi-meet";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "src/JitsiMeet-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -1100,91 +973,6 @@
};
name = Release;
};
4ECA497228FD590100085365 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = WidgetsExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Widgets Extension/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = Widgets;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Facebook. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.widgets.extension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
4ECA497328FD590100085365 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = WidgetsExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Widgets Extension/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = Widgets;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Facebook. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.widgets.extension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1338,15 +1126,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4ECA497428FD590100085365 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4ECA497228FD590100085365 /* Debug */,
4ECA497328FD590100085365 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4ECA496328FD590000085365"
BuildableName = "WidgetsExtension.appex"
BlueprintName = "WidgetsExtension"
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "jitsi-meet.app"
BlueprintName = "JitsiMeet"
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.springboard">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4ECA496328FD590000085365"
BuildableName = "WidgetsExtension.appex"
BlueprintName = "WidgetsExtension"
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "jitsi-meet.app"
BlueprintName = "JitsiMeet"
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = "LockScreenLeaveMeetingWidget"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetDefaultView"
value = "timeline"
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "medium"
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "jitsi-meet.app"
BlueprintName = "JitsiMeet"
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -17,7 +17,6 @@
#import "AppDelegate.h"
#import "FIRUtilities.h"
#import "RoutesHandler.h"
#import "Types.h"
#import "ViewController.h"
@@ -70,7 +69,7 @@
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
// 1. Attempt to handle Universal Links through Firebase in order to support
// its Dynamic Links (which we utilize for the purposes of deferred deep
@@ -108,10 +107,6 @@
if ([[url absoluteString] containsString:@"google/link/?dismiss=1&is_weak_match=1"]) {
return NO;
}
if ([[RoutesHandler sharedInstance] routeURL:url]) {
return YES;
}
NSURL *openUrl = url;

View File

@@ -1,6 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
"version" : 1,
"author" : "xcode"
}
}
}

View File

@@ -1,4 +0,0 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

View File

@@ -1,27 +0,0 @@
//
// RoutesHandler.h
// JitsiMeet
//
// Created by Alex Bumbu on 19.10.2022.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol RouteObserving <NSObject>
@property (nonatomic, readonly) void (^didRouteCallback)(NSString *route);
@end
@interface RoutesHandler : NSObject
+ (instancetype)sharedInstance;
- (void)registerObserver:(id<RouteObserving>)observer forRoute:(NSString *)route;
- (void)unregisterObserver:(id<RouteObserving>)observer;
- (BOOL)routeURL:(NSURL *)url;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,123 +0,0 @@
//
// RoutesHandler.m
// JitsiMeet
//
// Created by Alex Bumbu on 19.10.2022.
//
#import "RoutesHandler.h"
@protocol Routing <NSObject>
@property (nonatomic, readonly) NSString *route;
@property (nonatomic, readonly) id<RouteObserving> observer;
@end
@interface Route: NSObject <Routing>
@property (nonatomic, readonly) NSString *route;
@property (nonatomic, readonly, weak) id<RouteObserving> observer;
+ (instancetype)routeWithString:(nonnull NSString *)route observer:(id<RouteObserving>)observer;
@end
#pragma mark -
@implementation RoutesHandler {
NSMutableArray *routes;
}
+ (instancetype)sharedInstance {
static RoutesHandler *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
routes = [[NSMutableArray alloc] init];
}
return self;
}
- (void)registerObserver:(id<RouteObserving>)observer forRoute:(NSString *)route {
[routes addObject:[Route routeWithString:route observer:observer]];
}
- (void)unregisterObserver:(id<RouteObserving>)observer {
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<Routing> _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return evaluatedObject.observer == nil || evaluatedObject.observer == observer;
}];
NSArray *routesToClear = [routes filteredArrayUsingPredicate:predicate];
[routes removeObjectsInArray:routesToClear];
}
- (BOOL)routeURL:(NSURL *)url {
[self clearRoutes];
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:false];
if (!components) {
return false;
}
NSString *route = components.path;
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<Routing> _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return [evaluatedObject.route isEqualToString:route];
}];
NSArray *routesToHandle = [routes filteredArrayUsingPredicate:predicate];
if ([routesToHandle count] == 0) {
return false;
}
for (id<Routing> route in routesToHandle) {
route.observer.didRouteCallback(route.route);
}
return true;
}
- (void)clearRoutes {
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<Routing> _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return evaluatedObject.observer == nil;
}];
NSArray *routesToClear = [routes filteredArrayUsingPredicate:predicate];
[routes removeObjectsInArray:routesToClear];
}
@end
#pragma mark -
@interface Route()
@property (nonatomic, nonnull, copy) NSString *route;
@property (nonatomic, weak) id<RouteObserving> observer;
@end
@implementation Route
+ (instancetype)routeWithString:(nonnull NSString *)route observer:(nonnull id<RouteObserving>)observer {
return [[Route alloc] initWithString:route observer:observer];
}
- (instancetype)initWithString:(nonnull NSString *)route observer:(nonnull id<RouteObserving>)observer {
self = [super init];
if (self) {
self.route = route;
self.observer = observer;
}
return self;
}
@end

View File

@@ -21,16 +21,8 @@
@import JitsiMeetSDK;
#import "Types.h"
#import "RoutesHandler.h"
#import "ViewController.h"
#import "jitsi_meet-Swift.h"
@interface ViewController() <RouteObserving>
@property (nonatomic, nonnull, copy) void (^didRouteCallback)(NSString *);
@property (nonatomic, assign) BOOL audioMuted;
@end
@implementation ViewController
@@ -41,8 +33,6 @@
view.delegate = self;
[view join:[[JitsiMeet sharedInstance] getInitialConferenceOptions]];
[self registerRouteObserver];
}
// JitsiMeetViewDelegate
@@ -63,10 +53,6 @@
- (void)conferenceJoined:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_JOINED" withData:data];
self.audioMuted = [[data objectForKey:@"isAudioMuted"] boolValue];
[self refreshWidgetState:self.audioMuted];
// Register a NSUserActivity for this conference so it can be invoked as a
// Siri shortcut.
@@ -96,12 +82,6 @@
- (void)conferenceTerminated:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_TERMINATED" withData:data];
NSURL *sharedContainer = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.org.jitsi.meet.appgroup"];
NSURL *widgetStateFileURL = [sharedContainer URLByAppendingPathComponent:@"widgetState"];
[[NSFileManager defaultManager] removeItemAtURL:widgetStateFileURL error:nil];
[WidgetKitHelper reloadAllWidgets];
}
- (void)conferenceWillJoin:(NSDictionary *)data {
@@ -127,16 +107,7 @@
}
- (void)audioMutedChanged:(NSDictionary *)data {
NSLog(@"%@%@", @"Audio muted changed: ", data[@"muted"]);
// CFNotificationCenterRef notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
// CFNotificationCenterPostNotification(notificationCenter,
// (__bridge CFStringRef)@"iOS_MeetingMutedChanged",
// NULL,
// NULL,
// true);
self.audioMuted = [[data objectForKey:@"muted"] boolValue];
[self refreshWidgetState:self.audioMuted];
NSLog(@"%@%@", @"Audio muted changed: ", data[@"muted"]);
}
- (void)endpointTextMessageReceived:(NSDictionary *)data {
@@ -161,40 +132,9 @@
#pragma mark - Helpers
- (void)registerRouteObserver {
__weak typeof(self) weakSelf = self;
__weak JitsiMeetView *view = (JitsiMeetView *)self.view;
self.didRouteCallback = ^(NSString *route) {
if ([route isEqual:@"meet/toggleAudioMute"]) {
weakSelf.audioMuted = !weakSelf.audioMuted;
[view setAudioMuted:weakSelf.audioMuted];
} else if ([route isEqualToString:@"meet/leaveMeeting"]) {
[weakSelf terminate];
}
};
[[RoutesHandler sharedInstance] registerObserver:self forRoute:@"meet/toggleAudioMute"];
[[RoutesHandler sharedInstance] registerObserver:self forRoute:@"meet/leaveMeeting"];
}
- (void)terminate {
JitsiMeetView *view = (JitsiMeetView *) self.view;
[view leave];
}
- (void)refreshWidgetState:(BOOL)audioMuted {
// let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)
// return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
NSURL *sharedContainer = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.org.jitsi.meet.appgroup"];
NSURL *widgetStateFileURL = [sharedContainer URLByAppendingPathComponent:@"widgetState"];
NSDictionary *meetingState = @{@"audioMuted": @(audioMuted)};
if (![meetingState writeToURL:widgetStateFileURL atomically:true]) {
NSLog(@"error saving state file");
}
[WidgetKitHelper reloadAllWidgets];
}
@end

View File

@@ -1,20 +0,0 @@
//
// WidgetKitHelper.swift
// JitsiMeet
//
// Created by Alex Bumbu on 21.10.2022.
//
import WidgetKit
@available(iOS 14.0, *)
@objcMembers final class WidgetKitHelper: NSObject {
class func reloadAllWidgets(){
#if arch(arm64) || arch(i386) || arch(x86_64)
WidgetCenter.shared.reloadAllTimelines()
#endif
}
}

View File

@@ -82,7 +82,7 @@ platform :ios do
export_xcargs: "-allowProvisioningUpdates"
)
# Upload the build to TestFlight
# Upload the build to TestFlight (but don't distribute it)
upload_to_testflight(
beta_app_description: ENV["JITSI_CHANGELOG"],
beta_app_feedback_email: ENV["JITSI_REVIEW_EMAIL"],
@@ -102,10 +102,24 @@ platform :ios do
uses_non_exempt_encryption: false
)
upload_symbols_to_crashlytics
# Cleanup
clean_build_artifacts
reset_git_repo(skip_clean: true)
end
lane :refresh_dsyms do
# Connect to Apple Store Connect
app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
issuer_id: ENV["ASC_ISSUER_ID"],
key_content: ENV["ASC_KEY_CONTENT"],
duration: 1200,
in_house: false
)
# Upload dSYMs to Crashlytics
download_dsyms(min_version: ENV["DSYMS_MIN_VERSION"]) # Download dSYM files from iTC
upload_symbols_to_crashlytics # Upload them to Crashlytics
clean_build_artifacts # Delete the local dSYM files
end
end

View File

@@ -35,12 +35,14 @@
4E0EF63C28CA4069005D1B03 /* JMCallKitProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E0EF63828CA4069005D1B03 /* JMCallKitProxy.m */; };
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */; };
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */; };
4E812F3328F9A4290087ACC9 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E812F3228F9A4290087ACC9 /* JitsiMeetView+Private.h */; };
4E812F3428F9A4290087ACC9 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E812F3228F9A4290087ACC9 /* JitsiMeetView+Private.h */; };
4E812F3928F9A4ED0087ACC9 /* JitsiMeetView+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E812F3628F9A4ED0087ACC9 /* JitsiMeetView+Private.m */; };
4E812F3A28F9A4ED0087ACC9 /* JitsiMeetView+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E812F3628F9A4ED0087ACC9 /* JitsiMeetView+Private.m */; };
4EBA6E61286072E300B31882 /* JitsiMeetViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */; };
4EBA6E62286072E300B31882 /* JitsiMeetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */; };
4EBA6E652860B1E800B31882 /* JitsiMeetRenderingView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */; };
4EBA6E662860B1E800B31882 /* JitsiMeetRenderingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */; };
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */; };
4EEC9630286C73A2008705FA /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */; };
4EEC9631286C73A2008705FA /* JitsiMeetView+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */; };
6F08DF7D4458EE3CF3F36F6D /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
9A8EE79C77C17743BB66E8BD /* libPods-JitsiMeetSDKLite.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B46497DE9A95BDF5E312AF48 /* libPods-JitsiMeetSDKLite.a */; };
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
@@ -68,16 +70,20 @@
DE9A0134289A9A9A00E41CBB /* JitsiMeetSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = DEA9F283258A5D9900D4CD74 /* JitsiMeetSDK.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE9A0135289A9A9A00E41CBB /* ScheenshareEventEmiter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */; };
DE9A0136289A9A9A00E41CBB /* JitsiMeetBaseLogHandler+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */; };
DE9A0137289A9A9A00E41CBB /* JitsiMeetRenderingView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */; };
DE9A0138289A9A9A00E41CBB /* JitsiMeetViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE9A0139289A9A9A00E41CBB /* JitsiAudioSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE9A013A289A9A9A00E41CBB /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */; };
DE9A013B289A9A9A00E41CBB /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE9A013C289A9A9A00E41CBB /* JitsiMeetLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = DE81A2D22316AC4D00AE1940 /* JitsiMeetLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE9A013D289A9A9A00E41CBB /* LogUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AAC92317FFCD00290BEC /* LogUtils.h */; };
DE9A013E289A9A9A00E41CBB /* JitsiMeetViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */; };
DE9A013F289A9A9A00E41CBB /* JitsiMeetConferenceOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE9A0140289A9A9A00E41CBB /* ExternalAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */; };
DE9A0141289A9A9A00E41CBB /* InfoPlistUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE9A0143289A9A9A00E41CBB /* CallKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */; };
DE9A0144289A9A9A00E41CBB /* JitsiMeetBaseLogHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = DE81A2DD2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m */; };
DE9A0145289A9A9A00E41CBB /* JitsiMeetRenderingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */; };
DE9A0146289A9A9A00E41CBB /* JitsiAudioSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */; };
DE9A0147289A9A9A00E41CBB /* AppInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BB9AD7C1F60356D001C08DB /* AppInfo.m */; };
DE9A0148289A9A9A00E41CBB /* LogBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = DE81A2D72316AC7600AE1940 /* LogBridge.m */; };
@@ -93,8 +99,10 @@
DE9A0152289A9A9A00E41CBB /* AudioMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495C1EC4B6C600B793EE /* AudioMode.m */; };
DE9A0153289A9A9A00E41CBB /* Proximity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495E1EC4B6C600B793EE /* Proximity.m */; };
DE9A0155289A9A9A00E41CBB /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
DE9A0156289A9A9A00E41CBB /* JitsiMeetView+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */; };
DE9A0157289A9A9A00E41CBB /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
DE9A0158289A9A9A00E41CBB /* ScheenshareEventEmiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */; };
DE9A015A289A9A9A00E41CBB /* JitsiMeetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */; };
DE9A015C289A9A9A00E41CBB /* JitsiMeetLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = DE81A2D32316AC4D00AE1940 /* JitsiMeetLogger.m */; };
DE9A015E289A9A9A00E41CBB /* JitsiMeetView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */; };
DE9A015F289A9A9A00E41CBB /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
@@ -140,11 +148,15 @@
4E0EF63828CA4069005D1B03 /* JMCallKitProxy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JMCallKitProxy.m; sourceTree = "<group>"; };
4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScheenshareEventEmiter.h; sourceTree = "<group>"; };
4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScheenshareEventEmiter.m; sourceTree = "<group>"; };
4E812F3228F9A4290087ACC9 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
4E812F3628F9A4ED0087ACC9 /* JitsiMeetView+Private.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "JitsiMeetView+Private.m"; sourceTree = "<group>"; };
4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetViewController.h; sourceTree = "<group>"; };
4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetViewController.m; sourceTree = "<group>"; };
4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetRenderingView.h; sourceTree = "<group>"; };
4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetRenderingView.m; sourceTree = "<group>"; };
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiAudioSession.h; sourceTree = "<group>"; };
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiAudioSession.m; sourceTree = "<group>"; };
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiAudioSession+Private.h"; sourceTree = "<group>"; };
4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "JitsiMeetView+Private.m"; sourceTree = "<group>"; };
86389F55993FAAF6AEB3FA3E /* Pods-JitsiMeetSDKLite.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.release.xcconfig"; sourceTree = "<group>"; };
891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDK.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK.release.xcconfig"; sourceTree = "<group>"; };
8F48C340DE0D91D1012976C5 /* Pods-JitsiMeetSDKLite.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.debug.xcconfig"; sourceTree = "<group>"; };
@@ -269,10 +281,14 @@
DE81A2DC2317ED5400AE1940 /* JitsiMeetBaseLogHandler.h */,
DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */,
DE81A2DD2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m */,
4E812F3228F9A4290087ACC9 /* JitsiMeetView+Private.h */,
4E812F3628F9A4ED0087ACC9 /* JitsiMeetView+Private.m */,
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */,
4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */,
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */,
4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */,
4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */,
4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */,
DE81A2D72316AC7600AE1940 /* LogBridge.m */,
DE65AAC92317FFCD00290BEC /* LogUtils.h */,
DEAFA777229EAD3B0033A7FA /* RNRootView.h */,
@@ -367,18 +383,20 @@
DEA9F284258A5D9900D4CD74 /* JitsiMeetSDK.h in Headers */,
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */,
DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */,
4EBA6E652860B1E800B31882 /* JitsiMeetRenderingView.h in Headers */,
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
4E0EF63528CA317E005D1B03 /* JMCallKitListener.h in Headers */,
4E0EF63028CA2FB3005D1B03 /* JMCallKitEmitter.h in Headers */,
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */,
4E0EF63928CA4069005D1B03 /* JMCallKitProxy.h in Headers */,
4EEC9630286C73A2008705FA /* JitsiMeetView+Private.h in Headers */,
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
DE81A2D42316AC4D00AE1940 /* JitsiMeetLogger.h in Headers */,
DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */,
4EBA6E61286072E300B31882 /* JitsiMeetViewController.h in Headers */,
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */,
C81E9AB925AC5AD800B134D9 /* ExternalAPI.h in Headers */,
C8AFD27F2462C613000293D2 /* InfoPlistUtil.h in Headers */,
4E812F3328F9A4290087ACC9 /* JitsiMeetView+Private.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -393,18 +411,20 @@
DE9A0134289A9A9A00E41CBB /* JitsiMeetSDK.h in Headers */,
DE9A0135289A9A9A00E41CBB /* ScheenshareEventEmiter.h in Headers */,
DE9A0136289A9A9A00E41CBB /* JitsiMeetBaseLogHandler+Private.h in Headers */,
DE9A0137289A9A9A00E41CBB /* JitsiMeetRenderingView.h in Headers */,
DE9A0138289A9A9A00E41CBB /* JitsiMeetViewDelegate.h in Headers */,
4E0EF63628CA317E005D1B03 /* JMCallKitListener.h in Headers */,
4E0EF63128CA2FB3005D1B03 /* JMCallKitEmitter.h in Headers */,
DE9A0139289A9A9A00E41CBB /* JitsiAudioSession.h in Headers */,
4E0EF63A28CA4069005D1B03 /* JMCallKitProxy.h in Headers */,
DE9A013A289A9A9A00E41CBB /* JitsiMeetView+Private.h in Headers */,
DE9A013B289A9A9A00E41CBB /* JitsiMeet.h in Headers */,
DE9A013C289A9A9A00E41CBB /* JitsiMeetLogger.h in Headers */,
DE9A013D289A9A9A00E41CBB /* LogUtils.h in Headers */,
DE9A013E289A9A9A00E41CBB /* JitsiMeetViewController.h in Headers */,
DE9A013F289A9A9A00E41CBB /* JitsiMeetConferenceOptions.h in Headers */,
DE9A0140289A9A9A00E41CBB /* ExternalAPI.h in Headers */,
DE9A0141289A9A9A00E41CBB /* InfoPlistUtil.h in Headers */,
4E812F3428F9A4290087ACC9 /* JitsiMeetView+Private.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -666,8 +686,8 @@
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
DE81A2DF2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m in Sources */,
4E0EF63B28CA4069005D1B03 /* JMCallKitProxy.m in Sources */,
4EBA6E662860B1E800B31882 /* JitsiMeetRenderingView.m in Sources */,
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */,
4E812F3928F9A4ED0087ACC9 /* JitsiMeetView+Private.m in Sources */,
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
DE81A2D92316AC7600AE1940 /* LogBridge.m in Sources */,
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */,
@@ -683,9 +703,11 @@
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
4E0EF63228CA2FB3005D1B03 /* JMCallKitEmitter.m in Sources */,
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */,
4EEC9631286C73A2008705FA /* JitsiMeetView+Private.m in Sources */,
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */,
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,
4EBA6E62286072E300B31882 /* JitsiMeetViewController.m in Sources */,
DE81A2D52316AC4D00AE1940 /* JitsiMeetLogger.m in Sources */,
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */,
@@ -699,6 +721,7 @@
files = (
DE9A0143289A9A9A00E41CBB /* CallKit.m in Sources */,
DE9A0144289A9A9A00E41CBB /* JitsiMeetBaseLogHandler.m in Sources */,
DE9A0145289A9A9A00E41CBB /* JitsiMeetRenderingView.m in Sources */,
DE9A0146289A9A9A00E41CBB /* JitsiAudioSession.m in Sources */,
4E0EF63C28CA4069005D1B03 /* JMCallKitProxy.m in Sources */,
DE9A0147289A9A9A00E41CBB /* AppInfo.m in Sources */,
@@ -714,10 +737,11 @@
DE9A0151289A9A9A00E41CBB /* LocaleDetector.m in Sources */,
DE9A0152289A9A9A00E41CBB /* AudioMode.m in Sources */,
DE9A0153289A9A9A00E41CBB /* Proximity.m in Sources */,
4E812F3A28F9A4ED0087ACC9 /* JitsiMeetView+Private.m in Sources */,
DE9A0155289A9A9A00E41CBB /* ReactUtils.m in Sources */,
DE9A0156289A9A9A00E41CBB /* JitsiMeetView+Private.m in Sources */,
DE9A0157289A9A9A00E41CBB /* DragGestureController.swift in Sources */,
DE9A0158289A9A9A00E41CBB /* ScheenshareEventEmiter.m in Sources */,
DE9A015A289A9A9A00E41CBB /* JitsiMeetViewController.m in Sources */,
DE9A015C289A9A9A00E41CBB /* JitsiMeetLogger.m in Sources */,
4E0EF63328CA2FB3005D1B03 /* JMCallKitEmitter.m in Sources */,
DE9A015E289A9A9A00E41CBB /* JitsiMeetView.m in Sources */,

View File

@@ -57,8 +57,8 @@
openURL:(NSURL *_Nonnull)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *_Nonnull)options;
- (UIInterfaceOrientationMask)application:(UIApplication *_Nonnull)application
supportedInterfaceOrientationsForWindow:(UIWindow *_Nonnull)window;
- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window;
#pragma mark - Utility methods

View File

@@ -0,0 +1,30 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* 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 <UIKit/UIKit.h>
#import "JitsiMeetViewDelegate.h"
NS_ASSUME_NONNULL_BEGIN
@interface JitsiMeetRenderingView : UIView
@property (nonatomic, assign) BOOL isPiPEnabled;
- (void)setProps:(NSDictionary *_Nonnull)newProps;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,83 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* 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.
*/
#include <mach/mach_time.h>
#import "JitsiMeetRenderingView.h"
#import "ReactUtils.h"
#import "RNRootView.h"
#import "JitsiMeet+Private.h"
/**
* Backwards compatibility: turn the boolean prop into a feature flag.
*/
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
@interface JitsiMeetRenderingView ()
@end
@implementation JitsiMeetRenderingView {
/**
* React Native view where the entire content will be rendered.
*/
RNRootView *rootView;
}
/**
* Passes the given props to the React Native application. The props which we pass
* are a combination of 3 different sources:
*
* - JitsiMeet.defaultConferenceOptions
* - This function's parameters
* - Some extras which are added by this function
*/
- (void)setProps:(NSDictionary *_Nonnull)newProps {
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
// Set the PiP flag if it wasn't manually set.
NSMutableDictionary *featureFlags = props[@"flags"];
// TODO: temporary implementation
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
featureFlags[PiPEnabledFeatureFlag] = @(self.isPiPEnabled);
}
// This method is supposed to be imperative i.e. a second
// invocation with one and the same URL is expected to join the respective
// conference again if the first invocation was followed by leaving the
// conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one and
// the same URL will not trigger an automatic re-render in the JavaScript
// source code. The workaround implemented below introduces imperativeness
// in React Component props by defining a unique value per invocation.
props[@"timestamp"] = @(mach_absolute_time());
if (rootView) {
// Update props with the new URL.
rootView.appProperties = props;
} else {
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
rootView = [[RNRootView alloc] initWithBridge:bridge
moduleName:@"App"
initialProperties:props];
rootView.backgroundColor = self.backgroundColor;
// Add rootView as a subview which completely covers this one.
[rootView setFrame:[self bounds]];
rootView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addSubview:rootView];
}
}
@end

View File

@@ -20,23 +20,22 @@
#import "ExternalAPI.h"
#import "JitsiMeet+Private.h"
#import "JitsiMeetConferenceOptions+Private.h"
#import "JitsiMeetView+Private.h"
#import "JitsiMeetView.h"
#import "JitsiMeetViewController.h"
#import "ReactUtils.h"
#import "RNRootView.h"
@interface JitsiMeetView ()
/**
* Backwards compatibility: turn the boolean prop into a feature flag.
*/
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
@property (nonatomic, strong) JitsiMeetViewController *jitsiMeetViewController;
@property (nonatomic, strong) UINavigationController *navController;
@property (nonatomic, readonly) BOOL isPiPEnabled;
@end
@implementation JitsiMeetView {
/**
* React Native view where the entire content will be rendered.
*/
RNRootView *rootView;
}
@implementation JitsiMeetView
@dynamic isPiPEnabled;
#pragma mark Initializers
@@ -67,6 +66,10 @@ static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
/**
* Internal initialization:
*
@@ -74,72 +77,57 @@ static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
* - initializes the external API scope
*/
- (void)initWithXXX {
// Set a background color which is in accord with the JavaScript and Android
// parts of the application and causes less perceived visual flicker than
// the default background color.
self.backgroundColor
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
self.jitsiMeetViewController = [[JitsiMeetViewController alloc] init];
self.jitsiMeetViewController.view.frame = [self bounds];
[self addSubview:self.jitsiMeetViewController.view];
[self registerObservers];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark API
- (void)join:(JitsiMeetConferenceOptions *)options {
[self setProps:options == nil ? @{} : [options asProps]];
[self.jitsiMeetViewController join:options withPiP:self.isPiPEnabled];
}
- (void)leave {
[self setProps:@{}];
[self.jitsiMeetViewController leave];
}
- (void)hangUp {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendHangUp];
[self.jitsiMeetViewController hangUp];
}
- (void)setAudioMuted:(BOOL)muted {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetAudioMuted:muted];
[self.jitsiMeetViewController setAudioMuted:muted];
}
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendEndpointTextMessage:message :to];
[self.jitsiMeetViewController sendEndpointTextMessage:message :to];
}
- (void)toggleScreenShare:(BOOL)enabled {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI toggleScreenShare:enabled];
[self.jitsiMeetViewController toggleScreenShare:enabled];
}
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI retrieveParticipantsInfo:completionHandler];
[self.jitsiMeetViewController retrieveParticipantsInfo:completionHandler];
}
- (void)openChat:(NSString*)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI openChat:to];
[self.jitsiMeetViewController openChat:to];
}
- (void)closeChat {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI closeChat];
[self.jitsiMeetViewController closeChat];
}
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendChatMessage:message :to];
[self.jitsiMeetViewController sendChatMessage:message :to];
}
- (void)setVideoMuted:(BOOL)muted {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetVideoMuted:muted];
[self.jitsiMeetViewController setVideoMuted:muted];
}
- (void)setClosedCaptionsEnabled:(BOOL)enabled {
@@ -147,97 +135,47 @@ static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
[externalAPI sendSetClosedCaptionsEnabled:enabled];
}
#pragma mark Private methods
#pragma mark Private
- (BOOL)isPiPEnabled {
return self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)];
}
- (void)registerObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleUpdateViewPropsNotification:) name:updateViewPropsNotificationName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleSendEventNotification:) name:sendEventNotificationName object:nil];
}
- (void)handleUpdateViewPropsNotification:(NSNotification *)notification {
NSDictionary *props = [notification.userInfo objectForKey:@"props"];
[self setProps:props];
}
- (void)handleSendEventNotification:(NSNotification *)notification {
NSString *eventName = notification.userInfo[@"name"];
NSString *eventData = notification.userInfo[@"data"];
SEL sel = NSSelectorFromString([self methodNameFromEventName:eventName]);
if (sel && [self.delegate respondsToSelector:sel]) {
[self.delegate performSelector:sel withObject:eventData];
}
}
/**
* Converts a specific event name i.e. redux action type description to a
* method name.
*
* @param eventName The event name to convert to a method name.
* @return A method name constructed from the specified `eventName`.
*/
- (NSString *)methodNameFromEventName:(NSString *)eventName {
NSMutableString *methodName
= [NSMutableString stringWithCapacity:eventName.length];
for (NSString *c in [eventName componentsSeparatedByString:@"_"]) {
if (c.length) {
[methodName appendString:
methodName.length ? c.capitalizedString : c.lowercaseString];
}
}
[methodName appendString:@":"];
return methodName;
}
/**
* Passes the given props to the React Native application. The props which we pass
* are a combination of 3 different sources:
* Converts a specific event name i.e. redux action type description to a
* method name.
*
* - JitsiMeet.defaultConferenceOptions
* - This function's parameters
* - Some extras which are added by this function
* @param eventName The event name to convert to a method name.
* @return A method name constructed from the specified `eventName`.
*/
- (void)setProps:(NSDictionary *_Nonnull)newProps {
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
- (NSString *)methodNameFromEventName:(NSString *)eventName {
NSMutableString *methodName
= [NSMutableString stringWithCapacity:eventName.length];
// Set the PiP flag if it wasn't manually set.
NSMutableDictionary *featureFlags = props[@"flags"];
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
featureFlags[PiPEnabledFeatureFlag]
= [NSNumber numberWithBool:
self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]];
}
for (NSString *c in [eventName componentsSeparatedByString:@"_"]) {
if (c.length) {
[methodName appendString:
methodName.length ? c.capitalizedString : c.lowercaseString];
}
}
[methodName appendString:@":"];
// This method is supposed to be imperative i.e. a second
// invocation with one and the same URL is expected to join the respective
// conference again if the first invocation was followed by leaving the
// conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one and
// the same URL will not trigger an automatic re-render in the JavaScript
// source code. The workaround implemented below introduces imperativeness
// in React Component props by defining a unique value per invocation.
props[@"timestamp"] = @(mach_absolute_time());
if (rootView) {
// Update props with the new URL.
rootView.appProperties = props;
} else {
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
rootView
= [[RNRootView alloc] initWithBridge:bridge
moduleName:@"App"
initialProperties:props];
rootView.backgroundColor = self.backgroundColor;
// Add rootView as a subview which completely covers this one.
[rootView setFrame:[self bounds]];
rootView.autoresizingMask
= UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight;
[self addSubview:rootView];
}
return methodName;
}
@end

View File

@@ -0,0 +1,38 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* 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 <UIKit/UIKit.h>
#import "JitsiMeetConferenceOptions.h"
NS_ASSUME_NONNULL_BEGIN
@interface JitsiMeetViewController : UIViewController
- (void)join:(JitsiMeetConferenceOptions *)options withPiP:(BOOL)enablePiP;
- (void)leave;
- (void)hangUp;
- (void)setAudioMuted:(BOOL)muted;
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
- (void)toggleScreenShare:(BOOL)enabled;
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler;
- (void)openChat:(NSString*)to;
- (void)closeChat;
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
- (void)setVideoMuted:(BOOL)muted;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,127 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* 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 "JitsiMeetViewController.h"
#import "JitsiMeet+Private.h"
#import "JitsiMeetConferenceOptions+Private.h"
#import "JitsiMeetRenderingView.h"
#import "JitsiMeetView+Private.h"
@interface JitsiMeetViewController ()
@property (strong, nonatomic) JitsiMeetRenderingView *view;
@end
@implementation JitsiMeetViewController
@dynamic view;
- (instancetype)init {
self = [super init];
if (self) {
[self registerObservers];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)loadView {
[super loadView];
self.view = [[JitsiMeetRenderingView alloc] init];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Set a background color which is in accord with the JavaScript and Android
// parts of the application and causes less perceived visual flicker than
// the default background color.
self.view.backgroundColor = [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
}
- (void)join:(JitsiMeetConferenceOptions *)options withPiP:(BOOL)enablePiP {
self.view.isPiPEnabled = enablePiP;
[self.view setProps:options == nil ? @{} : [options asProps]];
}
- (void)leave {
[self.view setProps:@{}];
}
- (void)hangUp {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendHangUp];
}
- (void)setAudioMuted:(BOOL)muted {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetAudioMuted:muted];
}
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendEndpointTextMessage:message :to];
}
- (void)toggleScreenShare:(BOOL)enabled {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI toggleScreenShare:enabled];
}
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI retrieveParticipantsInfo:completionHandler];
}
- (void)openChat:(NSString*)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI openChat:to];
}
- (void)closeChat {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI closeChat];
}
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendChatMessage:message :to];
}
- (void)setVideoMuted:(BOOL)muted {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetVideoMuted:muted];
}
#pragma mark Private
- (void)registerObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleUpdateViewPropsNotification:) name:updateViewPropsNotificationName object:nil];
}
- (void)handleUpdateViewPropsNotification:(NSNotification *)notification {
NSDictionary *props = [notification.userInfo objectForKey:@"props"];
[self.view setProps:props];
}
@end

View File

@@ -253,7 +253,7 @@ static CXProviderConfiguration *_providerConfiguration = nil;
// XXX keep track of muted actions to avoid "ping-pong"ing. See
// JMCallKitEmitter for details on the CXSetMutedCallAction handling.
for (CXAction *action in transaction.actions) {
if ([action isKindOfClass:[CXSetMutedCallAction class]]) {
if ([CXAction isKindOfClass:[CXSetMutedCallAction class]]) {
[self.emitter addMuteAction:action.UUID];
}
}

27
lang/languages-af.json Normal file
View File

@@ -0,0 +1,27 @@
{
"af": "",
"bg": "Bulgaars",
"cs": "Tsjeggies",
"de": "Duits",
"el": "Grieks",
"en": "Engels",
"eo": "Esperanto",
"es": "Spaans",
"fr": "Frans",
"hy": "Armeens",
"it": "Italiaans",
"ja": "Japannees",
"ko": "Koreaans",
"nb": "Bokmal-Noorweegs",
"oc": "Oksitaans",
"pl": "Pools",
"ptBR": "Portugees (Brasilië)",
"ru": "Russies",
"sk": "Slowaaks",
"sl": "Sloweens",
"sv": "Sweeds",
"tr": "Turks",
"uk": "Oekraïens",
"vi": "Viëtnamees",
"zhCN": "Sjinees (Sjina)"
}

46
lang/languages-ar.json Normal file
View File

@@ -0,0 +1,46 @@
{
"af": "الأفريكانية",
"ar": "العربية",
"bg": "البلغارية",
"ca": "الكاتالانية",
"cs": "التشيكية",
"da": "الدنماركية",
"de": "الألمانية",
"el": "اليونانية",
"en": "الإنجليزية",
"enGB": "الإنجليزية (المملكة المتحدة)",
"eo": "الإسبرانتو",
"es": "الإسبانية",
"esUS": "الإسبانية (أمريكا اللاتينية)",
"et": "الإستونية",
"eu": "الباسكية",
"fi": "الفنلندية",
"fr": "الفرنسية",
"frCA": "الفرنسية (الكندية)",
"he": "العبرية",
"hr": "الكرواتية",
"hu": "الهنغارية",
"hy": "الأرمنية",
"id": "الإندونيسية",
"it": "الإيطالية",
"ja": "اليابانية",
"kab": "قَبَلي",
"ko": "الكورية",
"lt": "الليتوانية",
"mr": "الماراثى",
"nl": "الهولندية",
"oc": "القسطانية",
"pl": "البولندية",
"ptBR": "البرتغالية (البرازيل)",
"ro": "الرومانية",
"ru": "الروسية",
"sc": "السردينية",
"sk": "السلوفاكية",
"sl": "السلوفينية",
"sv": "السويدية",
"tr": "التركية",
"uk": "الأوكرانية",
"vi": "الفيتنامية",
"zhCN": "الصينية (الصين)",
"zhTW": "الصينية (تايوان)"
}

35
lang/languages-be.json Normal file
View File

@@ -0,0 +1,35 @@
{
"af": "Афрыкаанс",
"bg": "Балгарская",
"ca": "Каталанская",
"cs": "Чэшская",
"da": "Дацкая",
"de": "Нямецкая",
"el": "Грэцкая",
"en": "Англійская",
"enGB": "Англійская (Вялікабрытанія)",
"eo": "Эсперанта",
"es": "Іспанская",
"esUS": "Іспанская (Лацінская Амерыка)",
"et": "Эстонская",
"fi": "Фінская",
"fr": "Французская",
"frCA": "Французская (канадская)",
"hr": "Харвацкая",
"hu": "Венгерская",
"hy": "Армянская",
"it": "Італьянская",
"ja": "Японская",
"ko": "Карэйская",
"nl": "Галандская",
"oc": "Аксітанская",
"pl": "Польская",
"ptBR": "Партугальская (Бразілія)",
"ru": "Расійская",
"sv": "Шведская",
"tr": "Турэцкая",
"uk": "Украінская",
"vi": "В'етнамская",
"zhCN": "Кітайская (Кітай)",
"zhTW": "Кітайская (Тайвань)"
}

33
lang/languages-bg.json Normal file
View File

@@ -0,0 +1,33 @@
{
"af": "Африканс",
"bg": "Български",
"ca": "Каталонски",
"cs": "Чешки",
"de": "Немски",
"el": "Гръцки",
"en": "Английски",
"enGB": "Английски (Великобритания)",
"eo": "Есперанто",
"es": "Испански",
"esUS": "Испански (Латинска Америка)",
"fi": "Фински",
"fr": "Френски",
"frCA": "Френски (Канада)",
"hr": "Хърватски",
"hu": "Унгарски",
"hy": "Арменски",
"it": "Италиански",
"ja": "Японски",
"ko": "Корейски",
"nl": "Нидерландски",
"oc": "Окситански",
"pl": "Полски",
"ptBR": "Португалски (Бразилия)",
"ru": "Руски",
"sv": "Шведски",
"tr": "Турски",
"uk": "Украински",
"vi": "Виетнамски",
"zhCN": "Китайски (Китай)",
"zhTW": "Тайвански"
}

54
lang/languages-ca.json Normal file
View File

@@ -0,0 +1,54 @@
{
"af": "Afrikaans",
"ar": "Àrab",
"bg": "Búlgar",
"ca": "Català",
"cs": "Txec",
"da": "Danès",
"de": "Alemany",
"el": "Grec",
"en": "Anglès",
"enGB": "Anglès (Regne Unit)",
"eo": "Esperanto",
"es": "Espanyol",
"esUS": "Espanyol (Amèrica llatina)",
"et": "Estonià",
"eu": "Basc",
"fa": "Persa",
"fi": "Finès",
"fr": "Francès",
"frCA": "Francès (Canadà)",
"he": "Hebreu",
"hi": "Hindi",
"hr": "Croat",
"hu": "Hongarès",
"hy": "Armeni",
"id": "Indonesi",
"it": "Italià",
"ja": "Japonès",
"kab": "Cabilenc",
"ko": "Coreà",
"lt": "Lituà",
"lv": "Letó",
"ml": "Malaiàlam",
"mr": "Marathi",
"nl": "Neerlandès",
"oc": "Occità",
"pl": "Polonès",
"pt": "Portuguès",
"ptBR": "Portuguès (Brasil)",
"ro": "Romanès",
"ru": "Rus",
"sc": "Sard",
"sk": "Eslovac",
"sl": "Eslovè",
"sq": "Albanès",
"sr": "Serbi",
"sv": "Suec",
"te": "Telugu",
"tr": "Turc",
"uk": "Ucraïnès",
"vi": "Vietnamita",
"zhCN": "Xinès (Xina)",
"zhTW": "Xinès (Taiwan)"
}

48
lang/languages-cs.json Normal file
View File

@@ -0,0 +1,48 @@
{
"af": "Afrikánština",
"ar": "Arabština",
"bg": "Bulharština",
"ca": "Katalánština",
"cs": "Čeština",
"da": "Dánština",
"de": "Němčina",
"el": "Řečtina",
"en": "Angličtina",
"enGB": "Angličtina (Spojené království)",
"eo": "Esperanto",
"es": "Španělština",
"esUS": "Španělština (Latinská Amerika)",
"et": "Estonština",
"eu": "Baskičtina",
"fi": "Finština",
"fr": "Francouština",
"frCA": "Francouzština (Kanada)",
"he": "Hebrejština",
"hr": "Chorvatština",
"hu": "Maďarština",
"hy": "Arménština",
"id": "Indonéština",
"it": "Italština",
"ja": "Japonština",
"kab": "Kabylština",
"ko": "Korejština",
"lt": "Litevština",
"mr": "Maráthština",
"nb": "Norština Bokmal",
"nl": "Nizozemština",
"oc": "Okcitánština",
"pl": "Polština",
"ptBR": "Portugalština (Brazílie)",
"ro": "Rumunština",
"ru": "Ruština",
"sc": "Sardinština",
"sk": "Slovenština",
"sl": "Slovinština",
"sr": "Srbština",
"sv": "Švédština",
"tr": "Turečtina",
"uk": "Ukrajinština",
"vi": "Vietnamština",
"zhCN": "Čínština (Čína)",
"zhTW": "Čínština (Taiwan)"
}

34
lang/languages-da.json Normal file
View File

@@ -0,0 +1,34 @@
{
"af": "Sydafrikansk",
"bg": "Bulgarsk",
"ca": "Katalansk",
"cs": "Tjekkisk",
"da": "Dansk",
"de": "Tysk",
"el": "Græsk",
"en": "Engelsk",
"enGB": "Engelsk (Storbritannien)",
"eo": "Esperanto",
"es": "Spansk",
"esUS": "Spansk (Latin Amerika)",
"fi": "Finsk",
"fr": "Fransk",
"frCA": "Fransk (Canada)",
"hr": "Kroatisk",
"hu": "Ungarsk",
"hy": "Armensk",
"it": "Italiensk",
"ja": "Japansk",
"ko": "Koreansk",
"nl": "Hollandsk",
"oc": "Occitansk",
"pl": "Polsk",
"ptBR": "Portugisisk (Brazilien)",
"ru": "Russisk",
"sv": "Svensk",
"tr": "Tyrkisk",
"uk": "Ukrainsk",
"vi": "Vietnamesisk",
"zhCN": "Kinesisk (China)",
"zhTW": "Kinesisk (Taiwan)"
}

40
lang/languages-de.json Normal file
View File

@@ -0,0 +1,40 @@
{
"af": "Afrikaans",
"bg": "Bulgarisch",
"ca": "Katalanisch",
"cs": "Tschechisch",
"da": "Dänisch",
"de": "Deutsch",
"el": "Griechisch",
"en": "Englisch",
"enGB": "Englisch (Vereinigtes Königreich)",
"eo": "Esperanto",
"es": "Spanisch",
"esUS": "Spanisch (Lateinamerika)",
"et": "Estnisch",
"fi": "Finnisch",
"fr": "Französisch",
"frCA": "Französisch (Kanada)",
"hr": "Kroatisch",
"hsb": "Obersorbisch",
"hu": "Ungarisch",
"hy": "Armenisch",
"it": "Italienisch",
"ja": "Japanisch",
"ko": "Koreanisch",
"nb": "Norwegisch (Bokmal)",
"nl": "Niederländisch",
"oc": "Okzitanisch",
"pl": "Polnisch",
"ptBR": "Portugiesisch (Brasilien)",
"ru": "Russisch",
"sk": "Slowakisch",
"sl": "Slowenisch",
"sr": "Serbish",
"sv": "Schwedisch",
"tr": "Türkisch",
"uk": "Ukrainisch",
"vi": "Vietnamesisch",
"zhCN": "Chinesisch (China)",
"zhTW": "Chinesisch (Taiwan)"
}

41
lang/languages-el.json Normal file
View File

@@ -0,0 +1,41 @@
{
"af": "Αφρικανικά",
"bg": "Βουλγάρικα",
"ca": "Καταλανικά",
"cs": "Τσέχικα",
"da": "Δανέζικα",
"de": "Γερμανικά",
"el": "Ελληνικά",
"en": "Αγγλικά",
"enGB": "Αγγλικά (Ηνωμένου Βασιλείου)",
"eo": "Εσπεράντο",
"es": "Ισπανικά",
"esUS": "Ισπανικά (Λατινικής Αμερικής)",
"et": "Εσθονικά",
"eu": "Βάσκικα",
"fi": "Φινλανδικά",
"fr": "Γαλλικά",
"frCA": "Γαλλικά (Καναδικά)",
"he": "Εβραϊκά",
"hr": "Κροατικά",
"hu": "Ουγγρικά",
"hy": "Αρμένικα",
"id": "Ινδονησιακά",
"it": "Ιταλικά",
"ja": "Ιαπωνικά",
"ko": "Κορεάτικα",
"lt": "Λιθουανικά",
"nl": "Ολλανδικά",
"oc": "Οξιτανικά",
"pl": "Πολωνικά",
"ptBR": "Πορτογαλικά (Βραζιλίας)",
"ru": "Ρωσικά",
"sc": "Σαρδηνικά",
"sk": "Σλοβακικά",
"sv": "Σουηδικά",
"tr": "Τουρκικά",
"uk": "Ουκρανικά",
"vi": "Βιετναμέζικα",
"zhCN": "Κινέζικα (Κίνας)",
"zhTW": "Κινέζικα (Ταϊβάν)"
}

40
lang/languages-enGB.json Normal file
View File

@@ -0,0 +1,40 @@
{
"af": "Afrikaans",
"bg": "Bulgarian",
"ca": "Catalan",
"cs": "Czech",
"da": "Danish",
"de": "German",
"el": "Greek",
"en": "English",
"enGB": "English (United Kingdom)",
"eo": "Esperanto",
"es": "Spanish",
"esUS": "Spanish (Latin America)",
"et": "Estonian",
"fi": "Finnish",
"fr": "French",
"frCA": "French (Canadian)",
"hr": "Croatian",
"hsb": "Upper Sorbian",
"hu": "Hungarian",
"hy": "Armenian",
"it": "Italian",
"ja": "Japanese",
"ko": "Korean",
"nb": "",
"nl": "Dutch",
"oc": "Occitan",
"pl": "Polish",
"ptBR": "Portuguese (Brazil)",
"ru": "Russian",
"sk": "",
"sl": "Slovenian",
"sr": "Serbian",
"sv": "Swedish",
"tr": "Turkish",
"uk": "Ukrainian",
"vi": "Vietnamese",
"zhCN": "Chinese (China)",
"zhTW": "Chinese (Taiwan)"
}

27
lang/languages-eo.json Normal file
View File

@@ -0,0 +1,27 @@
{
"af": "Afrikansa",
"bg": "Bulgara",
"cs": "Ĉeĥa",
"de": "Germana",
"el": "Greka",
"en": "Angla",
"eo": "Esperanto",
"es": "Hispana",
"fr": "Franca",
"hy": "Armena",
"it": "Itala",
"ja": "Japana",
"ko": "Korea",
"nb": "Norvega (Bukmola)",
"oc": "Okcitana",
"pl": "Pola",
"ptBR": "Portugala (Brazila)",
"ru": "Rusa",
"sk": "Slovaka",
"sl": "Slovena",
"sv": "Sveda",
"tr": "Turka",
"uk": "Ukraina",
"vi": "Vjetnama",
"zhCN": "Ĉina (Ĉinuja)"
}

37
lang/languages-es.json Normal file
View File

@@ -0,0 +1,37 @@
{
"af": "Afrikáans",
"bg": "Búlgaro",
"ca": "Catalán",
"cs": "Checo",
"da": "Danés",
"de": "Alemán",
"el": "Griego",
"en": "Inglés",
"enGB": "Inglés (Reino Unido)",
"eo": "Esperanto",
"es": "Español",
"esUS": "Español (América Latina)",
"et": "Estonio",
"fi": "Finlandés",
"fr": "Francés",
"frCA": "Francés (Canadiense)",
"he": "Hebreo",
"hr": "Croata",
"hu": "Húngaro",
"hy": "Armenio",
"it": "Italiano",
"ja": "Japonés",
"ko": "Coreano",
"nl": "Holandés",
"oc": "Occitano",
"pl": "Polaco",
"ptBR": "Portugués (Brasil)",
"ru": "Ruso",
"sk": "Eslovaco",
"sv": "Sueco",
"tr": "Turco",
"uk": "Ucraniano",
"vi": "Vietnamita",
"zhCN": "Chino (China)",
"zhTW": "Chino (Taiwán)"
}

35
lang/languages-esUS.json Normal file
View File

@@ -0,0 +1,35 @@
{
"af": "Africano",
"bg": "Búlgaro",
"ca": "Catalán",
"cs": "Checo",
"de": "Alemán",
"el": "Griego",
"en": "Inglés",
"enGB": "Inglés",
"eo": "Esperanto",
"es": "Español",
"esUS": "Español (Latinoamérica)",
"fi": "Finlandés",
"fr": "Francés",
"frCA": "Francés (Canadiense)",
"he": "Hebreo",
"hr": "Croata",
"hu": "Húngaro",
"hy": "Armenio",
"it": "Italiano",
"ja": "Japonés",
"ko": "Coreano",
"nl": "Holandés",
"oc": "Occitano",
"pl": "Polaco",
"ptBR": "Portugués (Brasil)",
"ru": "Ruso",
"sk": "Eslovaco",
"sv": "Sueco",
"tr": "Turco",
"uk": "Ucraniano",
"vi": "Vietnamita",
"zhCN": "Chino (China)",
"zhTW": "Chino (Taiwan)"
}

34
lang/languages-et.json Normal file
View File

@@ -0,0 +1,34 @@
{
"af": "Afrikaani keel",
"bg": "Bulgaaria keel",
"ca": "Katalaani keel",
"cs": "Tšehhi keel",
"de": "Saksa keel",
"el": "Kreeka keel",
"en": "Inglise keel",
"enGB": "Inglise keel (Ühendkuningriik)",
"eo": "Esperanto keel",
"es": "Hispaania keel",
"esUS": "Hispaania keel (Ladina-Ameerika)",
"et": "Eesti keel",
"fi": "Soome keel",
"fr": "Prantsuse keel",
"frCA": "Prantsuse keel (Kanada)",
"hr": "Horvaadi keel",
"hu": "Ungari keel",
"hy": "Armeenia keel",
"it": "Itaalia keel",
"ja": "Jaapani keel",
"ko": "Korea keel",
"nl": "Hollandi keel",
"oc": "Oksitaani keel",
"pl": "Poola keel",
"ptBR": "Portigali keel (Brasiilia)",
"ru": "Vene keel",
"sv": "Roosi keel",
"tr": "Türgi keel",
"uk": "Ukraina keel",
"vi": "Vietnami keel",
"zhCN": "Hiina keel (Hiina)",
"zhTW": "Hiina keel (Tai)"
}

34
lang/languages-eu.json Normal file
View File

@@ -0,0 +1,34 @@
{
"af": "Afrikaans",
"bg": "Bulgariera",
"ca": "Katalana",
"cs": "Txekiera",
"da": "Daniera",
"de": "Alemana",
"el": "Greziera",
"en": "Ingelesa",
"enGB": "Ingelesa (Erresuma Batua)",
"eo": "Esperantoa",
"es": "Gaztelania",
"esUS": "Gaztelania (Latinamerika)",
"et": "Estoniera",
"fi": "Finlandiera",
"fr": "Frantsesa",
"frCA": "Frantsesa (Kanada)",
"hr": "Kroaziera",
"hu": "Hungariera",
"hy": "Armeniera",
"it": "Italiera",
"ja": "Japoniera",
"ko": "Koreera",
"nl": "Nederlandera",
"oc": "Okzitaniera",
"pl": "Poloniera",
"ptBR": "Portugesa (Brasil)",
"ru": "Errusiera",
"sv": "Suediera",
"tr": "Turkiera",
"vi": "Vietnamera",
"zhCN": "Txinera (Txina)",
"zhTW": "Txinera (Taiwan)"
}

50
lang/languages-fa.json Normal file
View File

@@ -0,0 +1,50 @@
{
"af": "آفریقایی",
"ar": "عربی",
"bg": "بلغاری",
"ca": "کاتالانی",
"cs": "چک",
"da": "دانمارکی",
"de": "آلمانی",
"el": "یونانی",
"en": "انگلیسی",
"enGB": "انگلیسی (انگلستان)",
"eo": "اسپرانتو",
"es": "اسپانیایی",
"esUS": "اسپانیایی (آمریکا لاتین)",
"et": "استونیایی",
"eu": "باسکایی",
"fa": "فارسی",
"fi": "فنلاندی",
"fr": "فرانسوی",
"frCA": "فرانسوی (کانادا)",
"he": "عبری",
"hr": "کرواتی",
"hu": "بلغاری",
"hy": "ارمنی",
"id": "اندونزیایی",
"it": "ایتالیایی",
"ja": "ژاپنی",
"kab": "کابیلی",
"ko": "کره ای",
"lt": "لیتوانیایی",
"lv": "لتونیایی",
"ml": "مالایایی",
"mr": "مراتی",
"nl": "هلندی",
"oc": "اکسیتان(قدیمی)",
"pl": "لهستانی",
"ptBR": "پرتغالی (برزیل)",
"ro": "رومانیایی",
"ru": "روسی",
"sc": "ساردینی",
"sk": "اسلواکیایی",
"sl": "اسلوونیایی",
"sr": "صربی",
"sv": "سوئدی",
"tr": "ترکی",
"uk": "اوکراینی",
"vi": "ویتنامی",
"zhCN": "چینی",
"zhTW": "چینی (تایوان)"
}

38
lang/languages-fi.json Normal file
View File

@@ -0,0 +1,38 @@
{
"af": "afrikaans",
"bg": "bulgaria",
"ca": "katalaani",
"cs": "tšekki",
"da": "tanska",
"de": "saksa",
"el": "kreikka",
"en": "englanti",
"enGB": "englanti (Yhdistynyt kuningaskunta)",
"eo": "esperanto",
"es": "espanja",
"esUS": "espanja (Latinalainen Amerikka)",
"et": "viro",
"fi": "suomi",
"fr": "ranska",
"frCA": "ranska (Kanada)",
"hr": "kroaatti",
"hu": "unkari",
"hy": "armenia",
"it": "italia",
"ja": "japani",
"ko": "korea",
"nb": "",
"nl": "hollanti",
"oc": "oksitaani",
"pl": "puola",
"ptBR": "portugali (Brasilia)",
"ru": "venäjä",
"sk": "",
"sl": "",
"sv": "ruotsi",
"tr": "turkki",
"uk": "ukraina",
"vi": "vietnam",
"zhCN": "kiina (Kiina)",
"zhTW": "kiina (Taiwan)"
}

36
lang/languages-fr.json Normal file
View File

@@ -0,0 +1,36 @@
{
"af": "Afrikaans",
"bg": "Bulgare",
"ca": "Catalan",
"cs": "Tchèque",
"da": "Danois",
"de": "Allemand",
"el": "Grec",
"en": "Anglais",
"enGB": "Anglais (Royaume-Uni)",
"eo": "Espéranto",
"es": "Espagnol",
"esUS": "Espagnol (Amérique latine)",
"et": "Estonien",
"fi": "Finnois",
"fr": "Français",
"frCA": "Français (Canada)",
"hr": "Croate",
"hu": "Hongrois",
"hy": "Arménien",
"it": "Italien",
"ja": "Japonais",
"ko": "Coréen",
"nl": "Néerlandais",
"oc": "Occitan",
"pl": "Polonais",
"ptBR": "Portugais (Brésil)",
"ru": "Russe",
"sk": "Slovaque",
"sv": "Suédois",
"tr": "Turc",
"uk": "Ukrainien",
"vi": "Vietnamien",
"zhCN": "Chinois (Chine)",
"zhTW": "Chinois (Taiwan)"
}

36
lang/languages-frCA.json Normal file
View File

@@ -0,0 +1,36 @@
{
"af": "Africain",
"bg": "Bulgare",
"ca": "Catalan",
"cs": "Tchèque",
"da": "Danois",
"de": "Allemand",
"el": "Grec",
"en": "Anglais",
"enGB": "Anglais (Royaume-Uni)",
"eo": "Espéranto",
"es": "Espagnol",
"esUS": "Espagnol (Amérique latine)",
"et": "Estonien",
"fi": "Finnois",
"fr": "Français",
"frCA": "Français (Canadien)",
"hr": "Croate",
"hu": "Hongrois",
"hy": "Arménien",
"it": "Italien",
"ja": "Japonais",
"ko": "Coréen",
"nl": "Néerlandais",
"oc": "Occitan",
"pl": "Polonais",
"ptBR": "Portugais (Brésil)",
"ru": "Russe",
"sk": "Slovaque",
"sv": "Suédois",
"tr": "Turc",
"uk": "Ukrainien",
"vi": "Vietnamien",
"zhCN": "Chinois (Chine)",
"zhTW": "Chinois (Taiwan)"
}

49
lang/languages-gl.json Normal file
View File

@@ -0,0 +1,49 @@
{
"af": "Afrikans",
"ar": "Árabe",
"bg": "Búlgaro",
"ca": "Catalán",
"cs": "Checo",
"da": "Dinamarqués",
"de": "Alemán",
"el": "Grego",
"en": "Inglés",
"enGB": "Inglés (Reino Unido)",
"eo": "Esperanto",
"es": "Español",
"esUS": "Español (Hispanoamérica)",
"et": "Estoniano",
"eu": "Éuscaro",
"fi": "Finlandés",
"fr": "Francés",
"frCA": "Francés (Canadá)",
"he": "Hebreo",
"hr": "Croata",
"hu": "Húngaro",
"hy": "Armenio",
"id": "Indonesio",
"it": "Italiano",
"ja": "Xaponés",
"kab": "Cabila",
"ko": "Coreano",
"lt": "Lituano",
"lv": "Letón",
"ml": "Malayalam",
"mr": "Marathi",
"nl": "Holandés",
"oc": "Occitano",
"pl": "Polaco",
"ptBR": "Portugués (Brasil)",
"ro": "Romanés",
"ru": "Ruso",
"sc": "Sardo",
"sk": "Eslovaco",
"sl": "Esloveno",
"sr": "Serbio",
"sv": "Sueco",
"tr": "Turco",
"uk": "Ucraíno",
"vi": "Vietnamita",
"zhCN": "Chinés (China)",
"zhTW": "Chinés (Taiwán)"
}

37
lang/languages-he.json Normal file
View File

@@ -0,0 +1,37 @@
{
"af": "אפריקאית",
"bg": "בולגרית",
"ca": "קטלנית",
"cs": "קזחית",
"da": "דנית",
"de": "גרמנית",
"el": "יוונית",
"en": "אנגלית",
"enGB": "אנגלית (בריטניה)",
"eo": "אספרנטו",
"es": "ספרדית",
"esUS": "ספרדית (אמריקה הלטינית)",
"et": "אסטונית",
"fi": "פינית",
"fr": "צרפתית",
"frCA": "צרפתית (קנדה)",
"he": "עברית",
"hr": "קראוטית",
"hu": "הונגרית",
"hy": "ארמנית",
"it": "איטלקית",
"ja": "יפנית",
"ko": "קוראנית",
"nl": "הולנדית",
"oc": "אוקיאנית",
"pl": "פולנית",
"ptBR": "פורטוגזית (ברזיל)",
"ru": "רוסית",
"sc": "סרבית",
"sk": "סלובקית",
"sv": "שוודית",
"tr": "טורקית",
"vi": "ויטנאמית",
"zhCN": "סינית (סין)",
"zhTW": "סינית (טיוואן)"
}

52
lang/languages-hi.json Normal file
View File

@@ -0,0 +1,52 @@
{
"af": "Afrikaans",
"ar": "Arabic",
"bg": "Bulgarian",
"ca": "Catalan",
"cs": "Czech",
"da": "Danish",
"de": "German",
"el": "Greek",
"en": "अंग्रेज़ी",
"enGB": "अंग्रेज़ी (UK)",
"eo": "Esperanto",
"es": "Spanish",
"esUS": "Spanish (Latin America)",
"et": "Estonian",
"eu": "Basque",
"fa": "Persian",
"fi": "Finnish",
"fr": "French",
"frCA": "French (Canadian)",
"he": "Hebrew",
"hi": "हिन्दी",
"hr": "Croatian",
"hu": "Hungarian",
"hy": "Armenian",
"id": "Indonesian",
"it": "Italian",
"ja": "Japanese",
"kab": "Kabyle",
"ko": "Korean",
"lt": "Lithuanian",
"lv": "Latvian",
"ml": "मलयालम",
"mr": "मराठी",
"nl": "Dutch",
"oc": "Occitan",
"pl": "Polish",
"pt": "Portuguese",
"ptBR": "Portuguese (Brazil)",
"ro": "Romanian",
"ru": "Russian",
"sc": "Sardinian",
"sk": "Slovak",
"sl": "Slovenian",
"sr": "Serbian",
"sv": "Swedish",
"tr": "Turkish",
"uk": "Ukrainian",
"vi": "Vietnamese",
"zhCN": "Chinese (China)",
"zhTW": "Chinese (Taiwan)"
}

Some files were not shown because too many files have changed in this diff Show More