Compare commits
204 Commits
android-sd
...
3579
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53f98df8f3 | ||
|
|
6616f728f6 | ||
|
|
1c1e8a942b | ||
|
|
9721d99918 | ||
|
|
69c21cbb11 | ||
|
|
abefc56750 | ||
|
|
a4a1685224 | ||
|
|
21fb225726 | ||
|
|
75ab890707 | ||
|
|
2ded8363ad | ||
|
|
c0a8a386a5 | ||
|
|
7af081ea99 | ||
|
|
8800cb4580 | ||
|
|
b658f20a30 | ||
|
|
c3e52f32f9 | ||
|
|
ab73d808fe | ||
|
|
e110460793 | ||
|
|
c0ee451ca1 | ||
|
|
67dca97d1d | ||
|
|
fd0ca76255 | ||
|
|
149e53af76 | ||
|
|
f3e7952e51 | ||
|
|
0f77cf9e0c | ||
|
|
66eb09fb63 | ||
|
|
b7f2c7d487 | ||
|
|
bed3f62536 | ||
|
|
e8eb7e1e1f | ||
|
|
1409d6fb5e | ||
|
|
a2cbd9229a | ||
|
|
bf50a110c7 | ||
|
|
467c9d36cf | ||
|
|
6ddd17c769 | ||
|
|
732f2c1963 | ||
|
|
e7144eb674 | ||
|
|
b5489b7c81 | ||
|
|
4d5332cadf | ||
|
|
5261f61110 | ||
|
|
8c7d6da5d5 | ||
|
|
6d91728a74 | ||
|
|
9b32fc95eb | ||
|
|
6bc1d87753 | ||
|
|
a5fc62b920 | ||
|
|
49f6010905 | ||
|
|
1c27f567ee | ||
|
|
7684b2bf98 | ||
|
|
8886bcdb73 | ||
|
|
437bdb92be | ||
|
|
6943659dc9 | ||
|
|
35f934e197 | ||
|
|
aca0469bd0 | ||
|
|
11f2e7291e | ||
|
|
7a985dd843 | ||
|
|
e09ea36055 | ||
|
|
ad3a087091 | ||
|
|
d379b4d53b | ||
|
|
7a99d57274 | ||
|
|
85bc0ed5fc | ||
|
|
f4dda6e7ce | ||
|
|
5c8dfabf7c | ||
|
|
20d03f3228 | ||
|
|
268137bb97 | ||
|
|
f87463780c | ||
|
|
6b2ced63c7 | ||
|
|
d6fbe55360 | ||
|
|
0feeb94fc7 | ||
|
|
c9c509ec51 | ||
|
|
f7ff457a3b | ||
|
|
72127aaa0b | ||
|
|
b8854a3b45 | ||
|
|
e6e7001857 | ||
|
|
6d4601fe66 | ||
|
|
8ecb01ec21 | ||
|
|
14ccb41015 | ||
|
|
348cde54e7 | ||
|
|
430bd3f141 | ||
|
|
b36f419e86 | ||
|
|
ed1d03db71 | ||
|
|
5d837571c0 | ||
|
|
f23532d7f4 | ||
|
|
0280294a8f | ||
|
|
fdd1418236 | ||
|
|
bae47434b1 | ||
|
|
3c98cfc136 | ||
|
|
17e5bc7b73 | ||
|
|
6092655099 | ||
|
|
721cca669f | ||
|
|
c62ff0939d | ||
|
|
ecf050c2d9 | ||
|
|
069137b02a | ||
|
|
f053cad66c | ||
|
|
4be3042cb0 | ||
|
|
593235d683 | ||
|
|
7bd8ddbba2 | ||
|
|
14232b1d93 | ||
|
|
ff4887a3c8 | ||
|
|
0289aacd4f | ||
|
|
99c13eaec0 | ||
|
|
1a713dfa27 | ||
|
|
7e84b51ee0 | ||
|
|
86fa442d0e | ||
|
|
93b88f1a16 | ||
|
|
2041d4fffb | ||
|
|
83c3e7c725 | ||
|
|
a34331891a | ||
|
|
56dd83740c | ||
|
|
debe50c3f4 | ||
|
|
d9985c786c | ||
|
|
ed910947d4 | ||
|
|
c4c5eace25 | ||
|
|
c6bf646fe8 | ||
|
|
ab2bb7686e | ||
|
|
12b53c38b9 | ||
|
|
ca31c8b199 | ||
|
|
1b331ce090 | ||
|
|
edd55e7798 | ||
|
|
9f4da84701 | ||
|
|
0097360396 | ||
|
|
b5cef05941 | ||
|
|
ddab9224b2 | ||
|
|
a5693c6e96 | ||
|
|
8dd345b192 | ||
|
|
130ab886ee | ||
|
|
c0e9f2b95a | ||
|
|
011972872e | ||
|
|
ef5720a3f0 | ||
|
|
ec30af2844 | ||
|
|
e08aeca28c | ||
|
|
ad7892ebce | ||
|
|
cbc7e1b6be | ||
|
|
da7d959b9a | ||
|
|
ada57ebcd0 | ||
|
|
1993ad10eb | ||
|
|
d305caf910 | ||
|
|
a25a504a59 | ||
|
|
250c61279b | ||
|
|
1e18af2d1a | ||
|
|
eb1fd4fd88 | ||
|
|
e0c8b6b3c0 | ||
|
|
9071cd4813 | ||
|
|
9bf650c700 | ||
|
|
49e3b03885 | ||
|
|
31e996ac3f | ||
|
|
d1c447e97b | ||
|
|
8a34c0a80f | ||
|
|
2f626ea474 | ||
|
|
0a76eebca7 | ||
|
|
0e9e695251 | ||
|
|
b86df7a8e3 | ||
|
|
5b25e02e26 | ||
|
|
0e6f14bb7c | ||
|
|
57b9954d9c | ||
|
|
249dd7b8b8 | ||
|
|
b31d7b4451 | ||
|
|
8dea3389ee | ||
|
|
e7f9e8e7f7 | ||
|
|
a53a86ee7a | ||
|
|
598b6f0598 | ||
|
|
b245945c4a | ||
|
|
301a8e319a | ||
|
|
820abfd059 | ||
|
|
1d42420a36 | ||
|
|
1b4bdb5142 | ||
|
|
f030a3f1fb | ||
|
|
8f79779ca7 | ||
|
|
c5111bb359 | ||
|
|
42814eac7d | ||
|
|
74d0013acc | ||
|
|
88e4850c4d | ||
|
|
87f9b1cf92 | ||
|
|
fe1187d7b7 | ||
|
|
a35b36d6df | ||
|
|
a04982fd96 | ||
|
|
658679f89e | ||
|
|
f90fc665f8 | ||
|
|
0c8130af41 | ||
|
|
e4c5968459 | ||
|
|
a148cd41a4 | ||
|
|
f8a049759d | ||
|
|
4b4225e14f | ||
|
|
3b0c5d0b6a | ||
|
|
3b750ddd5a | ||
|
|
6383d000a9 | ||
|
|
a48d67bdc7 | ||
|
|
2f92e72858 | ||
|
|
d2c85ada1b | ||
|
|
55b95c52d6 | ||
|
|
52362c4675 | ||
|
|
8da0552541 | ||
|
|
7ce0def995 | ||
|
|
48285e8a2d | ||
|
|
21dcc41d31 | ||
|
|
625d268373 | ||
|
|
681782ed20 | ||
|
|
1baa85b649 | ||
|
|
72137a2811 | ||
|
|
0734ce7ae3 | ||
|
|
2dc06c28e3 | ||
|
|
5848669552 | ||
|
|
c0376d238a | ||
|
|
979b773c3c | ||
|
|
3195a449ca | ||
|
|
d7483f07e3 | ||
|
|
9c1b802997 | ||
|
|
bb3a10b0fc |
2
Makefile
@@ -45,6 +45,8 @@ deploy-appbundle:
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.map \
|
||||
$(BUILD_DIR)/video-blur-effect.min.js \
|
||||
$(BUILD_DIR)/video-blur-effect.min.map \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
|
||||
@@ -207,24 +207,6 @@ public class MainActivity extends FragmentActivity implements JitsiMeetActivityI
|
||||
|
||||
</details>
|
||||
|
||||
Starting with SDK version 1.22, a Glide module must be provided by the host app.
|
||||
This makes it possible to use the Glide image processing library from both the
|
||||
SDK and the host app itself.
|
||||
|
||||
You can use the code in `JitsiGlideModule.java` and adjust the package name.
|
||||
When building, add the following code in your `app/build.gradle` file, adjusting
|
||||
the Glide version to match the one in https://github.com/jitsi/jitsi-meet/blob/master/android/build.gradle
|
||||
|
||||
```
|
||||
// Glide
|
||||
implementation("com.github.bumptech.glide:glide:${glideVersion}") {
|
||||
exclude group: "com.android.support", module: "glide"
|
||||
}
|
||||
implementation("com.github.bumptech.glide:annotations:${glideVersion}") {
|
||||
exclude group: "com.android.support", module: "annotations"
|
||||
}
|
||||
```
|
||||
|
||||
### JitsiMeetActivity
|
||||
|
||||
This class encapsulates a high level API in the form of an Android `FragmentActivity`
|
||||
|
||||
6
android/app/.classpath
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
23
android/app/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>app</name>
|
||||
<comment>Project app created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
2
android/app/.settings/org.eclipse.buildship.core.prefs
Normal file
@@ -0,0 +1,2 @@
|
||||
connection.project.dir=..
|
||||
eclipse.preferences.version=1
|
||||
@@ -88,15 +88,6 @@ dependencies {
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
|
||||
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
|
||||
|
||||
// Glide
|
||||
implementation("com.github.bumptech.glide:glide:${rootProject.ext.glideVersion}") {
|
||||
exclude group: "com.android.support", module: "glide"
|
||||
}
|
||||
implementation("com.github.bumptech.glide:annotations:${rootProject.ext.glideVersion}") {
|
||||
exclude group: "com.android.support", module: "annotations"
|
||||
}
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:${rootProject.ext.glideVersion}"
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
|
||||
12
android/app/proguard-rules.pro
vendored
@@ -60,19 +60,9 @@
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
-dontwarn java.nio.file.*
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||
-keep class okio.** { *; }
|
||||
-dontwarn okio.**
|
||||
|
||||
# FastImage + Glide
|
||||
|
||||
-keep public class com.dylanvann.fastimage.* {*;}
|
||||
-keep public class com.dylanvann.fastimage.** {*;}
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
# WebRTC
|
||||
|
||||
-keep class org.webrtc.** { *; }
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.jitsi.meet;
|
||||
|
||||
import com.bumptech.glide.annotation.GlideModule;
|
||||
import com.bumptech.glide.module.AppGlideModule;
|
||||
|
||||
/**
|
||||
* An AppGlideModule needs to be present for image loading events to work in
|
||||
* react-native-fast-image. However, if this is defined by the SDK it will cause trouble with
|
||||
* apps which are using Glide themselves.
|
||||
*
|
||||
* In order to avoid the problem, define a Jitsi Glide module here, so applications already using
|
||||
* it are not in trouble.
|
||||
*/
|
||||
@GlideModule
|
||||
public final class JitsiGlideModule extends AppGlideModule {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 2.3 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#66A8DD</color>
|
||||
</resources>
|
||||
@@ -164,10 +164,6 @@ ext {
|
||||
mavenUser = System.env.MVN_USER ?: ""
|
||||
mavenPassword = System.env.MVN_PASSWORD ?: ""
|
||||
|
||||
// Glide
|
||||
excludeAppGlideModule = true
|
||||
glideVersion = "4.7.1" // keep in sync with react-native-fast-image
|
||||
|
||||
// Libre build
|
||||
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
appVersion=19.2.0
|
||||
sdkVersion=2.2.0
|
||||
appVersion=19.3.0
|
||||
sdkVersion=2.2.2
|
||||
|
||||
@@ -8,13 +8,14 @@ THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOUR
|
||||
export RCT_METRO_PORT="${RCT_METRO_PORT:=8081}"
|
||||
echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" > "${THIS_DIR}/../../node_modules/react-native/scripts/.packager.env"
|
||||
|
||||
adb reverse tcp:8081 tcp:8081
|
||||
|
||||
if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then
|
||||
if ! curl -s "http://localhost:${RCT_METRO_PORT}/status" | grep -q "packager-status:running" ; then
|
||||
echo "Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly"
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
adb reverse tcp:8081 tcp:8081
|
||||
CMD="${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command"
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
open -g "${CMD}" || echo "Can't start packager automatically"
|
||||
|
||||
6
android/sdk/.classpath
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
23
android/sdk/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>sdk</name>
|
||||
<comment>Project sdk created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
2
android/sdk/.settings/org.eclipse.buildship.core.prefs
Normal file
@@ -0,0 +1,2 @@
|
||||
connection.project.dir=..
|
||||
eclipse.preferences.version=1
|
||||
@@ -54,9 +54,6 @@ dependencies {
|
||||
implementation project(':react-native-background-timer')
|
||||
implementation project(':react-native-calendar-events')
|
||||
implementation project(':react-native-community-async-storage')
|
||||
implementation(project(':react-native-fast-image')) {
|
||||
exclude group: 'com.android.support'
|
||||
}
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-linear-gradient')
|
||||
@@ -152,6 +149,8 @@ android.libraryVariants.all { def variant ->
|
||||
from("${projectDir}/../../sounds/incomingMessage.wav")
|
||||
from("${projectDir}/../../sounds/joined.wav")
|
||||
from("${projectDir}/../../sounds/left.wav")
|
||||
from("${projectDir}/../../sounds/liveStreamingOn.mp3")
|
||||
from("${projectDir}/../../sounds/liveStreamingOff.mp3")
|
||||
from("${projectDir}/../../sounds/outgoingRinging.wav")
|
||||
from("${projectDir}/../../sounds/outgoingStart.wav")
|
||||
from("${projectDir}/../../sounds/recordingOn.mp3")
|
||||
|
||||
@@ -256,6 +256,11 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
private static final String DEVICE_HEADPHONES = "HEADPHONES";
|
||||
private static final String DEVICE_SPEAKER = "SPEAKER";
|
||||
|
||||
/**
|
||||
* Device change event.
|
||||
*/
|
||||
private static final String DEVICE_CHANGE_EVENT = "org.jitsi.meet:features/audio-mode#devices-update";
|
||||
|
||||
/**
|
||||
* List of currently available audio devices.
|
||||
*/
|
||||
@@ -303,7 +308,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// Do an initial detection on Android >= M.
|
||||
runInAudioThread(onAudioDeviceChangeRunner);
|
||||
onAudioDeviceChange();
|
||||
} else {
|
||||
// On Android < M, detect if we have an earpiece.
|
||||
PackageManager pm = reactContext.getPackageManager();
|
||||
@@ -327,6 +332,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
public Map<String, Object> getConstants() {
|
||||
Map<String, Object> constants = new HashMap<>();
|
||||
|
||||
constants.put("DEVICE_CHANGE_EVENT", DEVICE_CHANGE_EVENT);
|
||||
constants.put("AUDIO_CALL", AUDIO_CALL);
|
||||
constants.put("DEFAULT", DEFAULT);
|
||||
constants.put("VIDEO_CALL", VIDEO_CALL);
|
||||
@@ -335,31 +341,26 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of available audio device categories, i.e. 'bluetooth',
|
||||
* 'earpiece ', 'speaker', 'headphones'.
|
||||
*
|
||||
* @param promise a {@link Promise} which will be resolved with an object
|
||||
* containing a 'devices' key with a list of devices, plus a
|
||||
* 'selected' key with the selected one.
|
||||
* Notifies JS land that the devices list has changed.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void getAudioDevices(final Promise promise) {
|
||||
private void notifyDevicesChanged() {
|
||||
runInAudioThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WritableMap map = Arguments.createMap();
|
||||
map.putString("selected", selectedDevice);
|
||||
WritableArray devices = Arguments.createArray();
|
||||
WritableArray data = Arguments.createArray();
|
||||
final boolean hasHeadphones = availableDevices.contains(DEVICE_HEADPHONES);
|
||||
for (String device : availableDevices) {
|
||||
if (mode == VIDEO_CALL && device.equals(DEVICE_EARPIECE)) {
|
||||
// Skip earpiece when in video call mode.
|
||||
if (hasHeadphones && device.equals(DEVICE_EARPIECE)) {
|
||||
// Skip earpiece when headphones are plugged in.
|
||||
continue;
|
||||
}
|
||||
devices.pushString(device);
|
||||
WritableMap deviceInfo = Arguments.createMap();
|
||||
deviceInfo.putString("type", device);
|
||||
deviceInfo.putBoolean("selected", device.equals(selectedDevice));
|
||||
data.pushMap(deviceInfo);
|
||||
}
|
||||
map.putArray("devices", devices);
|
||||
|
||||
promise.resolve(map);
|
||||
ReactInstanceManagerHolder.emitEvent(DEVICE_CHANGE_EVENT, data);
|
||||
Log.i(TAG, "Updating audio device list");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -584,7 +585,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
runInAudioThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean success;
|
||||
@@ -607,8 +608,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
"Failed to set audio mode to " + mode);
|
||||
}
|
||||
}
|
||||
};
|
||||
runInAudioThread(r);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -690,6 +690,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
selectedDevice = null;
|
||||
userSelectedDevice = null;
|
||||
|
||||
notifyDevicesChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -708,7 +709,6 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
}
|
||||
|
||||
boolean bluetoothAvailable = availableDevices.contains(DEVICE_BLUETOOTH);
|
||||
boolean earpieceAvailable = availableDevices.contains(DEVICE_EARPIECE);
|
||||
boolean headsetAvailable = availableDevices.contains(DEVICE_HEADPHONES);
|
||||
|
||||
// Pick the desired device based on what's available and the mode.
|
||||
@@ -717,8 +717,6 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
audioDevice = DEVICE_BLUETOOTH;
|
||||
} else if (headsetAvailable) {
|
||||
audioDevice = DEVICE_HEADPHONES;
|
||||
} else if (mode == AUDIO_CALL && earpieceAvailable) {
|
||||
audioDevice = DEVICE_EARPIECE;
|
||||
} else {
|
||||
audioDevice = DEVICE_SPEAKER;
|
||||
}
|
||||
@@ -744,6 +742,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
|
||||
setAudioRoutePreO(audioDevice);
|
||||
}
|
||||
|
||||
notifyDevicesChanged();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
dest.writeString(token);
|
||||
dest.writeBundle(colorScheme);
|
||||
dest.writeBundle(featureFlags);
|
||||
dest.writeBundle(userInfo.asBundle());
|
||||
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());
|
||||
dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2));
|
||||
dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2));
|
||||
dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2));
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -47,7 +48,12 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
intent.setAction(Actions.START);
|
||||
|
||||
ComponentName componentName = context.startService(intent);
|
||||
ComponentName componentName;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
componentName = context.startForegroundService(intent);
|
||||
} else {
|
||||
componentName = context.startService(intent);
|
||||
}
|
||||
if (componentName == null) {
|
||||
Log.w(TAG, "Ongoing conference service not started");
|
||||
}
|
||||
@@ -82,8 +88,13 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
final String action = intent.getAction();
|
||||
if (action.equals(Actions.START)) {
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification();
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
Log.i(TAG, "Service started");
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
Log.w(TAG, "Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
Log.i(TAG, "Service started");
|
||||
}
|
||||
} else if (action.equals(Actions.HANGUP)) {
|
||||
Log.i(TAG, "Hangup requested");
|
||||
// Abort all ongoing calls
|
||||
|
||||
@@ -39,13 +39,6 @@ class JitsiMeetUncaughtExceptionHandler implements Thread.UncaughtExceptionHandl
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
Log.e(this.getClass().getSimpleName(), "FATAL ERROR", e);
|
||||
|
||||
// Terminate all conferences
|
||||
for (BaseReactView view: BaseReactView.getViews()) {
|
||||
if (view instanceof JitsiMeetView) {
|
||||
((JitsiMeetView) view).leave();
|
||||
}
|
||||
}
|
||||
|
||||
// Abort all ConnectionService ongoing calls
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
ConnectionService.abortConnections();
|
||||
|
||||
@@ -86,8 +86,10 @@ class OngoingConferenceTracker {
|
||||
}
|
||||
|
||||
private void updateListeners() {
|
||||
for (OngoingConferenceListener listener : listeners) {
|
||||
listener.onCurrentConferenceChanged(currentConference);
|
||||
synchronized (listeners) {
|
||||
for (OngoingConferenceListener listener : listeners) {
|
||||
listener.onCurrentConferenceChanged(currentConference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,17 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.devsupport.DevInternalSettings;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
|
||||
import org.webrtc.SoftwareVideoDecoderFactory;
|
||||
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||
import org.webrtc.VideoDecoderFactory;
|
||||
import org.webrtc.VideoEncoderFactory;
|
||||
import org.webrtc.audio.AudioDeviceModule;
|
||||
import org.webrtc.audio.JavaAudioDeviceModule;
|
||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
@@ -47,8 +58,7 @@ class ReactInstanceManagerHolder {
|
||||
*/
|
||||
private static ReactInstanceManager reactInstanceManager;
|
||||
|
||||
private static List<NativeModule> createNativeModules(
|
||||
ReactApplicationContext reactContext) {
|
||||
private static List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> nativeModules
|
||||
= new ArrayList<>(Arrays.<NativeModule>asList(
|
||||
new AndroidSettingsModule(reactContext),
|
||||
@@ -66,6 +76,21 @@ class ReactInstanceManagerHolder {
|
||||
nativeModules.add(new RNConnectionService(reactContext));
|
||||
}
|
||||
|
||||
// Initialize the WebRTC module by hand, since we want to override some
|
||||
// initialization options.
|
||||
WebRTCModule.Options options = new WebRTCModule.Options();
|
||||
|
||||
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
|
||||
.createAudioDeviceModule();
|
||||
VideoDecoderFactory videoDecoderFactory = new SoftwareVideoDecoderFactory();
|
||||
VideoEncoderFactory videoEncoderFactory = new SoftwareVideoEncoderFactory();
|
||||
|
||||
options.setAudioDeviceModule(adm);
|
||||
options.setVideoDecoderFactory(videoDecoderFactory);
|
||||
options.setVideoEncoderFactory(videoEncoderFactory);
|
||||
|
||||
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||
|
||||
try {
|
||||
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
|
||||
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
|
||||
@@ -77,6 +102,13 @@ class ReactInstanceManagerHolder {
|
||||
return nativeModules;
|
||||
}
|
||||
|
||||
private static List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Arrays.<ViewManager>asList(
|
||||
// WebRTC, see createNativeModules for details.
|
||||
new RTCVideoViewManager()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to send an event to JavaScript.
|
||||
*
|
||||
@@ -155,11 +187,9 @@ class ReactInstanceManagerHolder {
|
||||
new com.BV.LinearGradient.LinearGradientPackage(),
|
||||
new com.calendarevents.CalendarEventsPackage(),
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.dylanvann.fastimage.FastImageViewPackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.oblador.vectoricons.VectorIconsPackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
@@ -169,6 +199,10 @@ class ReactInstanceManagerHolder {
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createNativeModules(reactContext);
|
||||
}
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createViewManagers(reactContext);
|
||||
}
|
||||
}));
|
||||
|
||||
try {
|
||||
|
||||
@@ -144,8 +144,7 @@ class WiFiStatsModule
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("rssi", rssi)
|
||||
.put("signal", signalLevel)
|
||||
.put("timestamp",
|
||||
String.valueOf(System.currentTimeMillis()));
|
||||
.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
JSONArray addresses = new JSONArray();
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ include ':react-native-calendar-events'
|
||||
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
|
||||
include ':react-native-community-async-storage'
|
||||
project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
|
||||
include ':react-native-fast-image'
|
||||
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
|
||||
include ':react-native-google-signin'
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-signin/android')
|
||||
include ':react-native-immersive'
|
||||
|
||||
251
conference.js
@@ -23,7 +23,8 @@ import {
|
||||
sendAnalytics
|
||||
} from './react/features/analytics';
|
||||
import {
|
||||
redirectWithStoredParams,
|
||||
maybeRedirectToWelcomePage,
|
||||
redirectToStaticPage,
|
||||
reloadWithStoredParams
|
||||
} from './react/features/app';
|
||||
|
||||
@@ -43,6 +44,7 @@ import {
|
||||
conferenceWillJoin,
|
||||
conferenceWillLeave,
|
||||
dataChannelOpened,
|
||||
kickedOut,
|
||||
lockStateChanged,
|
||||
onStartMutedPolicyChanged,
|
||||
p2pStatusChanged,
|
||||
@@ -96,15 +98,12 @@ import {
|
||||
createLocalTracksF,
|
||||
destroyLocalTracks,
|
||||
isLocalTrackMuted,
|
||||
isUserInteractionRequiredForUnmute,
|
||||
replaceLocalTrack,
|
||||
trackAdded,
|
||||
trackRemoved
|
||||
} from './react/features/base/tracks';
|
||||
import {
|
||||
getLocationContextRoot,
|
||||
getJitsiMeetGlobalNS
|
||||
} from './react/features/base/util';
|
||||
import { notifyKickedOut } from './react/features/conference';
|
||||
import { getJitsiMeetGlobalNS } from './react/features/base/util';
|
||||
import { addMessage } from './react/features/chat';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
@@ -112,10 +111,8 @@ import {
|
||||
maybeOpenFeedbackDialog,
|
||||
submitFeedback
|
||||
} from './react/features/feedback';
|
||||
import {
|
||||
mediaPermissionPromptVisibilityChanged,
|
||||
suspendDetected
|
||||
} from './react/features/overlay';
|
||||
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
|
||||
import { suspendDetected } from './react/features/power-monitor';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video';
|
||||
import { isButtonEnabled } from './react/features/toolbox';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
@@ -211,77 +208,6 @@ function muteLocalVideo(muted) {
|
||||
APP.store.dispatch(setVideoMuted(muted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the welcome page is enabled and redirects to it.
|
||||
* If requested show a thank you dialog before that.
|
||||
* If we have a close page enabled, redirect to it without
|
||||
* showing any other dialog.
|
||||
*
|
||||
* @param {object} options used to decide which particular close page to show
|
||||
* or if close page is disabled, whether we should show the thankyou dialog
|
||||
* @param {boolean} options.showThankYou - whether we should
|
||||
* show thank you dialog
|
||||
* @param {boolean} options.feedbackSubmitted - whether feedback was submitted
|
||||
*/
|
||||
function maybeRedirectToWelcomePage(options) {
|
||||
// if close page is enabled redirect to it, without further action
|
||||
if (config.enableClosePage) {
|
||||
const { isGuest } = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
// save whether current user is guest or not, before navigating
|
||||
// to close page
|
||||
window.sessionStorage.setItem('guest', isGuest);
|
||||
redirectToStaticPage(`static/${
|
||||
options.feedbackSubmitted ? 'close.html' : 'close2.html'}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// else: show thankYou dialog only if there is no feedback
|
||||
if (options.showThankYou) {
|
||||
APP.store.dispatch(showNotification({
|
||||
titleArguments: { appName: interfaceConfig.APP_NAME },
|
||||
titleKey: 'dialog.thankYou'
|
||||
}));
|
||||
}
|
||||
|
||||
// if Welcome page is enabled redirect to welcome page after 3 sec, if
|
||||
// there is a thank you message to be shown, 0.5s otherwise.
|
||||
if (config.enableWelcomePage) {
|
||||
setTimeout(
|
||||
() => {
|
||||
APP.store.dispatch(redirectWithStoredParams('/'));
|
||||
},
|
||||
options.showThankYou ? 3000 : 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a specific pathname to window.location.pathname taking into account
|
||||
* the context root of the Web app.
|
||||
*
|
||||
* @param {string} pathname - The pathname to assign to
|
||||
* window.location.pathname. If the specified pathname is relative, the context
|
||||
* root of the Web app will be prepended to the specified pathname before
|
||||
* assigning it to window.location.pathname.
|
||||
* @return {void}
|
||||
*/
|
||||
function redirectToStaticPage(pathname) {
|
||||
const windowLocation = window.location;
|
||||
let newPathname = pathname;
|
||||
|
||||
if (!newPathname.startsWith('/')) {
|
||||
// A pathname equal to ./ specifies the current directory. It will be
|
||||
// fine but pointless to include it because contextRoot is the current
|
||||
// directory.
|
||||
newPathname.startsWith('./')
|
||||
&& (newPathname = newPathname.substring(2));
|
||||
newPathname = getLocationContextRoot(windowLocation) + newPathname;
|
||||
}
|
||||
|
||||
windowLocation.pathname = newPathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* A queue for the async replaceLocalTrack action so that multiple audio
|
||||
* replacements cannot happen simultaneously. This solves the issue where
|
||||
@@ -347,7 +273,7 @@ class ConferenceConnector {
|
||||
|
||||
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
|
||||
// let's show some auth not allowed page
|
||||
redirectToStaticPage('static/authError.html');
|
||||
APP.store.dispatch(redirectToStaticPage('static/authError.html'));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -629,8 +555,7 @@ export default {
|
||||
// Resolve with no tracks
|
||||
tryCreateLocalTracks = Promise.resolve([]);
|
||||
} else {
|
||||
tryCreateLocalTracks = createLocalTracksF(
|
||||
{ devices: initialDevices }, true)
|
||||
tryCreateLocalTracks = createLocalTracksF({ devices: initialDevices }, true)
|
||||
.catch(err => {
|
||||
if (requestedAudio && requestedVideo) {
|
||||
|
||||
@@ -734,8 +659,11 @@ export default {
|
||||
options.roomName, {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: config.startWithAudioMuted || config.startSilent,
|
||||
startWithAudioMuted: config.startWithAudioMuted
|
||||
|| config.startSilent
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
|
||||
startWithVideoMuted: config.startWithVideoMuted
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState())
|
||||
}))
|
||||
.then(([ tracks, con ]) => {
|
||||
tracks.forEach(track => {
|
||||
@@ -836,6 +764,13 @@ export default {
|
||||
* dialogs in case of media permissions error.
|
||||
*/
|
||||
muteAudio(mute, showUI = true) {
|
||||
if (!mute
|
||||
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
|
||||
logger.error('Unmuting audio requires user interaction');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not ready to modify track's state yet
|
||||
if (!this._localTracksInitialized) {
|
||||
// This will only modify base/media.audio.muted which is then synced
|
||||
@@ -899,6 +834,13 @@ export default {
|
||||
* dialogs in case of media permissions error.
|
||||
*/
|
||||
muteVideo(mute, showUI = true) {
|
||||
if (!mute
|
||||
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
|
||||
logger.error('Unmuting video requires user interaction');
|
||||
|
||||
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
|
||||
@@ -1018,17 +960,15 @@ export default {
|
||||
* Returns the connection times stored in the library.
|
||||
*/
|
||||
getConnectionTimes() {
|
||||
return this._room.getConnectionTimes();
|
||||
return room.getConnectionTimes();
|
||||
},
|
||||
|
||||
// used by torture currently
|
||||
isJoined() {
|
||||
return this._room
|
||||
&& this._room.isJoined();
|
||||
return room && room.isJoined();
|
||||
},
|
||||
getConnectionState() {
|
||||
return this._room
|
||||
&& this._room.getConnectionState();
|
||||
return room && room.getConnectionState();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1037,8 +977,7 @@ export default {
|
||||
* P2P connection
|
||||
*/
|
||||
getP2PConnectionState() {
|
||||
return this._room
|
||||
&& this._room.getP2PConnectionState();
|
||||
return room && room.getP2PConnectionState();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1047,7 +986,7 @@ export default {
|
||||
*/
|
||||
_startP2P() {
|
||||
try {
|
||||
this._room && this._room.startP2PSession();
|
||||
room && room.startP2PSession();
|
||||
} catch (error) {
|
||||
logger.error('Start P2P failed', error);
|
||||
throw error;
|
||||
@@ -1060,7 +999,7 @@ export default {
|
||||
*/
|
||||
_stopP2P() {
|
||||
try {
|
||||
this._room && this._room.stopP2PSession();
|
||||
room && room.stopP2PSession();
|
||||
} catch (error) {
|
||||
logger.error('Stop P2P failed', error);
|
||||
throw error;
|
||||
@@ -1075,7 +1014,7 @@ export default {
|
||||
* false otherwise.
|
||||
*/
|
||||
isConnectionInterrupted() {
|
||||
return this._room.isConnectionInterrupted();
|
||||
return room.isConnectionInterrupted();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1136,7 +1075,7 @@ export default {
|
||||
},
|
||||
|
||||
getMyUserId() {
|
||||
return this._room && this._room.myUserId();
|
||||
return room && room.myUserId();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1159,7 +1098,7 @@ export default {
|
||||
* least one track.
|
||||
*/
|
||||
getNumberOfParticipantsWithTracks() {
|
||||
return this._room.getParticipants()
|
||||
return room.getParticipants()
|
||||
.filter(p => p.getTracks().length > 0)
|
||||
.length;
|
||||
},
|
||||
@@ -1297,17 +1236,34 @@ export default {
|
||||
const options = config;
|
||||
|
||||
const nick = APP.store.getState()['features/base/settings'].displayName;
|
||||
const { locationURL } = APP.store.getState()['features/base/connection'];
|
||||
|
||||
if (nick) {
|
||||
options.displayName = nick;
|
||||
}
|
||||
|
||||
options.applicationName = interfaceConfig.APP_NAME;
|
||||
options.getWiFiStatsMethod = getJitsiMeetGlobalNS().getWiFiStats;
|
||||
options.getWiFiStatsMethod = this._getWiFiStatsMethod;
|
||||
options.confID = `${locationURL.host}${locationURL.pathname}`;
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the result of getWiFiStats from the global NS or does nothing
|
||||
* (returns empty result).
|
||||
* Fixes a concurrency problem where we need to pass a function when creating
|
||||
* JitsiConference, but that method is added to the context later.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
_getWiFiStatsMethod() {
|
||||
const gloabalNS = getJitsiMeetGlobalNS();
|
||||
|
||||
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
|
||||
},
|
||||
|
||||
/**
|
||||
* Start using provided video stream.
|
||||
* Stops previous video stream.
|
||||
@@ -1323,7 +1279,7 @@ export default {
|
||||
this.localVideo = newStream;
|
||||
this._setSharingScreen(newStream);
|
||||
if (newStream) {
|
||||
APP.UI.addLocalStream(newStream);
|
||||
APP.UI.addLocalVideoStream(newStream);
|
||||
}
|
||||
this.setVideoMuteStatus(this.isLocalVideoMuted());
|
||||
})
|
||||
@@ -1374,9 +1330,6 @@ export default {
|
||||
replaceLocalTrack(this.localAudio, newStream, room))
|
||||
.then(() => {
|
||||
this.localAudio = newStream;
|
||||
if (newStream) {
|
||||
APP.UI.addLocalStream(newStream);
|
||||
}
|
||||
this.setAudioMuteStatus(this.isLocalAudioMuted());
|
||||
})
|
||||
.then(resolve)
|
||||
@@ -1392,8 +1345,7 @@ export default {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isAudioOnly() {
|
||||
return Boolean(
|
||||
APP.store.getState()['features/base/conference'].audioOnly);
|
||||
return Boolean(APP.store.getState()['features/base/audio-only'].enabled);
|
||||
},
|
||||
|
||||
videoSwitchInProgress: false,
|
||||
@@ -1757,14 +1709,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const displayName = user.getDisplayName();
|
||||
|
||||
logger.log(`USER ${id} connnected:`, user);
|
||||
APP.API.notifyUserJoined(id, {
|
||||
displayName,
|
||||
formattedDisplayName: appendSuffix(
|
||||
displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME)
|
||||
});
|
||||
APP.UI.addUser(user);
|
||||
|
||||
// check the roles for the new user and reflect them
|
||||
@@ -1780,12 +1725,7 @@ export default {
|
||||
}
|
||||
|
||||
logger.log(`USER ${id} LEFT:`, user);
|
||||
APP.API.notifyUserLeft(id);
|
||||
APP.UI.messageHandler.participantNotification(
|
||||
user.getDisplayName(),
|
||||
'notify.somebody',
|
||||
'disconnected',
|
||||
'notify.disconnected');
|
||||
|
||||
APP.UI.onSharedVideoStop(id);
|
||||
});
|
||||
|
||||
@@ -1854,15 +1794,12 @@ export default {
|
||||
APP.UI.setAudioLevel(id, newLvl);
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (_, participantThatMutedUs) => {
|
||||
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (track, participantThatMutedUs) => {
|
||||
if (participantThatMutedUs) {
|
||||
APP.store.dispatch(participantMutedUs(participantThatMutedUs));
|
||||
}
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.TALK_WHILE_MUTED, () => {
|
||||
APP.UI.showToolbar(6000);
|
||||
});
|
||||
room.on(JitsiConferenceEvents.SUBJECT_CHANGED,
|
||||
subject => APP.store.dispatch(conferenceSubjectChanged(subject)));
|
||||
|
||||
@@ -1962,7 +1899,7 @@ export default {
|
||||
|
||||
room.on(JitsiConferenceEvents.KICKED, participant => {
|
||||
APP.UI.hideStats();
|
||||
APP.store.dispatch(notifyKickedOut(participant));
|
||||
APP.store.dispatch(kickedOut(room, participant));
|
||||
|
||||
// FIXME close
|
||||
});
|
||||
@@ -1973,33 +1910,6 @@ export default {
|
||||
|
||||
room.on(JitsiConferenceEvents.SUSPEND_DETECTED, () => {
|
||||
APP.store.dispatch(suspendDetected());
|
||||
|
||||
// After wake up, we will be in a state where conference is left
|
||||
// there will be dialog shown to user.
|
||||
// We do not want video/audio as we show an overlay and after it
|
||||
// user need to rejoin or close, while waking up we can detect
|
||||
// camera wakeup as a problem with device.
|
||||
// We also do not care about device change, which happens
|
||||
// on resume after suspending PC.
|
||||
if (this.deviceChangeListener) {
|
||||
JitsiMeetJS.mediaDevices.removeEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
|
||||
// stop local video
|
||||
if (this.localVideo) {
|
||||
this.localVideo.dispose();
|
||||
this.localVideo = null;
|
||||
}
|
||||
|
||||
// stop local audio
|
||||
if (this.localAudio) {
|
||||
this.localAudio.dispose();
|
||||
this.localAudio = null;
|
||||
}
|
||||
|
||||
APP.API.notifySuspendDetected();
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
|
||||
@@ -2255,6 +2165,27 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanups local conference on suspend.
|
||||
*/
|
||||
onSuspendDetected() {
|
||||
// After wake up, we will be in a state where conference is left
|
||||
// there will be dialog shown to user.
|
||||
// We do not want video/audio as we show an overlay and after it
|
||||
// user need to rejoin or close, while waking up we can detect
|
||||
// camera wakeup as a problem with device.
|
||||
// We also do not care about device change, which happens
|
||||
// on resume after suspending PC.
|
||||
if (this.deviceChangeListener) {
|
||||
JitsiMeetJS.mediaDevices.removeEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
|
||||
this.localVideo = null;
|
||||
this.localAudio = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked when the conference has been successfully joined.
|
||||
* Initializes the UI and various other features.
|
||||
@@ -2269,7 +2200,7 @@ export default {
|
||||
|
||||
if (config.requireDisplayName
|
||||
&& !APP.conference.getLocalDisplayName()
|
||||
&& !this._room.isHidden()) {
|
||||
&& !room.isHidden()) {
|
||||
APP.UI.promptDisplayName();
|
||||
}
|
||||
|
||||
@@ -2601,7 +2532,7 @@ export default {
|
||||
room = undefined;
|
||||
|
||||
APP.API.notifyReadyToClose();
|
||||
maybeRedirectToWelcomePage(values[0]);
|
||||
APP.store.dispatch(maybeRedirectToWelcomePage(values[0]));
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2613,7 +2544,11 @@ export default {
|
||||
leaveRoomAndDisconnect() {
|
||||
APP.store.dispatch(conferenceWillLeave(room));
|
||||
|
||||
return room.leave().then(disconnect, disconnect);
|
||||
if (room.isJoined()) {
|
||||
return room.leave().then(disconnect, disconnect);
|
||||
}
|
||||
|
||||
return disconnect();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2795,6 +2730,18 @@ export default {
|
||||
*/
|
||||
convertVideoToDesktop: true,
|
||||
|
||||
/**
|
||||
* Callback invoked when the connection has been closed
|
||||
* automatically. Triggers cleanup of screensharing if active.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
onConnectionClosed: () => {
|
||||
if (this._untoggleScreenSharing) {
|
||||
this._untoggleScreenSharing();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked to pass messages from the local client back
|
||||
* out to the external client.
|
||||
|
||||
@@ -418,6 +418,10 @@ var config = {
|
||||
// use only.
|
||||
// _desktopSharingSourceDevice: 'sample-id-or-label'
|
||||
|
||||
// If true, any checks to handoff to another application will be prevented
|
||||
// and instead the app will continue to display in the current browser.
|
||||
// disableDeepLinking: false
|
||||
|
||||
// A property to disable the right click context menu for localVideo
|
||||
// the menu has option to flip the locally seen video for local presentations
|
||||
// disableLocalVideoFlip: false
|
||||
|
||||
31
css/_avatar.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
.avatar {
|
||||
align-items: center;
|
||||
background-color: #AAA;
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-weight: 100;
|
||||
justify-content: center;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-foreign {
|
||||
align-items: center;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
font-size: 40pt;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.avatar-svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.defaultAvatar {
|
||||
opacity: 0.6
|
||||
}
|
||||
192
css/_font.scss
@@ -1,12 +1,12 @@
|
||||
@font-face {
|
||||
font-family: 'jitsi';
|
||||
src: url('../fonts/jitsi.eot?3vw865');
|
||||
src: url('../fonts/jitsi.eot?3vw865#iefix') format('embedded-opentype'),
|
||||
url('../fonts/jitsi.ttf?3vw865') format('truetype'),
|
||||
url('../fonts/jitsi.woff?3vw865') format('woff'),
|
||||
url('../fonts/jitsi.svg?3vw865#jitsi') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'jitsi';
|
||||
src: url('../fonts/jitsi.eot?icrce1');
|
||||
src: url('../fonts/jitsi.eot?icrce1#iefix') format('embedded-opentype'),
|
||||
url('../fonts/jitsi.ttf?icrce1') format('truetype'),
|
||||
url('../fonts/jitsi.woff?icrce1') format('woff'),
|
||||
url('../fonts/jitsi.svg?icrce1#jitsi') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
@@ -25,92 +25,9 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-enlarge:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
.icon-signal_cellular_0:before {
|
||||
content: "\e901";
|
||||
}
|
||||
.icon-signal_cellular_1:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.icon-signal_cellular_2:before {
|
||||
content: "\e907";
|
||||
}
|
||||
.icon-phone:before {
|
||||
content: "\e0cd";
|
||||
}
|
||||
.icon-radio_button_unchecked:before {
|
||||
content: "\e836";
|
||||
}
|
||||
.icon-radio_button_checked:before {
|
||||
content: "\e837";
|
||||
}
|
||||
.icon-search:before {
|
||||
content: "\e8b6";
|
||||
}
|
||||
.icon-chat-unread:before {
|
||||
content: "\e0b7";
|
||||
}
|
||||
.icon-closed_caption:before {
|
||||
content: "\e930";
|
||||
}
|
||||
.icon-tiles-many:before {
|
||||
content: "\e92e";
|
||||
}
|
||||
.icon-close:before {
|
||||
content: "\e5cd";
|
||||
}
|
||||
.icon-open_in_new:before {
|
||||
content: "\e89e";
|
||||
}
|
||||
.icon-restore:before {
|
||||
content: "\e8b3";
|
||||
}
|
||||
.icon-navigate_next:before {
|
||||
content: "\e409";
|
||||
}
|
||||
.icon-menu:before {
|
||||
content: "\e5d2";
|
||||
}
|
||||
.icon-arrow_back:before {
|
||||
content: "\e5c4";
|
||||
}
|
||||
.icon-public:before {
|
||||
content: "\e80b";
|
||||
}
|
||||
.icon-event_note:before {
|
||||
content: "\e616";
|
||||
}
|
||||
.icon-bluetooth:before {
|
||||
content: "\e1aa";
|
||||
}
|
||||
.icon-headset:before {
|
||||
content: "\e310";
|
||||
}
|
||||
.icon-phone-talk:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
.icon-thumb-menu:before {
|
||||
content: "\e5d4";
|
||||
}
|
||||
.icon-ninja:before {
|
||||
content: "\e909";
|
||||
}
|
||||
.icon-invite:before {
|
||||
content: "\e145";
|
||||
}
|
||||
.icon-add:before {
|
||||
content: "\e146";
|
||||
}
|
||||
.icon-play:before {
|
||||
content: "\f04b";
|
||||
}
|
||||
.icon-stop:before {
|
||||
content: "\f04d";
|
||||
}
|
||||
.icon-dominant-speaker:before {
|
||||
content: "\f0a1";
|
||||
.icon-blur-background:before {
|
||||
content: "\e90f";
|
||||
color: #a4b8d1;
|
||||
}
|
||||
.icon-speaker:before {
|
||||
content: "\e92d";
|
||||
@@ -220,3 +137,90 @@
|
||||
.icon-visibility-off:before {
|
||||
content: "\e924";
|
||||
}
|
||||
.icon-enlarge:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
.icon-signal_cellular_0:before {
|
||||
content: "\e901";
|
||||
}
|
||||
.icon-signal_cellular_1:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.icon-signal_cellular_2:before {
|
||||
content: "\e907";
|
||||
}
|
||||
.icon-phone:before {
|
||||
content: "\e0cd";
|
||||
}
|
||||
.icon-radio_button_unchecked:before {
|
||||
content: "\e836";
|
||||
}
|
||||
.icon-radio_button_checked:before {
|
||||
content: "\e837";
|
||||
}
|
||||
.icon-search:before {
|
||||
content: "\e8b6";
|
||||
}
|
||||
.icon-chat-unread:before {
|
||||
content: "\e0b7";
|
||||
}
|
||||
.icon-closed_caption:before {
|
||||
content: "\e930";
|
||||
}
|
||||
.icon-tiles-many:before {
|
||||
content: "\e92e";
|
||||
}
|
||||
.icon-close:before {
|
||||
content: "\e5cd";
|
||||
}
|
||||
.icon-open_in_new:before {
|
||||
content: "\e89e";
|
||||
}
|
||||
.icon-restore:before {
|
||||
content: "\e8b3";
|
||||
}
|
||||
.icon-navigate_next:before {
|
||||
content: "\e409";
|
||||
}
|
||||
.icon-menu:before {
|
||||
content: "\e5d2";
|
||||
}
|
||||
.icon-arrow_back:before {
|
||||
content: "\e5c4";
|
||||
}
|
||||
.icon-public:before {
|
||||
content: "\e80b";
|
||||
}
|
||||
.icon-event_note:before {
|
||||
content: "\e616";
|
||||
}
|
||||
.icon-bluetooth:before {
|
||||
content: "\e1aa";
|
||||
}
|
||||
.icon-headset:before {
|
||||
content: "\e310";
|
||||
}
|
||||
.icon-phone-talk:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
.icon-thumb-menu:before {
|
||||
content: "\e5d4";
|
||||
}
|
||||
.icon-ninja:before {
|
||||
content: "\e909";
|
||||
}
|
||||
.icon-invite:before {
|
||||
content: "\e145";
|
||||
}
|
||||
.icon-add:before {
|
||||
content: "\e146";
|
||||
}
|
||||
.icon-play:before {
|
||||
content: "\f04b";
|
||||
}
|
||||
.icon-stop:before {
|
||||
content: "\f04d";
|
||||
}
|
||||
.icon-dominant-speaker:before {
|
||||
content: "\f0a1";
|
||||
}
|
||||
|
||||
@@ -493,7 +493,6 @@
|
||||
}
|
||||
|
||||
#dominantSpeakerAvatarContainer,
|
||||
#dominantSpeakerAvatar,
|
||||
.dynamic-shadow {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
@@ -503,14 +502,9 @@
|
||||
top: 50px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
border-radius: 100px;
|
||||
overflow: hidden;
|
||||
visibility: inherit;
|
||||
}
|
||||
#dominantSpeakerAvatar {
|
||||
background-color: #000000;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dynamic-shadow {
|
||||
border-radius: 50%;
|
||||
@@ -524,7 +518,6 @@
|
||||
.avatar-container {
|
||||
@include maxSize(60px);
|
||||
@include absoluteAligning();
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 50%;
|
||||
|
||||
@@ -86,5 +86,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'navigate_section_list';
|
||||
@import 'third-party-branding/google';
|
||||
@import 'third-party-branding/microsoft';
|
||||
@import 'avatar';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
7
debian/rules
vendored
@@ -17,8 +17,15 @@ override_dh_install: $(LANGUAGES)
|
||||
dh_install -X/config.js -X/package.json
|
||||
|
||||
$(LANGUAGES):
|
||||
LOCALE=$$(echo $@ | cut -c1-2) ; \
|
||||
if [ -f $(COUNTRIES_DIR)/$@.json ] ; \
|
||||
then \
|
||||
dh_install -pjitsi-meet-web $(COUNTRIES_DIR)/$@.json usr/share/jitsi-meet/lang/; \
|
||||
mv debian/jitsi-meet-web/usr/share/jitsi-meet/lang/$@.json debian/jitsi-meet-web/usr/share/jitsi-meet/lang/countries-$@.json; \
|
||||
else \
|
||||
if [ -f $(COUNTRIES_DIR)/$$LOCALE.json ] ; \
|
||||
then \
|
||||
dh_install -pjitsi-meet-web $(COUNTRIES_DIR)/$$LOCALE.json usr/share/jitsi-meet/lang/; \
|
||||
mv debian/jitsi-meet-web/usr/share/jitsi-meet/lang/$$LOCALE.json debian/jitsi-meet-web/usr/share/jitsi-meet/lang/countries-$@.json; \
|
||||
fi; \
|
||||
fi;
|
||||
|
||||
27
doc/api.md
@@ -322,6 +322,13 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **dominantSpeakerChanged** - receives event notifications about change in the dominant speaker. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
id: string //participantId of the new dominant speaker
|
||||
}
|
||||
```
|
||||
|
||||
* **tileViewChanged** - event notifications about tile view layout mode being entered or exited. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
@@ -372,6 +379,13 @@ changes. The listener will receive an object with the following structure:
|
||||
email: string // the new email
|
||||
}
|
||||
```
|
||||
* **feedbackSubmitted** - event notifications about conference feedback submission
|
||||
```javascript
|
||||
{
|
||||
error: string // The error which occurred during submission, if any.
|
||||
}
|
||||
```
|
||||
|
||||
* **filmstripDisplayChanged** - event notifications about the visibility of the filmstrip being updated.
|
||||
```javascript
|
||||
{
|
||||
@@ -387,6 +401,19 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **participantKickedOut** - event notifications about a participants being removed from the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
kicked: {
|
||||
id: string, // the id of the participant removed from the room
|
||||
local: boolean // whether or not the participant is the local particiapnt
|
||||
},
|
||||
kicker: {
|
||||
id: string // the id of the participant who kicked out the other participant
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **participantLeft** - event notifications about participants that leave the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
|
||||
BIN
fonts/jitsi.eot
@@ -42,6 +42,7 @@
|
||||
<glyph unicode="" glyph-name="exit-full-screen" d="M682 682h128v-84h-212v212h84v-128zM598 214v212h212v-84h-128v-128h-84zM342 682v128h84v-212h-212v84h128zM214 342v84h212v-212h-84v128h-128z" />
|
||||
<glyph unicode="" glyph-name="security" d="M768 170v428h-512v-428h512zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h388v86c0 72-60 132-132 132s-132-60-132-132h-82c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
|
||||
<glyph unicode="" glyph-name="security-locked" d="M768 170v428h-512v-428h512zM380 768v-86h264v86c0 72-60 132-132 132s-132-60-132-132zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h42v86c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
|
||||
<glyph unicode="" glyph-name="blur-background" d="M469.333 640c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333zM725.333 640c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333zM469.333 384c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333zM426.667 170.667c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM682.667 170.667c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM213.333 384c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM213.333 640c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM896 384c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM896 640c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM426.667 853.333c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM682.667 853.333c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM725.333 384c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333z" />
|
||||
<glyph unicode="" glyph-name="microphone" d="M738 554h72c0-146-116-266-256-286v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216s226 88 226 216zM512 426c-70 0-128 58-128 128v256c0 70 58 128 128 128s128-58 128-128v-256c0-70-58-128-128-128z" />
|
||||
<glyph unicode="" glyph-name="mic-disabled" d="M182 896l714-714-54-54-178 178c-32-20-72-32-110-38v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216 34 0 68 8 98 22l-70 70c-8-2-18-4-28-4-70 0-128 58-128 128v32l-256 256zM640 548l-256 254v8c0 70 58 128 128 128s128-58 128-128v-262zM810 554c0-50-14-98-38-140l-52 54c12 26 18 54 18 86h72z" />
|
||||
<glyph unicode="" glyph-name="link" d="M640 426c114 0 342-56 342-170v-86h-684v86c0 114 228 170 342 170zM256 598h128v-86h-128v-128h-86v128h-128v86h128v128h86v-128zM640 512c-94 0-170 76-170 170s76 172 170 172 170-78 170-172-76-170-170-170z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.woff
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -50,7 +50,7 @@ var interfaceConfig = {
|
||||
'fodeviceselection', 'hangup', 'profile', 'info', 'chat', 'recording',
|
||||
'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
'tileview'
|
||||
'tileview', 'videobackgroundblur'
|
||||
],
|
||||
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ],
|
||||
|
||||
@@ -39,7 +39,6 @@ target 'JitsiMeet' do
|
||||
|
||||
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
|
||||
pod 'react-native-fast-image', :path => '../node_modules/react-native-fast-image'
|
||||
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
|
||||
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
|
||||
@@ -35,7 +35,6 @@ PODS:
|
||||
- FirebaseCore (~> 5.2)
|
||||
- GoogleUtilities/Environment (~> 5.2)
|
||||
- GoogleUtilities/UserDefaults (~> 5.2)
|
||||
- FLAnimatedImage (1.0.12)
|
||||
- Folly (2018.10.22.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
@@ -84,67 +83,62 @@ PODS:
|
||||
- nanopb/decode (0.3.901)
|
||||
- nanopb/encode (0.3.901)
|
||||
- ObjectiveDropboxOfficial (3.9.4)
|
||||
- React (0.59.8):
|
||||
- React/Core (= 0.59.8)
|
||||
- React (0.59.10):
|
||||
- React/Core (= 0.59.10)
|
||||
- react-native-background-timer (2.1.1):
|
||||
- React
|
||||
- react-native-calendar-events (1.6.4):
|
||||
- React
|
||||
- react-native-fast-image (5.1.1):
|
||||
- FLAnimatedImage
|
||||
- React
|
||||
- SDWebImage/Core
|
||||
- SDWebImage/GIF
|
||||
- react-native-keep-awake (4.0.0):
|
||||
- React
|
||||
- react-native-webrtc (1.69.1):
|
||||
- react-native-webrtc (1.69.2):
|
||||
- React
|
||||
- react-native-webview (5.8.1):
|
||||
- React
|
||||
- React/Core (0.59.8):
|
||||
- yoga (= 0.59.8.React)
|
||||
- React/CxxBridge (0.59.8):
|
||||
- React/Core (0.59.10):
|
||||
- yoga (= 0.59.10.React)
|
||||
- React/CxxBridge (0.59.10):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React/Core
|
||||
- React/cxxreact
|
||||
- React/jsiexecutor
|
||||
- React/cxxreact (0.59.8):
|
||||
- React/cxxreact (0.59.10):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/jsinspector
|
||||
- React/DevSupport (0.59.8):
|
||||
- React/DevSupport (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTWebSocket
|
||||
- React/fishhook (0.59.8)
|
||||
- React/jsi (0.59.8):
|
||||
- React/fishhook (0.59.10)
|
||||
- React/jsi (0.59.10):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/jsiexecutor (0.59.8):
|
||||
- React/jsiexecutor (0.59.10):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/cxxreact
|
||||
- React/jsi
|
||||
- React/jsinspector (0.59.8)
|
||||
- React/RCTActionSheet (0.59.8):
|
||||
- React/jsinspector (0.59.10)
|
||||
- React/RCTActionSheet (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTAnimation (0.59.8):
|
||||
- React/RCTAnimation (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTBlob (0.59.8):
|
||||
- React/RCTBlob (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTImage (0.59.8):
|
||||
- React/RCTImage (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTNetwork
|
||||
- React/RCTLinkingIOS (0.59.8):
|
||||
- React/RCTLinkingIOS (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTNetwork (0.59.8):
|
||||
- React/RCTNetwork (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTText (0.59.8):
|
||||
- React/RCTText (0.59.10):
|
||||
- React/Core
|
||||
- React/RCTWebSocket (0.59.8):
|
||||
- React/RCTWebSocket (0.59.10):
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
@@ -162,11 +156,7 @@ PODS:
|
||||
- React
|
||||
- RNWatch (0.2.0):
|
||||
- React
|
||||
- SDWebImage/Core (4.4.6)
|
||||
- SDWebImage/GIF (4.4.6):
|
||||
- FLAnimatedImage (~> 1.0)
|
||||
- SDWebImage/Core
|
||||
- yoga (0.59.8.React)
|
||||
- yoga (0.59.10.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Amplitude-iOS (~> 4.0.4)
|
||||
@@ -181,7 +171,6 @@ DEPENDENCIES:
|
||||
- ObjectiveDropboxOfficial (~> 3.9.4)
|
||||
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
|
||||
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
|
||||
- react-native-fast-image (from `../node_modules/react-native-fast-image`)
|
||||
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
|
||||
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
@@ -214,7 +203,6 @@ SPEC REPOS:
|
||||
- FirebaseCore
|
||||
- FirebaseDynamicLinks
|
||||
- FirebaseInstanceID
|
||||
- FLAnimatedImage
|
||||
- GoogleAppMeasurement
|
||||
- GoogleSignIn
|
||||
- GoogleToolboxForMac
|
||||
@@ -222,7 +210,6 @@ SPEC REPOS:
|
||||
- GTMSessionFetcher
|
||||
- nanopb
|
||||
- ObjectiveDropboxOfficial
|
||||
- SDWebImage
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
BVLinearGradient:
|
||||
@@ -239,8 +226,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-background-timer"
|
||||
react-native-calendar-events:
|
||||
:path: "../node_modules/react-native-calendar-events"
|
||||
react-native-fast-image:
|
||||
:path: "../node_modules/react-native-fast-image"
|
||||
react-native-keep-awake:
|
||||
:path: "../node_modules/react-native-keep-awake"
|
||||
react-native-webrtc:
|
||||
@@ -273,7 +258,6 @@ SPEC CHECKSUMS:
|
||||
FirebaseCore: 52f851b30e11360f1e67cf04b1edfebf0a47a2d3
|
||||
FirebaseDynamicLinks: f209c3caccd82102caa0e91d393e3ccc593501fd
|
||||
FirebaseInstanceID: bd6fc5a258884e206fd5c474ebe4f5b00e21770e
|
||||
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
|
||||
Folly: de497beb10f102453a1afa9edbf8cf8a251890de
|
||||
glog: aefd1eb5dda2ab95ba0938556f34b98e2da3a60d
|
||||
GoogleAppMeasurement: 6cf307834da065863f9faf4c0de0a936d81dd832
|
||||
@@ -283,21 +267,19 @@ SPEC CHECKSUMS:
|
||||
GTMSessionFetcher: 32aeca0aa144acea523e1c8e053089dec2cb98ca
|
||||
nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48
|
||||
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
|
||||
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
|
||||
React: 36d0768f9e93be2473b37e7fa64f92c1d5341eef
|
||||
react-native-background-timer: 0d34748e53a972507c66963490c775321a88f6f2
|
||||
react-native-calendar-events: ee9573e355711ac679e071be70789542431f4ce3
|
||||
react-native-fast-image: 47487b71169aea34868e7b38bf870b6b3f2157c5
|
||||
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
|
||||
react-native-webrtc: 90a847d19deb2d7323fef8cc89ca12b8995fbc90
|
||||
react-native-webrtc: 1415d2a54b2246dd85ba95eb3e4bf2b66533f951
|
||||
react-native-webview: a95842e3f351a6d2c8bc8bcc9eab689c7e7e5ad4
|
||||
RNCAsyncStorage: 8e31405a9f12fbf42c2bb330e4560bfd79c18323
|
||||
RNGoogleSignin: d030c6c6591db24c3cee649f64c7babf0a1699a0
|
||||
RNSound: e157320f503bdd4f4ee6d8542e948d54f90c3c3a
|
||||
RNVectorIcons: d819334932bcda3332deb3d2c8ea4d069e0b98f9
|
||||
RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
|
||||
SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8
|
||||
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
|
||||
yoga: 684513b14b03201579ba3cee20218c9d1298b0cc
|
||||
|
||||
PODFILE CHECKSUM: b55338cc43312051ed83f8d9c6aadbd8c9402e6a
|
||||
PODFILE CHECKSUM: d540f088d564bfe3b8ca3d13eec4cc0ce9c6e4bc
|
||||
|
||||
COCOAPODS: 1.7.2
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>19.2.0</string>
|
||||
<string>19.3.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>19.2.0</string>
|
||||
<string>19.3.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>19.2.0</string>
|
||||
<string>19.3.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */; };
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */; };
|
||||
0B49424520AD8DBD00BD2DE0 /* outgoingStart.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */; };
|
||||
0B49424620AD8DBD00BD2DE0 /* outgoingRinging.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */; };
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
|
||||
@@ -27,6 +26,8 @@
|
||||
0BCA496C1EC4BBF900B793EE /* jitsi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0BCA496B1EC4BBF900B793EE /* jitsi.ttf */; };
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0F65EECE1D95DA94561BB47E /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 03F2ADC957FF109849B7FCA1 /* libPods-JitsiMeet.a */; };
|
||||
C30F88D0CB0F4F5593216D24 /* liveStreamingOff.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C30F88D1CB0F4F5593216D24 /* liveStreamingOff.mp3 */; };
|
||||
C30F88D2CB0F4F5593216D24 /* liveStreamingOn.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C30F88D3CB0F4F5593216D24 /* liveStreamingOn.mp3 */; };
|
||||
6C31EDC820C06D490089C899 /* recordingOn.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 6C31EDC720C06D490089C899 /* recordingOn.mp3 */; };
|
||||
6C31EDCA20C06D530089C899 /* recordingOff.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 6C31EDC920C06D530089C899 /* recordingOff.mp3 */; };
|
||||
75635B0A20751D6D00F29C9F /* joined.wav in Resources */ = {isa = PBXBuildFile; fileRef = 75635B0820751D6D00F29C9F /* joined.wav */; };
|
||||
@@ -57,7 +58,6 @@
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JitsiMeetView.h; sourceTree = "<group>"; };
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetView.m; sourceTree = "<group>"; };
|
||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetViewDelegate.h; sourceTree = "<group>"; };
|
||||
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPVolumeViewManager.m; sourceTree = "<group>"; };
|
||||
0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = outgoingStart.wav; path = ../../sounds/outgoingStart.wav; sourceTree = "<group>"; };
|
||||
0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = outgoingRinging.wav; path = ../../sounds/outgoingRinging.wav; sourceTree = "<group>"; };
|
||||
0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
||||
@@ -77,6 +77,8 @@
|
||||
0BD906E51EC0C00300C8C18E /* JitsiMeet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JitsiMeet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeet.h; sourceTree = "<group>"; };
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C30F88D1CB0F4F5593216D24 /* liveStreamingOff.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = liveStreamingOff.mp3; path = ../../sounds/liveStreamingOff.mp3; sourceTree = "<group>"; };
|
||||
C30F88D3CB0F4F5593216D24 /* liveStreamingOn.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = liveStreamingOn.mp3; path = ../../sounds/liveStreamingOn.mp3; sourceTree = "<group>"; };
|
||||
6C31EDC720C06D490089C899 /* recordingOn.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = recordingOn.mp3; path = ../../sounds/recordingOn.mp3; sourceTree = "<group>"; };
|
||||
6C31EDC920C06D530089C899 /* recordingOff.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = recordingOff.mp3; path = ../../sounds/recordingOff.mp3; sourceTree = "<group>"; };
|
||||
75635B0820751D6D00F29C9F /* joined.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = joined.wav; path = ../../sounds/joined.wav; sourceTree = "<group>"; };
|
||||
@@ -135,6 +137,8 @@
|
||||
0BCA496B1EC4BBF900B793EE /* jitsi.ttf */,
|
||||
75635B0820751D6D00F29C9F /* joined.wav */,
|
||||
75635B0920751D6D00F29C9F /* left.wav */,
|
||||
C30F88D1CB0F4F5593216D24 /* liveStreamingOff.mp3 */,
|
||||
C30F88D3CB0F4F5593216D24 /* liveStreamingOn.mp3 */,
|
||||
0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */,
|
||||
0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */,
|
||||
6C31EDC920C06D530089C899 /* recordingOff.mp3 */,
|
||||
@@ -189,7 +193,6 @@
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */,
|
||||
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */,
|
||||
C6A3426B204F127900E062DD /* picture-in-picture */,
|
||||
0BCA495D1EC4B6C600B793EE /* POSIX.m */,
|
||||
0BCA495E1EC4B6C600B793EE /* Proximity.m */,
|
||||
@@ -343,6 +346,8 @@
|
||||
files = (
|
||||
87FE6F3321E52437004A5DC7 /* incomingMessage.wav in Resources */,
|
||||
0B49424520AD8DBD00BD2DE0 /* outgoingStart.wav in Resources */,
|
||||
C30F88D0CB0F4F5593216D24 /* liveStreamingOff.mp3 in Resources */,
|
||||
C30F88D2CB0F4F5593216D24 /* liveStreamingOn.mp3 in Resources */,
|
||||
6C31EDCA20C06D530089C899 /* recordingOff.mp3 in Resources */,
|
||||
A4414AE020B37F1A003546E6 /* rejected.wav in Resources */,
|
||||
0B49424620AD8DBD00BD2DE0 /* outgoingRinging.wav in Resources */,
|
||||
@@ -501,7 +506,6 @@
|
||||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */,
|
||||
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */,
|
||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
||||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
A480429C21EE335600289B73 /* AmplitudeModule.m in Sources */,
|
||||
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,17 +16,30 @@
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <WebRTC/WebRTC.h>
|
||||
|
||||
|
||||
// Audio mode
|
||||
typedef enum {
|
||||
kAudioModeDefault,
|
||||
kAudioModeAudioCall,
|
||||
kAudioModeVideoCall
|
||||
} JitsiMeetAudioMode;
|
||||
|
||||
@interface AudioMode : NSObject<RCTBridgeModule, RTCAudioSessionDelegate>
|
||||
// Events
|
||||
static NSString * const kDevicesChanged = @"org.jitsi.meet:features/audio-mode#devices-update";
|
||||
|
||||
// Device types (must match JS and Java)
|
||||
static NSString * const kDeviceTypeHeadphones = @"HEADPHONES";
|
||||
static NSString * const kDeviceTypeBluetooth = @"BLUETOOTH";
|
||||
static NSString * const kDeviceTypeEarpiece = @"EARPIECE";
|
||||
static NSString * const kDeviceTypeSpeaker = @"SPEAKER";
|
||||
static NSString * const kDeviceTypeUnknown = @"UNKNOWN";
|
||||
|
||||
|
||||
@interface AudioMode : RCTEventEmitter<RTCAudioSessionDelegate>
|
||||
|
||||
@property(nonatomic, strong) dispatch_queue_t workerQueue;
|
||||
|
||||
@@ -38,6 +50,11 @@ typedef enum {
|
||||
RTCAudioSessionConfiguration *defaultConfig;
|
||||
RTCAudioSessionConfiguration *audioCallConfig;
|
||||
RTCAudioSessionConfiguration *videoCallConfig;
|
||||
RTCAudioSessionConfiguration *earpieceConfig;
|
||||
BOOL forceSpeaker;
|
||||
BOOL forceEarpiece;
|
||||
BOOL isSpeakerOn;
|
||||
BOOL isEarpieceOn;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
@@ -46,8 +63,13 @@ RCT_EXPORT_MODULE();
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[ kDevicesChanged ];
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport {
|
||||
return @{
|
||||
@"DEVICE_CHANGE_EVENT": kDevicesChanged,
|
||||
@"AUDIO_CALL" : [NSNumber numberWithInt: kAudioModeAudioCall],
|
||||
@"DEFAULT" : [NSNumber numberWithInt: kAudioModeDefault],
|
||||
@"VIDEO_CALL" : [NSNumber numberWithInt: kAudioModeVideoCall]
|
||||
@@ -58,8 +80,7 @@ RCT_EXPORT_MODULE();
|
||||
self = [super init];
|
||||
if (self) {
|
||||
dispatch_queue_attr_t attributes =
|
||||
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
|
||||
QOS_CLASS_USER_INITIATED, -1);
|
||||
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, -1);
|
||||
_workerQueue = dispatch_queue_create("AudioMode.queue", attributes);
|
||||
|
||||
activeMode = kAudioModeDefault;
|
||||
@@ -71,7 +92,7 @@ RCT_EXPORT_MODULE();
|
||||
|
||||
audioCallConfig = [[RTCAudioSessionConfiguration alloc] init];
|
||||
audioCallConfig.category = AVAudioSessionCategoryPlayAndRecord;
|
||||
audioCallConfig.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth;
|
||||
audioCallConfig.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionDefaultToSpeaker;
|
||||
audioCallConfig.mode = AVAudioSessionModeVoiceChat;
|
||||
|
||||
videoCallConfig = [[RTCAudioSessionConfiguration alloc] init];
|
||||
@@ -79,6 +100,17 @@ RCT_EXPORT_MODULE();
|
||||
videoCallConfig.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth;
|
||||
videoCallConfig.mode = AVAudioSessionModeVideoChat;
|
||||
|
||||
// Manually routing audio to the earpiece doesn't quite work unless one disables BT (weird, I know).
|
||||
earpieceConfig = [[RTCAudioSessionConfiguration alloc] init];
|
||||
earpieceConfig.category = AVAudioSessionCategoryPlayAndRecord;
|
||||
earpieceConfig.categoryOptions = 0;
|
||||
earpieceConfig.mode = AVAudioSessionModeVoiceChat;
|
||||
|
||||
forceSpeaker = NO;
|
||||
forceEarpiece = NO;
|
||||
isSpeakerOn = NO;
|
||||
isEarpieceOn = NO;
|
||||
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
[session addDelegate:self];
|
||||
}
|
||||
@@ -107,24 +139,20 @@ RCT_EXPORT_MODULE();
|
||||
RCT_EXPORT_METHOD(setMode:(int)mode
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
RTCAudioSessionConfiguration *config;
|
||||
RTCAudioSessionConfiguration *config = [self configForMode:mode];
|
||||
NSError *error;
|
||||
|
||||
switch (mode) {
|
||||
case kAudioModeAudioCall:
|
||||
config = audioCallConfig;
|
||||
break;
|
||||
case kAudioModeDefault:
|
||||
config = defaultConfig;
|
||||
break;
|
||||
case kAudioModeVideoCall:
|
||||
config = videoCallConfig;
|
||||
break;
|
||||
default:
|
||||
if (config == nil) {
|
||||
reject(@"setMode", @"Invalid mode", nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset.
|
||||
if (mode == kAudioModeDefault) {
|
||||
forceSpeaker = NO;
|
||||
forceEarpiece = NO;
|
||||
}
|
||||
|
||||
activeMode = mode;
|
||||
|
||||
if ([self setConfig:config error:&error]) {
|
||||
@@ -132,6 +160,76 @@ RCT_EXPORT_METHOD(setMode:(int)mode
|
||||
} else {
|
||||
reject(@"setMode", error.localizedDescription, error);
|
||||
}
|
||||
|
||||
[self notifyDevicesChanged];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setAudioDevice:(NSString *)device
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
NSLog(@"[AudioMode] Selected device: %@", device);
|
||||
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
[session lockForConfiguration];
|
||||
BOOL success;
|
||||
NSError *error = nil;
|
||||
|
||||
// Reset these, as we are about to compute them.
|
||||
forceSpeaker = NO;
|
||||
forceEarpiece = NO;
|
||||
|
||||
// The speaker is special, so test for it first.
|
||||
if ([device isEqualToString:kDeviceTypeSpeaker]) {
|
||||
forceSpeaker = NO;
|
||||
success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
|
||||
} else {
|
||||
// Here we use AVAudioSession because RTCAudioSession doesn't expose availableInputs.
|
||||
AVAudioSession *_session = [AVAudioSession sharedInstance];
|
||||
AVAudioSessionPortDescription *port = nil;
|
||||
|
||||
// Find the matching input device.
|
||||
for (AVAudioSessionPortDescription *portDesc in _session.availableInputs) {
|
||||
if ([portDesc.UID isEqualToString:device]) {
|
||||
port = portDesc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port != nil) {
|
||||
// First remove the override if we are going to select a different device.
|
||||
if (isSpeakerOn) {
|
||||
[session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
|
||||
}
|
||||
|
||||
// Special case for the earpiece.
|
||||
if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
|
||||
forceEarpiece = YES;
|
||||
[self setConfig:earpieceConfig error:nil];
|
||||
} else if (isEarpieceOn) {
|
||||
// Reset the config.
|
||||
RTCAudioSessionConfiguration *config = [self configForMode:activeMode];
|
||||
[self setConfig:config error:nil];
|
||||
}
|
||||
|
||||
// Select our preferred input.
|
||||
success = [session setPreferredInput:port error:&error];
|
||||
} else {
|
||||
success = NO;
|
||||
error = RCTErrorWithMessage(@"Could not find audio device");
|
||||
}
|
||||
}
|
||||
|
||||
[session unlockForConfiguration];
|
||||
|
||||
if (success) {
|
||||
resolve(nil);
|
||||
} else {
|
||||
reject(@"setAudioDevice", error != nil ? error.localizedDescription : @"", error);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(updateDeviceList) {
|
||||
[self notifyDevicesChanged];
|
||||
}
|
||||
|
||||
#pragma mark - RTCAudioSessionDelegate
|
||||
@@ -139,26 +237,141 @@ RCT_EXPORT_METHOD(setMode:(int)mode
|
||||
- (void)audioSessionDidChangeRoute:(RTCAudioSession *)session
|
||||
reason:(AVAudioSessionRouteChangeReason)reason
|
||||
previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
|
||||
if (reason == AVAudioSessionRouteChangeReasonCategoryChange) {
|
||||
// The category has changed. Check if it's the one we want and adjust as
|
||||
// needed. This notification is posted on a secondary thread, so make
|
||||
// sure we switch to our worker thread.
|
||||
dispatch_async(_workerQueue, ^{
|
||||
// We don't want to touch the category when in default mode.
|
||||
// This is to play well with other components which could be integrated
|
||||
// into the final application.
|
||||
if (self->activeMode != kAudioModeDefault) {
|
||||
NSLog(@"Audio route changed, reapplying RTCAudioSession config");
|
||||
RTCAudioSessionConfiguration *config
|
||||
= self->activeMode == kAudioModeAudioCall ? self->audioCallConfig : self->videoCallConfig;
|
||||
[self setConfig:config error:nil];
|
||||
// Update JS about the changes.
|
||||
[self notifyDevicesChanged];
|
||||
|
||||
dispatch_async(_workerQueue, ^{
|
||||
switch (reason) {
|
||||
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
|
||||
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
|
||||
// If the device list changed, reset our overrides.
|
||||
self->forceSpeaker = NO;
|
||||
self->forceEarpiece = NO;
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonCategoryChange:
|
||||
// The category has changed. Check if it's the one we want and adjust as
|
||||
// needed.
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't want to touch the category when in default mode.
|
||||
// This is to play well with other components which could be integrated
|
||||
// into the final application.
|
||||
if (self->activeMode != kAudioModeDefault) {
|
||||
NSLog(@"[AudioMode] Route changed, reapplying RTCAudioSession config");
|
||||
RTCAudioSessionConfiguration *config = [self configForMode:self->activeMode];
|
||||
[self setConfig:config error:nil];
|
||||
if (self->forceSpeaker && !self->isSpeakerOn) {
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
[session lockForConfiguration];
|
||||
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
|
||||
[session unlockForConfiguration];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)audioSession:(RTCAudioSession *)audioSession didSetActive:(BOOL)active {
|
||||
NSLog(@"[AudioMode] Audio session didSetActive:%d", active);
|
||||
}
|
||||
|
||||
#pragma mark - Helper methods
|
||||
|
||||
- (RTCAudioSessionConfiguration *)configForMode:(int) mode {
|
||||
if (mode != kAudioModeDefault && forceEarpiece) {
|
||||
return earpieceConfig;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case kAudioModeAudioCall:
|
||||
return audioCallConfig;
|
||||
case kAudioModeDefault:
|
||||
return defaultConfig;
|
||||
case kAudioModeVideoCall:
|
||||
return videoCallConfig;
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Here we convert input and output port types into a single type.
|
||||
- (NSString *)portTypeToString:(AVAudioSessionPort) portType {
|
||||
if ([portType isEqualToString:AVAudioSessionPortHeadphones]
|
||||
|| [portType isEqualToString:AVAudioSessionPortHeadsetMic]) {
|
||||
return kDeviceTypeHeadphones;
|
||||
} else if ([portType isEqualToString:AVAudioSessionPortBuiltInMic]
|
||||
|| [portType isEqualToString:AVAudioSessionPortBuiltInReceiver]) {
|
||||
return kDeviceTypeEarpiece;
|
||||
} else if ([portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
|
||||
return kDeviceTypeSpeaker;
|
||||
} else if ([portType isEqualToString:AVAudioSessionPortBluetoothHFP]
|
||||
|| [portType isEqualToString:AVAudioSessionPortBluetoothLE]
|
||||
|| [portType isEqualToString:AVAudioSessionPortBluetoothA2DP]) {
|
||||
return kDeviceTypeBluetooth;
|
||||
} else {
|
||||
return kDeviceTypeUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyDevicesChanged {
|
||||
dispatch_async(_workerQueue, ^{
|
||||
NSMutableArray *data = [[NSMutableArray alloc] init];
|
||||
// Here we use AVAudioSession because RTCAudioSession doesn't expose availableInputs.
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
NSString *currentPort = @"";
|
||||
AVAudioSessionRouteDescription *currentRoute = session.currentRoute;
|
||||
|
||||
// Check what the current device is. Because the speaker is somewhat special, we need to
|
||||
// check for it first.
|
||||
if (currentRoute != nil) {
|
||||
AVAudioSessionPortDescription *output = currentRoute.outputs.firstObject;
|
||||
AVAudioSessionPortDescription *input = currentRoute.inputs.firstObject;
|
||||
if (output != nil && [output.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
|
||||
currentPort = kDeviceTypeSpeaker;
|
||||
self->isSpeakerOn = YES;
|
||||
} else if (input != nil) {
|
||||
currentPort = input.UID;
|
||||
self->isSpeakerOn = NO;
|
||||
self->isEarpieceOn = [input.portType isEqualToString:AVAudioSessionPortBuiltInMic];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL headphonesAvailable = NO;
|
||||
for (AVAudioSessionPortDescription *portDesc in session.availableInputs) {
|
||||
if ([portDesc.portType isEqualToString:AVAudioSessionPortHeadsetMic] || [portDesc.portType isEqualToString:AVAudioSessionPortHeadphones]) {
|
||||
headphonesAvailable = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (AVAudioSessionPortDescription *portDesc in session.availableInputs) {
|
||||
// Skip "Phone" if headphones are present.
|
||||
if (headphonesAvailable && [portDesc.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
|
||||
continue;
|
||||
}
|
||||
id deviceData
|
||||
= @{
|
||||
@"type": [self portTypeToString:portDesc.portType],
|
||||
@"name": portDesc.portName,
|
||||
@"uid": portDesc.UID,
|
||||
@"selected": [NSNumber numberWithBool:[portDesc.UID isEqualToString:currentPort]]
|
||||
};
|
||||
[data addObject:deviceData];
|
||||
}
|
||||
|
||||
// We need to manually add the speaker because it will never show up in the
|
||||
// previous list, as it's not an input.
|
||||
[data addObject:
|
||||
@{ @"type": kDeviceTypeSpeaker,
|
||||
@"name": @"Speaker",
|
||||
@"uid": kDeviceTypeSpeaker,
|
||||
@"selected": [NSNumber numberWithBool:[kDeviceTypeSpeaker isEqualToString:currentPort]]
|
||||
}];
|
||||
|
||||
[self sendEventWithName:kDevicesChanged body:data];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
*/
|
||||
@property (nonatomic, nullable) JitsiMeetConferenceOptions *defaultConferenceOptions;
|
||||
|
||||
#pragma mak - This class is a singleton
|
||||
#pragma mark - This class is a singleton
|
||||
|
||||
+ (instancetype _Nonnull)sharedInstance;
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@import MediaPlayer;
|
||||
|
||||
|
||||
@interface MPVolumeViewManager : RCTViewManager
|
||||
@end
|
||||
|
||||
@implementation MPVolumeViewManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (UIView *)view {
|
||||
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
|
||||
volumeView.showsRouteButton = YES;
|
||||
volumeView.showsVolumeSlider = NO;
|
||||
|
||||
return (UIView *) volumeView;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(show:(nonnull NSNumber *)reactTag) {
|
||||
[self.bridge.uiManager addUIBlock:^(
|
||||
__unused RCTUIManager *uiManager,
|
||||
NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
id view = viewRegistry[reactTag];
|
||||
if (![view isKindOfClass:[MPVolumeView class]]) {
|
||||
RCTLogError(@"Invalid view returned from registry, expecting \
|
||||
MPVolumeView, got: %@", view);
|
||||
} else {
|
||||
// Simulate a click
|
||||
UIButton *btn = nil;
|
||||
for (UIView *buttonView in ((UIView *) view).subviews) {
|
||||
if ([buttonView isKindOfClass:[UIButton class]]) {
|
||||
btn = (UIButton *) buttonView;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (btn != nil) {
|
||||
[btn sendActionsForControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"en": "Engels",
|
||||
"af": "",
|
||||
"az": "Azerbeidjans",
|
||||
"bg": "Bulgaars",
|
||||
"cs": "Tsjeggies",
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"en": "İngilis",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bolqar",
|
||||
"cs": "",
|
||||
"de": "Alman",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "İspan",
|
||||
"fr": "Fransız",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"en": "Английски",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"af": "Африканс",
|
||||
"az": "Азербайджански",
|
||||
"bg": "Български",
|
||||
"cs": "",
|
||||
"cs": "Чешки",
|
||||
"de": "Немски",
|
||||
"el": "",
|
||||
"el": "Гръцки",
|
||||
"eo": "Есперанто",
|
||||
"es": "Испански",
|
||||
"fr": "Френски",
|
||||
"hy": "Арменски",
|
||||
"it": "Италиански",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"ja": "Японски",
|
||||
"ko": "Корейски",
|
||||
"nb": "Норвежки букмол",
|
||||
"oc": "Окситански",
|
||||
"pl": "Полски",
|
||||
@@ -22,6 +22,6 @@
|
||||
"sl": "Словенски",
|
||||
"sv": "Шведски",
|
||||
"tr": "Турски",
|
||||
"vi": "",
|
||||
"vi": "Виетнамски",
|
||||
"zhCN": "Китайски (Китай)"
|
||||
}
|
||||
@@ -1 +1,27 @@
|
||||
{}
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
{
|
||||
"en": "Angličtina",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bulharština",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"cs": "",
|
||||
"de": "Němčina",
|
||||
"el": "",
|
||||
"eo": "Esperanto",
|
||||
"es": "Španělština",
|
||||
"fr": "Francouština",
|
||||
"hy": "Arménština",
|
||||
"it": "Italština",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Norština Bokmal",
|
||||
"oc": "Okcitánština",
|
||||
"pl": "Polština",
|
||||
"ptBR": "Portugalština (Brazilská)",
|
||||
"ru": "Ruština",
|
||||
"sk": "Slovenština",
|
||||
"sl": "Slovinština",
|
||||
"sv": "Švédština",
|
||||
"tr": "Turečtina",
|
||||
"vi": "",
|
||||
"zhCN": "Čínština (Čína)"
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"en": "Englisch",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bulgarisch",
|
||||
"cs": "",
|
||||
|
||||
27
lang/languages-enGB.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
{
|
||||
"en": "Angla",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bulgara",
|
||||
"cs": "",
|
||||
"de": "Germana",
|
||||
"el": "",
|
||||
"eo": "Esperanto",
|
||||
"es": "Hispana",
|
||||
"fr": "Franca",
|
||||
"hy": "Armena",
|
||||
"it": "Itala",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Norvega (Bukmola)",
|
||||
"oc": "Okcitana",
|
||||
"pl": "Pola",
|
||||
"ptBR": "Portugala (Brazilo)",
|
||||
"ptBR": "Portugala (Brazila)",
|
||||
"ru": "Rusa",
|
||||
"sk": "Slovaka",
|
||||
"sl": "Slovena",
|
||||
"sv": "Sveda",
|
||||
"tr": "Turka",
|
||||
"zhCN": "Ĉina (Ĉinujo)",
|
||||
"nb": "Norvega (Bokmål)",
|
||||
"eo": "Esperanto"
|
||||
"vi": "",
|
||||
"zhCN": "Ĉina (Ĉinuja)"
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "Inglés",
|
||||
"af": "Africano",
|
||||
"az": "Azerbaijani",
|
||||
"bg": "Búlgaro",
|
||||
"cs": "Czech",
|
||||
"de": "Alemán",
|
||||
"el": "Griego",
|
||||
"eo": "Esperanto",
|
||||
"es": "Español",
|
||||
"fr": "Francés",
|
||||
"hy": "Armenio",
|
||||
"it": "Italiano",
|
||||
"ja": "Jopones",
|
||||
"ko": "Coreano",
|
||||
"nb": "Noruego (bokmal)",
|
||||
"oc": "Occitano",
|
||||
"pl": "Polaco",
|
||||
"ptBR": "Portugués (Brasil)",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Esloveno",
|
||||
"sv": "Sueco",
|
||||
"tr": "Turco",
|
||||
"zhCN": "Chino (China)",
|
||||
"nb": "Noruego (bokmal)",
|
||||
"eo": "Esperanto"
|
||||
"vi": "Vietnamita",
|
||||
"zhCN": "Chino (China)"
|
||||
}
|
||||
27
lang/languages-esUS.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"en": "انگلیسی",
|
||||
"af": "",
|
||||
"az": "آذری , آذربایجان",
|
||||
"bg": "بلغاری",
|
||||
"cs": "چک",
|
||||
"de": "آلمانی",
|
||||
"el": "یونانی",
|
||||
"eo": "اسپرانتو",
|
||||
"es": "اسپانیایی",
|
||||
"fr": "فرانسوی",
|
||||
"hy": "ارمنی",
|
||||
"it": "ایتالیایی",
|
||||
"ja": "ژاپنی",
|
||||
"ko": "کره ای",
|
||||
"nb": "بوکمال نروژی",
|
||||
"oc": "اکسیتان(قدیمی)",
|
||||
"pl": "لهستانی",
|
||||
"ptBR": "پرتغالی (برزیل)",
|
||||
"ru": "روسی",
|
||||
"sk": "اسلواکی",
|
||||
"sl": "اسلوونیایی",
|
||||
"sv": "سوئدی",
|
||||
"tr": "ترکی",
|
||||
"vi": "ویتنامی",
|
||||
"zhCN": "چینی"
|
||||
}
|
||||
27
lang/languages-fi.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"en": "Anglais",
|
||||
"af": "",
|
||||
"az": "Azerbaïdjanais",
|
||||
"bg": "Bulgare",
|
||||
"cs": "Tchèque",
|
||||
|
||||
27
lang/languages-frCA.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,27 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
"en": "Engleski",
|
||||
"af": "Afrikanski",
|
||||
"az": "Ažerbejdžanski",
|
||||
"bg": "Bugarski",
|
||||
"cs": "Češki",
|
||||
"de": "Njemački",
|
||||
"el": "Grčki",
|
||||
"eo": "Esperanto",
|
||||
"es": "Španjolski",
|
||||
"fr": "Francuski",
|
||||
"hy": "Armenski",
|
||||
"it": "Talijanski",
|
||||
"ja": "Japanski",
|
||||
"ko": "Korejski",
|
||||
"nb": "Norveški Bokmal",
|
||||
"oc": "Okcitanski",
|
||||
"pl": "Poljski",
|
||||
"ptBR": "Portugalski (Brazil)",
|
||||
"ru": "Ruski",
|
||||
"sk": "Slovački",
|
||||
"sl": "Slovenski",
|
||||
"sv": "Švedski",
|
||||
"tr": "Turski",
|
||||
"vi": "Vijetnamski",
|
||||
"zhCN": "Kineski (Kina)"
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"en": "Angol",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bolgár",
|
||||
"cs": "",
|
||||
"de": "Német",
|
||||
"el": "",
|
||||
"eo": "Eszperantó",
|
||||
"es": "Spanyol",
|
||||
"fr": "Francia",
|
||||
"hy": "Örmény",
|
||||
"it": "Olasz",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Norvég bokmal",
|
||||
"oc": "Okszitán",
|
||||
"pl": "Lengyel",
|
||||
"ptBR": "Portugál (Brazil)",
|
||||
"ru": "Orosz",
|
||||
"sk": "Szlovák",
|
||||
"sl": "Szlovén",
|
||||
"sv": "Svéd",
|
||||
"tr": "Török",
|
||||
"vi": "",
|
||||
"zhCN": "Kínai (Kína)"
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "Անգլերեն",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Բուլղարերեն",
|
||||
"cs": "",
|
||||
"de": "Գերմաներեն ",
|
||||
"el": "",
|
||||
"eo": "Էսպերանտո",
|
||||
"es": "Իսպաներեն",
|
||||
"fr": "Ֆրանսերեն",
|
||||
"hy": "Հայերեն",
|
||||
"it": "Իտալերեն",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Նորվեգերեն",
|
||||
"oc": "Օքսիտաներեն",
|
||||
"pl": "Լեհերեն",
|
||||
"ptBR": "Պորտուգալերեն (Բրազիլիա)",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Սլովեներեն ",
|
||||
"sv": "Շվեդերեն ",
|
||||
"tr": "Թուրքերեն",
|
||||
"zhCN": "Չիներեն",
|
||||
"nb": "Նորվեգերեն",
|
||||
"eo": "Էսպերանտո"
|
||||
"vi": "",
|
||||
"zhCN": "Չիներեն"
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"en": "Inglese",
|
||||
"af": "",
|
||||
"az": "Azero",
|
||||
"bg": "Bulgaro",
|
||||
"cs": "Ceco",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"en": "英語",
|
||||
"af": "アフリカーンス語",
|
||||
"az": "アゼルバイジャン語",
|
||||
"bg": "ブルガリア語",
|
||||
"cs": "チェコ語",
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
{
|
||||
"en": "Taglizit",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Tabulgarit",
|
||||
"cs": "Taččikit",
|
||||
"de": "Talmanit",
|
||||
"es": "Taspenyult",
|
||||
"el": "Tagrigit",
|
||||
"eo": "Taspiṛantit",
|
||||
"es": "Taspanit",
|
||||
"fr": "Tafransist",
|
||||
"hy": "Tarminit",
|
||||
"it": "Taṭalyant",
|
||||
"ja": "Tajapunit",
|
||||
"ko": "Takurit",
|
||||
"nb": "Tanurvijit Bukmal",
|
||||
"oc": "Tuksitant",
|
||||
"pl": "Tapulunit",
|
||||
"ptBR": "Tapurtuɣalt (Brazil)",
|
||||
"ptBR": "Tapurtugit (Brazil)",
|
||||
"ru": "Tarusit",
|
||||
"sk": "Tasluvakit",
|
||||
"sl": "Tasluvinit",
|
||||
"sv": "Taswidit",
|
||||
"tr": "Taṭurkit",
|
||||
"zhCN": "Tacinwat",
|
||||
"nb": "Tanurvijit Bukmal",
|
||||
"eo": "Taspiṛantit"
|
||||
"vi": "Tavyitnamit",
|
||||
"zhCN": "Tavyitnamit"
|
||||
}
|
||||
27
lang/languages-km.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"en": "អង់គ្លេស",
|
||||
"af": "អាហ្វ្រិច",
|
||||
"az": "Azerbaijani",
|
||||
"bg": "ប៊ុលហ្គារី",
|
||||
"cs": "ឆេក",
|
||||
"de": "អាល្លឺម៉ង់",
|
||||
"el": "ក្រិក",
|
||||
"eo": "Esperanto",
|
||||
"es": "អេស្ប៉ាញ",
|
||||
"fr": "បារាំង",
|
||||
"hy": "ភាសាអាមេនី",
|
||||
"it": "អ៊ីតាលី",
|
||||
"ja": "ជប៉ុន",
|
||||
"ko": "កូរ៉េ",
|
||||
"nb": "ន័រវេបាំម៉ាល",
|
||||
"oc": "អុកស៊ីត",
|
||||
"pl": "ប៉ូឡូញ",
|
||||
"ptBR": "ព័រទុយហ្គាល់",
|
||||
"ru": "រុស្ស៊ី",
|
||||
"sk": "ស្លូវ៉ាគី",
|
||||
"sl": "ស្លូវ៉ានី",
|
||||
"sv": "ស៊ុយអែត",
|
||||
"tr": "ទួរគី",
|
||||
"vi": "វៀតណាម",
|
||||
"zhCN": "ចិន (ចិន)"
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"en": "영어",
|
||||
"af": "",
|
||||
"az": "아제르바이잔어",
|
||||
"bg": "불가리어",
|
||||
"cs": "체코어",
|
||||
"de": "독일어",
|
||||
"el": "그리스어",
|
||||
"eo": "에스페란토어",
|
||||
"es": "스페인어",
|
||||
"fr": "프랑스어",
|
||||
"hy": "아르메니아어",
|
||||
"it": "이탈리아어",
|
||||
"ja": "일본어",
|
||||
"ko": "한국어",
|
||||
"nb": "노르웨이어",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"pl": "폴란드어",
|
||||
"ptBR": "포르투갈어(브라질)",
|
||||
"ru": "러시아어",
|
||||
"sk": "슬로바키아어",
|
||||
"sl": "슬로베니아어",
|
||||
"sv": "스웨덴어",
|
||||
"tr": "터키어",
|
||||
"vi": "베트남어",
|
||||
"zhCN": "중국어(중국)"
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
27
lang/languages-nl.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,12 +1,20 @@
|
||||
{
|
||||
"en": "Angielski",
|
||||
"en": "Anglik",
|
||||
"af": "",
|
||||
"az": "Azerski",
|
||||
"bg": "Bułgarski",
|
||||
"cs": "Czeski",
|
||||
"de": "Niemiecki",
|
||||
"el": "Grecki",
|
||||
"eo": "Esperanto",
|
||||
"es": "Hiszpański",
|
||||
"fr": "Francuski",
|
||||
"hy": "Ormiański",
|
||||
"it": "Włoski",
|
||||
"oc": "Okcytański",
|
||||
"ja": "Japoński",
|
||||
"ko": "Koreański",
|
||||
"nb": "Norweski Bokmal",
|
||||
"oc": "Oksytański",
|
||||
"pl": "Polski",
|
||||
"ptBR": "portugalski (brazylijski)",
|
||||
"ru": "Rosyjski",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Słoweński",
|
||||
"sv": "Szwedzki",
|
||||
"tr": "Turecki",
|
||||
"zhCN": "chiński (Chiny)",
|
||||
"nb": "norweski bokmal",
|
||||
"eo": "Esperanto"
|
||||
"vi": "Wietnamski",
|
||||
"zhCN": "Chiński (Chiny)"
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "Английский",
|
||||
"af": "",
|
||||
"az": "Азербайджанский",
|
||||
"bg": "Болгарский",
|
||||
"cs": "Чешский",
|
||||
"de": "Немецкий",
|
||||
"el": "Греческий",
|
||||
"eo": "Эсперанто",
|
||||
"es": "Испанский",
|
||||
"fr": "Французский",
|
||||
"hy": "Армянский",
|
||||
"it": "Итальянский",
|
||||
"ja": "Японский",
|
||||
"ko": "Корейский",
|
||||
"nb": "Норвежский букмол",
|
||||
"oc": "Окситанский",
|
||||
"pl": "Польский",
|
||||
"ptBR": "Португальский (Бразилия)",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Словенский",
|
||||
"sv": "Шведский",
|
||||
"tr": "Турецкий",
|
||||
"zhCN": "Китайский (Китай)",
|
||||
"nb": "Норвежский букмол",
|
||||
"eo": "Эсперанто"
|
||||
"vi": "Вьетнамский",
|
||||
"zhCN": "Китайский (Китай)"
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "Angličtina",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bulharština",
|
||||
"cs": "",
|
||||
"de": "Nemčina",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "Francúžtina",
|
||||
"hy": "",
|
||||
"it": "Taliančina",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Slovinčina",
|
||||
"sv": "",
|
||||
"tr": "Turečtina",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "Angleščina",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bolgarščina",
|
||||
"cs": "",
|
||||
"de": "Nemščina",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "Francoščina",
|
||||
"hy": "",
|
||||
"it": "Italjanščina",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Slovenščina",
|
||||
"sv": "",
|
||||
"tr": "Turščina",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "Engelska",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bulgariska",
|
||||
"cs": "",
|
||||
"de": "Tyska",
|
||||
"el": "",
|
||||
"eo": "Esperanto",
|
||||
"es": "Spanska",
|
||||
"fr": "Franska",
|
||||
"hy": "Armeniska",
|
||||
"it": "Italienska",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Norska (Bokmål)",
|
||||
"oc": "Occitanska",
|
||||
"pl": "Polska",
|
||||
"ptBR": "Portugisiska (Brasilien)",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Slovenska",
|
||||
"sv": "Svenska",
|
||||
"tr": "Turkiska",
|
||||
"zhCN": "Kinesiska (Kina)",
|
||||
"nb": "Norska (Bokmål)",
|
||||
"eo": "Esperanto"
|
||||
"vi": "",
|
||||
"zhCN": "Kinesiska (Kina)"
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"en": "İngilizce",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Bulgarca",
|
||||
"cs": "",
|
||||
"de": "Almanca",
|
||||
"el": "",
|
||||
"eo": "Esperanto",
|
||||
"es": "İspanyolca",
|
||||
"fr": "Fransızca",
|
||||
"hy": "Ermenice",
|
||||
"it": "İtalyanca",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Norveççe Bokmal",
|
||||
"oc": "Oksitan dili",
|
||||
"pl": "Lehçe",
|
||||
"ptBR": "Portekizce (Brezilya)",
|
||||
@@ -14,7 +22,6 @@
|
||||
"sl": "Slovence",
|
||||
"sv": "Isveççe",
|
||||
"tr": "Türkçe",
|
||||
"zhCN": "Çince (Tayvan)",
|
||||
"nb": "Norveççe Bokmal",
|
||||
"eo": "Esperanto"
|
||||
"vi": "",
|
||||
"zhCN": "Çince (Tayvan)"
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
{
|
||||
"en": "英语",
|
||||
"az": "",
|
||||
"af": "南非荷兰语",
|
||||
"az": "阿塞拜疆语",
|
||||
"bg": "保加利亚语",
|
||||
"cs": "",
|
||||
"cs": "捷克语",
|
||||
"de": "德语",
|
||||
"el": "",
|
||||
"el": "希腊语",
|
||||
"eo": "世界语",
|
||||
"es": "西班牙语",
|
||||
"fr": "法语",
|
||||
"hy": "亚美尼亚语",
|
||||
"it": "意大利语",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"ja": "日语",
|
||||
"ko": "韩语",
|
||||
"nb": "挪威布克摩尔语",
|
||||
"oc": "欧西坦语",
|
||||
"pl": "波兰语",
|
||||
@@ -21,6 +22,6 @@
|
||||
"sl": "斯洛文尼亚语",
|
||||
"sv": "瑞典语",
|
||||
"tr": "土耳其语",
|
||||
"vi": "",
|
||||
"vi": "越南语",
|
||||
"zhCN": "中文(中国)"
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"en": "English",
|
||||
"af": "",
|
||||
"az": "Azerbaijani",
|
||||
"bg": "Bulgarian",
|
||||
"cs": "Czech",
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
{
|
||||
"en": "English",
|
||||
"af": "Afrikaans",
|
||||
"az": "Azerbaijani",
|
||||
"bg": "Bulgarian",
|
||||
"ca": "Catalan",
|
||||
"cs": "Czech",
|
||||
"de": "German",
|
||||
"el": "Greek",
|
||||
"enGB": "English (United Kingdom)",
|
||||
"eo": "Esperanto",
|
||||
"es": "Spanish",
|
||||
"esUS": "Spanish (Latin America)",
|
||||
"fi": "Finnish",
|
||||
"fr": "French",
|
||||
"frCA": "French (Canadian)",
|
||||
"hr": "Croatian",
|
||||
"hy": "Armenian",
|
||||
"it": "Italian",
|
||||
"ja": "Japanese",
|
||||
"ko": "Korean",
|
||||
"nb": "Norwegian Bokmal",
|
||||
"nl": "Dutch",
|
||||
"oc": "Occitan",
|
||||
"pl": "Polish",
|
||||
"ptBR": "Portuguese (Brazil)",
|
||||
"ru": "Russian",
|
||||
"sk": "Slovak",
|
||||
"sl": "Slovenian",
|
||||
"sv": "Swedish",
|
||||
"tr": "Turkish",
|
||||
"vi": "Vietnamese",
|
||||
"zhCN": "Chinese (China)"
|
||||
"zhCN": "Chinese (China)",
|
||||
"zhTW": "Chinese (Taiwan)"
|
||||
}
|
||||
|
||||