mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-05 06:12:28 +00:00
Compare commits
168 Commits
android-sd
...
ios_locksc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1893ced556 | ||
|
|
7d9c13a618 | ||
|
|
48ed3b7dc6 | ||
|
|
04abfe1a3b | ||
|
|
d45decc393 | ||
|
|
88f8f48465 | ||
|
|
8127ea2479 | ||
|
|
b22915c169 | ||
|
|
14fcd153e5 | ||
|
|
48efe36cdf | ||
|
|
0c373e105b | ||
|
|
e57c7f92a8 | ||
|
|
70fa44f85f | ||
|
|
bcc5beb73d | ||
|
|
4ef4e45ee4 | ||
|
|
c9ff0bb75f | ||
|
|
282817db28 | ||
|
|
93ab7725e7 | ||
|
|
53ccb97d34 | ||
|
|
6346d99d0e | ||
|
|
79c4cabbad | ||
|
|
7a9f51b01b | ||
|
|
a884a6b232 | ||
|
|
b00a17c1c3 | ||
|
|
99955df5c8 | ||
|
|
c0f0803f01 | ||
|
|
b2c864cbba | ||
|
|
31957d4085 | ||
|
|
4f83fc2a6d | ||
|
|
4bd27730ce | ||
|
|
817d7f0c0d | ||
|
|
19a9b17425 | ||
|
|
cc33930259 | ||
|
|
6223e16ca5 | ||
|
|
99385ae3d7 | ||
|
|
17cace070e | ||
|
|
cdb2436b73 | ||
|
|
10db5ecf1f | ||
|
|
cbb8b5f620 | ||
|
|
f3e4c57036 | ||
|
|
1731d5188d | ||
|
|
2c7dc5e40e | ||
|
|
74cd486232 | ||
|
|
30f3bac217 | ||
|
|
84fc4dd63f | ||
|
|
936a1a6b0e | ||
|
|
f4e8f860b1 | ||
|
|
ee544d25f8 | ||
|
|
c32866f6a2 | ||
|
|
215c2825de | ||
|
|
edb614783b | ||
|
|
0e19bc9cd6 | ||
|
|
caff0c64af | ||
|
|
fcb1845e2a | ||
|
|
977295bd87 | ||
|
|
ed139f53ca | ||
|
|
5692c3cb4d | ||
|
|
ed62409ad5 | ||
|
|
b52b4c2a78 | ||
|
|
36bef94c3c | ||
|
|
a995b33753 | ||
|
|
bfb15a2523 | ||
|
|
1d59c8122d | ||
|
|
31766c891b | ||
|
|
7a3b8d6ac4 | ||
|
|
edf5e1c094 | ||
|
|
7cd39b7983 | ||
|
|
49bcf5c179 | ||
|
|
2b3989e5e6 | ||
|
|
9b1e662a93 | ||
|
|
73160de3b7 | ||
|
|
f5fc524030 | ||
|
|
6e3b3879cc | ||
|
|
c9f9708166 | ||
|
|
7a8350356a | ||
|
|
2596c463fe | ||
|
|
2de0520835 | ||
|
|
c276c204ac | ||
|
|
67cf6e6d18 | ||
|
|
b321cf3639 | ||
|
|
8e54a69160 | ||
|
|
93e5e48648 | ||
|
|
c945b9cc5e | ||
|
|
7c3d2010e4 | ||
|
|
894c5cd788 | ||
|
|
1c7e457879 | ||
|
|
b6b536962d | ||
|
|
b625b7b0ec | ||
|
|
eb59949dfa | ||
|
|
69f4b116a9 | ||
|
|
75d7c4b160 | ||
|
|
b5065306e5 | ||
|
|
9f5a56bbc7 | ||
|
|
b4809fe083 | ||
|
|
6dedc7fb1a | ||
|
|
a780051720 | ||
|
|
51ac3ef64a | ||
|
|
a915238b49 | ||
|
|
687837310a | ||
|
|
7f879d2154 | ||
|
|
faec46dd17 | ||
|
|
33ec975e19 | ||
|
|
db6165209b | ||
|
|
27775de48d | ||
|
|
8818f4f4e6 | ||
|
|
aebe4885bb | ||
|
|
146cc2f9c9 | ||
|
|
3b5878d078 | ||
|
|
6ab996568b | ||
|
|
4755f5a031 | ||
|
|
0b48e55a35 | ||
|
|
9e1ac3bea6 | ||
|
|
7249406960 | ||
|
|
a9f5829e58 | ||
|
|
4bb1697115 | ||
|
|
2938d1f2dc | ||
|
|
10d202439b | ||
|
|
af0715dc17 | ||
|
|
601e21ffcd | ||
|
|
ff29196f3a | ||
|
|
748b66b04a | ||
|
|
ca4db54e6e | ||
|
|
3eecf8063f | ||
|
|
440339dea8 | ||
|
|
dbb0a953c6 | ||
|
|
442ae6c3cb | ||
|
|
30051d2b0e | ||
|
|
9ae236a010 | ||
|
|
6f209a8139 | ||
|
|
69567fb371 | ||
|
|
66cddddd3e | ||
|
|
485c875ee5 | ||
|
|
ceb1cd9673 | ||
|
|
4d817af060 | ||
|
|
a082a3fb0f | ||
|
|
2a321d6b1f | ||
|
|
dd6478b3cf | ||
|
|
6274299d49 | ||
|
|
d77d2f0c5b | ||
|
|
33be0dc32e | ||
|
|
44c8b31187 | ||
|
|
1279c5b0da | ||
|
|
495957104c | ||
|
|
b558d7936c | ||
|
|
1fe2a9193f | ||
|
|
e8e6beabab | ||
|
|
3e31e1816d | ||
|
|
eb1c3d8c56 | ||
|
|
4a54f4bedc | ||
|
|
4d84123c71 | ||
|
|
4018e8875f | ||
|
|
be93fbd512 | ||
|
|
51bbc3f87f | ||
|
|
f5fb402784 | ||
|
|
70503d2518 | ||
|
|
40e1f28fc2 | ||
|
|
935f95a3d5 | ||
|
|
53e4f584f9 | ||
|
|
e218c0d3af | ||
|
|
6c3206e4d4 | ||
|
|
6365b699c1 | ||
|
|
768cca6162 | ||
|
|
abdc5123eb | ||
|
|
6423ed8fb5 | ||
|
|
b858496adb | ||
|
|
68f79e52e7 | ||
|
|
3f78de2d34 | ||
|
|
2029db5a59 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -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
|
||||
- run: npm run lint:ci
|
||||
- run: for file in lang/*.json; do npx --yes jsonlint -q $file || exit 1; done
|
||||
linux-build:
|
||||
name: Build Frontend (Linux)
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -92,3 +92,4 @@ twa/*.apk
|
||||
twa/*.aab
|
||||
twa/assetlinks.json
|
||||
|
||||
tsconfig.json
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#17A0DB</color>
|
||||
<color name="colorPrimaryDark">#1081B2</color>
|
||||
<color name="navigationBarColor">#161618</color>
|
||||
</resources>
|
||||
@@ -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/colorPrimaryDark</item>
|
||||
<item name="android:navigationBarColor">@color/navigationBarColor</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -26,5 +26,5 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=22.6.0
|
||||
sdkVersion=6.2.0
|
||||
appVersion=99.0.0
|
||||
sdkVersion=99.0.0
|
||||
|
||||
@@ -22,6 +22,8 @@ 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;
|
||||
|
||||
@@ -49,6 +51,8 @@ class AudioDeviceHandlerConnectionService implements
|
||||
*/
|
||||
private AudioModeModule module;
|
||||
|
||||
private RNConnectionService rcs;
|
||||
|
||||
/**
|
||||
* Converts any of the "DEVICE_" constants into the corresponding
|
||||
* {@link android.telecom.CallAudioState} "ROUTE_" number.
|
||||
@@ -141,8 +145,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 {
|
||||
@@ -152,9 +156,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");
|
||||
}
|
||||
|
||||
@@ -26,10 +26,13 @@ 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;
|
||||
|
||||
@@ -196,7 +199,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
deviceInfo.putBoolean("selected", device.equals(selectedDevice));
|
||||
data.pushMap(deviceInfo);
|
||||
}
|
||||
ReactInstanceManagerHolder.emitEvent(DEVICE_CHANGE_EVENT, data);
|
||||
getContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(DEVICE_CHANGE_EVENT, data);
|
||||
JitsiMeetLogger.i(TAG + " Updating audio device list");
|
||||
}
|
||||
});
|
||||
@@ -212,6 +215,10 @@ 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}
|
||||
|
||||
46
android/sdk/src/main/java/org/jitsi/meet/sdk/H264Utils.java
Normal file
46
android/sdk/src/main/java/org/jitsi/meet/sdk/H264Utils.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
@@ -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.SoftwareVideoDecoderFactory;
|
||||
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.audio.AudioDeviceModule;
|
||||
import org.webrtc.audio.JavaAudioDeviceModule;
|
||||
|
||||
@@ -89,8 +89,10 @@ class ReactInstanceManagerHolder {
|
||||
.createAudioDeviceModule();
|
||||
options.setAudioDeviceModule(adm);
|
||||
|
||||
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
|
||||
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
|
||||
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
|
||||
|
||||
options.setVideoDecoderFactory(new WebRTCVideoDecoderFactory(eglContext));
|
||||
options.setVideoEncoderFactory(new WebRTCVideoEncoderFactory(eglContext));
|
||||
|
||||
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
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()]);
|
||||
}
|
||||
}
|
||||
562
conference.js
562
conference.js
@@ -27,7 +27,6 @@ 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,
|
||||
@@ -62,12 +61,14 @@ import {
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
getAvailableDevices,
|
||||
getDefaultDeviceId,
|
||||
notifyCameraError,
|
||||
notifyMicError,
|
||||
setAudioOutputDeviceId,
|
||||
updateDeviceList
|
||||
} from './react/features/base/devices';
|
||||
} from './react/features/base/devices/actions.web';
|
||||
import {
|
||||
getDefaultDeviceId,
|
||||
setAudioOutputDeviceId
|
||||
} from './react/features/base/devices/functions.web';
|
||||
import {
|
||||
JitsiConferenceErrors,
|
||||
JitsiConferenceEvents,
|
||||
@@ -75,7 +76,6 @@ import {
|
||||
JitsiConnectionEvents,
|
||||
JitsiE2ePingEvents,
|
||||
JitsiMediaDevicesEvents,
|
||||
JitsiParticipantConnectionStatus,
|
||||
JitsiTrackErrors,
|
||||
JitsiTrackEvents,
|
||||
browser
|
||||
@@ -99,9 +99,7 @@ import {
|
||||
getNormalizedDisplayName,
|
||||
getVirtualScreenshareParticipantByOwnerId,
|
||||
localParticipantAudioLevelChanged,
|
||||
localParticipantConnectionStatusChanged,
|
||||
localParticipantRoleChanged,
|
||||
participantConnectionStatusChanged,
|
||||
participantKicked,
|
||||
participantMutedUs,
|
||||
participantPresenceChanged,
|
||||
@@ -110,23 +108,19 @@ import {
|
||||
screenshareParticipantDisplayNameChanged,
|
||||
updateRemoteParticipantFeatures
|
||||
} from './react/features/base/participants';
|
||||
import {
|
||||
getUserSelectedCameraDeviceId,
|
||||
updateSettings
|
||||
} from './react/features/base/settings';
|
||||
import { 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';
|
||||
@@ -146,18 +140,12 @@ import {
|
||||
} from './react/features/notifications';
|
||||
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
|
||||
import { suspendDetected } from './react/features/power-monitor';
|
||||
import {
|
||||
initPrejoin,
|
||||
isPrejoinPageVisible,
|
||||
makePrecallTest,
|
||||
setJoiningInProgress
|
||||
} from './react/features/prejoin';
|
||||
import { initPrejoin, makePrecallTest, setJoiningInProgress } from './react/features/prejoin/actions';
|
||||
import { isPrejoinPageVisible } from './react/features/prejoin/functions';
|
||||
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';
|
||||
@@ -189,15 +177,6 @@ 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
|
||||
@@ -481,12 +460,6 @@ 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.
|
||||
@@ -531,22 +504,10 @@ export default {
|
||||
firePermissionPromptIsShownEvent: true
|
||||
};
|
||||
|
||||
// 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
|
||||
// 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()
|
||||
.then(([ desktopStream ]) => {
|
||||
if (!requestedAudio) {
|
||||
return [ desktopStream ];
|
||||
@@ -911,8 +872,7 @@ export default {
|
||||
isLocalVideoMuted() {
|
||||
// If the tracks are not ready, read from base/media state
|
||||
return this._localTracksInitialized
|
||||
? isLocalCameraTrackMuted(
|
||||
APP.store.getState()['features/base/tracks'])
|
||||
? isLocalTrackMuted(APP.store.getState()['features/base/tracks'], MEDIA_TYPE.VIDEO)
|
||||
: isVideoMutedByUser(APP.store);
|
||||
},
|
||||
|
||||
@@ -1032,36 +992,6 @@ 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.
|
||||
@@ -1070,8 +1000,6 @@ 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;
|
||||
@@ -1084,13 +1012,6 @@ 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
|
||||
@@ -1402,8 +1323,6 @@ export default {
|
||||
// Restore initial state.
|
||||
this._localTracksInitialized = false;
|
||||
this.isSharingScreen = false;
|
||||
this.localPresenterVideo = null;
|
||||
|
||||
this.roomName = roomName;
|
||||
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(options);
|
||||
@@ -1535,33 +1454,6 @@ 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.
|
||||
@@ -1641,35 +1533,6 @@ 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());
|
||||
@@ -1688,9 +1551,10 @@ export default {
|
||||
}
|
||||
|
||||
APP.store.dispatch(setScreenAudioShareState(false));
|
||||
let promise;
|
||||
|
||||
if (didHaveVideo && !ignoreDidHaveVideo) {
|
||||
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
|
||||
promise = createLocalTracksF({ devices: [ 'video' ] })
|
||||
.then(([ stream ]) => {
|
||||
logger.debug(`_turnScreenSharingOff using ${stream} for useVideoStream`);
|
||||
|
||||
@@ -1706,11 +1570,7 @@ export default {
|
||||
);
|
||||
});
|
||||
} else {
|
||||
promise = promise.then(() => {
|
||||
logger.debug('_turnScreenSharingOff using null for useVideoStream');
|
||||
|
||||
return this.useVideoStream(null);
|
||||
});
|
||||
promise = this.useVideoStream(null);
|
||||
}
|
||||
|
||||
return promise.then(
|
||||
@@ -1728,54 +1588,6 @@ 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}
|
||||
*
|
||||
@@ -1848,228 +1660,6 @@ 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
|
||||
@@ -2274,11 +1864,6 @@ 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) => {
|
||||
@@ -2289,16 +1874,6 @@ 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) => {
|
||||
@@ -2518,77 +2093,38 @@ export default {
|
||||
APP.UI.addListener(
|
||||
UIEvents.VIDEO_DEVICE_CHANGED,
|
||||
cameraDeviceId => {
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
|
||||
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||
|
||||
// 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();
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 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}`);
|
||||
|
||||
// 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));
|
||||
});
|
||||
}
|
||||
return APP.store.dispatch(notifyCameraError(error));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -2612,7 +2148,6 @@ export default {
|
||||
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
cameraDeviceId: null,
|
||||
micDeviceId: selectedDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
@@ -2657,12 +2192,6 @@ export default {
|
||||
APP.UI.updateLargeVideo(displayedUserId, true);
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.TOGGLE_SCREENSHARING, ({ enabled, audioOnly, ignoreDidHaveVideo }) => {
|
||||
this.toggleScreenSharing(enabled, { audioOnly }, ignoreDidHaveVideo);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2761,13 +2290,6 @@ 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()
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -3216,7 +2738,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.toggleScreenSharing(undefined, { desktopStream });
|
||||
APP.store.dispatch(toggleScreensharingA(undefined, false, { desktopStream }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
56
config.js
56
config.js
@@ -65,10 +65,6 @@ 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
|
||||
|
||||
@@ -83,15 +79,6 @@ 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
|
||||
@@ -103,20 +90,6 @@ 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,
|
||||
|
||||
@@ -224,9 +197,29 @@ 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
|
||||
@@ -507,9 +500,6 @@ 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
|
||||
@@ -741,6 +731,7 @@ var config = {
|
||||
// 'toggle-camera',
|
||||
// 'undock-iframe',
|
||||
// 'videoquality',
|
||||
// 'whiteboard',
|
||||
// ],
|
||||
|
||||
// Holds values related to toolbar visibility control.
|
||||
@@ -776,11 +767,13 @@ var config = {
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'end-meeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'hangup-menu',
|
||||
// 'help',
|
||||
// {
|
||||
// key: 'invite',
|
||||
@@ -813,6 +806,7 @@ 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:
|
||||
@@ -1487,6 +1481,8 @@ 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
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
connectionEstablished,
|
||||
connectionFailed,
|
||||
constructOptions
|
||||
} from './react/features/base/connection/actions';
|
||||
} from './react/features/base/connection/actions.web';
|
||||
import { openDialog } from './react/features/base/dialog/actions';
|
||||
import { setJWT } from './react/features/base/jwt';
|
||||
import {
|
||||
|
||||
@@ -182,3 +182,8 @@ 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;
|
||||
}
|
||||
|
||||
@@ -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: 4px;
|
||||
padding: 1px;
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
top: -3px;
|
||||
|
||||
@@ -8,7 +8,6 @@ $hangupColor:#DD3849;
|
||||
$hangupHoverColor: #F25363;
|
||||
$hangupMenuButtonColor:#0056E0;;
|
||||
$hangupMenuButtonHoverColor: #246FE5;
|
||||
$hangupFontSize: 2em;
|
||||
|
||||
/**
|
||||
* Size variables.
|
||||
@@ -45,7 +44,6 @@ $newToolbarSizeMobile: 60px;
|
||||
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
|
||||
$toolbarTitleFontSize: 19px;
|
||||
$overflowMenuItemColor: #fff;
|
||||
$overflowMenuItemBackground: #36383C;
|
||||
|
||||
|
||||
/**
|
||||
@@ -88,7 +86,7 @@ $chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
|
||||
$chatRemoteMessageBackgroundColor: #242528;
|
||||
$sidebarWidth: 315px;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Misc.
|
||||
*/
|
||||
$borderRadius: 4px;
|
||||
@@ -103,8 +101,6 @@ $zindex0: 0;
|
||||
$zindex1: 1;
|
||||
$zindex2: 2;
|
||||
$zindex3: 3;
|
||||
$toolbarBackgroundZ: 4;
|
||||
$labelsZ: 5;
|
||||
$subtitlesZ: 7;
|
||||
$popoverZ: 8;
|
||||
$reloadZ: 20;
|
||||
@@ -113,10 +109,7 @@ $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.
|
||||
@@ -250,7 +243,6 @@ $chromeExtensionBannerRightInMeeeting: 10px;
|
||||
/**
|
||||
* media type thresholds
|
||||
*/
|
||||
$smallScreen: 700px;
|
||||
$verySmallScreen: 500px;
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&-entry {
|
||||
|
||||
@@ -38,3 +38,7 @@
|
||||
margin-top: 2px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dialog-bottom-margin {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
font-size: 15px;
|
||||
margin-left: auto;
|
||||
margin-top: 16px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&-code {
|
||||
|
||||
@@ -84,56 +84,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.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;
|
||||
|
||||
@@ -204,3 +204,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lobby-button-margin {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ $baseLight: #FFFFFF;
|
||||
/**
|
||||
* Controls
|
||||
*/
|
||||
$controlBackground: $baseLight;
|
||||
$controlColor: #333333;
|
||||
$sliderTrackBackground: #474747;
|
||||
$sliderThumbBackground: #3572b0;
|
||||
|
||||
@@ -60,42 +58,21 @@ $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
|
||||
*/
|
||||
//dropdown
|
||||
$selectFontColor: $controlColor;
|
||||
$selectBg: $controlBackground;
|
||||
$selectActiveBg: darken($controlBackground, 5%);
|
||||
$selectActiveItemBg: darken($controlBackground, 20%);
|
||||
|
||||
/**
|
||||
* TODO: Replace by themed component.
|
||||
*/
|
||||
$videoQualityActive: #57A0ff;
|
||||
$selectBg: $baseLight;
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -20,7 +20,7 @@ Description: WebRTC JavaScript video conferences
|
||||
|
||||
Package: jitsi-meet-web-config
|
||||
Architecture: all
|
||||
Depends: openssl, nginx | nginx-full | nginx-extras | apache2, curl
|
||||
Depends: openssl, nginx | nginx-full | nginx-extras | openresty | 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.
|
||||
|
||||
8
debian/jitsi-meet-prosody.templates
vendored
8
debian/jitsi-meet-prosody.templates
vendored
@@ -1,12 +1,12 @@
|
||||
Template: jitsi-meet-prosody/jvb-hostname
|
||||
Type: string
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
_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.
|
||||
|
||||
Template: jitsi-videobridge/jvb-hostname
|
||||
Type: string
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
_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.
|
||||
|
||||
Template: jitsi-videobridge/jvbsecret
|
||||
Type: password
|
||||
|
||||
8
debian/jitsi-meet-turnserver.templates
vendored
8
debian/jitsi-meet-turnserver.templates
vendored
@@ -1,9 +1,9 @@
|
||||
Template: jitsi-meet-turnserver/jvb-hostname
|
||||
Type: string
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
_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.
|
||||
|
||||
Template: jitsi-videobridge/jvb-hostname
|
||||
Type: string
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
_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.
|
||||
|
||||
42
debian/jitsi-meet-web-config.postinst
vendored
42
debian/jitsi-meet-web-config.postinst
vendored
@@ -57,6 +57,10 @@ 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"
|
||||
@@ -182,21 +186,41 @@ case "$1" in
|
||||
echo "config.flags.receiveMultipleVideoStreams = true;" >> $JITSI_MEET_CONFIG
|
||||
fi
|
||||
|
||||
if [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
|
||||
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
|
||||
|
||||
# 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 /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
|
||||
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
|
||||
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
|
||||
@@ -204,14 +228,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" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
"$NGX_COMMON_CONF_PATH"
|
||||
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" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
"$NGX_COMMON_CONF_PATH"
|
||||
fi
|
||||
|
||||
invoke-rc.d nginx reload || true
|
||||
invoke-rc.d $NGX_SVC_NAME reload || true
|
||||
elif [[ "$FORCE_APACHE" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
|
||||
|
||||
# this is a reconfigure, lets just delete old links
|
||||
|
||||
4
debian/jitsi-meet-web-config.postrm
vendored
4
debian/jitsi-meet-web-config.postrm
vendored
@@ -24,6 +24,9 @@ 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
|
||||
@@ -38,6 +41,7 @@ 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
|
||||
|
||||
8
debian/jitsi-meet-web-config.templates
vendored
8
debian/jitsi-meet-web-config.templates
vendored
@@ -23,8 +23,8 @@ _Description: Full local server path to the SSL certificate file:
|
||||
|
||||
Template: jitsi-meet/jvb-hostname
|
||||
Type: string
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
_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.
|
||||
|
||||
Template: jitsi-videobridge/jvb-hostname
|
||||
Type: string
|
||||
@@ -33,8 +33,8 @@ _Description: Hostname:
|
||||
|
||||
Template: jitsi-meet/jaas-choice
|
||||
Type: boolean
|
||||
_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.
|
||||
_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.
|
||||
|
||||
Template: jitsi-meet/email
|
||||
Type: string
|
||||
|
||||
2
debian/po/templates.pot
vendored
2
debian/po/templates.pot
vendored
@@ -84,7 +84,7 @@ msgstr ""
|
||||
#. Description
|
||||
#: ../jitsi-meet-web-config.templates:4001
|
||||
msgid ""
|
||||
"The value for the hostname that is set in Jitsi Videobridge installation."
|
||||
"The value of the domain that is set in the Jitsi Videobridge installation."
|
||||
msgstr ""
|
||||
|
||||
#. Type: boolean
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 114 KiB |
Binary file not shown.
Binary file not shown.
8
globals.d.ts
vendored
8
globals.d.ts
vendored
@@ -9,6 +9,11 @@ declare global {
|
||||
UI: any;
|
||||
API: any;
|
||||
conference: any;
|
||||
debugLogs: any;
|
||||
keyboardshortcut: {
|
||||
registerShortcut: Function;
|
||||
unregisterShortcut: Function;
|
||||
}
|
||||
};
|
||||
const interfaceConfig: any;
|
||||
|
||||
@@ -16,7 +21,10 @@ declare global {
|
||||
config: IConfig;
|
||||
JITSI_MEET_LITE_SDK?: boolean;
|
||||
interfaceConfig?: any;
|
||||
JitsiMeetJS?: any;
|
||||
}
|
||||
|
||||
const config: IConfig;
|
||||
|
||||
const JitsiMeetJS: any;
|
||||
}
|
||||
|
||||
43
globals.native.d.ts
vendored
Normal file
43
globals.native.d.ts
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
@@ -457,7 +457,7 @@ PODS:
|
||||
- React-perflogger (= 0.68.4)
|
||||
- RNCalendarEvents (2.2.0):
|
||||
- React
|
||||
- RNCAsyncStorage (1.15.14):
|
||||
- RNCAsyncStorage (1.17.3):
|
||||
- React-Core
|
||||
- RNCClipboard (1.5.1):
|
||||
- React-Core
|
||||
@@ -770,7 +770,7 @@ SPEC CHECKSUMS:
|
||||
React-runtimeexecutor: 088723cf020113e64736a709f52719dbb359c73e
|
||||
ReactCommon: 1a4f19f3b4366feec03a98bdbb200b6085c5000f
|
||||
RNCalendarEvents: 7e65eb4a94f53c1744d1e275f7fafcfaa619f7a3
|
||||
RNCAsyncStorage: ea6b5c280997b2b32a587793163b1f10e580c4f7
|
||||
RNCAsyncStorage: 005c0e2f09575360f142d0d1f1f15e4ec575b1af
|
||||
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
|
||||
RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd
|
||||
RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
6
ios/app/Widgets Extension/Assets.xcassets/Contents.json
Normal file
6
ios/app/Widgets Extension/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
12
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/Contents.json
vendored
Normal file
12
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "end_call_button.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/end_call_button.pdf
vendored
Normal file
BIN
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/end_call_button.pdf
vendored
Normal file
Binary file not shown.
12
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/Contents.json
vendored
Normal file
12
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "microphone_off_button.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/microphone_off_button.pdf
vendored
Normal file
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/microphone_off_button.pdf
vendored
Normal file
Binary file not shown.
12
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/Contents.json
vendored
Normal file
12
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "microphone_on_button.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/microphone_on_button.pdf
vendored
Normal file
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/microphone_on_button.pdf
vendored
Normal file
Binary file not shown.
101
ios/app/Widgets Extension/DarwinNotificationsObserver.swift
Normal file
101
ios/app/Widgets Extension/DarwinNotificationsObserver.swift
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// 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
|
||||
}
|
||||
}
|
||||
11
ios/app/Widgets Extension/Info.plist
Normal file
11
ios/app/Widgets Extension/Info.plist
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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>
|
||||
54
ios/app/Widgets Extension/LockScreenLeaveMeetingWidget.swift
Normal file
54
ios/app/Widgets Extension/LockScreenLeaveMeetingWidget.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// 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")!)
|
||||
}
|
||||
}
|
||||
89
ios/app/Widgets Extension/LockScreenMuteAudioWidget.swift
Normal file
89
ios/app/Widgets Extension/LockScreenMuteAudioWidget.swift
Normal file
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// 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")!)
|
||||
}
|
||||
}
|
||||
30
ios/app/Widgets Extension/MeetingState.swift
Normal file
30
ios/app/Widgets Extension/MeetingState.swift
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
44
ios/app/Widgets Extension/Provider.swift
Normal file
44
ios/app/Widgets Extension/Provider.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
19
ios/app/Widgets Extension/Widgets.swift
Normal file
19
ios/app/Widgets Extension/Widgets.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
10
ios/app/WidgetsExtension.entitlements
Normal file
10
ios/app/WidgetsExtension.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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>
|
||||
@@ -23,14 +23,26 @@
|
||||
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 */; };
|
||||
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */; };
|
||||
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 */; };
|
||||
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, ); }; };
|
||||
@@ -64,6 +76,13 @@
|
||||
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 */
|
||||
@@ -108,6 +127,7 @@
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
4ECA497128FD590000085365 /* WidgetsExtension.appex in Embed App Extensions */,
|
||||
4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
@@ -134,6 +154,7 @@
|
||||
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>"; };
|
||||
@@ -141,8 +162,12 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -150,10 +175,21 @@
|
||||
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; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -180,7 +216,7 @@
|
||||
DED016F128ECBC9D009D5E8D /* WebRTC.xcframework in Frameworks */,
|
||||
DEA9F289258A6EA800D4CD74 /* JitsiMeetSDK.framework in Frameworks */,
|
||||
FD572B9827EDF32300A800FB /* GiphyUISDK.xcframework in Frameworks */,
|
||||
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */,
|
||||
C2116A7673E01A1CCD5DC1F4 /* libPods-JitsiMeet.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -199,6 +235,15 @@
|
||||
);
|
||||
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 */
|
||||
@@ -213,7 +258,9 @@
|
||||
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */,
|
||||
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */,
|
||||
4EC49B8625BED71300E76218 /* ReplayKit.framework */,
|
||||
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */,
|
||||
4ECA496528FD590000085365 /* WidgetKit.framework */,
|
||||
4ECA496728FD590000085365 /* SwiftUI.framework */,
|
||||
0BF9FEBA4DEAB800AD735681 /* libPods-JitsiMeet.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -261,6 +308,10 @@
|
||||
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>";
|
||||
@@ -282,9 +333,25 @@
|
||||
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 */,
|
||||
@@ -292,11 +359,12 @@
|
||||
0BEA5C261F7B8F73000D0AB4 /* Watch app */,
|
||||
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
|
||||
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
|
||||
CDD71F5E1157E9F283DF92A8 /* Pods */,
|
||||
4ECA496928FD590000085365 /* Widgets Extension */,
|
||||
BD4E28FA984EA7018FD927DF /* Pods */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
indentWidth = 4;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
tabWidth = 4;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
@@ -305,15 +373,16 @@
|
||||
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
|
||||
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
|
||||
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */,
|
||||
4ECA496428FD590000085365 /* WidgetsExtension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CDD71F5E1157E9F283DF92A8 /* Pods */ = {
|
||||
BD4E28FA984EA7018FD927DF /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */,
|
||||
3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */,
|
||||
7052390E12D7319D36D8E4CA /* Pods-JitsiMeet.debug.xcconfig */,
|
||||
8CAA3C5A38E868335D1C1EC1 /* Pods-JitsiMeet.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = ../Pods;
|
||||
@@ -361,7 +430,7 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
|
||||
buildPhases = (
|
||||
69BC5020DBE393B56BD76636 /* [CP] Check Pods Manifest.lock */,
|
||||
58E2CB346F2C2A873294F481 /* [CP] Check Pods Manifest.lock */,
|
||||
0BBA83C41EC9F7600075A103 /* Run React packager */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
@@ -380,6 +449,7 @@
|
||||
dependencies = (
|
||||
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
|
||||
4EB0602A260E026600F524C5 /* PBXTargetDependency */,
|
||||
4ECA497028FD590000085365 /* PBXTargetDependency */,
|
||||
);
|
||||
name = JitsiMeet;
|
||||
productName = "Jitsi Meet";
|
||||
@@ -403,15 +473,32 @@
|
||||
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 = 1240;
|
||||
LastSwiftUpdateCheck = 1400;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
0BEA5C241F7B8F73000D0AB4 = {
|
||||
CreatedOnToolsVersion = 9.0;
|
||||
@@ -424,6 +511,7 @@
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1400;
|
||||
SystemCapabilities = {
|
||||
com.apple.SafariKeychain = {
|
||||
enabled = 1;
|
||||
@@ -436,6 +524,9 @@
|
||||
4EB06022260E026600F524C5 = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
};
|
||||
4ECA496328FD590000085365 = {
|
||||
CreatedOnToolsVersion = 14.0.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
|
||||
@@ -455,6 +546,7 @@
|
||||
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
|
||||
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
|
||||
4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */,
|
||||
4ECA496328FD590000085365 /* WidgetsExtension */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -494,6 +586,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4ECA496228FD590000085365 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4ECA496D28FD590000085365 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
@@ -543,7 +643,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";
|
||||
};
|
||||
69BC5020DBE393B56BD76636 /* [CP] Check Pods Manifest.lock */ = {
|
||||
58E2CB346F2C2A873294F481 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -642,8 +742,10 @@
|
||||
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;
|
||||
};
|
||||
@@ -659,6 +761,19 @@
|
||||
);
|
||||
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 */
|
||||
@@ -677,6 +792,11 @@
|
||||
target = 4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */;
|
||||
targetProxy = 4EB06029260E026600F524C5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
4ECA497028FD590000085365 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 4ECA496328FD590000085365 /* WidgetsExtension */;
|
||||
targetProxy = 4ECA496F28FD590000085365 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -845,11 +965,12 @@
|
||||
};
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */;
|
||||
baseConfigurationReference = 7052390E12D7319D36D8E4CA /* 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;
|
||||
@@ -871,16 +992,20 @@
|
||||
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 = 3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */;
|
||||
baseConfigurationReference = 8CAA3C5A38E868335D1C1EC1 /* 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;
|
||||
@@ -901,6 +1026,8 @@
|
||||
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;
|
||||
};
|
||||
@@ -973,6 +1100,91 @@
|
||||
};
|
||||
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 = {
|
||||
@@ -1126,6 +1338,15 @@
|
||||
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 = (
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<?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>
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.6.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "FIRUtilities.h"
|
||||
#import "RoutesHandler.h"
|
||||
#import "Types.h"
|
||||
#import "ViewController.h"
|
||||
|
||||
@@ -69,7 +70,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
|
||||
@@ -107,6 +108,10 @@
|
||||
if ([[url absoluteString] containsString:@"google/link/?dismiss=1&is_weak_match=1"]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([[RoutesHandler sharedInstance] routeURL:url]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSURL *openUrl = url;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.6.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
4
ios/app/src/JitsiMeet-Bridging-Header.h
Normal file
4
ios/app/src/JitsiMeet-Bridging-Header.h
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
27
ios/app/src/RoutesHandler.h
Normal file
27
ios/app/src/RoutesHandler.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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
|
||||
123
ios/app/src/RoutesHandler.m
Normal file
123
ios/app/src/RoutesHandler.m
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// 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
|
||||
@@ -21,8 +21,16 @@
|
||||
@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
|
||||
|
||||
@@ -33,6 +41,8 @@
|
||||
view.delegate = self;
|
||||
|
||||
[view join:[[JitsiMeet sharedInstance] getInitialConferenceOptions]];
|
||||
|
||||
[self registerRouteObserver];
|
||||
}
|
||||
|
||||
// JitsiMeetViewDelegate
|
||||
@@ -53,6 +63,10 @@
|
||||
|
||||
- (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.
|
||||
@@ -82,6 +96,12 @@
|
||||
|
||||
- (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 {
|
||||
@@ -107,7 +127,16 @@
|
||||
}
|
||||
|
||||
- (void)audioMutedChanged:(NSDictionary *)data {
|
||||
NSLog(@"%@%@", @"Audio muted changed: ", data[@"muted"]);
|
||||
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];
|
||||
}
|
||||
|
||||
- (void)endpointTextMessageReceived:(NSDictionary *)data {
|
||||
@@ -132,9 +161,40 @@
|
||||
|
||||
#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
|
||||
|
||||
20
ios/app/src/WidgetKitHelper.swift
Normal file
20
ios/app/src/WidgetKitHelper.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.6.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.6.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -82,7 +82,7 @@ platform :ios do
|
||||
export_xcargs: "-allowProvisioningUpdates"
|
||||
)
|
||||
|
||||
# Upload the build to TestFlight (but don't distribute it)
|
||||
# Upload the build to TestFlight
|
||||
upload_to_testflight(
|
||||
beta_app_description: ENV["JITSI_CHANGELOG"],
|
||||
beta_app_feedback_email: ENV["JITSI_REVIEW_EMAIL"],
|
||||
@@ -102,24 +102,10 @@ 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
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>6.2.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -57,8 +57,8 @@
|
||||
openURL:(NSURL *_Nonnull)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *_Nonnull)options;
|
||||
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *)application
|
||||
supportedInterfaceOrientationsForWindow:(UIWindow *)window;
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *_Nonnull)application
|
||||
supportedInterfaceOrientationsForWindow:(UIWindow *_Nonnull)window;
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"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": "الصينية (تايوان)"
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"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": "Кітайская (Тайвань)"
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"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": "Тайвански"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"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": "Κινέζικα (Ταϊβάν)"
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"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": "چینی (تایوان)"
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"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": "סינית (טיוואן)"
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"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)"
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"af": "Afrikanski",
|
||||
"ar": "Arapski",
|
||||
"bg": "Bugarski",
|
||||
"ca": "Katalanski",
|
||||
"cs": "Češki",
|
||||
"da": "Danski",
|
||||
"de": "Njemački",
|
||||
"el": "Grčki",
|
||||
"en": "Engleski",
|
||||
"enGB": "Engleski (Ujedinjeno Kraljevstvo)",
|
||||
"eo": "Esperanto",
|
||||
"es": "Španjolski",
|
||||
"esUS": "Španjolski (Latinska Amerika)",
|
||||
"et": "Estonski",
|
||||
"eu": "Baskijski",
|
||||
"fa": "Perzijski",
|
||||
"fi": "Finski",
|
||||
"fr": "Francuski",
|
||||
"frCA": "Francuski (Canada)",
|
||||
"he": "Hebrejski",
|
||||
"hi": "Hindski",
|
||||
"hr": "Hrvatski",
|
||||
"hsb": "Gornjolužičkosrpski",
|
||||
"hu": "Mađarski",
|
||||
"hy": "Armenski",
|
||||
"id": "Indonezijski",
|
||||
"it": "Talijanski",
|
||||
"ja": "Japanski",
|
||||
"ko": "Korejski",
|
||||
"lt": "Litvanski",
|
||||
"lv": "Latvijski",
|
||||
"ml": "Malayalam",
|
||||
"mr": "Marathi",
|
||||
"nb": "Norveški Bokmal",
|
||||
"nl": "Nizozemski",
|
||||
"oc": "Okcitanski",
|
||||
"pl": "Poljski",
|
||||
"pt": "Portugalski",
|
||||
"ptBR": "Portugalski (Brazil)",
|
||||
"ro": "Rumunjski",
|
||||
"ru": "Ruski",
|
||||
"sc": "Sardijski",
|
||||
"sk": "Slovački",
|
||||
"sl": "Slovenski",
|
||||
"sq": "Albanski",
|
||||
"sr": "Srpski",
|
||||
"sv": "Švedski",
|
||||
"te": "Teluški",
|
||||
"tr": "Turski",
|
||||
"uk": "Ukrajinski",
|
||||
"vi": "Vijetnamski",
|
||||
"zhCN": "Kineski (Kina)",
|
||||
"zhTW": "Kineski (Taiwan)"
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"ar": "arabšćina",
|
||||
"bg": "bołharšćina",
|
||||
"cs": "čěšćina",
|
||||
"da": "danšćina",
|
||||
"de": "němčina",
|
||||
"el": "grjekšćina",
|
||||
"en": "jendźelšćina",
|
||||
"enGB": "jendźelšćina (Wulka Britaniska)",
|
||||
"eo": "esperanto",
|
||||
"es": "španišćina",
|
||||
"esUS": "španišćina (Łaćonska)",
|
||||
"et": "estišćina",
|
||||
"eu": "baskišćina",
|
||||
"fa": "persišćina",
|
||||
"fi": "finšćina",
|
||||
"fr": "francošćina",
|
||||
"frCA": "francošćina (Kanada)",
|
||||
"he": "hebrejšćina",
|
||||
"hi": "hindišćina",
|
||||
"hr": "chorwatšćina",
|
||||
"hsb": "hornjoserbšćina",
|
||||
"hu": "madźaršćina",
|
||||
"hy": "armenšćina",
|
||||
"id": "indonešćina",
|
||||
"it": "italšćina",
|
||||
"ja": "japanšćina",
|
||||
"ko": "korejšćina",
|
||||
"lt": "litawšćina",
|
||||
"lv": "letišćina",
|
||||
"nl": "nižozemšćina",
|
||||
"pl": "pólšćina",
|
||||
"pt": "portugalšćina",
|
||||
"ptBR": "portugalšćina (Brazilska)",
|
||||
"ro": "rumunšćina",
|
||||
"ru": "rušćina",
|
||||
"sk": "słowakšćina",
|
||||
"sl": "słowjenšćina",
|
||||
"sq": "albanšćina",
|
||||
"sr": "serbišćina",
|
||||
"sv": "šwedšćina",
|
||||
"tr": "turkowšćina",
|
||||
"uk": "ukrainšćina",
|
||||
"vi": "vietnamšćina",
|
||||
"zhCN": "chinšćina (China)",
|
||||
"zhTW": "chinšćina (Taiwan)"
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"af": "Afrikaans",
|
||||
"bg": "Bolgár",
|
||||
"ca": "Katalán",
|
||||
"cs": "Cseh",
|
||||
"da": "Dán",
|
||||
"de": "Német",
|
||||
"el": "Görög",
|
||||
"en": "Angol",
|
||||
"enGB": "Angol (Egyesült Királyság)",
|
||||
"eo": "Eszperantó",
|
||||
"es": "Spanyol",
|
||||
"esUS": "Spanyol (Latin-Amerika)",
|
||||
"et": "Észt",
|
||||
"fi": "Finn",
|
||||
"fr": "Francia",
|
||||
"frCA": "Francia (kanadai)",
|
||||
"hr": "Horvát",
|
||||
"hu": "Magyar",
|
||||
"hy": "Örmény",
|
||||
"it": "Olasz",
|
||||
"ja": "Japán",
|
||||
"ko": "Koreai",
|
||||
"nl": "Holland",
|
||||
"oc": "Okszitán",
|
||||
"pl": "Lengyel",
|
||||
"ptBR": "Portugál (Brazil)",
|
||||
"ru": "Orosz",
|
||||
"sk": "Szlovákul",
|
||||
"sv": "Svéd",
|
||||
"tr": "Török",
|
||||
"uk": "Ukrán",
|
||||
"vi": "Vietnámi",
|
||||
"zhCN": "Kínai (Kína)",
|
||||
"zhTW": "Kínai (Tajvan)"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"af": "",
|
||||
"bg": "Բուլղարերեն",
|
||||
"cs": "",
|
||||
"de": "Գերմաներեն ",
|
||||
"el": "",
|
||||
"en": "Անգլերեն",
|
||||
"eo": "Էսպերանտո",
|
||||
"es": "Իսպաներեն",
|
||||
"fr": "Ֆրանսերեն",
|
||||
"hy": "Հայերեն",
|
||||
"it": "Իտալերեն",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Նորվեգերեն",
|
||||
"oc": "Օքսիտաներեն",
|
||||
"pl": "Լեհերեն",
|
||||
"ptBR": "Պորտուգալերեն (Բրազիլիա)",
|
||||
"ru": "Ռուսերեն",
|
||||
"sk": "Սլովակերեն",
|
||||
"sl": "Սլովեներեն ",
|
||||
"sv": "Շվեդերեն ",
|
||||
"tr": "Թուրքերեն",
|
||||
"uk": "Ուկրաիներեն",
|
||||
"vi": "",
|
||||
"zhCN": "Չիներեն"
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"af": "Bahasa Afrika",
|
||||
"bg": "Bahasa Bulgaria",
|
||||
"ca": "Bahasa Katalan",
|
||||
"cs": "Bahasa Ceko",
|
||||
"da": "Bahasa Denmark",
|
||||
"de": "Bahasa Jerman",
|
||||
"el": "Bahasa Yunani",
|
||||
"en": "Bahasa Inggris - US",
|
||||
"enGB": "Bahasa Inggris - UK",
|
||||
"eo": "Esperanto",
|
||||
"es": "Bahasa Spanyol",
|
||||
"esUS": "Bahasa Spanyol - Latin",
|
||||
"et": "Bahasa Estonia",
|
||||
"fi": "Bahasa Finlandia",
|
||||
"fr": "Bahasa Prancis",
|
||||
"frCA": "Bahasa Prancis - Kanada",
|
||||
"he": "Bahasa Hebrew",
|
||||
"hr": "Bahasa Kroasia",
|
||||
"hu": "Bahasa Hongaria",
|
||||
"hy": "Bahasa Armenia",
|
||||
"id": "Bahasa Indonesia",
|
||||
"it": "Bahasa Italia",
|
||||
"ja": "Bahasa Jepang",
|
||||
"ko": "Bahasa Korea",
|
||||
"lt": "Bahasa Lituania",
|
||||
"nl": "Bahasa Belanda",
|
||||
"oc": "Bahasa Oceania",
|
||||
"pl": "Bahasa Polandia",
|
||||
"ptBR": "Bahasa Portugis - Brazil",
|
||||
"ru": "Bahasa Rusia",
|
||||
"sc": "Bahasa Sardinia",
|
||||
"sk": "Bahasa Slovakia",
|
||||
"sv": "Bahasa Swedia",
|
||||
"tr": "Bahasa Turki",
|
||||
"uk": "Bahasa Ukraina",
|
||||
"vi": "Bahasa Vietnam",
|
||||
"zhCN": "Bahasa Mandarin",
|
||||
"zhTW": "Bahasa Mandarin - Taiwan"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user