Compare commits

...

27 Commits
2000 ... 2012

Author SHA1 Message Date
Lyubo Marinov
e5cc8cd32b [RN] locationURL instead of inviteURL
The value of inviteURL is derived from locationURL by removing the hash
and query/search params in order to make it fit for display and/or
public purposes. The Jitsi Meet SDK consumers do not fall into that
category and our intention is to provide them with the URL they used
with JitsiMeetView.openURL(URL) anyway.

Also rewrites to remove repetition. I'm not saying the new source code
is better really but at least I got to examine it and comment on some of
its weaknesses.
2017-06-09 00:03:23 -05:00
Lyubo Marinov
01b397faef Fixes a jsdoc/require-description-complete-sentence warning 2017-06-09 00:03:23 -05:00
Lyubo Marinov
90466183d6 [RN] Consistency in Jitsi Meet SDK for Android 2017-06-09 00:03:23 -05:00
Saúl Ibarra Corretgé
84463d8cf0 [RN] Add workaround for loading different URLs on Android
Changing the props from native (Java) code was only added in 0.45, so add a
workaround until we get to updating our React Native dependency.
2017-06-09 00:03:23 -05:00
Saúl Ibarra Corretgé
a075f24000 [RN] Add conference events to native SDKs
The current implementation doesn't use the API and Transport modules. This is
due to the fact that they are too tied to APP at the moment, which is web only.

Once API is refactored and moved into the Redux store this will be adjusted,
though it's unlikely that the lowest level React Native module (ExternalAPI)
changes drastically.

This commit also introduces a stopgap limitation of only allowing a single
instance for JitsiMeetView objects on both Android and iOS. React Native doesn't
really play well with having multiple instances of the same modules on the same
bridge, since they behave a bit like singletons. Even if we were to use multiple
bridges, some features depend on system-level global state, such as the
AVAudioSession mode or Android's immersive mode. Further attempts will be made
at lifting this limitation in the future, though.
2017-06-09 00:03:23 -05:00
Saúl Ibarra Corretgé
ddea60efe9 [RN] Add initial Jitsi Meet SDK for Android
Dames en heren, welcome to Jitsi Meet SDK for Android, the Jitsi Meet library
for Android.

The Jitsi Meet SDK encapsulates React Native and all the dependencies Jitsi
Meet has so other aopplications can integrate it easily.

Unlike iOS, creating "fat" libraries is not allways (if at all) possible on
Android, however, effort was put into making the integration as easy as
possible.

While React Native can be embedded in native applications, I don't think it was
designed to be embedded as part of an Android library, hidden away from the
application using it. This surfaced as a number of issues which had to be
addressed specifically due to our use-case:

- Activity lifecycle methods must be linked with the React Native engine, so the
  library provides wrapper methods.
- Custom fonts have to be manually added as assets, since the provided gradle
  script doesn't work properly in a library target.
- The RN packager has to be manually triggered since the gradle script will no
  longer do it for us.

At this stage, the Jitsi Meet application is just a small single activity
application which uses the Jitsi Meet SDK to create a single activity which
represents the entire application. Events and external conference handling are
forthcoming.

PS: Yours truly would like to add that it was a lot more fun to work on the iOS
side of things.
2017-06-09 00:03:23 -05:00
hristoterezov
5a14d1ed6c fix(deviceselectionpopup): the name and position of device selection button for film strip only mode 2017-06-08 21:46:58 -05:00
Leonard Kim
9837181d5d fix(popover): z-index should be greater than toasts
Currently, the JitsiPopover z-index will cause it to display below
any toast notifications so this changes modifies the z-index
values so JitsiPopover is higher than the notification toasts.
2017-06-08 09:37:40 -05:00
Saúl Ibarra Corretgé
f6ccacb7df [RN] Fix regression handling ToolbarButton onPress
Introduced in
96e83989a5
as part of a refactor + feature.
2017-06-08 09:36:58 -05:00
Saúl Ibarra Corretgé
a0054ada08 [RN] Simplify signing embedded iOS frameworks 2017-06-08 01:13:12 -05:00
Lyubo Marinov
2251a17f96 [RN] Consistency in Jitsi Meet SDK for iOS
1. Aligns the project structure of Jitsi Meet SDK for iOS with that for
   Android for better comprehension.

2. The command `react-native run-ios` uses the last Xcode project or
   workspace in the list of these sorted in alphabetical order. Which
   limits our freedom in naming. Thus having only an Xcode project in
   the root directory of the iOS project structure gives us back the
   freedom in naming.

3. Allows the Podspec to work for the app project in addition to the sdk
   project because we need Crashlytics in the app which is integrated
   via Cocoapods as well.

4. Further removes references to JitsiKit in the source code for the
   sake of consistent naming.
2017-06-08 01:13:12 -05:00
Saúl Ibarra Corretgé
b1100a9c7a [RN] Add initial Jitsi Meet SDK for iOS
Ladies and gentlemen, allow me to introduce you to Jitsi Meet SDK for iOS, the
mobile SDK which powers Jitsi Meet.

The goal is to encapsulate the entire React Native app into a framework / SDK
and offer an API for native (ObjC or Swift) applications to embed the Jitsi
conferencing experience.

While React Native can be embedded in native applications, I don't think it was
designed to be embedded as part of a framework, hidden away from the application
using it. This surfaced as a number of issues which had to be addressed
specifically due to our use-case:

- Universal / deep linking needed to be wrapped to avoid the embedding app from
  linking with RN.
- The bundle URL had to be manually constructed, since RN considers that all
  resources are in the main bundle, but in case of a framework that is not the
  case.
- Custom fonts had to be manually loaded, since UIAppFonts doesn't work on the
  framework's Info.plist file.
- The RN packager has to be manually triggered since the React project will no
  longer do it for us.
- Custom App Transport Security rules were added since the builtin way to do it
  modifies the framework's Info.plist, which is useless in this case.

At this stage, the Jitsi Meet application is just a small single view
application which uses the Jitsi Meet SDK to create a single view which
represents the entire application. Events and external conference handling are
forthcoming.
2017-06-08 00:50:00 -05:00
damencho
8e3dfcf0d0 Handles Enter key to submit dialogs.
If there is no focused node inside the dialog we focus any of the available buttons, submit button is first.
2017-06-07 17:06:48 -05:00
damencho
bf7415e6b5 Updates field-text dependency version and add autofocus prop. 2017-06-07 17:06:48 -05:00
Lyubo Marinov
6072978454 [RN] Fix the bundling broken by unnecessary dependencies 2017-06-07 16:19:57 -05:00
Lyubo Marinov
5a74080839 Comply w/ coding style 2017-06-07 13:27:45 -05:00
Lyubo Marinov
89862cbea9 [RN] Fix exports/imports of nonexistent files 2017-06-07 13:27:44 -05:00
Leonard Kim
0451e7c9e7 fix(conference): ensure avatar url and email changes act on strings
Both conference.changeLocalEmail and conference.changeLocalAvatarUrl
are exposed in the external api. It is possible for users to then
pass in non-string values. To make it more visibly obvious of the
error and to prevent script errors, convert whatever is passed in
into a string.
2017-06-07 13:25:24 -05:00
Leonard Kim
2e08815644 fix(video): use onplaying to ensure video height and width are set
When using onplay in firefox, the event fires before data is flowing,
which can cause videoHeight and videoWidth to be 0 during resizing.
By using onplaying, there is some assurance data is being received,
so videoHeight and videoWidth should be set.
2017-06-07 10:44:05 -05:00
hristoterezov
96e83989a5 feat(device_selection): Implement popup 2017-06-07 09:23:40 -05:00
paweldomas
2c002c875d fix(SmallVideo): remove invalid character
Removes ' character which should not be there.
2017-06-06 11:15:39 -05:00
paweldomas
e38dd0e9d3 ref(LargeVideoManager): remove unused method
'_isConnectionActive' is not used anymore
2017-06-06 11:13:02 -05:00
paweldomas
a2a2a583de doc(LargeVideoManager): invalid default
The default for 'show' argument of 'showRemoteConnectionMessage' is
undefined (unspecified).
2017-06-06 11:13:02 -05:00
paweldomas
a3ba28f507 ref(LargeVideoManager): simplify
Simplify 'updateParticipantConnStatusIndication' by getting rid of
'showMessage' argument.
2017-06-06 11:13:02 -05:00
paweldomas
6865b03338 fix(RemoteVideo): broken grey avatar
Also moves the logic about participant connection status from SmallVideo
to RemoteVideo, because it doesn't make sense for local videos.
2017-06-06 11:13:02 -05:00
paweldomas
12d7e61362 feat(VideoLayout): add ninja icon
Add ninja icon which wil be displayed when user's connection status is
inactive.

Apply grey filter only for interrupted state.

Do not use isLastN directly, but check ParticipantConnectionStatus.
2017-06-06 11:13:02 -05:00
paweldomas
5c5864e94a font(jitsi): add ninja icon 2017-06-06 11:13:02 -05:00
130 changed files with 5092 additions and 2501 deletions

4
.gitignore vendored
View File

@@ -65,3 +65,7 @@ buck-out/
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
# CocoaPods
Pods/
Podfile.lock

View File

@@ -31,6 +31,8 @@ deploy-appbundle:
$(BUILD_DIR)/do_external_connect.min.map \
$(BUILD_DIR)/external_api.min.js \
$(BUILD_DIR)/external_api.min.map \
$(BUILD_DIR)/device_selection_popup_bundle.min.js \
$(BUILD_DIR)/device_selection_popup_bundle.min.map \
$(OUTPUT_DIR)/analytics.js \
$(DEPLOY_DIR)

182
android/README.md Normal file
View File

@@ -0,0 +1,182 @@
# Jitsi Meet SDK for Android
This directory contains the source code of the Jitsi Meet app and the Jitsi Meet
SDK for Android.
## Jitsi Meet SDK
Jitsi Meet SDK is an Android library which embodies the whole Jitsi Meet
experience and makes it reusable by third-party apps.
To get started, extends your `android.app.Activity` from
`org.jitsi.meet.sdk.JitsiMeetActivity`:
```java
package org.jitsi.example;
import org.jitsi.meet.sdk.JitsiMeetActivity;
public class MainActivity extends JitsiMeetActivity {
}
```
Alternatively, you can use the `org.jitsi.meet.sdk.JitsiMeetView` class which
extends `android.view.View`:
```java
package org.jitsi.example;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import org.jitsi.meet.sdk.JitsiMeetView;
public class MainActivity extends AppCompatActivity {
private JitsiMeetView view;
@Override
public void onBackPressed() {
if (!JitsiMeetView.onBackPressed()) {
// Invoke the default handler if it wasn't handled by React.
super.onBackPressed();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new JitsiMeetView(this);
view.loadURL(null);
setContentView(view);
}
@Override
protected void onDestroy() {
super.onDestroy();
JitsiMeetView.onHostDestroy(this);
}
@Override
public void onNewIntent(Intent intent) {
JitsiMeetView.onNewIntent(intent);
}
@Override
protected void onPause() {
super.onPause();
JitsiMeetView.onHostPause(this);
}
@Override
protected void onResume() {
super.onResume();
JitsiMeetView.onHostResume(this);
}
}
```
### JitsiMeetActivity
This class encapsulates a high level API in the form of an Android `Activity`
which displays a single `JitsiMeetView`.
#### loadURL(url)
See JitsiMeetView.loadURL.
### JitsiMeetView
The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to
display a Jitsi Meet conference (or a welcome page).
#### getListener()
Returns the `JitsiMeetView.Listener` instance attached to the view.
#### loadURL(url)
Loads the given URL and joins the room. If `null` is specified, the welcome page
is displayed instead.
#### setListener(listener)
Sets the given listener (class implementing the `JitsiMeetView.Listener`
interface) on the view.
#### onBackPressed()
Helper method which should be called from the activity's `onBackPressed` method.
If this function returns `true`, it means the action was handled and thus no
extra processing is required; otherwise the app should call the parent's
`onBackPressed` method.
This is a static method.
#### onHostDestroy(activity)
Helper method which should be called from the activity's `onDestroy` method.
This is a static method.
#### onHostPause(activity)
Helper method which should be called from the activity's `onPause` method.
This is a static method.
#### onHostResume(activity)
Helper method which should be called from the activity's `onResume` method.
This is a static method.
#### onNewIntent(intent)
Helper method for integrating the *deep linking* functionality. If your app's
activity is launched in "singleTask" mode this method should be called from the
activity's `onNewIntent` method.
This is a static method.
#### Listener
JitsiMeetView.Listener provides an interface apps can implement in order to get
notified about the state of the Jitsi Meet conference.
##### onConferenceFailed
Called when a joining a conference was unsuccessful or when there was an error
while in a conference.
The `data` HashMap contains an "error" key describing the error and a "url"
key with the conference URL.
#### onConferenceJoined
Called when a conference was joined.
The `data` HashMap contains a "url" key with the conference URL.
#### onConferenceLeft
Called when a conference was left.
The `data` HashMap contains a "url" key with the conference URL.
#### onConferenceWillJoin
Called before a conference is joined.
The `data` HashMap contains a "url" key with the conference URL.
#### onConferenceWillLeave
Called before a conference is left.
The `data` HashMap contains a "url" key with the conference URL.

View File

@@ -1,66 +0,0 @@
import re
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
android_library(
name = 'all-libs',
exported_deps = lib_deps
)
android_library(
name = 'app-code',
srcs = glob([
'src/main/java/**/*.java',
]),
deps = [
':all-libs',
':build_config',
':res',
],
)
android_build_config(
name = 'build_config',
package = 'org.jitsi.meet',
)
android_resource(
name = 'res',
res = 'src/main/res',
package = 'org.jitsi.meet',
)
android_binary(
name = 'app',
package_type = 'debug',
manifest = 'src/main/AndroidManifest.xml',
keystore = '//android/keystores:debug',
deps = [
':app-code',
],
)

View File

@@ -1,136 +1,37 @@
apply plugin: 'com.android.application'
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"]
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
apply from: '../../node_modules/react-native/react.gradle'
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId 'org.jitsi.meet'
minSdkVersion 16
targetSdkVersion 22
versionCode Integer.parseInt("${version}")
versionName "1.4.${version}"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
packagingOptions {
// The project react-native does not provide 64-bit binaries at the
// time of this writing. Unfortunately, packaging any 64-bit
// binaries into the .apk will crash the app at runtime on 64-bit
// platforms.
exclude 'lib/x86_64/libjingle_peerconnection_so.so'
exclude 'lib/arm64-v8a/libjingle_peerconnection_so.so'
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include 'armeabi-v7a', 'x86'
exclude '/lib/mips64/**'
exclude '/lib/arm64-v8a/**'
exclude '/lib/x86_64/**'
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ['armeabi-v7a':1, 'x86':2]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
if (project.hasProperty('JITSI_SIGNING')
@@ -139,21 +40,8 @@ if (project.hasProperty('JITSI_SIGNING')
}
dependencies {
compile project(':react-native-background-timer')
compile project(':react-native-immersive')
compile project(':react-native-keep-awake')
compile project(':react-native-vector-icons')
compile project(':react-native-webrtc')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.react:react-native:+' // From node_modules
}
apply from: '../../node_modules/react-native-vector-icons/fonts.gradle'
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
debugCompile project(path: ':sdk', configuration: 'debug')
releaseCompile project(path: ':sdk', configuration: 'release')
}

View File

@@ -1,30 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jitsi.meet"
android:versionCode="1"
android:versionName="1.0">
<!-- XXX: ACCESS_NETWORK_STATE is required by WebRTC. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="23" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jitsi.meet">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".MainApplication"
android:theme="@style/AppTheme">
<activity
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
@@ -54,7 +32,5 @@
<data android:scheme="org.jitsi.meet" />
</intent-filter>
</activity>
<activity
android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

View File

@@ -1,46 +1,34 @@
/*
* 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.
*/
package org.jitsi.meet;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import org.jitsi.meet.sdk.JitsiMeetActivity;
public class MainActivity extends ReactActivity {
/**
* {@inheritDoc}
*
* Overrides {@link ReactActivity#createRootActivityDelegate()} to customize
* the {@link ReactRootView} with a background color that is in accord with
* the JavaScript and iOS parts of the application and causes less perceived
* visual flicker than the default background color.
*/
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
/**
* {@inheritDoc}
*
* Overrides {@link ReactActivityDelegate#createRootView()} to
* customize the {@link ReactRootView} with a background color that
* is in accord with the JavaScript and iOS parts of the application
* and causes less perceived visual flicker than the default
* background color.
*/
@Override
protected ReactRootView createRootView() {
ReactRootView rootView = super.createRootView();
rootView.setBackgroundColor(0xFF111111);
return rootView;
}
};
}
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "App";
}
/**
* The one and only {@link Activity} that the Jitsi Meet app needs. The
* {@code Activity} is launched in {@code singleTask} mode, so it will be
* created upon application initialization and there will be a single instance
* of it. Further attempts at launching the application once it was already
* launched will result in {@link Activity#onNewIntent(Intent)} being called.
*
* This {@code Activity} extends {@link JitsiMeetActivity} without adding
* anything to it. It exists to merely keep the React Native CLI working, since
* the latter always tries to launch an {@code Activity} named
* {@code MainActivity} when doing {@code react-native run-android}.
*/
public class MainActivity extends JitsiMeetActivity {
}

View File

@@ -1,65 +0,0 @@
package org.jitsi.meet;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
/**
* {@inheritDoc}
*/
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
/**
* {@inheritDoc}
*/
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.oblador.vectoricons.VectorIconsPackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.rnimmersive.RNImmersivePackage(),
new org.jitsi.meet.audiomode.AudioModePackage(),
new org.jitsi.meet.proximity.ProximityPackage()
);
}
};
/**
* {@inheritDoc}
*/
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
if (!getReactNativeHost()
.getReactInstanceManager()
.getDevSupportManager()
.getDevSupportEnabled()) {
// TODO Auto-generated method stub
}
}
}

View File

@@ -1,9 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:windowTranslucentStatus">true</item>
</style>
</resources>

View File

@@ -1,4 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
// Top-level build file where you can add configuration options common to all
// sub-projects/modules.
buildscript {
repositories {
@@ -8,7 +9,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
// in the individual module build.gradle files.
}
}
@@ -17,8 +18,16 @@ allprojects {
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
// All of React Native (JS, Obj-C sources, Android binaries) is
// installed from npm.
url "$rootDir/../node_modules/react-native/android"
}
}
}
ext {
compileSdkVersion = 23
buildToolsVersion = "23.0.1"
minSdkVersion = 16
targetSdkVersion = 23
}

1
android/sdk/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

116
android/sdk/build.gradle Normal file
View File

@@ -0,0 +1,116 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
publishNonDefault true
}
buildTypes {
debug {}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.react:react-native:+'
compile project(':react-native-background-timer')
compile project(':react-native-immersive')
compile project(':react-native-keep-awake')
compile project(':react-native-vector-icons')
compile project(':react-native-webrtc')
}
// Build process helpers
//
void runBefore(String dependentTaskName, Task task) {
Task dependentTask = tasks.findByPath(dependentTaskName);
if (dependentTask != null) {
dependentTask.dependsOn task
}
}
gradle.projectsEvaluated {
android.buildTypes.all { buildType ->
def buildNameCapitalized = "${buildType.name.capitalize()}"
def bundlePath = "${buildDir}/intermediates/bundles/${buildType.name}"
// Bundle fonts in react-native-vector-icons.
//
def currentFontTask = tasks.create(name: "${buildType.name}CopyFonts", type: Copy) {
from("${projectDir}/../../node_modules/react-native-vector-icons/Fonts/")
into("${bundlePath}/assets/fonts")
}
currentFontTask.dependsOn("merge${buildNameCapitalized}Resources")
currentFontTask.dependsOn("merge${buildNameCapitalized}Assets")
runBefore("processArmeabi-v7a${buildNameCapitalized}Resources", currentFontTask)
runBefore("processX86${buildNameCapitalized}Resources", currentFontTask)
runBefore("processUniversal${buildNameCapitalized}Resources", currentFontTask)
runBefore("process${buildNameCapitalized}Resources", currentFontTask)
// Bundle JavaScript and React resources.
// (adapted from react-native/react.gradle)
//
// React JS bundle directories
def jsBundleDir = file("${bundlePath}/assets")
def resourcesDir = file("${bundlePath}/res/merged")
def jsBundleFile = file("${jsBundleDir}/index.android.bundle")
// Bundle task name for variant.
def bundleJsAndAssetsTaskName = "bundle${buildNameCapitalized}JsAndAssets"
def currentBundleTask = tasks.create(
name: bundleJsAndAssetsTaskName,
type: Exec) {
// Set up inputs and outputs so gradle can cache the result.
def reactRoot = file("${projectDir}/../../")
inputs.files fileTree(dir: reactRoot, excludes: ["android/**", "ios/**"])
outputs.dir jsBundleDir
outputs.dir resourcesDir
// Set up the call to the react-native cli.
workingDir reactRoot
// Create JS bundle
def devEnabled = !buildNameCapitalized.toLowerCase().contains("release")
commandLine(
"node",
"node_modules/react-native/local-cli/cli.js",
"bundle",
"--assets-dest", resourcesDir,
"--bundle-output", jsBundleFile,
"--dev", "${devEnabled}",
"--entry-file", "index.android.js",
"--platform", "android",
"--reset-cache")
// TODO: disable task in Debug mode?
}
// Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process
currentBundleTask.dependsOn("merge${buildNameCapitalized}Resources")
currentBundleTask.dependsOn("merge${buildNameCapitalized}Assets")
runBefore("processArmeabi-v7a${buildNameCapitalized}Resources", currentBundleTask)
runBefore("processX86${buildNameCapitalized}Resources", currentBundleTask)
runBefore("processUniversal${buildNameCapitalized}Resources", currentBundleTask)
runBefore("process${buildNameCapitalized}Resources", currentBundleTask)
}
}

25
android/sdk/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/scorretge/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jitsi.meet.sdk">
<!-- XXX: ACCESS_NETWORK_STATE is required by WebRTC. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus"/>
<application android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

View File

@@ -0,0 +1,158 @@
/*
* 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.
*/
package org.jitsi.meet.sdk;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.net.URL;
/**
* Base Activity for applications integrating Jitsi Meet at a higher level. It
* contains all the required wiring between the <tt>JKConferenceView</tt> and
* the Activity lifecycle methods already implemented.
*
* In this activity we use a single <tt>JKConferenceView</tt> instance. This
* instance gives us access to a view which displays the welcome page and the
* conference itself. All lifetime methods associated with this Activity are
* hooked to the React Native subsystem via proxy calls through the
* <tt>JKConferenceView</tt> static methods.
*/
public class JitsiMeetActivity extends AppCompatActivity {
/**
* Code for identifying the permission to draw on top of other apps. The
* number is chosen arbitrarily and used to correlate the intent with its
* result.
*/
public static final int OVERLAY_PERMISSION_REQ_CODE = 4242;
/**
* Instance of the {@JitsiMeetView} which this activity will display.
*/
private JitsiMeetView view;
/**
* Loads the given URL and displays the conference. If the specified URL is
* null, the welcome page is displayed instead.
*
* @param url - The conference URL.
*/
public void loadURL(@Nullable URL url) {
view.loadURL(url);
}
/**
* {@inheritDoc}
*/
@Override
protected void onActivityResult(
int requestCode,
int resultCode,
Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
// Permission not granted.
return;
}
}
setContentView(view);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onBackPressed() {
if (!JitsiMeetView.onBackPressed()) {
// Invoke the default handler if it wasn't handled by React.
super.onBackPressed();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new JitsiMeetView(this);
view.loadURL(null);
// In debug mode React needs permission to write over other apps in
// order to display the warning and error overlays.
if (BuildConfig.DEBUG
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !Settings.canDrawOverlays(this)) {
Intent intent
= new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
return;
}
setContentView(view);
}
/**
* {@inheritDoc}
*/
@Override
protected void onDestroy() {
super.onDestroy();
JitsiMeetView.onHostDestroy(this);
}
/**
* {@inheritDoc}
*/
@Override
public void onNewIntent(Intent intent) {
JitsiMeetView.onNewIntent(intent);
}
/**
* {@inheritDoc}
*/
@Override
protected void onPause() {
super.onPause();
JitsiMeetView.onHostPause(this);
}
/**
* {@inheritDoc}
*/
@Override
protected void onResume() {
super.onResume();
JitsiMeetView.onHostResume(this);
}
}

View File

@@ -0,0 +1,282 @@
/*
* 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.
*/
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.FrameLayout;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import java.net.URL;
import java.util.HashMap;
public class JitsiMeetView extends FrameLayout {
/**
* Background color used by {@code JitsiMeetView} and the React Native root
* view.
*/
private static final int BACKGROUND_COLOR = 0xFF111111;
/**
* Reference to the single instance of this class we currently allow. It's
* currently used for fetching the instance from the listener's callbacks.
*
* TODO: lift this limitation.
*/
private static JitsiMeetView instance;
/**
* React Native bridge. The instance manager allows embedding applications
* to create multiple root views off the same JavaScript bundle.
*/
private static ReactInstanceManager reactInstanceManager;
/**
* {@JitsiMeetView.Listener} instance for reporting events occurring in
* Jitsi Meet.
*/
private JitsiMeetView.Listener listener;
/**
* React Native root view.
*/
private ReactRootView reactRootView;
public JitsiMeetView(@NonNull Context context) {
super(context);
if (instance != null) {
throw new RuntimeException(
"Only a single instance is currently allowed");
}
/*
* TODO: Only allow a single instance for now. All React Native modules
* are kinda singletons so global state would be broken since we have a
* single bridge. Once we have that sorted out multiple instances of
* JitsiMeetView will be allowed.
*/
instance = this;
setBackgroundColor(BACKGROUND_COLOR);
if (reactInstanceManager == null) {
initReactInstanceManager(((Activity) context).getApplication());
}
}
/**
* Returns the only instance of this class we currently allow creating.
*
* @returns The {@JitsiMeetView} instance.
*/
public static JitsiMeetView getInstance() {
return instance;
}
/**
* Getter for the {@JitsiMeetView.Listener} set on this view.
*
* @returns The {@JitsiMeetView.Listener} instance.
*/
public Listener getListener() {
return listener;
}
/**
* Internal method to initialize the React Native instance manager. We
* create a single instance in order to load the JavaScript bundle a single
* time. All <tt>ReactRootView</tt> instances will be tied to the one and
* only <tt>ReactInstanceManager</tt>.
*
* @param application - <tt>Application</tt> instance which is running.
*/
private static void initReactInstanceManager(Application application) {
reactInstanceManager = ReactInstanceManager.builder()
.setApplication(application)
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new com.corbt.keepawake.KCKeepAwakePackage())
.addPackage(new com.facebook.react.shell.MainReactPackage())
.addPackage(new com.oblador.vectoricons.VectorIconsPackage())
.addPackage(new com.ocetnik.timer.BackgroundTimerPackage())
.addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
.addPackage(new com.rnimmersive.RNImmersivePackage())
.addPackage(new org.jitsi.meet.sdk.audiomode.AudioModePackage())
.addPackage(new org.jitsi.meet.sdk.externalapi.ExternalAPIPackage())
.addPackage(new org.jitsi.meet.sdk.proximity.ProximityPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
}
/**
* Loads the given URL and displays the conference. If the specified URL is
* null, the welcome page is displayed instead.
*
* @param url - The conference URL.
*/
public void loadURL(@Nullable URL url) {
Bundle props = new Bundle();
if (url != null) {
props.putString("url", url.toString());
}
// TODO: ReactRootView#setAppProperties is only available on React
// Native 0.45, so destroy the current root view and create a new one.
if (reactRootView != null) {
removeView(reactRootView);
reactRootView = null;
}
reactRootView = new ReactRootView(getContext());
reactRootView
.startReactApplication(reactInstanceManager, "App", props);
reactRootView.setBackgroundColor(BACKGROUND_COLOR);
addView(reactRootView);
}
/**
* Setter for the {@JitsiMeetView.Listener} set on this view.
*
* @param listener - Listener for this view.
*/
public void setListener(Listener listener) {
this.listener = listener;
}
/**
* Activity lifecycle method which should be called from
* <tt>Activity.onBackPressed</tt> so we can do the required internal
* processing.
*
* @return - true if the back-press was processed, false otherwise. In case
* false is returned the application should call the parent's
* implementation.
*/
public static boolean onBackPressed() {
if (reactInstanceManager != null) {
reactInstanceManager.onBackPressed();
return true;
} else {
return false;
}
}
/**
* Activity lifecycle method which should be called from
* <tt>Activity.onDestroy</tt> so we can do the required internal
* processing.
*
* @param activity - <tt>Activity</tt> being destroyed.
*/
public static void onHostDestroy(Activity activity) {
if (reactInstanceManager != null) {
reactInstanceManager.onHostDestroy(activity);
}
}
/**
* Activity lifecycle method which should be called from
* <tt>Activity.onPause</tt> so we can do the required internal processing.
*
* @param activity - <tt>Activity</tt> being paused.
*/
public static void onHostPause(Activity activity) {
if (reactInstanceManager != null) {
reactInstanceManager.onHostPause(activity);
}
}
/**
* Activity lifecycle method which should be called from
* <tt>Activity.onResume</tt> so we can do the required internal processing.
*
* @param activity - <tt>Activity</tt> being resumed.
*/
public static void onHostResume(Activity activity) {
if (reactInstanceManager != null) {
reactInstanceManager.onHostResume(activity, null);
}
}
/**
* Activity lifecycle method which should be called from
* <tt>Activity.onNewIntent</tt> so we can do the required internal
* processing. Note that this is only needed if the activity's "launchMode"
* was set to "singleTask". This is required for deep linking to work once
* the application is already running.
*
* @param intent - <tt>Intent</tt> instance which was received.
*/
public static void onNewIntent(Intent intent) {
if (reactInstanceManager != null) {
reactInstanceManager.onNewIntent(intent);
}
}
/**
* Interface for listening to events coming from Jitsi Meet.
*/
public interface Listener {
/**
* Called when joining a conference fails or an ongoing conference is
* interrupted due to a failure.
*
* @param data - HashMap with an "error" key describing the problem, and
* a "url" key with the conference URL.
*/
void onConferenceFailed(HashMap<String, Object> data);
/**
* Called when a conference was joined.
*
* @param data - HashMap with a "url" key with the conference URL.
*/
void onConferenceJoined(HashMap<String, Object> data);
/**
* Called when the conference was left, typically after hanging up.
*
* @param data - HashMap with a "url" key with the conference URL.
*/
void onConferenceLeft(HashMap<String, Object> data);
/**
* Called before the conference is joined.
*
* @param data - HashMap with a "url" key with the conference URL.
*/
void onConferenceWillJoin(HashMap<String, Object> data);
/**
* Called before the conference is left.
*
* @param data - HashMap with a "url" key with the conference URL.
*/
void onConferenceWillLeave(HashMap<String, Object> data);
}
}

View File

@@ -1,4 +1,20 @@
package org.jitsi.meet.audiomode;
/*
* 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.
*/
package org.jitsi.meet.sdk.audiomode;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;

View File

@@ -1,4 +1,20 @@
package org.jitsi.meet.audiomode;
/*
* 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.
*/
package org.jitsi.meet.sdk.audiomode;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;

View File

@@ -1,7 +1,22 @@
package org.jitsi.meet.audiomode;
/*
* 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.
*/
package org.jitsi.meet.sdk.audiomode;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;

View File

@@ -0,0 +1,109 @@
/*
* 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.
*/
package org.jitsi.meet.sdk.externalapi;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import org.jitsi.meet.sdk.JitsiMeetView;
import java.util.HashMap;
/**
* Module implementing a simple API to enable a proximity sensor-controlled
* wake lock. When the lock is held, if the proximity sensor detects a nearby
* object it will dim the screen and disable touch controls. The functionality
* is used with the conference audio-only mode.
*/
public class ExternalAPIModule extends ReactContextBaseJavaModule {
/**
* React Native module name.
*/
private static final String MODULE_NAME = "ExternalAPI";
/**
* Initializes a new module instance. There shall be a single instance of
* this module throughout the lifetime of the application.
*
* @param reactContext the {@link ReactApplicationContext} where this module
* is created.
*/
public ExternalAPIModule(ReactApplicationContext reactContext) {
super(reactContext);
}
/**
* Gets the name of this module to be used in the React Native bridge.
*
* @return The name of this module to be used in the React Native bridge.
*/
@Override
public String getName() {
return MODULE_NAME;
}
/**
* Dispatches an event that occurred on JavaScript to the view's listener.
*
* @param name - Event name.
* @param data - Ancillary data for the event.
*/
@ReactMethod
public void sendEvent(final String name, ReadableMap data) {
JitsiMeetView view = JitsiMeetView.getInstance();
JitsiMeetView.Listener listener
= view != null ? view.getListener() : null;
if (listener == null) {
return;
}
// TODO: Sigh, converting a ReadableMap to a HashMap is not supported
// until React Native 0.46.
final HashMap<String, Object> dataMap = new HashMap<>();
try {
switch (name) {
case "CONFERENCE_FAILED":
dataMap.put("error", data.getString("error"));
dataMap.put("url", data.getString("url"));
listener.onConferenceFailed(dataMap);
break;
case "CONFERENCE_JOINED":
dataMap.put("url", data.getString("url"));
listener.onConferenceJoined(dataMap);
break;
case "CONFERENCE_LEFT":
dataMap.put("url", data.getString("url"));
listener.onConferenceLeft(dataMap);
break;
case "CONFERENCE_WILL_JOIN":
dataMap.put("url", data.getString("url"));
listener.onConferenceWillJoin(dataMap);
break;
case "CONFERENCE_WILL_LEAVE":
dataMap.put("url", data.getString("url"));
listener.onConferenceWillLeave(dataMap);
break;
}
} catch (UnsupportedOperationException e) {
// Allow partial interface implementations.
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.
*/
package org.jitsi.meet.sdk.externalapi;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Implements {@link ReactPackage} for {@link ExternalAPIModule}.
*/
public class ExternalAPIPackage implements ReactPackage {
/**
* {@inheritDoc}
*/
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* {@inheritDoc}
*
* @return List of native modules to be exposed by React Native.
*/
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ExternalAPIModule(reactContext));
return modules;
}
/**
* {@inheritDoc}
*/
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@@ -1,4 +1,20 @@
package org.jitsi.meet.proximity;
/*
* 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.
*/
package org.jitsi.meet.sdk.proximity;
import android.content.Context;
import android.os.PowerManager;

View File

@@ -1,4 +1,20 @@
package org.jitsi.meet.proximity;
/*
* 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.
*/
package org.jitsi.meet.sdk.proximity;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Jitsi Meet SDK</string>
</resources>

View File

@@ -1,6 +1,6 @@
rootProject.name = 'jitsi-meet'
include ':app'
include ':app', ':sdk'
include ':react-native-background-timer'
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
include ':react-native-immersive'

View File

@@ -2051,7 +2051,7 @@ export default {
* @param email {string} the new email
*/
changeLocalEmail(email = '') {
email = email.trim();
email = String(email).trim();
if (email === APP.settings.getEmail()) {
return;
@@ -2075,7 +2075,7 @@ export default {
* @param url {string} the new url
*/
changeLocalAvatarUrl(url = '') {
url = url.trim();
url = String(url).trim();
if (url === APP.settings.getAvatarUrl()) {
return;
@@ -2125,18 +2125,6 @@ export default {
eventEmitter.removeListener(eventName, listener);
},
/**
* Checks if the participant given by participantId is currently in the
* last N set if there's one supported.
*
* @param participantId the identifier of the participant
* @returns {boolean} {true} if the participant given by the participantId
* is currently in the last N set or if there's no last N set at this point
* and {false} otherwise
*/
isInLastN(participantId) {
return room.isInLastN(participantId);
},
/**
* Changes the display name for the local user
* @param nickname {string} the new display name

View File

@@ -136,7 +136,7 @@
&__videos-filmstripOnly {
margin-top: auto;
margin-bottom: auto;
padding-right: $defaultToolbarSize;
padding-right: $defaultFilmStripOnlyToolbarSize;
}
.remote-videos-container {

View File

@@ -88,6 +88,9 @@
.icon-mic-disabled:before {
content: "\e912";
}
.icon-ninja:before {
content: "\e909";
}
.icon-raised-hand:before {
content: "\e91e";
}

View File

@@ -200,7 +200,13 @@
height: auto;
position: absolute;
right: 0;
width: $defaultToolbarSize;
width: $defaultFilmStripOnlyToolbarSize;
.button {
height: 37px;
line-height: 37px !important;
width: 37px;
}
.button:first-child {
border-top-left-radius: 3px;

View File

@@ -34,6 +34,7 @@ $tooltipBg: rgba(0,0,0, 0.7);
* Toolbar
*/
$defaultToolbarSize: 50px;
$defaultFilmStripOnlyToolbarSize: 37px;
$splitterToolbarButtonMargin: 18px;
$toolbarBackground: rgba(0, 0, 0, 0.5);
$toolbarBadgeBackground: #165ECC;
@@ -127,9 +128,9 @@ $toolbarZ: 400;
$tooltipsZ: 401;
$dropdownMaskZ: 900;
$dropdownZ: 901;
$jitsipopoverZ: 1010;
$centeredVideoLabelZ: 1011;
$notificationZ: 1012;
$centeredVideoLabelZ: 1010;
$notificationZ: 1011;
$jitsipopoverZ: 1012;
$popoverZ: 1015;
$overlayZ: 1016;

View File

@@ -97,6 +97,11 @@
color: #FFFFFF;/*#15A1ED*/
overflow: hidden;
}
&_ninja
{
font-size: 1.5em;
}
}
.icon-connection,

Binary file not shown.

View File

@@ -21,6 +21,7 @@
<glyph unicode="&#xe906;" glyph-name="chat" d="M854 342v512h-684v-598l86 86h598zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe907;" glyph-name="edit" d="M884 724l-78-78-160 160 78 78c16 16 44 16 60 0l100-100c16-16 16-44 0-60zM128 288l472 472 160-160-472-472h-160v160z" />
<glyph unicode="&#xe908;" glyph-name="share-doc" d="M554 640h236l-236 234v-234zM682 426v86h-340v-86h340zM682 256v86h-340v-86h340zM598 938l256-256v-512c0-46-40-84-86-84h-512c-46 0-86 38-86 84l2 684c0 46 38 84 84 84h342z" />
<glyph unicode="&#xe909;" glyph-name="ninja" d="M330.667 469.333c-0.427 14.933 6.4 29.44 17.92 39.253 32-6.827 61.867-20.053 88.747-39.253 0-29.013-23.893-52.907-53.333-52.907s-52.907 23.467-53.333 52.907zM586.667 469.333c26.88 18.773 56.747 32 88.747 38.827 11.52-9.813 18.347-24.32 17.92-38.827 0-29.867-23.893-53.76-53.333-53.76s-53.333 23.893-53.333 53.76v0zM512 640c-118.187 1.707-234.667-27.733-338.347-85.333l-2.987-42.667c0-52.48 12.373-104.107 35.84-151.040 101.12 15.36 203.093 23.040 305.493 23.040s204.373-7.68 305.493-23.040c23.467 46.933 35.84 98.56 35.84 151.040l-2.987 42.667c-103.68 57.6-220.16 87.040-338.347 85.333zM512 938.667c235.641 0 426.667-191.025 426.667-426.667s-191.025-426.667-426.667-426.667c-235.641 0-426.667 191.025-426.667 426.667s191.025 426.667 426.667 426.667z" />
<glyph unicode="&#xe90a;" glyph-name="star-full" d="M512 288l-264-160 70 300-232 202 306 26 120 282 120-282 306-26-232-202 70-300z" />
<glyph unicode="&#xe90b;" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />
<glyph unicode="&#xe90c;" glyph-name="exit-full-screen" d="M682 682h128v-84h-212v212h84v-128zM598 214v212h212v-84h-128v-128h-84zM342 682v128h84v-212h-212v84h128zM214 342v84h212v-212h-84v128h-128z" />

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,62 @@
{
"IcoMoonType": "selection",
"icons": [
{
"icon": {
"paths": [
"M330.667 554.667c-0.427-14.933 6.4-29.44 17.92-39.253 32 6.827 61.867 20.053 88.747 39.253 0 29.013-23.893 52.907-53.333 52.907s-52.907-23.467-53.333-52.907zM586.667 554.667c26.88-18.773 56.747-32 88.747-38.827 11.52 9.813 18.347 24.32 17.92 38.827 0 29.867-23.893 53.76-53.333 53.76s-53.333-23.893-53.333-53.76v0zM512 384c-118.187-1.707-234.667 27.733-338.347 85.333l-2.987 42.667c0 52.48 12.373 104.107 35.84 151.040 101.12-15.36 203.093-23.040 305.493-23.040s204.373 7.68 305.493 23.040c23.467-46.933 35.84-98.56 35.84-151.040l-2.987-42.667c-103.68-57.6-220.16-87.040-338.347-85.333zM512 85.333c235.641 0 426.667 191.025 426.667 426.667s-191.025 426.667-426.667 426.667c-235.641 0-426.667-191.025-426.667-426.667s191.025-426.667 426.667-426.667z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 24,
"tags": [
"ninja"
]
},
"attrs": [
{}
],
"properties": {
"order": 851,
"id": 121,
"name": "ninja",
"prevSize": 32,
"code": 59657
},
"setIdx": 0,
"setId": 1,
"iconIdx": 0
},
{
"icon": {
"paths": [
"M282 460c62 120 162 220 282 282l94-94c12-12 30-16 44-10 48 16 100 24 152 24 24 0 42 18 42 42v150c0 24-18 42-42 42-400 0-726-326-726-726 0-24 18-42 42-42h150c24 0 42 18 42 42 0 54 8 104 24 152 4 14 2 32-10 44z"
],
"attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"phone"
],
"defaultCode": 57549,
"grid": 24
},
"attrs": [],
"properties": {
"ligatures": "call, local_phone, phone",
"id": 120,
"order": 848,
"prevSize": 32,
"code": 57549,
"name": "phone"
},
"setIdx": 0,
"setId": 1,
"iconIdx": 41
},
{
"icon": {
"paths": [
@@ -28,7 +84,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 0
"iconIdx": 1
},
{
"icon": {
@@ -57,7 +113,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 1
"iconIdx": 2
},
{
"icon": {
@@ -86,7 +142,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 2
"iconIdx": 3
},
{
"icon": {
@@ -112,7 +168,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 3
"iconIdx": 4
},
{
"icon": {
@@ -130,7 +186,7 @@
"attrs": [],
"properties": {
"id": 4,
"order": 63,
"order": 849,
"ligatures": "call_end",
"prevSize": 32,
"code": 59653,
@@ -138,7 +194,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 4
"iconIdx": 5
},
{
"icon": {
@@ -164,7 +220,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 5
"iconIdx": 6
},
{
"icon": {
@@ -190,7 +246,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 6
"iconIdx": 7
},
{
"icon": {
@@ -216,7 +272,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 7
"iconIdx": 8
},
{
"icon": {
@@ -242,7 +298,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 8
"iconIdx": 9
},
{
"icon": {
@@ -1129,33 +1185,6 @@
"setIdx": 0,
"setId": 1,
"iconIdx": 40
},
{
"icon": {
"paths": [
"M282 460c62 120 162 220 282 282l94-94c12-12 30-16 44-10 48 16 100 24 152 24 24 0 42 18 42 42v150c0 24-18 42-42 42-400 0-726-326-726-726 0-24 18-42 42-42h150c24 0 42 18 42 42 0 54 8 104 24 152 4 14 2 32-10 44z"
],
"attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"phone"
],
"defaultCode": 57549,
"grid": 24
},
"attrs": [],
"properties": {
"ligatures": "call, local_phone, phone",
"id": 120,
"order": 848,
"prevSize": 24,
"code": 57549,
"name": "phone"
},
"setIdx": 1,
"setId": 0,
"iconIdx": 120
}
],
"height": 1024,

View File

@@ -36,14 +36,14 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
*/
TOOLBAR_BUTTONS: [
//main toolbar
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'hangup',
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'fodeviceselection', 'hangup', // jshint ignore:line
//extended toolbar
'profile', 'contacts', 'chat', 'recording', 'etherpad', 'sharedvideo', 'dialout', 'settings', 'raisehand', 'filmstrip'], // jshint ignore:line
/**
* Main Toolbar Buttons
* All of them should be in TOOLBAR_BUTTONS
*/
MAIN_TOOLBAR_BUTTONS: ['microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'hangup'], // jshint ignore:line
MAIN_TOOLBAR_BUTTONS: ['microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'fodeviceselection', 'hangup'], // jshint ignore:line
SETTINGS_SECTIONS: ['language', 'devices', 'moderator'],
// Determines how the video would fit the screen. 'both' would fit the whole
// screen, 'height' would fit the original video height to the height of the

32
ios/Podfile Normal file
View File

@@ -0,0 +1,32 @@
platform :ios, '9.0'
workspace 'jitsi-meet'
target 'JitsiMeet' do
project 'sdk/sdk.xcodeproj'
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTActionSheet',
'RCTAnimation',
'RCTImage',
'RCTLinkingIOS',
'RCTNetwork',
'RCTText',
'RCTWebSocket',
]
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end

110
ios/README.md Normal file
View File

@@ -0,0 +1,110 @@
# Jitsi Meet SDK for iOS
This directory contains the source code of the Jitsi Meet app and the Jitsi Meet
SDK for iOS.
## Jitsi Meet SDK
JitsiMeet is an iOS framework which embodies the whole Jitsi Meet experience and
makes it reusable by third-party apps.
To get started:
1. Add a `JitsiMeetView` to your app using a Storyboard or Interface Builder,
for example.
2. Then, once the view has loaded, set the delegate in your controller and load
the desired URL:
```objc
- (void)viewDidLoad {
[super viewDidLoad];
JitsiMeetView *view = (JitsiMeetView *) self.view;
view.delegate = self;
[view loadURL:nil];
}
```
### JitsiMeetView class
The `JitsiMeetView` class is the entry point to the SDK. It a subclass of
`UIView` which renders a full conference in the designated area.
```objc
[meetView loadURL:[NSURL URLWithString:@"https://meet.jit.si/test123"]];
```
Loads the given URL and joins the room. If `null` is specified, the welcome page
is displayed instead.
#### Universal / deep linking
In order to support Universal / deep linking, `JitsiMeetView` offers 2 class
methods that you app's delegate should call in order for the app to follow those
links.
```objc
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler
{
return [JitsiMeetView application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
return [JitsiMeetView application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
```
### JitsiMeetViewDelegate
This delegate is optional, and can be set on the `JitsiMeetView` instance using
the `delegate` property.
It provides information about the conference state: was it joined, left, did it
fail?
All methods in this delegate are optional.
##### conferenceFailed
Called when a joining a conference was unsuccessful or when there was an error
while in a conference.
The `data` dictionary contains an "error" key describing the error and a "url"
key with the conference URL.
#### conferenceJoined
Called when a conference was joined.
The `data` dictionary contains a "url" key with the conference URL.
#### conferenceLeft
Called when a conference was left.
The `data` dictionary contains a "url" key with the conference URL.
#### conferenceWillJoin
Called before a conference is joined.
The `data` dictionary contains a "url" key with the conference URL.
#### conferenceWillLeave
Called before a conference is left.
The `data` dictionary contains a "url" key with the conference URL.

View File

@@ -1,16 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

View File

@@ -1,101 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "AppDelegate.h"
#import <React/RCTAssert.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTRootView.h>
/**
* A <tt>RCTFatalHandler</tt> implementation which swallows JavaScript errors.
* In the Release configuration, React Native will (intentionally) raise an
* unhandled NSException for an unhandled JavaScript error. This will
* effectively kill the application. <tt>_RCTFatal</tt> is suitable to be in
* accord with the Web i.e. not kill the application.
*/
RCTFatalHandler _RCTFatal = ^(NSError *error) {
id jsStackTrace = error.userInfo[RCTJSStackTraceKey];
@try {
NSString *name
= [NSString stringWithFormat:@"%@: %@",
RCTFatalExceptionName,
error.localizedDescription];
NSString *message
= RCTFormatError(error.localizedDescription, jsStackTrace, 75);
[NSException raise:name format:@"%@", message];
} @catch (NSException *e) {
if (!jsStackTrace) {
@throw;
}
}
};
@implementation AppDelegate
// https://facebook.github.io/react-native/docs/linking.html
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if !DEBUG
// In the Release configuration, React Native will (intentionally) raise an
// unhandled NSException for an unhandled JavaScript error. This will
// effectively kill the application. In accord with the Web, do not kill the
// application.
if (!RCTGetFatalHandler()) {
RCTSetFatalHandler(_RCTFatal);
}
#endif
NSURL *jsCodeLocation
= [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios"
fallbackResource:nil];
RCTRootView *rootView
= [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"App"
initialProperties:nil
launchOptions:launchOptions];
// Set a background color which is in accord with the JavaScript and Android
// parts of the application and causes less perceived visual flicker than the
// default background color.
rootView.backgroundColor
= [[UIColor alloc] initWithRed:.07f green:.07f blue:.07f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
// https://facebook.github.io/react-native/docs/linking.html
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
return [RCTLinkingManager application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
@end

View File

@@ -1,24 +0,0 @@
#import "RCTBridgeModule.h"
#import <UIKit/UIKit.h>
@interface Proximity : NSObject<RCTBridgeModule>
@end
@implementation Proximity
RCT_EXPORT_MODULE();
/**
* Enables / disables the proximity sensor monitoring. On iOS enabling the
* proximity sensor automatically dims the screen and disables touch controls,
* so there is nothing else to do (unlike on Android)!
*
* @param enabled {@code YES} to enable proximity (sensor) monitoring;
* {@code NO}, otherwise.
*/
RCT_EXPORT_METHOD(setEnabled:(BOOL)enabled) {
[[UIDevice currentDevice] setProximityMonitoringEnabled:enabled];
}
@end

View File

@@ -0,0 +1,421 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
0B26BE6E1EC5BC3C00EEFB41 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */; };
0B26BE6F1EC5BC3C00EEFB41 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0B412F1F1EDEE6E800B1A0A6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */; };
0B412F211EDEE95300B1A0A6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B412F201EDEE95300B1A0A6 /* Main.storyboard */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
0B26BE6F1EC5BC3C00EEFB41 /* JitsiMeet.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
0B412F201EDEE95300B1A0A6 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0B26BE6E1EC5BC3C00EEFB41 /* JitsiMeet.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0B26BE711EC5BC4D00EEFB41 /* Frameworks */ = {
isa = PBXGroup;
children = (
0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* src */ = {
isa = PBXGroup;
children = (
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
0B412F201EDEE95300B1A0A6 /* Main.storyboard */,
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */,
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */,
);
path = src;
sourceTree = "<group>";
};
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
B3B083EB1D4955FF0069CEE7 /* app.entitlements */,
0B26BE711EC5BC4D00EEFB41 /* Frameworks */,
83CBBA001A601CBA00E9B192 /* Products */,
13B07FAE1A68108700A75B9A /* src */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* jitsi-meet.app */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
13B07F861A680F5B00A75B9A /* jitsi-meet */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet" */;
buildPhases = (
0BBA83C41EC9F7600075A103 /* Run React packager */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */,
B35383AD1DDA0083008F406A /* Adjust embedded framework architectures */,
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
);
buildRules = (
);
dependencies = (
);
name = "jitsi-meet";
productName = "Jitsi Meet";
productReference = 13B07F961A680F5B00A75B9A /* jitsi-meet.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = BQNXB4G3KQ;
SystemCapabilities = {
com.apple.SafariKeychain = {
enabled = 1;
};
};
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 83CBB9F61A601CBA00E9B192;
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* jitsi-meet */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0B412F211EDEE95300B1A0A6 /* Main.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0BB7DA181EC9E695007AAE98 /* Adjust ATS */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Adjust ATS";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../scripts/fixup-ats.sh";
};
0BBA83C41EC9F7600075A103 /* Run React packager */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run React packager";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../scripts/run-packager.sh";
};
B35383AD1DDA0083008F406A /* Adjust embedded framework architectures */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Adjust embedded framework architectures";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../scripts/fixup-frameworks.sh";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0B412F1F1EDEE6E800B1A0A6 /* ViewController.m in Sources */,
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = NO;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
INFOPLIST_FILE = src/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeet.ios;
PRODUCT_NAME = "jitsi-meet";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CURRENT_PROJECT_VERSION = 1;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
INFOPLIST_FILE = src/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeet.ios;
PRODUCT_NAME = "jitsi-meet";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
83CBBA211A601CBA00E9B192 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
13B07F951A680F5B00A75B9A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,
83CBBA211A601CBA00E9B192 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
}

View File

@@ -14,10 +14,10 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a"
BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
BlueprintIdentifier = "0BD906E41EC0C00300C8C18E"
BuildableName = "JitsiMeet.framework"
BlueprintName = "JitsiMeet"
ReferencedContainer = "container:sdk.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
@@ -31,7 +31,7 @@
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "jitsi-meet.app"
BlueprintName = "jitsi-meet"
ReferencedContainer = "container:jitsi-meet.xcodeproj">
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@@ -49,7 +49,7 @@
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "jitsi-meet.app"
BlueprintName = "jitsi-meet"
ReferencedContainer = "container:jitsi-meet.xcodeproj">
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
@@ -72,7 +72,7 @@
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "jitsi-meet.app"
BlueprintName = "jitsi-meet"
ReferencedContainer = "container:jitsi-meet.xcodeproj">
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
@@ -91,7 +91,7 @@
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "jitsi-meet.app"
BlueprintName = "jitsi-meet"
ReferencedContainer = "container:jitsi-meet.xcodeproj">
ReferencedContainer = "container:app.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>

View File

@@ -1,18 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

23
ios/app/src/AppDelegate.h Normal file
View File

@@ -0,0 +1,23 @@
/*
* 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 <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

48
ios/app/src/AppDelegate.m Normal file
View File

@@ -0,0 +1,48 @@
/*
* 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 "AppDelegate.h"
#import <JitsiMeet/JitsiMeet.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
#pragma mark Linking delegate methods
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
return [JitsiMeetView application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [JitsiMeetView application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
@end

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="u7g-vg-A8m">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="xSm-U5-Hdu">
<objects>
<viewController id="u7g-vg-A8m" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="d40-fc-Y8P"/>
<viewControllerLayoutGuide type="bottom" id="1KD-Ho-g0H"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="QpR-jB-WOw" customClass="JitsiMeetView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ZIj-K3-jVH" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="28" y="138"/>
</scene>
</scenes>
</document>

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 167 KiB

View File

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 278 KiB

View File

@@ -61,17 +61,14 @@
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string>Participate in conferences with audio.</string>
<key>UIAppFonts</key>
<array>
<string>jitsi.ttf</string>
<string>FontAwesome.ttf</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
<string>voip</string>
</array>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>

View File

@@ -0,0 +1,23 @@
/*
* 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 <UIKit/UIKit.h>
#import <JitsiMeet/JitsiMeet.h>
@interface ViewController : UIViewController<JitsiMeetViewDelegate>
@end

View File

@@ -0,0 +1,38 @@
/*
* 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 "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidLoad {
[super viewDidLoad];
JitsiMeetView *view = (JitsiMeetView *) self.view;
view.delegate = self;
[view loadURL:nil];
}
@end

28
ios/app/src/main.m Normal file
View File

@@ -0,0 +1,28 @@
/*
* 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 <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(
argc, argv,
nil,
NSStringFromClass([AppDelegate class]));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,12 @@
<Workspace
version = "1.0">
<FileRef
location = "group:jitsi-meet.xcodeproj">
location = "group:app/app.xcodeproj">
</FileRef>
<FileRef
location = "group:sdk/sdk.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

36
ios/scripts/fixup-ats.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/bash
# This script gets executed from Xcode to disable ATS (App Transport Security)
# on Debug builds. Doing this allows loading resources over HTTP, such as the
# JS bundle.
set -x
case "$CONFIGURATION" in
Debug)
# Speed up build times by skipping the creation of the offline package for debug
# builds on the simulator since the packager is supposed to be running anyways.
if [[ "$PLATFORM_NAME" == *simulator ]]; then
echo "Skipping bundling for Simulator platform"
exit 0;
fi
DEV=true
;;
"")
echo "$0 must be invoked by Xcode"
exit 1
;;
*)
DEV=false
;;
esac
DEST=$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH
if [[ "$CONFIGURATION" = "Debug" && ! "$PLATFORM_NAME" == *simulator ]]; then
PLISTBUDDY='/usr/libexec/PlistBuddy'
PLIST=$TARGET_BUILD_DIR/$INFOPLIST_PATH
`$PLISTBUDDY -c "Add NSAppTransportSecurity:NSAllowsArbitraryLoads bool true" "$PLIST"` || true
fi

45
ios/scripts/fixup-frameworks.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
# This script gets executed from Xcode to fixup the embedded frameworks and
# bundle the necessary architectures.
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
if lipo -info "$FRAMEWORK_EXECUTABLE_PATH" | grep -q -v "^Non-fat"
then
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
fi
done
if [ -n "$EXTRACTED_ARCHS" ]
then
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
fi
echo "Code signing framework"
codesign \
--force --sign $EXPANDED_CODE_SIGN_IDENTITY \
--preserve-metadata=identifier,entitlements \
$FRAMEWORK
done

16
ios/scripts/run-packager.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
# This script is executed from Xcode to start the React packager for Debug
# targets.
if [[ "$CONFIGURATION" = "Debug" ]]; then
if nc -w 5 -z localhost 8081 ; then
if ! curl -s "http://localhost:8081/status" | grep -q "packager-status:running" ; then
echo "Port 8081 already in use, packager is either not running or not running correctly"
exit 2
fi
else
open "$SRCROOT/../../node_modules/react-native/packager/launchPackager.command" || echo "Can't start packager automatically"
fi
fi

View File

@@ -0,0 +1,452 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
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, ); }; };
0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B93EF7A1EC608550030D24D /* CoreText.framework */; };
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */; };
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495C1EC4B6C600B793EE /* AudioMode.m */; };
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495D1EC4B6C600B793EE /* POSIX.m */; };
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495E1EC4B6C600B793EE /* Proximity.m */; };
0BCA49641EC4B76D00B793EE /* WebRTC.framework in Copy embedded WebRTC framework */ = {isa = PBXBuildFile; fileRef = 0BCA49631EC4B76D00B793EE /* WebRTC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
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 */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
0BCA49621EC4B74500B793EE /* Copy embedded WebRTC framework */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
0BCA49641EC4B76D00B793EE /* WebRTC.framework in Copy embedded WebRTC framework */,
);
name = "Copy embedded WebRTC framework";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
03F2ADC957FF109849B7FCA1 /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExternalAPI.m; sourceTree = "<group>"; };
0BCA495C1EC4B6C600B793EE /* AudioMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioMode.m; sourceTree = "<group>"; };
0BCA495D1EC4B6C600B793EE /* POSIX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSIX.m; sourceTree = "<group>"; };
0BCA495E1EC4B6C600B793EE /* Proximity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Proximity.m; sourceTree = "<group>"; };
0BCA49631EC4B76D00B793EE /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
0BCA496B1EC4BBF900B793EE /* jitsi.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = jitsi.ttf; path = ../../fonts/jitsi.ttf; sourceTree = "<group>"; };
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>"; };
98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
0BD906E11EC0C00300C8C18E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */,
0F65EECE1D95DA94561BB47E /* libPods-JitsiMeet.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0BCA49681EC4BBE500B793EE /* Resources */ = {
isa = PBXGroup;
children = (
0BCA496B1EC4BBF900B793EE /* jitsi.ttf */,
);
name = Resources;
sourceTree = "<group>";
};
0BD906DB1EC0C00300C8C18E = {
isa = PBXGroup;
children = (
9C3C6FA2341729836589B856 /* Frameworks */,
C5E72ADFC30ED96F9B35F076 /* Pods */,
0BD906E61EC0C00300C8C18E /* Products */,
0BCA49681EC4BBE500B793EE /* Resources */,
0BD906E71EC0C00300C8C18E /* src */,
);
sourceTree = "<group>";
};
0BD906E61EC0C00300C8C18E /* Products */ = {
isa = PBXGroup;
children = (
0BD906E51EC0C00300C8C18E /* JitsiMeet.framework */,
);
name = Products;
sourceTree = "<group>";
};
0BD906E71EC0C00300C8C18E /* src */ = {
isa = PBXGroup;
children = (
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
0BD906E91EC0C00300C8C18E /* Info.plist */,
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
0BCA495D1EC4B6C600B793EE /* POSIX.m */,
0BCA495E1EC4B6C600B793EE /* Proximity.m */,
);
path = src;
sourceTree = "<group>";
};
9C3C6FA2341729836589B856 /* Frameworks */ = {
isa = PBXGroup;
children = (
0B93EF7A1EC608550030D24D /* CoreText.framework */,
0BCA49631EC4B76D00B793EE /* WebRTC.framework */,
03F2ADC957FF109849B7FCA1 /* libPods-JitsiMeet.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
C5E72ADFC30ED96F9B35F076 /* Pods */ = {
isa = PBXGroup;
children = (
98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */,
9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
0BD906E21EC0C00300C8C18E /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */,
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
0BD906E41EC0C00300C8C18E /* JitsiMeet */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0BD906ED1EC0C00300C8C18E /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
buildPhases = (
26796D8589142D80C8AFDA51 /* [CP] Check Pods Manifest.lock */,
0BD906E01EC0C00300C8C18E /* Sources */,
0BD906E11EC0C00300C8C18E /* Frameworks */,
0BD906E21EC0C00300C8C18E /* Headers */,
0BD906E31EC0C00300C8C18E /* Resources */,
0BCA49621EC4B74500B793EE /* Copy embedded WebRTC framework */,
0BCA49651EC4B77500B793EE /* Package React bundle */,
C7BC10B338C94EEB98048E64 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = JitsiMeet;
productName = "Jitsi Meet SDK";
productReference = 0BD906E51EC0C00300C8C18E /* JitsiMeet.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0BD906DC1EC0C00300C8C18E /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0830;
ORGANIZATIONNAME = Jitsi;
TargetAttributes = {
0BD906E41EC0C00300C8C18E = {
CreatedOnToolsVersion = 8.3.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 0BD906DF1EC0C00300C8C18E /* Build configuration list for PBXProject "sdk" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 0BD906DB1EC0C00300C8C18E;
productRefGroup = 0BD906E61EC0C00300C8C18E /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
0BD906E41EC0C00300C8C18E /* JitsiMeet */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
0BD906E31EC0C00300C8C18E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0BCA496C1EC4BBF900B793EE /* jitsi.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0BCA49651EC4B77500B793EE /* Package React bundle */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Package React bundle";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../../node_modules/react-native/packager/react-native-xcode.sh";
};
26796D8589142D80C8AFDA51 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
C7BC10B338C94EEB98048E64 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0BD906E01EC0C00300C8C18E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
0BD906EB1EC0C00300C8C18E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
0BD906EC1EC0C00300C8C18E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
0BD906EE1EC0C00300C8C18E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = src/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeetSDK.ios;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
};
name = Debug;
};
0BD906EF1EC0C00300C8C18E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = src/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeetSDK.ios;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0BD906DF1EC0C00300C8C18E /* Build configuration list for PBXProject "sdk" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0BD906EB1EC0C00300C8C18E /* Debug */,
0BD906EC1EC0C00300C8C18E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0BD906ED1EC0C00300C8C18E /* Build configuration list for PBXNativeTarget "JitsiMeet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0BD906EE1EC0C00300C8C18E /* Debug */,
0BD906EF1EC0C00300C8C18E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0BD906DC1EC0C00300C8C18E /* Project object */;
}

View File

@@ -1,3 +1,19 @@
/*
* 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 "RCTBridgeModule.h"
#import "RCTLog.h"

60
ios/sdk/src/ExternalAPI.m Normal file
View File

@@ -0,0 +1,60 @@
/*
* 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 "RCTBridgeModule.h"
#import "JitsiMeetView.h"
@interface ExternalAPI : NSObject<RCTBridgeModule>
@end
@implementation ExternalAPI
RCT_EXPORT_MODULE();
/**
* Dispatches an event that occurred on JavaScript to the view's delegate.
*
* - name: name of the event.
* - data: dictionary (JSON object in JS) with data associated with the event.
*/
RCT_EXPORT_METHOD(sendEvent:(NSString*)name data:(NSDictionary *) data) {
JitsiMeetView *view = [JitsiMeetView getInstance];
id delegate = view != nil ? view.delegate : nil;
if (delegate == nil) {
return;
}
if ([name isEqualToString:@"CONFERENCE_FAILED"]
&& [delegate respondsToSelector:@selector(conferenceFailed:)]) {
[delegate conferenceFailed:data];
} else if ([name isEqualToString:@"CONFERENCE_JOINED"]
&& [delegate respondsToSelector:@selector(conferenceJoined:)]) {
[delegate conferenceJoined:data];
} else if ([name isEqualToString:@"CONFERENCE_LEFT"]
&& [delegate respondsToSelector:@selector(conferenceLeft:)]) {
[delegate conferenceLeft:data];
} else if ([name isEqualToString:@"CONFERENCE_WILL_JOIN"]
&& [delegate respondsToSelector:@selector(conferenceWillJoin:)]) {
[delegate conferenceWillJoin:data];
} else if ([name isEqualToString:@"CONFERENCE_WILL_LEAVE"]
&& [delegate respondsToSelector:@selector(conferenceWillLeave:)]) {
[delegate conferenceWillLeave:data];
}
}
@end

29
ios/sdk/src/Info.plist Normal file
View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
<key>JitsiMeetFonts</key>
<array>
<string>FontAwesome.ttf</string>
<string>jitsi.ttf</string>
</array>
</dict>
</plist>

18
ios/sdk/src/JitsiMeet.h Normal file
View File

@@ -0,0 +1,18 @@
/*
* 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 <JitsiMeet/JitsiMeetView.h>
#import <JitsiMeet/JitsiMeetViewDelegate.h>

View File

@@ -0,0 +1,39 @@
/*
* 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 <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "JitsiMeetViewDelegate.h"
@interface JitsiMeetView : UIView
@property (nonatomic, weak, nullable) id<JitsiMeetViewDelegate> delegate;
+ (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *))restorationHandler;
+ (BOOL)application:(UIApplication *)application
openURL:(NSURL *)URL
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation;
+ (instancetype)getInstance;
- (void)loadURL:(nullable NSURL *)url;
@end

222
ios/sdk/src/JitsiMeetView.m Normal file
View File

@@ -0,0 +1,222 @@
/*
* 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 <CoreText/CoreText.h>
#import <React/RCTAssert.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTRootView.h>
#import "JitsiMeetView.h"
#import "RCTBridgeWrapper.h"
/**
* A <tt>RCTFatalHandler</tt> implementation which swallows JavaScript errors.
* In the Release configuration, React Native will (intentionally) raise an
* unhandled NSException for an unhandled JavaScript error. This will
* effectively kill the application. <tt>_RCTFatal</tt> is suitable to be in
* accord with the Web i.e. not kill the application.
*/
RCTFatalHandler _RCTFatal = ^(NSError *error) {
id jsStackTrace = error.userInfo[RCTJSStackTraceKey];
@try {
NSString *name
= [NSString stringWithFormat:@"%@: %@",
RCTFatalExceptionName, error.localizedDescription];
NSString *message
= RCTFormatError(error.localizedDescription, jsStackTrace, 75);
[NSException raise:name format:@"%@", message];
} @catch (NSException *e) {
if (!jsStackTrace) {
@throw;
}
}
};
@interface JitsiMeetView() {
RCTRootView *rootView;
}
@end
@implementation JitsiMeetView
static RCTBridgeWrapper *bridgeWrapper;
static JitsiMeetView *instance;
#pragma mark Linking delegate helpers
// https://facebook.github.io/react-native/docs/linking.html
+ (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
+ (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [RCTLinkingManager application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
#pragma mark initializers
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self initialize];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
#pragma mark API
/*
* Loads the given URL and joins the specified conference. If the specified URL
* is null, the welcome page is shown.
*/
- (void)loadURL:(NSURL *)url {
NSDictionary *props = url ? @{ url : url.absoluteString } : nil;
if (rootView == nil) {
rootView
= [[RCTRootView alloc] initWithBridge:bridgeWrapper.bridge
moduleName:@"App"
initialProperties:props];
rootView.backgroundColor = self.backgroundColor;
// Add React's root view as a subview which completely covers this one.
[rootView setFrame:[self bounds]];
[self addSubview:rootView];
} else {
// Update props with the new URL.
rootView.appProperties = props;
}
}
#pragma mark private methods
+ (instancetype)getInstance {
return instance;
}
/*
* Internal initialization:
*
* - sets the backgroudn color
* - creates the React bridge
* - loads the necessary custom fonts
* - registers a custom fatal error error handler for React
*/
- (void)initialize {
static dispatch_once_t onceToken;
/*
* TODO: Only allow a single instance for now. All React Native modules are
* kinda singletons so global state would be broken since we have a single
* bridge. Once we have that sorted out multiple instances of JitsiMeetView
* will be allowed.
*/
if (instance != nil) {
@throw [NSException
exceptionWithName:@"RuntimeError"
reason:@"Only a single instance is currently allowed"
userInfo:nil];
}
instance = self;
dispatch_once(&onceToken, ^{
// Set a background color which is in accord with the JavaScript and
// Android parts of the application and causes less perceived visual
// flicker than the default background color.
self.backgroundColor
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
// Initialize the React bridge.
bridgeWrapper = [[RCTBridgeWrapper alloc] init];
// Dynamically load custom bundled fonts.
[self loadCustomFonts];
// Register a fatal error handler for React.
[self registerFatalErrorHandler];
});
}
/*
* Helper function to dynamically load custom fonts. The UIAppFonts key in the
* plist file doesn't work for frameworks, so fonts have to be manually loaded.
*/
- (void)loadCustomFonts {
NSBundle *bundle = [NSBundle bundleForClass:self.class];
NSArray *fonts = [bundle objectForInfoDictionaryKey:@"JitsiMeetFonts"];
for (NSString *item in fonts) {
NSString *fontName = [item stringByDeletingPathExtension];
NSString *fontExt = [item pathExtension];
NSString *fontPath = [bundle pathForResource:fontName ofType:fontExt];
NSData *inData = [NSData dataWithContentsOfFile:fontPath];
CFErrorRef error;
CGDataProviderRef provider
= CGDataProviderCreateWithCFData((__bridge CFDataRef)inData);
CGFontRef font = CGFontCreateWithDataProvider(provider);
if (!CTFontManagerRegisterGraphicsFont(font, &error)) {
CFStringRef errorDescription = CFErrorCopyDescription(error);
NSLog(@"Failed to load font: %@", errorDescription);
CFRelease(errorDescription);
}
CFRelease(font);
CFRelease(provider);
}
}
/*
* Helper function to register a fatal error handler for React. Our handler
* won't kill the process, it will swallow JS errors and print stack traces
* instead.
*/
- (void)registerFatalErrorHandler {
#if !DEBUG
// In the Release configuration, React Native will (intentionally) raise
// an unhandled NSException for an unhandled JavaScript error. This will
// effectively kill the application. In accord with the Web, do not kill
// the application.
if (!RCTGetFatalHandler()) {
RCTSetFatalHandler(_RCTFatal);
}
#endif
}
@end

View File

@@ -0,0 +1,58 @@
/*
* 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.
*/
@protocol JitsiMeetViewDelegate <NSObject>
@optional
/*
* Called when a joining a conference was unsuccessful or when there was an error
* while in a conference.
*
* The `data` dictionary contains an "error" key describing the error and a "url"
* key with the conference URL.
*/
- (void) conferenceFailed:(NSDictionary *) data;
/*
* Called when a conference was joined.
*
* The `data` dictionary contains a "url" key with the conference URL.
*/
- (void) conferenceJoined:(NSDictionary *) data;
/*
* Called when a conference was left.
*
* The `data` dictionary contains a "url" key with the conference URL.
*/
- (void) conferenceLeft:(NSDictionary *) data;
/*
* Called before a conference is joined.
*
* The `data` dictionary contains a "url" key with the conference URL.
*/
- (void) conferenceWillJoin:(NSDictionary *) data;
/*
* Called before a conference is left.
*
* The `data` dictionary contains a "url" key with the conference URL.
*/
- (void) conferenceWillLeave:(NSDictionary *) data;
@end

View File

@@ -1,3 +1,19 @@
/*
* 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 "RCTBridgeModule.h"
#include <arpa/inet.h>

40
ios/sdk/src/Proximity.m Normal file
View File

@@ -0,0 +1,40 @@
/*
* 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 "RCTBridgeModule.h"
#import <UIKit/UIKit.h>
@interface Proximity : NSObject<RCTBridgeModule>
@end
@implementation Proximity
RCT_EXPORT_MODULE();
/**
* Enables / disables the proximity sensor monitoring. On iOS enabling the
* proximity sensor automatically dims the screen and disables touch controls,
* so there is nothing else to do (unlike on Android)!
*
* @param enabled {@code YES} to enable proximity (sensor) monitoring;
* {@code NO}, otherwise.
*/
RCT_EXPORT_METHOD(setEnabled:(BOOL)enabled) {
[[UIDevice currentDevice] setProximityMonitoringEnabled:enabled];
}
@end

View File

@@ -0,0 +1,37 @@
/*
* 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 <Foundation/Foundation.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeDelegate.h>
/**
* A wrapper around the <tt>RCTBridge</tt> which implements the delegate methods
* that allow us to serve the JS bundle from within the framework's resources
* directory. This is the recommended way for those cases where the builtin API
* doesn't cut it, as is the case.
*
* In addition, we will create a single bridge and then create all root views
* off it, thus only loading the JS bundle a single time. This class is not a
* singleton, however, so it's possible for us to create multiple instances of
* it, though that's not currently used.
*/
@interface RCTBridgeWrapper : NSObject<RCTBridgeDelegate>
@property (nonatomic, readonly, strong) RCTBridge *bridge;
@end

View File

@@ -0,0 +1,115 @@
/*
* 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.
*/
#include "RCTBridgeWrapper.h"
/*
* Wrapper around RCTBridge which also implements the RCTBridgeDelegate methods,
* allowing us to specify where the bundles are loaded from.
*/
@implementation RCTBridgeWrapper
- (instancetype)init {
self = [super init];
if (self) {
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];
}
return self;
}
#pragma mark helper methods for getting the packager URL
#if DEBUG
static NSURL *serverRootWithHost(NSString *host) {
return
[NSURL URLWithString:
[NSString stringWithFormat:@"http://%@:8081/", host]];
}
- (BOOL)isPackagerRunning:(NSString *)host {
NSURL *url
= [serverRootWithHost(host) URLByAppendingPathComponent:@"status"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:NULL];
NSString *status = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
return [status isEqualToString:@"packager-status:running"];
}
- (NSString *)guessPackagerHost {
static NSString *ipGuess;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *ipPath
= [[NSBundle bundleForClass:self.class] pathForResource:@"ip"
ofType:@"txt"];
ipGuess
= [[NSString stringWithContentsOfFile:ipPath
encoding:NSUTF8StringEncoding
error:nil]
stringByTrimmingCharactersInSet:
[NSCharacterSet newlineCharacterSet]];
});
NSString *host = ipGuess ?: @"localhost";
if ([self isPackagerRunning:host]) {
return host;
}
return nil;
}
#endif
#pragma mark RCTBridgeDelegate methods
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
#if DEBUG
// In debug mode, try to fetch the bundle from the packager, or fallback to
// the one inside the framework. The IP address for the packager host is
// fetched from the ip.txt file inside the framework.
//
// This duplicates some functionality present in RCTBundleURLProvider, but
// that mode is not designed to work inside a framework, because all
// resources are loaded from the main bundle.
NSString *host = [self guessPackagerHost];
if (host != nil) {
NSString *path = @"/index.ios.bundle";
NSString *query = @"platform=ios&dev=true&minify=false";
NSURLComponents *components
= [NSURLComponents componentsWithURL:serverRootWithHost(host)
resolvingAgainstBaseURL:NO];
components.path = path;
components.query = query;
return components.URL;
}
#endif
return [[NSBundle bundleForClass:self.class] URLForResource:@"main"
withExtension:@"jsbundle"];
}
@end

View File

@@ -1,9 +1,12 @@
/* global $, APP, interfaceConfig */
/* global $, APP, interfaceConfig, JitsiMeetJS */
/* jshint -W101 */
import JitsiPopover from "../util/JitsiPopover";
import UIUtil from "../util/UIUtil";
const ParticipantConnectionStatus
= JitsiMeetJS.constants.participantConnectionStatus;
/**
* Maps a connection quality value (in percent) to the width of the "full" icon.
*/
@@ -334,8 +337,11 @@ ConnectionIndicator.prototype.create = function () {
createIcon(["connection_full"], "icon-connection"));
this.interruptedIndicator = connectionIconContainer.appendChild(
createIcon(["connection_lost"],"icon-connection-lost"));
this.ninjaIndicator = connectionIconContainer.appendChild(
createIcon(["connection_ninja"],"icon-ninja"));
$(this.interruptedIndicator).hide();
$(this.ninjaIndicator).hide();
this.connectionIndicatorContainer.appendChild(connectionIconContainer);
};
@@ -351,23 +357,30 @@ ConnectionIndicator.prototype.remove = function() {
};
/**
* Updates the UI which displays warning about user's connectivity problems.
* Updates the UI which displays or not a warning about user's connectivity
* problems.
*
* @param {boolean} isActive true if the connection is working fine or false if
* the user is having connectivity issues.
* @param {ParticipantConnectionStatus} connectionStatus
*/
ConnectionIndicator.prototype.updateConnectionStatusIndicator
= function (isActive) {
this.isConnectionActive = isActive;
if (this.isConnectionActive) {
$(this.interruptedIndicator).hide();
$(this.emptyIcon).show();
$(this.fullIcon).show();
} else {
$(this.interruptedIndicator).show();
$(this.emptyIcon).hide();
$(this.fullIcon).hide();
}
= function (connectionStatus) {
this.connectionStatus = connectionStatus;
if (connectionStatus === ParticipantConnectionStatus.INTERRUPTED) {
$(this.interruptedIndicator).show();
$(this.emptyIcon).hide();
$(this.fullIcon).hide();
$(this.ninjaIndicator).hide();
} else if (connectionStatus === ParticipantConnectionStatus.INACTIVE) {
$(this.interruptedIndicator).hide();
$(this.emptyIcon).hide();
$(this.fullIcon).hide();
$(this.ninjaIndicator).show();
} else {
$(this.interruptedIndicator).hide();
$(this.emptyIcon).show();
$(this.fullIcon).show();
$(this.ninjaIndicator).hide();
}
};
/**

View File

@@ -26,6 +26,18 @@ const VIDEO_RESOLUTION_POLL_INTERVAL = 2000;
* Manager for all Large containers.
*/
export default class LargeVideoManager {
/**
* Checks whether given container is a {@link VIDEO_CONTAINER_TYPE}.
* FIXME currently this is a workaround for the problem where video type is
* mixed up with container type.
* @param {string} containerType
* @return {boolean}
*/
static isVideoContainer(containerType) {
return containerType === VIDEO_CONTAINER_TYPE
|| containerType === DESKTOP_CONTAINER_TYPE;
}
constructor (emitter) {
/**
* The map of <tt>LargeContainer</tt>s where the key is the video
@@ -116,7 +128,8 @@ export default class LargeVideoManager {
this.enableLocalConnectionProblemFilter(true);
this._setLocalConnectionMessage("connection.RECONNECTING");
// Show the message only if the video is currently being displayed
this.showLocalConnectionMessage(this.state === VIDEO_CONTAINER_TYPE);
this.showLocalConnectionMessage(
LargeVideoManager.isVideoContainer(this.state));
}
/**
@@ -146,7 +159,12 @@ export default class LargeVideoManager {
preUpdate.then(() => {
const { id, stream, videoType, resolve } = this.newStreamData;
const isVideoFromCamera = videoType === VIDEO_CONTAINER_TYPE;
// FIXME this does not really make sense, because the videoType
// (camera or desktop) is a completely different thing than
// the video container type (Etherpad, SharedVideo, VideoContainer).
const isVideoContainer
= LargeVideoManager.isVideoContainer(videoType);
this.newStreamData = null;
@@ -158,34 +176,26 @@ export default class LargeVideoManager {
// change the avatar url on large
this.updateAvatar(Avatar.getAvatarUrl(id));
// FIXME that does not really make sense, because the videoType
// (camera or desktop) is a completely different thing than
// the video container type (Etherpad, SharedVideo, VideoContainer).
// ----------------------------------------------------------------
// If the container is VIDEO_CONTAINER_TYPE, we need to check
// its stream whether exist and is muted to set isVideoMuted
// in rest of the cases it is false
let showAvatar = isVideoFromCamera && (!stream || stream.isMuted());
// If the user's connection is disrupted then the avatar will be
// displayed in case we have no video image cached. That is if
// there was a user switch(image is lost on stream detach) or if
// there was a user switch (image is lost on stream detach) or if
// the video was not rendered, before the connection has failed.
const isConnectionActive = this._isConnectionActive(id);
const wasUsersImageCached
= !isUserSwitch && container.wasVideoRendered;
const isVideoMuted = !stream || stream.isMuted();
if (isVideoFromCamera
&& !isConnectionActive
&& (isUserSwitch || !container.wasVideoRendered)) {
showAvatar = true;
}
const connectionStatus
= APP.conference.getParticipantConnectionStatus(id);
const isVideoRenderable
= !isVideoMuted
&& (APP.conference.isLocalId(id)
|| connectionStatus
=== ParticipantConnectionStatus.ACTIVE
|| wasUsersImageCached);
// If audio only mode is enabled, always show the avatar for
// videos from another participant.
if (APP.conference.isAudioOnly()
&& (isVideoFromCamera
|| videoType === DESKTOP_CONTAINER_TYPE)) {
showAvatar = true;
}
let showAvatar
= isVideoContainer
&& (APP.conference.isAudioOnly() || !isVideoRenderable);
let promise;
@@ -208,28 +218,30 @@ export default class LargeVideoManager {
this.updateLargeVideoAudioLevel(0);
}
const isConnectionInterrupted
= APP.conference.getParticipantConnectionStatus(id)
=== ParticipantConnectionStatus.INTERRUPTED;
let messageKey = null;
if (isConnectionInterrupted) {
messageKey = "connection.USER_CONNECTION_INTERRUPTED";
} else if (connectionStatus
=== ParticipantConnectionStatus.INACTIVE) {
messageKey = "connection.LOW_BANDWIDTH";
}
// Make sure no notification about remote failure is shown as
// its UI conflicts with the one for local connection interrupted.
// For the purposes of UI indicators, audio only is considered as
// an "active" connection.
const isConnected
const overrideAndHide
= APP.conference.isAudioOnly()
|| APP.conference.isConnectionInterrupted()
|| isConnectionActive;
// when isHavingConnectivityIssues, state can be inactive,
// interrupted or restoring. We show different message for
// interrupted and the rest.
const isConnectionInterrupted =
APP.conference.getParticipantConnectionStatus(id)
=== ParticipantConnectionStatus.INTERRUPTED;
|| APP.conference.isConnectionInterrupted();
this.updateParticipantConnStatusIndication(
id,
isConnected,
(isConnectionInterrupted)
? "connection.USER_CONNECTION_INTERRUPTED"
: "connection.LOW_BANDWIDTH");
!overrideAndHide && isConnectionInterrupted,
!overrideAndHide && messageKey);
// resolve updateLargeVideo promise after everything is done
promise.then(resolve);
@@ -244,39 +256,25 @@ export default class LargeVideoManager {
});
}
/**
* Checks whether a participant's peer connection status is active.
* There is no JitsiParticipant for local id we skip checking local
* participant and report it as having no connectivity issues.
*
* @param {string} id the id of participant(MUC nickname)
* @returns {boolean} <tt>true</tt> when participant connection status is
* {@link ParticipantConnectionStatus.ACTIVE} and <tt>false</tt> otherwise.
* @private
*/
_isConnectionActive(id) {
return APP.conference.isLocalId(id)
|| APP.conference.getParticipantConnectionStatus(this.id)
=== ParticipantConnectionStatus.ACTIVE;
}
/**
* Shows/hides notification about participant's connectivity issues to be
* shown on the large video area.
*
* @param {string} id the id of remote participant(MUC nickname)
* @param {boolean} isConnected true if the connection is active or false
* when the user is having connectivity issues.
* @param {string} messageKey the i18n key of the message
* @param {boolean} showProblemsIndication
* @param {string|null} messageKey the i18n key of the message which will be
* displayed on the large video or <tt>null</tt> to hide it.
*
* @private
*/
updateParticipantConnStatusIndication (id, isConnected, messageKey) {
updateParticipantConnStatusIndication (
id, showProblemsIndication, messageKey) {
// Apply grey filter on the large video
this.videoContainer.showRemoteConnectionProblemIndicator(!isConnected);
this.videoContainer.showRemoteConnectionProblemIndicator(
showProblemsIndication);
if (isConnected) {
if (!messageKey) {
// Hide the message
this.showRemoteConnectionMessage(false);
} else {
@@ -289,7 +287,7 @@ export default class LargeVideoManager {
// Show it now only if the VideoContainer is on top
this.showRemoteConnectionMessage(
this.state === VIDEO_CONTAINER_TYPE);
LargeVideoManager.isVideoContainer(this.state));
}
}
@@ -412,15 +410,20 @@ export default class LargeVideoManager {
* Shows hides the "avatar" message which is to be displayed either in
* the middle of the screen or below the avatar image.
*
* @param {null|boolean} show (optional) <tt>true</tt> to show the avatar
* message or <tt>false</tt> to hide it. If not provided then the connection
* status of the user currently on the large video will be obtained form
* "APP.conference" and the message will be displayed if the user's
* connection is interrupted.
* @param {boolean|undefined} [show=undefined] <tt>true</tt> to show
* the avatar message or <tt>false</tt> to hide it. If not provided then
* the connection status of the user currently on the large video will be
* obtained form "APP.conference" and the message will be displayed if
* the user's connection is either interrupted or inactive.
*/
showRemoteConnectionMessage (show) {
if (typeof show !== 'boolean') {
show = !this._isConnectionActive(this.id);
const connStatus
= APP.conference.getParticipantConnectionStatus(this.id);
show = !APP.conference.isLocalId(this.id)
&& (connStatus === ParticipantConnectionStatus.INTERRUPTED
|| connStatus === ParticipantConnectionStatus.INACTIVE);
}
if (show) {
@@ -526,7 +529,7 @@ export default class LargeVideoManager {
// FIXME when video is being replaced with other content we need to hide
// companion icons/messages. It would be best if the container would
// be taking care of it by itself, but that is a bigger refactoring
if (this.state === VIDEO_CONTAINER_TYPE) {
if (LargeVideoManager.isVideoContainer(this.state)) {
this.showWatermark(false);
this.showLocalConnectionMessage(false);
this.showRemoteConnectionMessage(false);
@@ -537,7 +540,7 @@ export default class LargeVideoManager {
let container = this.getContainer(type);
return container.show().then(() => {
if (type === VIDEO_CONTAINER_TYPE) {
if (LargeVideoManager.isVideoContainer(type)) {
// FIXME when video appears on top of other content we need to
// show companion icons/messages. It would be best if
// the container would be taking care of it by itself, but that

View File

@@ -448,29 +448,27 @@ RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted, force) {
/**
* @inheritDoc
* @override
*/
RemoteVideo.prototype.setMutedView = function(isMuted) {
SmallVideo.prototype.setMutedView.call(this, isMuted);
RemoteVideo.prototype.setVideoMutedView = function(isMuted) {
SmallVideo.prototype.setVideoMutedView.call(this, isMuted);
// Update 'mutedWhileDisconnected' flag
this._figureOutMutedWhileDisconnected(this.isConnectionInterrupted());
this._figureOutMutedWhileDisconnected();
};
/**
* Figures out the value of {@link #mutedWhileDisconnected} flag by taking into
* account remote participant's network connectivity and video muted status.
*
* @param {boolean} isDisconnected <tt>true</tt> if the remote participant is
* currently having connectivity issues or <tt>false</tt> otherwise.
*
* @private
*/
RemoteVideo.prototype._figureOutMutedWhileDisconnected
= function(isDisconnected) {
if (isDisconnected && this.isVideoMuted) {
this.mutedWhileDisconnected = true;
} else if (!isDisconnected && !this.isVideoMuted) {
this.mutedWhileDisconnected = false;
}
RemoteVideo.prototype._figureOutMutedWhileDisconnected = function() {
const isActive = this.isConnectionActive();
if (!isActive && this.isVideoMuted) {
this.mutedWhileDisconnected = true;
} else if (isActive && !this.isVideoMuted) {
this.mutedWhileDisconnected = false;
}
};
/**
@@ -546,13 +544,24 @@ RemoteVideo.prototype.isConnectionActive = function() {
/**
* The remote video is considered "playable" once the stream has started
* according to the {@link #hasVideoStarted} result.
* It will be allowed to display video also in
* {@link ParticipantConnectionStatus.INTERRUPTED} if the video was ever played
* and was not muted while not in ACTIVE state. This basically means that there
* is stalled video image cached that could be displayed. It's used to show
* "grey video image" in user's thumbnail when there are connectivity issues.
*
* @inheritdoc
* @override
*/
RemoteVideo.prototype.isVideoPlayable = function () {
const connectionState
= APP.conference.getParticipantConnectionStatus(this.id);
return SmallVideo.prototype.isVideoPlayable.call(this)
&& this.hasVideoStarted() && !this.mutedWhileDisconnected;
&& this.hasVideoStarted()
&& (connectionState === ParticipantConnectionStatus.ACTIVE
|| (connectionState === ParticipantConnectionStatus.INTERRUPTED
&& !this.mutedWhileDisconnected));
};
/**
@@ -572,26 +581,25 @@ RemoteVideo.prototype.updateView = function () {
* Updates the UI to reflect user's connectivity status.
*/
RemoteVideo.prototype.updateConnectionStatusIndicator = function () {
const isActive = this.isConnectionActive();
const connectionStatus = this.user.getConnectionStatus();
if (isActive === null) {
// Cancel processing at this point - no update
return;
logger.debug(`${this.id} thumbnail connection status: ${connectionStatus}`);
// FIXME rename 'mutedWhileDisconnected' to 'mutedWhileNotRendering'
// Update 'mutedWhileDisconnected' flag
this._figureOutMutedWhileDisconnected();
if(this.connectionIndicator) {
this.connectionIndicator.updateConnectionStatusIndicator(
connectionStatus);
}
logger.debug(this.id + " thumbnail is connection active ? " + isActive);
// Update 'mutedWhileDisconnected' flag
this._figureOutMutedWhileDisconnected(!isActive);
if(this.connectionIndicator)
this.connectionIndicator.updateConnectionStatusIndicator(isActive);
const isInterrupted
= connectionStatus === ParticipantConnectionStatus.INTERRUPTED;
// Toggle thumbnail video problem filter
this.selectVideoElement().toggleClass(
"videoThumbnailProblemFilter", !isActive);
"videoThumbnailProblemFilter", isInterrupted);
this.$avatar().toggleClass(
"videoThumbnailProblemFilter", !isActive);
"videoThumbnailProblemFilter", isInterrupted);
};
/**

View File

@@ -445,8 +445,7 @@ SmallVideo.prototype.isCurrentlyOnLargeVideo = function () {
*/
SmallVideo.prototype.isVideoPlayable = function() {
return this.videoStream // Is there anything to display ?
&& !this.isVideoMuted && !this.videoStream.isMuted() // Muted ?
&& (this.isLocal || APP.conference.isInLastN(this.id));
&& !this.isVideoMuted && !this.videoStream.isMuted(); // Muted ?
};
/**
@@ -566,7 +565,7 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
let indicatorSpanId = "dominantspeakerindicator";
let content = `<i id="indicatoricon"
' class="indicatoricon fa fa-bullhorn"></i>`;
class="indicatoricon fa fa-bullhorn"></i>`;
let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
videoSpanId: this.videoSpanId,
indicatorId: indicatorSpanId,

View File

@@ -206,7 +206,7 @@ export class VideoContainer extends LargeContainer {
this.avatarHeight = $("#dominantSpeakerAvatar").height();
var onPlayCallback = function (event) {
var onPlayingCallback = function (event) {
if (typeof resizeContainer === 'function') {
resizeContainer(event);
}
@@ -215,7 +215,7 @@ export class VideoContainer extends LargeContainer {
// This does not work with Temasys plugin - has to be a property to be
// copied between new <object> elements
//this.$video.on('play', onPlay);
this.$video[0].onplay = onPlayCallback;
this.$video[0].onplaying = onPlayingCallback;
/**
* A Set of functions to invoke when the video element resizes.

View File

@@ -1,4 +1,4 @@
/* global APP, $, interfaceConfig */
/* global APP, $, interfaceConfig, JitsiMeetJS */
const logger = require("jitsi-meet-logger").getLogger(__filename);
import Filmstrip from "./Filmstrip";
@@ -10,6 +10,9 @@ import LargeVideoManager from "./LargeVideoManager";
import {VIDEO_CONTAINER_TYPE} from "./VideoContainer";
import LocalVideo from "./LocalVideo";
const ParticipantConnectionStatus
= JitsiMeetJS.constants.participantConnectionStatus;
var remoteVideos = {};
var localVideoThumbnail = null;
@@ -559,8 +562,16 @@ var VideoLayout = {
* is fine.
*/
showLocalConnectionInterrupted (isInterrupted) {
localVideoThumbnail.connectionIndicator
.updateConnectionStatusIndicator(!isInterrupted);
// Currently local video thumbnail displays only "active" or
// "interrupted" despite the fact that ConnectionIndicator supports more
// states.
const status
= isInterrupted
? ParticipantConnectionStatus.INTERRUPTED
: ParticipantConnectionStatus.ACTIVE;
localVideoThumbnail
.connectionIndicator.updateConnectionStatusIndicator(status);
},
/**

View File

@@ -19,7 +19,7 @@
"@atlaskit/button": "1.0.3",
"@atlaskit/button-group": "1.0.0",
"@atlaskit/dropdown-menu": "1.4.0",
"@atlaskit/field-text": "2.0.3",
"@atlaskit/field-text": "2.7.0",
"@atlaskit/icon": "6.0.0",
"@atlaskit/modal-dialog": "1.2.4",
"@atlaskit/tabs": "1.2.5",

View File

@@ -5,6 +5,7 @@ import { Linking } from 'react-native';
import { Platform } from '../../base/react';
import '../../mobile/audio-mode';
import '../../mobile/background';
import '../../mobile/external-api';
import '../../mobile/full-screen';
import '../../mobile/proximity';
import '../../mobile/wake-lock';
@@ -22,7 +23,7 @@ export class App extends AbstractApp {
*
* @static
*/
static propTypes = AbstractApp.propTypes
static propTypes = AbstractApp.propTypes;
/**
* Initializes a new App instance.

View File

@@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { hideDialog } from '../actions';
import { DIALOG_PROP_TYPES } from '../constants';
/**
* Abstract dialog to display dialogs.
@@ -13,57 +14,12 @@ export default class AbstractDialog extends Component {
* @static
*/
static propTypes = {
/**
* Whether cancel button is disabled. Enabled by default.
*/
cancelDisabled: React.PropTypes.bool,
/**
* Optional i18n key to change the cancel button title.
*/
cancelTitleKey: React.PropTypes.string,
...DIALOG_PROP_TYPES,
/**
* Used to show/hide the dialog on cancel.
*/
dispatch: React.PropTypes.func,
/**
* Is ok button enabled/disabled. Enabled by default.
*/
okDisabled: React.PropTypes.bool,
/**
* Optional i18n key to change the ok button title.
*/
okTitleKey: React.PropTypes.string,
/**
* The handler for onCancel event.
*/
onCancel: React.PropTypes.func,
/**
* The handler for the event when submitting the dialog.
*/
onSubmit: React.PropTypes.func,
/**
* Used to obtain translations in children classes.
*/
t: React.PropTypes.func,
/**
* Key to use for showing a title.
*/
titleKey: React.PropTypes.string,
/**
* The string to use as a title instead of {@code titleKey}. If a truthy
* value is specified, it takes precedence over {@code titleKey} i.e.
* the latter is unused.
*/
titleString: React.PropTypes.string
dispatch: React.PropTypes.func
};
/**

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import Prompt from 'react-native-prompt';
import { connect } from 'react-redux';
import { translate } from '../../i18n';

View File

@@ -1,12 +1,8 @@
import AKButton from '@atlaskit/button';
import AKButtonGroup from '@atlaskit/button-group';
import ModalDialog from '@atlaskit/modal-dialog';
import React from 'react';
import { connect } from 'react-redux';
import { translate } from '../../i18n';
import AbstractDialog from './AbstractDialog';
import StatelessDialog from './StatelessDialog';
/**
* Web dialog that uses atlaskit modal-dialog to display dialogs.
@@ -19,6 +15,8 @@ class Dialog extends AbstractDialog {
* @static
*/
static propTypes = {
...AbstractDialog.propTypes,
/**
* This is the body of the dialog, the component children.
*/
@@ -30,6 +28,11 @@ class Dialog extends AbstractDialog {
*/
isModal: React.PropTypes.bool,
/**
* Disables rendering of the submit button.
*/
submitDisabled: React.PropTypes.bool,
/**
* Width of the dialog, can be:
* - 'small' (400px), 'medium' (600px), 'large' (800px),
@@ -40,6 +43,19 @@ class Dialog extends AbstractDialog {
width: React.PropTypes.string
};
/**
* Initializes a new Dialog instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
this._onCancel = this._onCancel.bind(this);
this._onSubmit = this._onSubmit.bind(this);
}
/**
* Implements React's {@link Component#render()}.
*
@@ -47,101 +63,15 @@ class Dialog extends AbstractDialog {
* @returns {ReactElement}
*/
render() {
return (
<ModalDialog
footer = { this._renderFooter() }
header = { this._renderHeader() }
isOpen = { true }
onDialogDismissed = { this._onCancel }
width = { this.props.width || 'medium' }>
<div>
<form
className = 'modal-dialog-form'
id = 'modal-dialog-form'
onSubmit = { this._onSubmit }>
{ this.props.children }
</form>
</div>
</ModalDialog>);
}
const props = {
...this.props,
onSubmit: this._onSubmit,
onCancel: this._onCancel
};
/**
* Render cancel button.
*
* @returns {*} The cancel button if enabled and dialog is not modal.
* @private
*/
_renderCancelButton() {
if (this.props.cancelDisabled || this.props.isModal) {
return null;
}
delete props.dispatch;
return (
<AKButton
appearance = 'subtle'
id = 'modal-dialog-cancel-button'
onClick = { this._onCancel }>
{ this.props.t(this.props.cancelTitleKey || 'dialog.Cancel') }
</AKButton>
);
}
/**
* Render component in dialog footer.
*
* @returns {ReactElement}
* @private
*/
_renderFooter() {
return (
<footer className = 'modal-dialog-footer'>
<AKButtonGroup>
{ this._renderCancelButton() }
{ this._renderOKButton() }
</AKButtonGroup>
</footer>
);
}
/**
* Render component in dialog header.
*
* @returns {ReactElement}
* @private
*/
_renderHeader() {
const { t } = this.props;
return (
<header>
<h2>
{ this.props.titleString || t(this.props.titleKey) }
</h2>
</header>
);
}
/**
* Render ok button.
*
* @returns {*} The ok button if enabled.
* @private
*/
_renderOKButton() {
if (this.props.submitDisabled) {
return null;
}
return (
<AKButton
appearance = 'primary'
form = 'modal-dialog-form'
id = 'modal-dialog-ok-button'
isDisabled = { this.props.okDisabled }
onClick = { this._onSubmit }>
{ this.props.t(this.props.okTitleKey || 'dialog.Ok') }
</AKButton>
);
return <StatelessDialog { ...props } />;
}
/**
@@ -158,4 +88,4 @@ class Dialog extends AbstractDialog {
}
}
export default translate(connect()(Dialog));
export default connect()(Dialog);

View File

@@ -0,0 +1,313 @@
import AKButton from '@atlaskit/button';
import AKButtonGroup from '@atlaskit/button-group';
import ModalDialog from '@atlaskit/modal-dialog';
import React, { Component } from 'react';
import { translate } from '../../i18n';
import { DIALOG_PROP_TYPES } from '../constants';
/**
* The ID to be used for the cancel button if enabled.
* @type {string}
*/
const CANCEL_BUTTON_ID = 'modal-dialog-cancel-button';
/**
* The ID to be used for the ok button if enabled.
* @type {string}
*/
const OK_BUTTON_ID = 'modal-dialog-ok-button';
/**
* Web dialog that uses atlaskit modal-dialog to display dialogs.
*/
class StatelessDialog extends Component {
/**
* {@code StatelessDialog} component's property types.
*
* @static
*/
static propTypes = {
...DIALOG_PROP_TYPES,
/**
* This is the body of the dialog, the component children.
*/
children: React.PropTypes.node,
/**
* Disables dismissing the dialog when the blanket is clicked. Enabled
* by default.
*/
disableBlanketClickDismiss: React.PropTypes.bool,
/**
* Whether the dialog is modal. This means clicking on the blanket will
* leave the dialog open. No cancel button.
*/
isModal: React.PropTypes.bool,
/**
* Disables rendering of the submit button.
*/
submitDisabled: React.PropTypes.bool,
/**
* Width of the dialog, can be:
* - 'small' (400px), 'medium' (600px), 'large' (800px),
* 'x-large' (968px)
* - integer value for pixel width
* - string value for percentage
*/
width: React.PropTypes.string
};
/**
* Initializes a new {@code StatelessDialog} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
// Bind event handlers so they are only bound once for every instance.
this._onCancel = this._onCancel.bind(this);
this._onDialogDismissed = this._onDialogDismissed.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._onSubmit = this._onSubmit.bind(this);
this._setDialogElement = this._setDialogElement.bind(this);
}
/**
* React Component method that executes once component is mounted.
*
* @inheritdoc
*/
componentDidMount() {
this._updateButtonFocus();
}
/**
* React Component method that executes once component is updated.
*
* @param {Object} prevProps - The previous properties, before the update.
* @returns {void}
*/
componentDidUpdate(prevProps) {
// if there is an update in any of the buttons enable/disable props
// update the focus if needed
if (prevProps.okDisabled !== this.props.okDisabled
|| prevProps.cancelDisabled !== this.props.cancelDisabled
|| prevProps.submitDisabled !== this.props.submitDisabled) {
this._updateButtonFocus();
}
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
return (
<div
onKeyDown = { this._onKeyDown }
ref = { this._setDialogElement }>
<ModalDialog
footer = { this._renderFooter() }
header = { this._renderHeader() }
isOpen = { true }
onDialogDismissed = { this._onDialogDismissed }
width = { this.props.width || 'medium' }>
<div>
<form
className = 'modal-dialog-form'
id = 'modal-dialog-form'
onSubmit = { this._onSubmit }>
{ this.props.children }
</form>
</div>
</ModalDialog>
</div>
);
}
/**
* Dispatches action to hide the dialog.
*
* @returns {void}
*/
_onCancel() {
if (!this.props.isModal) {
this.props.onCancel();
}
}
/**
* Handles click on the blanket area.
*
* @returns {void}
*/
_onDialogDismissed() {
if (!this.props.disableBlanketClickDismiss) {
this._onCancel();
}
}
/**
* Dispatches the action when submitting the dialog.
*
* @private
* @param {string} value - The submitted value if any.
* @returns {void}
*/
_onSubmit(value) {
this.props.onSubmit(value);
}
/**
* Renders Cancel button.
*
* @private
* @returns {*} The Cancel button if enabled and dialog is not modal.
*/
_renderCancelButton() {
if (this.props.cancelDisabled || this.props.isModal) {
return null;
}
return (
<AKButton
appearance = 'subtle'
id = { CANCEL_BUTTON_ID }
onClick = { this._onCancel }>
{ this.props.t(this.props.cancelTitleKey || 'dialog.Cancel') }
</AKButton>
);
}
/**
* Renders component in dialog footer.
*
* @private
* @returns {ReactElement}
*/
_renderFooter() {
return (
<footer className = 'modal-dialog-footer'>
<AKButtonGroup>
{ this._renderCancelButton() }
{ this._renderOKButton() }
</AKButtonGroup>
</footer>
);
}
/**
* Renders component in dialog header.
*
* @private
* @returns {ReactElement}
*/
_renderHeader() {
const { t } = this.props;
return (
<header>
<h2>
{ this.props.titleString || t(this.props.titleKey) }
</h2>
</header>
);
}
/**
* Renders OK button.
*
* @private
* @returns {*} The OK button if enabled.
*/
_renderOKButton() {
if (this.props.submitDisabled) {
return null;
}
return (
<AKButton
appearance = 'primary'
form = 'modal-dialog-form'
id = { OK_BUTTON_ID }
isDisabled = { this.props.okDisabled }
onClick = { this._onSubmit }>
{ this.props.t(this.props.okTitleKey || 'dialog.Ok') }
</AKButton>
);
}
/**
* Sets the instance variable for the div containing the component's dialog
* element so it can be accessed directly.
*
* @param {Object} element - The DOM element for the component's dialog.
* @private
* @returns {void}
*/
_setDialogElement(element) {
this._dialogElement = element;
}
/**
* Handles 'Enter' key in the dialog to submit/hide dialog depending on
* the available buttons and their disabled state.
*
* @param {Object} event - The key event.
* @private
* @returns {void}
*/
_onKeyDown(event) {
if (event.key === 'Enter') {
if (this.props.submitDisabled && !this.props.cancelDisabled) {
this._onCancel();
} else if (!this.props.okDisabled) {
this._onSubmit();
}
}
}
/**
* Updates focused button, if we have a reference to the dialog element.
* Focus on available button if there is no focus already.
*
* @private
* @returns {void}
*/
_updateButtonFocus() {
if (this._dialogElement) {
// if we have a focused element inside the dialog, skip changing
// the focus
if (this._dialogElement.contains(document.activeElement)) {
return;
}
let buttonToFocus;
if (this.props.submitDisabled) {
buttonToFocus = this._dialogElement
.querySelector(`[id=${CANCEL_BUTTON_ID}]`);
} else if (!this.props.okDisabled) {
buttonToFocus = this._dialogElement
.querySelector(`[id=${OK_BUTTON_ID}]`);
}
if (buttonToFocus) {
buttonToFocus.focus();
}
}
}
}
export default translate(StatelessDialog);

View File

@@ -1,2 +1,3 @@
export { default as DialogContainer } from './DialogContainer';
export { default as Dialog } from './Dialog';
export { default as StatelessDialog } from './StatelessDialog';

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