Showing preview only (258K chars total). Download the full file or copy to clipboard to get everything.
Repository: opentok/one-to-one-sample-apps
Branch: master
Commit: 4123c6ae17b7
Files: 80
Total size: 234.8 KB
Directory structure:
gitextract_smhvk9sm/
├── .github/
│ ├── CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE.md
│ └── PULL_REQUEST_TEMPLATE.md
├── LICENSE
├── README.md
├── android/
│ ├── .gitignore
│ ├── OneToOneSample/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── tokbox/
│ │ │ │ └── android/
│ │ │ │ └── onetoonesample/
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── config/
│ │ │ │ │ └── OpenTokConfig.java
│ │ │ │ └── ui/
│ │ │ │ ├── PreviewCameraFragment.java
│ │ │ │ ├── PreviewControlFragment.java
│ │ │ │ └── RemoteControlFragment.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── bckg_audio_only.xml
│ │ │ │ ├── bckg_icon.xml
│ │ │ │ ├── end_call_button.xml
│ │ │ │ ├── gradient_audionly.xml
│ │ │ │ ├── gradient_backg.xml
│ │ │ │ ├── initiate_call_button.xml
│ │ │ │ └── preview.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── preview_actionbar_fragment.xml
│ │ │ │ ├── preview_camera_fragment.xml
│ │ │ │ └── remote_actionbar_fragment.xml
│ │ │ ├── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── values-v21/
│ │ │ │ └── styles.xml
│ │ │ └── values-w820dp/
│ │ │ └── dimens.xml
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ └── README.md
├── iOS/
│ ├── .gitignore
│ ├── OneToOneSample.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── OneToOneSample.xcscheme
│ ├── Podfile
│ ├── README.md
│ └── SampleApp/
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── audio.imageset/
│ │ │ └── Contents.json
│ │ ├── hangUp.imageset/
│ │ │ └── Contents.json
│ │ ├── mic.imageset/
│ │ │ └── Contents.json
│ │ ├── mutedMic.imageset/
│ │ │ └── Contents.json
│ │ ├── noAudio.imageset/
│ │ │ └── Contents.json
│ │ ├── noVideo.imageset/
│ │ │ └── Contents.json
│ │ ├── reverse cameras.imageset/
│ │ │ └── Contents.json
│ │ ├── startCall.imageset/
│ │ │ └── Contents.json
│ │ └── video.imageset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── MainView.h
│ ├── MainView.m
│ ├── MainViewController.h
│ ├── MainViewController.m
│ ├── UIView+Helper.h
│ ├── UIView+Helper.m
│ └── main.m
└── js/
├── .eslintrc.json
├── .gitignore
├── .jsbeautifyrc
├── Procfile
├── README.md
├── package.json
├── public/
│ ├── css/
│ │ └── style.css
│ ├── index.html
│ └── js/
│ ├── app.js
│ └── components/
│ └── opentok-acc-core.js
└── server.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CONDUCT.md
================================================
**opentok/one-to-one-sample-apps** Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to OpenTok One-to-One Communication Sample Apps
## Code of Conduct
Please read our [Code of Conduct](https://github.com/opentok/one-to-one-sample-apps/blob/master/.github/CONDUCT.md). Intolerance, disrespect, and any of form of negativity will not be tolerated.
## Opening a new issue
1. Read *the entire* [README](https://github.com/opentok/one-to-one-sample-apps/blob/master/README.md).
* Search [open issues](https://github.com/opentok/one-to-one-sample-apps/issues) *and* [closed issues](https://github.com/opentok/one-to-one-sample-apps/issues?q=is%3Aissue+is%3Aclosed) to **avoid opening a duplicate issue.
* If your issue exists and you have some new information to contribute, you may add a comment to its thread.
* Otherwise, open a new issue with a clear title and description.
* Provide **all** of the following information:
- Library version(s)
- iOS, Android, or browser version(s)
- Devices, simulators, or machines affected
- Expected behavior vs actual behavior
- Complete steps to reproduce the issue
- Link to a project that exhibits the issue. It is recommended that you fork the repo and modify the demo project.
- Screenshots, GIFs, or videos depicting the issue, if applicable.
- Full crash log, if applicable.
- A list of all possibly related issues.
## Submitting a pull request
1. Link to the issue that the pull request resolves. If the issue does not exist, create one.
2. Write unit tests that test your changes, if applicable.
3. Update header documentation as needed.
4. Follow the existing coding style. For more information, see [style guidelines](https://github.com/NYTimes/objective-c-style-guide).
5. Resolve any merge conflicts.
6. Squash your commits into a single commit.
## Did you read all of this?
Be sure you have visited all the links in this document.
### New issue checklist
When opening your new issue and filling out the checklist, you'll be asked for confirmation. Confirm that you've read this with these emoji: :muscle::sunglasses::facepunch:
> - [x] I have reviewed the contributing guidelines. Confirmation: :muscle::sunglasses::facepunch:
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
## New issue checklist
<!-- Before submitting this issue, make sure you have done the following -->
- [ ] I have read all of the [`README`](https://github.com/opentok/one-to-one-sample-apps/blob/master/README.md)
- [ ] I have searched [existing issues](https://github.com/opentok/one-to-one-sample-apps/issues?q=is%3Aissue+sort%3Acreated-desc) and **this is not a duplicate**.
### General information
- Library version(s):
- iOS/Android/Browser version(s):
- Devices/Simulators/Machine affected:
- Reproducible in the demo project? (Yes/No):
- Related issues:
## Bug report
#### Expected behavior
> ...
#### Actual behavior
> ...
#### Steps to reproduce
> ...
#### Crash log? Screenshots? Videos? Sample project?
>...
## Question or Feature Request
> ...
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Pull request checklist
- [ ] All tests pass. Demo project builds and runs.
- [ ] I have resolved any merge conflicts. Confirmation: ____
#### This fixes issue #___.
## What's in this pull request?
>...
================================================
FILE: LICENSE
================================================
LICENSE
The MIT License (MIT)
Copyright (c) 2016 TokBox, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# DEPRECATED: OpenTok One-to-One Communication Sample App<br/>Version 1.3
<img src="https://assets.tokbox.com/img/vonage/Vonage_VideoAPI_black.svg" height="48px" alt="Tokbox is now known as Vonage" />
> This repository has been deprecated and is superseded by the following repositories:
>
> [accelerator-sample-apps-js](https://github.com/opentok/accelerator-sample-apps-js)
>
> [accelerator-sample-apps-android](https://github.com/opentok/accelerator-sample-apps-android)
>
> [accelerator-sample-apps-ios](https://github.com/opentok/accelerator-sample-apps-ios)
---
The OpenTok One-to-One Communication Sample App is an open-source solution that enables you to quickly get started in your development efforts to set up interoperable, production-quality audio/video communication between users.
With this sample app, you can:
- Start and end audio/visual communication between two users.
- Achieve interoperability between web and mobile devices.
- Mute or unmute audio.
- Enable or disable video.
- Control the camera to point in the forward direction or in the reverse direction (selfie mode).
- Customize the UI features and layout.
You can create mobile apps for Android and iOS, or embed the interactive session between users into any website. To get started with your development, visit the following sites:
- [OpenTok One-to-One Communication Sample App for Android](./android)
- [OpenTok One-to-One Communication Sample App for iOS](./iOS)
- [OpenTok One-to-One Communication Sample App for JavaScript](./js)
# Device interoperability with One-to-One communication
The OpenTok One-to-One Communication Sample App highlights the interoperability of web and mobile devices using the OpenTok platform. Regardless of the supported devices used, the OpenTok platform supports the ability of users to interact with each other and exchange audio and video. Even if the clients are on different platforms, they can both connect, publish, and subscribe to streams in the same session.
This sample app requires a **Session ID**, **Token**, and **API Key**. In the sample, you can get these values at the [OpenTok Developer Dashboard](https://dashboard.tokbox.com/). For production deployment, you must generate the **Session ID** and **Token** values using one of the [OpenTok Server SDKs](https://tokbox.com/developer/sdks/server/).
For example, suppose one user is using a web (JS) version of the One-to-One Communication Sample App and another user is using a mobile version (Android or iOS). If they are both using the same **Session ID** and **API Key**, they can subscribe to each other’s audio and video streams, and the user interface rendered on both devices will allow them to interact with each other and take advantage of all the features of the sample app.
Use the following approach to try this out:
1. Configure a web and mobile user with the required **Session ID**, **Token**, and **API Key** values, using the same **Session ID** and **API Key** for each.
2. Start the web and mobile apps. You will observe the following interactions:
- Both apps connect to the session.
- Both apps start publishing and subscribing to each other’s streams.
3. Observe what happens for each user when you:
- Enable or disable local audio/video on the mobile app.
- Enable or disable local audio/video on the web app.
- Enable or disable remote audio/video on the mobile app.
- Enable or disable remote audio/video on the web app.
As you get started with this OpenTok sample, you will learn the best practices used to develop and manage the audio, video, and camera elements on mobile devices or in the browser. We recommend this is as your first step in delivering [Real Time Communications (WebRTC)](https://tokbox.com/about-webrtc) solutions on the OpenTok platform.
## Getting Help
We love to hear from you so if you have questions or comments, let us know! You can either:
- See <https://support.tokbox.com/> for support options
- Tweet at us! We're [@VonageDev on Twitter](https://twitter.com/VonageDev)
- Or [join the Vonage Developer Community Slack](https://developer.nexmo.com/community/slack)
================================================
FILE: android/.gitignore
================================================
# built components files
*.jar
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# OSX files
.DS_Store
# Android Studio files
*.iml
.idea/
.idea/workspace.xml
.gradle
build/
*.ipr
*.iws
#libs
*.so
================================================
FILE: android/OneToOneSample/.gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
================================================
FILE: android/OneToOneSample/app/.gitignore
================================================
/build
/libs
================================================
FILE: android/OneToOneSample/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.tokbox.android.onetosample"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.3"
archivesBaseName = "OneToOneSample-$versionName"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/ASL2.0'
exclude 'META-INF/LICENSE'
}
configurations.all {
resolutionStrategy {
cacheChangingModulesFor 0, 'seconds'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.+'
compile 'com.android.support:design:25.+'
compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'
compile 'com.opentok.android:opentok-accelerator-core:1.0.+'
}
================================================
FILE: android/OneToOneSample/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/mserrano/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 *;
#}
================================================
FILE: android/OneToOneSample/app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.tokbox.android.onetoonesample">
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<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:theme="@style/AppTheme.NoActionBar"
android:icon="@mipmap/ic_launcher"
tools:replace="android:theme">
<activity
android:name="com.tokbox.android.onetoonesample.MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/MainActivity.java
================================================
package com.tokbox.android.onetoonesample;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.opentok.android.OpentokError;
import com.tokbox.android.onetoonesample.config.OpenTokConfig;
import com.tokbox.android.onetoonesample.ui.PreviewCameraFragment;
import com.tokbox.android.onetoonesample.ui.PreviewControlFragment;
import com.tokbox.android.onetoonesample.ui.RemoteControlFragment;
import com.tokbox.android.otsdkwrapper.listeners.AdvancedListener;
import com.tokbox.android.otsdkwrapper.listeners.BasicListener;
import com.tokbox.android.otsdkwrapper.listeners.ListenerException;
import com.tokbox.android.otsdkwrapper.listeners.PausableAdvancedListener;
import com.tokbox.android.otsdkwrapper.listeners.PausableBasicListener;
import com.tokbox.android.otsdkwrapper.utils.MediaType;
import com.tokbox.android.otsdkwrapper.utils.OTConfig;
import com.tokbox.android.otsdkwrapper.utils.PreviewConfig;
import com.tokbox.android.otsdkwrapper.wrapper.OTWrapper;
import java.util.UUID;
import android.widget.FrameLayout;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements PreviewControlFragment.PreviewControlCallbacks,
RemoteControlFragment.RemoteControlCallbacks, PreviewCameraFragment.PreviewCameraCallbacks {
private final String LOG_TAG = MainActivity.class.getSimpleName();
private final String[] permissions = {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA};
private final int permsRequestCode = 200;
private RelativeLayout mPreviewViewContainer;
private RelativeLayout mRemoteViewContainer;
private RelativeLayout.LayoutParams mLayoutParamsPreview;
private String mRemoteId;
private View mRemoteView;
private TextView mAlert;
//Audio only views
private RelativeLayout mLocalAudioOnlyView;
private RelativeLayout mRemoteAudioOnlyView;
private ImageView mLocalAudioOnlyImage;
//UI control bars fragments
private PreviewControlFragment mPreviewFragment;
private RemoteControlFragment mRemoteFragment;
private PreviewCameraFragment mCameraFragment;
private FragmentTransaction mFragmentTransaction;
//Loading dialog
ProgressDialog mProgressDialog;
//Permissions
private boolean mAudioPermission = false;
private boolean mVideoPermission = false;
//Communication status
private boolean isConnected = false;
private boolean isLocal = false;
private boolean isCallInProgress = false;
private OTWrapper mWrapper;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(LOG_TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPreviewViewContainer = (RelativeLayout) findViewById(R.id.publisherview);
mRemoteViewContainer = (RelativeLayout) findViewById(R.id.subscriberview);
mAlert = (TextView) findViewById(R.id.quality_warning);
//remote and local audio only view
mRemoteAudioOnlyView = (RelativeLayout) findViewById(R.id.remoteAudioOnlyView);
mLocalAudioOnlyView = (RelativeLayout) findViewById(R.id.localAudioOnlyView);
//request Marshmallow camera permission
if (ContextCompat.checkSelfPermission(this, permissions[1]) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, permsRequestCode);
}
} else {
mVideoPermission = true;
mAudioPermission = true;
}
//init the wrapper
OTConfig config =
new OTConfig.OTConfigBuilder(OpenTokConfig.SESSION_ID, OpenTokConfig.TOKEN,
OpenTokConfig.API_KEY).name("one-to-one-sample-app").subscribeAutomatically(false).subscribeToSelf(false).build();
if ( config != null ) {
mWrapper = new OTWrapper(MainActivity.this, config);
mWrapper.addBasicListener(mBasicListener);
mWrapper.addAdvancedListener(mAdvancedListener);
if (mWrapper != null) {
mWrapper.connect();
}
//init controls fragments
if (savedInstanceState == null) {
mFragmentTransaction = getSupportFragmentManager().beginTransaction();
initCameraFragment(); //to swap camera
initPreviewFragment(); //to enable/disable local media
mFragmentTransaction.commitAllowingStateLoss();
}
//show connecting dialog
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setTitle("Please wait");
mProgressDialog.setMessage("Connecting...");
mProgressDialog.show();
}
else {
Log.e(LOG_TAG, "OpenTok credentials are invalid");
Toast.makeText(MainActivity.this, "Credentials are invalid", Toast.LENGTH_LONG).show();
this.finish();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
reloadViews();
}
@Override
protected void onPause() {
super.onPause();
if (mWrapper != null) {
mWrapper.pause();
}
}
@Override
protected void onResume() {
super.onResume();
if ( mWrapper != null ){
mWrapper.resume(true);
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
if ( mWrapper != null && isConnected ){
mWrapper.disconnect();
}
}
@Override
public void onRequestPermissionsResult(final int permsRequestCode, final String[] permissions,
int[] grantResults) {
switch (permsRequestCode) {
case 200:
mVideoPermission = grantResults[0] == PackageManager.PERMISSION_GRANTED;
mAudioPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (!mVideoPermission || !mAudioPermission) {
final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(getResources().getString(R.string.permissions_denied_title));
builder.setMessage(getResources().getString(R.string.alert_permissions_denied));
builder.setPositiveButton("I'M SURE", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.setNegativeButton("RE-TRY", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, permsRequestCode);
}
}
});
builder.show();
}
break;
}
}
public void showRemoteControlBar(View v) {
if ( mRemoteFragment != null && mRemoteId != null ) {
mRemoteFragment.show();
}
}
public boolean isCallInProgress() {
return isCallInProgress;
}
public OTWrapper getWrapper() {
return mWrapper;
}
//Private methods
//Init the local fragment
private void initPreviewFragment() {
mPreviewFragment = new PreviewControlFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.actionbar_preview_fragment_container, mPreviewFragment).commit();
}
//Inti the remote fragment
private void initRemoteFragment(String remoteId) {
mRemoteFragment = new RemoteControlFragment();
Bundle args = new Bundle();
args.putString("remoteId", remoteId);
mRemoteFragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.add(R.id.actionbar_remote_fragment_container, mRemoteFragment).commit();
}
//Init the local camera fragment
private void initCameraFragment() {
mCameraFragment = new PreviewCameraFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.camera_preview_fragment_container, mCameraFragment).commit();
}
//Clean views
private void cleanViewsAndControls() {
if ( mRemoteId != null ) {
mWrapper.removeRemote(mRemoteId);
mRemoteView = null;
setRemoteView(null);
}
if (isLocal) {
isLocal = false;
setLocalView(null);
}
if (mPreviewFragment != null)
mPreviewFragment.restart();
if (mRemoteFragment != null)
mRemoteFragment.restart();
}
//Reload views
private void reloadViews(){
mRemoteViewContainer.removeAllViews();
if (mRemoteId != null){
setRemoteView(mWrapper.getRemoteStreamStatus(mRemoteId).getView());
}
}
//Check if there are some connected remotes in the session
private void checkRemotes(){
if (mRemoteId != null){
//add the remote participant to the communication
mWrapper.addRemote(mRemoteId);
//check the status of the remote video stream
if (!mWrapper.isReceivedMediaEnabled(mRemoteId, MediaType.VIDEO)){
onRemoteAudioOnly(true);
}
else {
setRemoteView(mWrapper.getRemoteStreamStatus(mRemoteId).getView());
}
}
}
//Set the local participant view
private void setLocalView(View localView){
if (localView != null) {
mPreviewViewContainer.removeAllViews();
isLocal = true;
mLayoutParamsPreview = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
if (mRemoteId != null) {
mLayoutParamsPreview.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,
RelativeLayout.TRUE);
mLayoutParamsPreview.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,
RelativeLayout.TRUE);
mLayoutParamsPreview.width = (int) getResources().getDimension(R.dimen.preview_width);
mLayoutParamsPreview.height = (int) getResources().getDimension(R.dimen.preview_height);
mLayoutParamsPreview.rightMargin = (int) getResources().getDimension(R.dimen.preview_rightMargin);
mLayoutParamsPreview.bottomMargin = (int) getResources().getDimension(R.dimen.preview_bottomMargin);
}
mPreviewViewContainer.addView(localView, mLayoutParamsPreview);
}
else {
mPreviewViewContainer.removeAllViews();
}
}
//Set the remote participant view
private void setRemoteView(View remoteView) {
if (mPreviewViewContainer.getChildCount() > 0) {
setLocalView(mPreviewViewContainer.getChildAt(0)); //main preview view
}
if (remoteView != null) {
//show remote view
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
this.getResources().getDisplayMetrics().widthPixels, this.getResources()
.getDisplayMetrics().heightPixels);
mRemoteViewContainer.removeView(remoteView);
mRemoteViewContainer.addView(remoteView, layoutParams);
mRemoteViewContainer.setClickable(true);
if (mRemoteFragment != null)
mRemoteFragment.show();
} else { //view null --> remove view
if (mRemoteViewContainer.getChildCount() > 0) {
mRemoteViewContainer.removeAllViews();
}
mRemoteViewContainer.setClickable(false);
mRemoteAudioOnlyView.setVisibility(View.GONE);
}
}
//Set the remote audio only view
private void onRemoteAudioOnly(boolean enabled) {
if (mRemoteView != null) {
if (enabled) {
mRemoteView.setVisibility(View.GONE);
mRemoteAudioOnlyView.setVisibility(View.VISIBLE);
} else {
mRemoteAudioOnlyView.setVisibility(View.GONE);
mRemoteView.setVisibility(View.VISIBLE);
}
}
}
//Converts dp to real pixels, according to the screen density.
private int dpToPx(int dp) {
double screenDensity = this.getResources().getDisplayMetrics().density;
return (int) (screenDensity * (double) dp);
}
//Basic Listener from OTWrapper
private BasicListener mBasicListener =
new PausableBasicListener(new BasicListener<OTWrapper>() {
@Override
public void onConnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException {
Log.i(LOG_TAG, "Connected to the session. Number of participants: "+participantsCount);
isConnected = true;
mProgressDialog.dismiss();
}
@Override
public void onDisconnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException {
Log.i(LOG_TAG, "Connection dropped: "+connId);
if ( connId == mWrapper.getOwnConnId() ) {
Log.i(LOG_TAG, "Disconnected to the session");
cleanViewsAndControls();
}
}
@Override
public void onPreviewViewReady(OTWrapper otWrapper, View localView) throws ListenerException {
Log.i(LOG_TAG, "Local preview view is ready");
setLocalView(localView);
}
@Override
public void onPreviewViewDestroyed(OTWrapper otWrapper, View localView) throws ListenerException {
Log.i(LOG_TAG, "Local preview view is destroyed");
setLocalView(null);
}
@Override
public void onRemoteViewReady(OTWrapper otWrapper, View remoteView, String remoteId, String data) throws ListenerException {
Log.i(LOG_TAG, "Remove view is ready");
if ( remoteId == mRemoteId ) {
if (isCallInProgress()) {
setRemoteView(remoteView);
}
mRemoteView = remoteView;
}
}
@Override
public void onRemoteViewDestroyed(OTWrapper otWrapper, View remoteView, String remoteId) throws ListenerException {
Log.i(LOG_TAG, "Remote view is destroyed");
setRemoteView(null);
mRemoteView = null;
}
@Override
public void onStartedPublishingMedia(OTWrapper otWrapper, boolean screensharing) throws ListenerException {
Log.i(LOG_TAG, "Local started streaming video.");
//Check if there are some connected remotes
checkRemotes();
}
@Override
public void onStoppedPublishingMedia(OTWrapper otWrapper, boolean screensharing) throws ListenerException {
Log.i(LOG_TAG, "Local stopped streaming video.");
}
@Override
public void onRemoteJoined(OTWrapper otWrapper, String remoteId) throws ListenerException {
Log.i(LOG_TAG, "A new remote joined.");
if (mRemoteId == null){ //one-to-one, the first to arrive, will be the used
MainActivity.this.mRemoteId = remoteId;
initRemoteFragment(remoteId);
if (mWrapper.isPublishing()){
mWrapper.addRemote(mRemoteId);
}
}
}
@Override
public void onRemoteLeft(OTWrapper otWrapper, String remoteId) throws ListenerException {
Log.i(LOG_TAG, "A new remote left.");
if ( mRemoteId != null && remoteId == mRemoteId ) { //one-to-one
mRemoteId = null;
}
}
@Override
public void onRemoteVideoChanged(OTWrapper otWrapper, String remoteId, String reason, boolean videoActive, boolean subscribed) throws ListenerException {
Log.i(LOG_TAG, "Remote video changed");
if (isCallInProgress) {
if (reason.equals("quality")) {
//network quality alert
mAlert.setBackgroundResource(R.color.quality_alert);
mAlert.setTextColor(MainActivity.this.getResources().getColor(R.color.white));
mAlert.bringToFront();
mAlert.setVisibility(View.VISIBLE);
mAlert.postDelayed(new Runnable() {
public void run() {
mAlert.setVisibility(View.GONE);
}
}, 7000);
}
if (!videoActive) {
onRemoteAudioOnly(true); //video is not active
} else {
onRemoteAudioOnly(false);
}
}
}
@Override
public void onError(OTWrapper otWrapper, OpentokError error) throws ListenerException {
Log.i(LOG_TAG, "Error "+error.getErrorCode()+"-"+error.getMessage());
Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();
mWrapper.disconnect(); //end communication
mProgressDialog.dismiss();
cleanViewsAndControls(); //restart views
}
});
//Advanced Listener from OTWrapper
private AdvancedListener mAdvancedListener =
new PausableAdvancedListener(new AdvancedListener<OTWrapper>() {
@Override
public void onCameraChanged(OTWrapper otWrapper) throws ListenerException {
Log.i(LOG_TAG, "The camera changed");
}
@Override
public void onReconnecting(OTWrapper otWrapper) throws ListenerException {
Log.i(LOG_TAG, "The session is reconnecting.");
Toast.makeText(MainActivity.this, R.string.reconnecting, Toast.LENGTH_LONG).show();
}
@Override
public void onReconnected(OTWrapper otWrapper) throws ListenerException {
Log.i(LOG_TAG, "The session reconnected.");
Toast.makeText(MainActivity.this, R.string.reconnected, Toast.LENGTH_LONG).show();
}
@Override
public void onVideoQualityWarning(OTWrapper otWrapper, String remoteId) throws ListenerException {
Log.i(LOG_TAG, "The quality has degraded");
mAlert.setBackgroundResource(R.color.quality_warning);
mAlert.setTextColor(MainActivity.this.getResources().getColor(R.color.warning_text));
mAlert.bringToFront();
mAlert.setVisibility(View.VISIBLE);
mAlert.postDelayed(new Runnable() {
public void run() {
mAlert.setVisibility(View.GONE);
}
}, 7000);
}
@Override
public void onVideoQualityWarningLifted(OTWrapper otWrapper, String remoteId) throws ListenerException {
Log.i(LOG_TAG, "The quality has improved");
}
@Override
public void onError(OTWrapper otWrapper, OpentokError error) throws ListenerException {
Log.i(LOG_TAG, "Error " + error.getErrorCode() + "-" + error.getMessage());
Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();
mWrapper.disconnect(); //end communication
mProgressDialog.dismiss();
cleanViewsAndControls(); //restart views
}
});
//Audio local button event
@Override
public void onDisableLocalAudio(boolean audio) {
if (mWrapper != null) {
mWrapper.enableLocalMedia(MediaType.AUDIO, audio);
}
}
//Video local button event
@Override
public void onDisableLocalVideo(boolean video) {
if (mWrapper != null) {
mWrapper.enableLocalMedia(MediaType.VIDEO, video);
if (mRemoteId != null) {
if (!video) {
mLocalAudioOnlyImage = new ImageView(this);
mLocalAudioOnlyImage.setImageResource(R.drawable.avatar);
mLocalAudioOnlyImage.setBackgroundResource(R.drawable.bckg_audio_only);
mPreviewViewContainer.addView(mLocalAudioOnlyImage, mLayoutParamsPreview);
} else {
mPreviewViewContainer.removeView(mLocalAudioOnlyImage);
}
} else {
if (!video) {
mLocalAudioOnlyView.setVisibility(View.VISIBLE);
mPreviewViewContainer.addView(mLocalAudioOnlyView);
} else {
mLocalAudioOnlyView.setVisibility(View.GONE);
mPreviewViewContainer.removeView(mLocalAudioOnlyView);
}
}
}
}
//Remote control callbacks
@Override
public void onDisableRemoteAudio(boolean audio) {
if (mWrapper != null) {
mWrapper.enableReceivedMedia(mRemoteId, MediaType.AUDIO, audio);
}
}
@Override
public void onDisableRemoteVideo(boolean video) {
if (mWrapper != null) {
mWrapper.enableReceivedMedia(mRemoteId, MediaType.VIDEO, video);
}
}
//Camera control callback
@Override
public void onCameraSwap() {
if (mWrapper != null) {
mWrapper.cycleCamera();
}
}
@Override
public void onCall() {
if (mWrapper != null && isConnected) {
if (!isCallInProgress) {
mWrapper.startPublishingMedia(new PreviewConfig.PreviewConfigBuilder().
name("Tokboxer").build(), false);
if ( mPreviewFragment != null ) {
mPreviewFragment.setEnabled(true);
}
isCallInProgress = true;
} else {
mWrapper.stopPublishingMedia(false);
isCallInProgress = false;
cleanViewsAndControls();
}
}
}
}
================================================
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/config/OpenTokConfig.java
================================================
package com.tokbox.android.onetoonesample.config;
public class OpenTokConfig {
// *** Fill the following variables using your own Project info from the OpenTok dashboard ***
// *** https://dashboard.tokbox.com/projects ***
// Replace with a generated Session ID
public static final String SESSION_ID = "";
// Replace with a generated token (from the dashboard or using an OpenTok server SDK)
public static final String TOKEN = "";
// Replace with your OpenTok API key
public static final String API_KEY = "";
}
================================================
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewCameraFragment.java
================================================
package com.tokbox.android.onetoonesample.ui;
import android.app.Activity;
import android.support.v4.app.Fragment;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import com.tokbox.android.onetoonesample.MainActivity;
import com.tokbox.android.onetoonesample.R;
public class PreviewCameraFragment extends Fragment {
private static final String LOGTAG = PreviewCameraFragment.class.getSimpleName();
private View mRootView;
private ImageButton mCameraBtn;
private PreviewCameraCallbacks mCameraCallbacks = cameraCallbacks;
public interface PreviewCameraCallbacks {
void onCameraSwap();
}
private static PreviewCameraCallbacks cameraCallbacks = new PreviewCameraCallbacks() {
@Override
public void onCameraSwap() {}
};
private View.OnClickListener mBtnClickListener = new View.OnClickListener() {
public void onClick(View v) {
cameraSwap();
}
};
@Override
public void onAttach(Context context) {
Log.i(LOGTAG, "OnAttach PreviewCameraFragment");
super.onAttach(context);
this.mCameraCallbacks = (PreviewCameraCallbacks) context;
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
this.mCameraCallbacks = (PreviewCameraCallbacks) activity;
}
}
@Override
public void onDetach() {
Log.i(LOGTAG, "OnDetach PreviewCameraFragment");
super.onDetach();
mCameraCallbacks = cameraCallbacks;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i(LOGTAG, "onCreate PreviewCameraFragment");
mRootView = inflater.inflate(R.layout.preview_camera_fragment, container, false);
mCameraBtn = (ImageButton) mRootView.findViewById(R.id.camera);
mCameraBtn.setOnClickListener(mBtnClickListener);
return mRootView;
}
public void cameraSwap() {
mCameraCallbacks.onCameraSwap();
}
}
================================================
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewControlFragment.java
================================================
package com.tokbox.android.onetoonesample.ui;
import android.app.Activity;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.app.Fragment;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import com.tokbox.android.onetoonesample.MainActivity;
import com.tokbox.android.onetoonesample.R;
import com.tokbox.android.otsdkwrapper.utils.MediaType;
public class PreviewControlFragment extends Fragment {
private static final String LOGTAG = MainActivity.class.getName();
private MainActivity mActivity;
private View rootView;
private ImageButton mAudioBtn;
private ImageButton mVideoBtn;
private ImageButton mCallBtn;
private VectorDrawableCompat drawableStartCall;
private VectorDrawableCompat drawableEndCall;
private VectorDrawableCompat drawableBckBtn;
private PreviewControlCallbacks mControlCallbacks = previewCallbacks;
public interface PreviewControlCallbacks {
void onDisableLocalAudio(boolean audio);
void onDisableLocalVideo(boolean video);
void onCall();
}
private static PreviewControlCallbacks previewCallbacks = new PreviewControlCallbacks() {
@Override
public void onDisableLocalAudio(boolean audio) { }
@Override
public void onDisableLocalVideo(boolean video) { }
@Override
public void onCall() { }
};
private View.OnClickListener mBtnClickListener = new View.OnClickListener() {
public void onClick(View v) {
switch (v.getId()) {
case R.id.localAudio:
updateLocalAudio();
break;
case R.id.localVideo:
updateLocalVideo();
break;
case R.id.call:
updateCall();
break;
}
}
};
@Override
public void onAttach(Context context) {
Log.i(LOGTAG, "OnAttach PreviewControlFragment");
super.onAttach(context);
this.mActivity = (MainActivity) context;
this.mControlCallbacks = (PreviewControlCallbacks) context;
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
this.mActivity = (MainActivity) activity;
this.mControlCallbacks = (PreviewControlCallbacks) activity;
}
}
@Override
public void onDetach() {
Log.i(LOGTAG, "onDetach PreviewControlFragment");
super.onDetach();
mControlCallbacks = previewCallbacks;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i(LOGTAG, "OnCreate PreviewControlFragment");
rootView = inflater.inflate(R.layout.preview_actionbar_fragment, container, false);
mAudioBtn = (ImageButton) rootView.findViewById(R.id.localAudio);
mVideoBtn = (ImageButton) rootView.findViewById(R.id.localVideo);
mCallBtn = (ImageButton) rootView.findViewById(R.id.call);
drawableStartCall = VectorDrawableCompat.create(getResources(), R.drawable.initiate_call_button, null);
drawableEndCall = VectorDrawableCompat.create(getResources(), R.drawable.end_call_button, null);
drawableBckBtn = VectorDrawableCompat.create(getResources(), R.drawable.bckg_icon, null);
mAudioBtn.setImageResource(mActivity.getWrapper().isLocalMediaEnabled(MediaType.AUDIO)
? R.drawable.mic_icon
: R.drawable.muted_mic_icon);
mAudioBtn.setBackground(drawableBckBtn);
mVideoBtn.setImageResource(mActivity.getWrapper().isLocalMediaEnabled(MediaType.VIDEO)
? R.drawable.video_icon
: R.drawable.no_video_icon);
mVideoBtn.setBackground(drawableBckBtn);
mCallBtn.setImageResource(mActivity.isCallInProgress()
? R.drawable.hang_up
: R.drawable.start_call);
mCallBtn.setBackground(mActivity.isCallInProgress()
? drawableEndCall
: drawableStartCall);
mCallBtn.setOnClickListener(mBtnClickListener);
setEnabled(mActivity.isCallInProgress());
return rootView;
}
public void updateLocalAudio() {
if (!mActivity.getWrapper().isLocalMediaEnabled(MediaType.AUDIO)) {
mControlCallbacks.onDisableLocalAudio(true);
mAudioBtn.setImageResource(R.drawable.mic_icon);
} else {
mControlCallbacks.onDisableLocalAudio(false);
mAudioBtn.setImageResource(R.drawable.muted_mic_icon);
}
}
public void updateLocalVideo() {
if (!mActivity.getWrapper().isLocalMediaEnabled(MediaType.VIDEO)){
mControlCallbacks.onDisableLocalVideo(true);
mVideoBtn.setImageResource(R.drawable.video_icon);
} else {
mControlCallbacks.onDisableLocalVideo(false);
mVideoBtn.setImageResource(R.drawable.no_video_icon);
}
}
public void updateCall() {
mCallBtn.setImageResource(!mActivity.isCallInProgress()
? R.drawable.hang_up
: R.drawable.start_call);
mCallBtn.setBackground(!mActivity.isCallInProgress()
? drawableEndCall
: drawableStartCall);
if ( mControlCallbacks != null )
mControlCallbacks.onCall();
}
public void setEnabled(boolean enabled) {
if (mVideoBtn != null && mAudioBtn != null) {
if (enabled) {
mAudioBtn.setOnClickListener(mBtnClickListener);
mVideoBtn.setOnClickListener(mBtnClickListener);
} else {
mAudioBtn.setOnClickListener(null);
mVideoBtn.setOnClickListener(null);
mAudioBtn.setImageResource(R.drawable.mic_icon);
mVideoBtn.setImageResource(R.drawable.video_icon);
}
}
}
public void restart() {
setEnabled(false);
mCallBtn.setBackground(drawableStartCall);
mCallBtn.setImageResource(R.drawable.start_call);
}
}
================================================
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/RemoteControlFragment.java
================================================
package com.tokbox.android.onetoonesample.ui;
import android.app.Activity;
import android.support.v4.app.Fragment;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import com.tokbox.android.onetoonesample.MainActivity;
import com.tokbox.android.onetoonesample.R;
import com.tokbox.android.otsdkwrapper.utils.MediaType;
public class RemoteControlFragment extends Fragment {
private static final String LOGTAG = RemoteControlFragment.class.getSimpleName();
private static final int ANIMATION_DURATION = 7000;
private MainActivity mActivity;
private RelativeLayout mContainer;
private View mRootView;
private ImageButton mAudioBtn;
private ImageButton mVideoBtn;
private RemoteControlCallbacks mControlCallbacks = remoteCallbacks;
private String mRemoteId;
public interface RemoteControlCallbacks {
void onDisableRemoteAudio(boolean audio);
void onDisableRemoteVideo(boolean video);
}
private static RemoteControlCallbacks remoteCallbacks = new RemoteControlCallbacks() {
@Override
public void onDisableRemoteAudio(boolean audio) { }
@Override
public void onDisableRemoteVideo(boolean video) { }
};
private View.OnClickListener mBtnClickListener = new View.OnClickListener() {
public void onClick(View v) {
switch (v.getId()) {
case R.id.remoteAudio:
updateRemoteAudio();
break;
case R.id.remoteVideo:
updateRemoteVideo();
break;
}
}
};
@Override
public void onAttach(Context context) {
Log.i(LOGTAG, "OnAttach RemoteControlFragment");
super.onAttach(context);
this.mActivity = (MainActivity) context;
this.mControlCallbacks = (RemoteControlCallbacks) context;
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
this.mActivity = (MainActivity) activity;
this.mControlCallbacks = (RemoteControlCallbacks) activity;
}
if ( mRemoteId == null ) {
mRemoteId = getArguments().getString("remoteId");
}
}
@Override
public void onDetach() {
Log.i(LOGTAG, "OnDetach RemoteControlFragment");
super.onDetach();
mControlCallbacks = remoteCallbacks;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i(LOGTAG, "OnCreate RemoteControlFragment");
mRootView = inflater.inflate(R.layout.remote_actionbar_fragment, container, false);
mContainer = (RelativeLayout) this.mActivity.findViewById(R.id.actionbar_remote_fragment_container);
mAudioBtn = (ImageButton) mRootView.findViewById(R.id.remoteAudio);
mVideoBtn = (ImageButton) mRootView.findViewById(R.id.remoteVideo);
mAudioBtn.setOnClickListener(mBtnClickListener);
mVideoBtn.setOnClickListener(mBtnClickListener);
return mRootView;
}
public void updateRemoteAudio(){
if(mRemoteId != null && !mActivity.getWrapper().isReceivedMediaEnabled(mRemoteId, MediaType.AUDIO)){
mControlCallbacks.onDisableRemoteAudio(true);
mAudioBtn.setImageResource(R.drawable.audio);
}
else {
mControlCallbacks.onDisableRemoteAudio(false);
mAudioBtn.setImageResource(R.drawable.no_audio);
}
}
public void updateRemoteVideo(){
if(mRemoteId != null && !mActivity.getWrapper().isReceivedMediaEnabled(mRemoteId, MediaType.VIDEO)){
mControlCallbacks.onDisableRemoteVideo(true);
mVideoBtn.setImageResource(R.drawable.video_icon);
}
else {
mControlCallbacks.onDisableRemoteVideo(false);
mVideoBtn.setImageResource(R.drawable.no_video_icon);
}
}
public void show(){
mContainer.setVisibility(View.VISIBLE);
mRootView.setVisibility(View.VISIBLE);
mContainer.postDelayed(new Runnable() {
public void run() {
mContainer.setVisibility(View.INVISIBLE);
}
}, ANIMATION_DURATION);
}
private void setEnabled(boolean enabled) {
if (mVideoBtn != null && mAudioBtn != null) {
if (!enabled) {
mAudioBtn.setImageResource(R.drawable.audio);
mVideoBtn.setImageResource(R.drawable.video_icon);
}
}
}
public void restart() {
setEnabled(false);
mContainer.setVisibility(View.INVISIBLE);
}
}
================================================
FILE: android/OneToOneSample/app/src/main/res/drawable/bckg_audio_only.xml
================================================
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
android:shape="rectangle">
<solid android:color="@color/audiOnlyBackground"/>
<stroke android:color="@color/white" android:width="1dp"/>
</shape>
================================================
FILE: android/OneToOneSample/app/src/main/res/drawable/bckg_icon.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="106dp"
android:height="106dp"
android:viewportWidth="106.0"
android:viewportHeight="106.0">
<path
android:pathData="M1.26,52.61a51.93,51.91 0,1 0,103.87 0a51.93,51.91 0,1 0,-103.87 0z"
android:strokeWidth="1"
android:fillColor="#66000000"
android:strokeColor="#979797" />
</vector>
================================================
FILE: android/OneToOneSample/app/src/main/res/drawable/end_call_button.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="105dp"
android:height="105dp"
android:viewportWidth="105.0"
android:viewportHeight="105.0">
<path
android:pathData="M0.25,52.68a51.93,51.91 0,1 0,103.87 0a51.93,51.91 0,1 0,-103.87 0z"
android:strokeWidth="1"
android:fillColor="@color/endCall"
android:strokeColor="#00000000"/>
</vector>
================================================
FILE: android/OneToOneSample/app/src/main/res/drawable/gradient_audionly.xml
================================================
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/gradientAudioOnlyStart"
android:endColor="@color/gradientAudioOnlyEnd"
android:angle="270" />
</shape>
================================================
FILE: android/OneToOneSample/app/src/main/res/drawable/gradient_backg.xml
================================================
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/gradientStart"
android:endColor="@color/gradientEnd"
android:angle="270" />
</shape>
================================================
FILE: android/OneToOneSample/app/src/main/res/drawable/initiate_call_button.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="105dp"
android:height="105dp"
android:viewportWidth="105.0"
android:viewportHeight="105.0">
<path
android:pathData="M0.25,52.68a51.93,51.91 0,1 0,103.87 0a51.93,51.91 0,1 0,-103.87 0z"
android:strokeWidth="1"
android:fillColor="@color/startCall"
android:strokeColor="#00000000"/>
</vector>
================================================
FILE: android/OneToOneSample/app/src/main/res/drawable/preview.xml
================================================
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
android:shape="rectangle">
<stroke android:color="@color/white" android:width="1dp"/>
</shape>
================================================
FILE: android/OneToOneSample/app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/gradient_backg"
android:keepScreenOn="true"
tools:context="com.tokbox.android.onetoonesample.MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/quality_warning"
android:layout_width="match_parent"
android:layout_height="@dimen/alert_bar_height"
android:background="@color/quality_warning"
android:gravity="center"
android:text="@string/network_quality"
android:textColor="@color/warning_text"
android:textSize="@dimen/alert_text"
android:visibility="gone"></TextView>
<RelativeLayout
android:id="@+id/subscriberview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:gravity="center_horizontal"
android:onClick="showRemoteControlBar">
<RelativeLayout
android:id="@+id/remoteAudioOnlyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@drawable/gradient_audionly"
android:visibility="gone" >
<ImageView
android:id="@+id/avatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp"
android:src="@drawable/avatar" />
</RelativeLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/publisherview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/localAudioOnlyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@drawable/gradient_audionly"
android:visibility="gone" >
<ImageView
android:id="@+id/localAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp"
android:src="@drawable/avatar" />
</RelativeLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/actionbar_preview_fragment_container"
android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_height"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:visibility="visible"></RelativeLayout>
<RelativeLayout
android:id="@+id/camera_preview_fragment_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="21.5dp"
android:layout_marginTop="37.5dp"
android:visibility="visible"></RelativeLayout>
<RelativeLayout
android:id="@+id/actionbar_remote_fragment_container"
android:layout_width="@dimen/action_bar_width"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="21.5dp"
android:layout_marginTop="37.5dp"
android:visibility="visible"></RelativeLayout>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
================================================
FILE: android/OneToOneSample/app/src/main/res/layout/preview_actionbar_fragment.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layoutPreviewActionBar"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/previewFragment"
android:layout_width="fill_parent"
android:layout_height="@dimen/action_bar_height"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true">
<ImageButton
android:id="@+id/localVideo"
android:layout_width="@dimen/icon_width"
android:layout_height="@dimen/icon_height"
android:layout_centerVertical="true"
android:layout_marginRight="14.1dp"
android:layout_toLeftOf="@+id/call"
android:background="@drawable/bckg_icon"
android:src="@drawable/video_icon" />
<ImageButton
android:id="@+id/call"
android:layout_width="@dimen/icon_width"
android:layout_height="@dimen/icon_height"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/initiate_call_button"
android:clickable="true"
android:src="@drawable/start_call" />
<ImageButton
android:id="@+id/localAudio"
android:layout_width="@dimen/icon_width"
android:layout_height="@dimen/icon_height"
android:layout_centerVertical="true"
android:layout_marginLeft="14.1dp"
android:layout_toRightOf="@+id/call"
android:background="@drawable/bckg_icon"
android:src="@drawable/mic_icon" />
</RelativeLayout>
</RelativeLayout>
================================================
FILE: android/OneToOneSample/app/src/main/res/layout/preview_camera_fragment.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layoutPreviewCamera"
android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_height"
android:layout_alignParentTop="true">
<ImageButton
android:id="@+id/camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="@null"
android:src="@drawable/camera" />
</RelativeLayout>
================================================
FILE: android/OneToOneSample/app/src/main/res/layout/remote_actionbar_fragment.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layoutPreviewActionBar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:visibility="gone">
<RelativeLayout
android:id="@+id/remoteFragment"
android:layout_width="@dimen/action_bar_width"
android:layout_height="match_parent"
android:layout_alignParentLeft="true">
<ImageButton
android:id="@+id/remoteVideo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:enabled="false"
android:src="@drawable/video_icon" />
<ImageButton
android:id="@+id/remoteAudio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/remoteVideo"
android:layout_marginTop="25dp"
android:background="@null"
android:enabled="false"
android:src="@drawable/audio" />
</RelativeLayout>
</RelativeLayout>
================================================
FILE: android/OneToOneSample/app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="gradientStart">#5B5B5B</color>
<color name="gradientEnd">#393939</color>
<color name="gradientAudioOnlyStart">#343434</color>
<color name="gradientAudioOnlyEnd">#6e6e6e</color>
<color name="audiOnlyBackground">#7e7e7e</color>
<color name="white">#ffffff</color>
<color name="black">#000000</color>
<color name="startCall">#6aadbf</color>
<color name="endCall">#cb1e28</color>
<color name="quality_alert">#cb1e28</color>
<color name="quality_warning">#fef9c8</color>
<color name="warning_text">#ad7212</color>
</resources>
================================================
FILE: android/OneToOneSample/app/src/main/res/values/dimens.xml
================================================
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="border">1dp</dimen>
<dimen name="action_bar_height">71dp</dimen>
<dimen name="action_bar_width">71dp</dimen>
<dimen name="icon_width">52dp</dimen>
<dimen name="icon_height">52dp</dimen>
<dimen name="alert_bar_height">30dp</dimen>
<dimen name="alert_text">14sp</dimen>
<dimen name="preview_width">90dp</dimen>
<dimen name="preview_height">78dp</dimen>
<dimen name="preview_rightMargin">24dp</dimen>
<dimen name="preview_bottomMargin">75dp</dimen>
</resources>
================================================
FILE: android/OneToOneSample/app/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">OneToOneSample</string>
<string name="network_quality">Network connection is unstable.</string>
<string name="reconnecting">The session is reconnecting</string>
<string name="reconnected">The session reconnected</string>
<string name="permissions_denied_title">Permissions Denied</string>
<string name="permissions_denied">Cannot use this app without requested permission. Please, grant audio and video permissions</string>
<string name="alert_permissions_denied">Without these permissions the app is unable to make call.Are you sure you want to deny these permissions?</string>
</resources>
================================================
FILE: android/OneToOneSample/app/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
================================================
FILE: android/OneToOneSample/app/src/main/res/values-v21/styles.xml
================================================
<resources>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
================================================
FILE: android/OneToOneSample/app/src/main/res/values-w820dp/dimens.xml
================================================
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
================================================
FILE: android/OneToOneSample/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
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
}
}
allprojects {
repositories {
jcenter()
maven {
url "http://tokbox.bintray.com/maven"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: android/OneToOneSample/gradle/wrapper/gradle-wrapper.properties
================================================
#Thu Jan 19 16:41:06 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
================================================
FILE: android/OneToOneSample/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: android/OneToOneSample/gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: android/OneToOneSample/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: android/OneToOneSample/settings.gradle
================================================
include ':app'
================================================
FILE: android/README.md
================================================

# OpenTok One-to-One Communication Sample App for Android
## Quick start
This section shows you how to prepare, build, and run the sample application.
### Install the project files
1. Clone the [OpenTok One-to-One Communication Sample App for Android repository](https://github.com/opentok/one-to-one-sample-apps/tree/master/android) from GitHub.
1. Start Android Studio.
1. In the **Quick Start** panel, click **Open an existing Android Studio Project**.
1. Navigate to the **android** folder, select the **OnetoOneSample** folder, and click **Choose**.
### Add the Accelerator Core Android
There are two options for installing the OpenTok SDK included in the Accelerator Pack Common for Android:
#### Using the repository
1. Clone the [OpenTok Accelerator Core repo](https://github.com/opentok/accelerator-core-android).
2. From your app project, right-click the app name and select **New > Module > Import Gradle Project**.
3. Navigate to the directory in which you cloned **OpenTok Accelerator Pack**, select **accelerator-core**, and click **Finish**.
4. Open the **build.gradle** file for the app and ensure the following lines have been added to the `dependencies` section:
```
compile project(':accelerator-core-android')
```
#### Using Maven
1. Modify the `build.gradle` for your solution and add the following code snippet to the section labeled `repositories`:
```gradle
maven { url "http://tokbox.bintray.com/maven" }
```
1. Modify the `build.gradle` for your activity and add the following code snippet to the section labeled `dependencies`:
```gradle
compile 'com.opentok.android:accelerator-core-android:+'
```
### Configure and build the app
Configure the sample app code. Then, build and run the app.
1. Get values for **API Key**, **Session ID**, and **Token**. See [OpenTok One-to-One Communication Sample App home page](../README.md) for important information.
In Android Studio, open **OpenTokConfig.java** and replace the following empty strings with the corresponding **API Key**, **Session ID**, and **Token** values:
```java
// Replace with a generated Session ID
public static final String SESSION_ID = "";
// Replace with a generated token
public static final String TOKEN = "";
// Replace with your OpenTok API key
public static final String API_KEY = "";
```
```java
//init the wrapper
OTConfig config =
new OTConfig.OTConfigBuilder(OpenTokConfig.SESSION_ID, OpenTokConfig.TOKEN,
OpenTokConfig.API_KEY).name("one-to-one-sample-app").subscribeAutomatically(true).subscribeToSelf(false).build();
if ( config != null ) {
mWrapper = new OTWrapper(MainActivity.this, config);
mWrapper.addBasicListener(mBasicListener);
mWrapper.addAdvancedListener(mAdvancedListener);
//...
}
```
## Exploring the code
This section describes best practices the sample app code uses to implement the one-to-one communication features.
For detail about the APIs used to develop this sample, see the [OpenTok Android SDK Reference](https://tokbox.com/developer/sdks/android/reference/) and [Android API Reference](http://developer.android.com/reference/packages.html).
### Class design
This section focuses on one-to-one communication features. For more information, see the [OpenTok One-to-One Communication Sample App](https://github.com/opentok/one-to-one-sample-apps).
| Class | Description |
| ------------- | ------------- |
| `MainActivity` | Implements the UI and media control callbacks. |
| `OpenTokConfig` | Stores the information required to configure the session and authorize the app to make requests to the backend server. |
| `PreviewControlFragment` | Manages the toolbar for the local audio and video controls, and the start/end call button. |
| `RemoteControlFragment` | Manages the icons to enable/disable the audio and video of the remote subscriber. |
| `PreviewCameraFragment ` | Manages the camera control. |
### Session and stream management
The `OTWrapper` class, included in the Accelerator Core for Android, is the backbone of the one-to-one communication features for the app.
This class uses the OpenTok API to initiate the client connection to the OpenTok session and manage the audio and video streams.
```java
mWrapper.connect();
mWrapper.startPublishingMedia(new PreviewConfig.PreviewConfigBuilder().
name("Tokboxer").build(), false);
mWrapper.enableLocalMedia(MediaType.AUDIO, audio);
mWrapper.disconnect();
```
The BasicListener and AdvancedListener interface monitor state changes in the communication, and defines the following methods:
```java
//Basic Listener from OTWrapper
private BasicListener mBasicListener =
new PausableBasicListener(new BasicListener<OTWrapper>() {
@Override
public void onConnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException { //...}
@Override
public void onDisconnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException { //...}
@Override
public void onPreviewViewReady(OTWrapper otWrapper, View localView) throws ListenerException { //...}
@Override
public void onRemoteViewReady(OTWrapper otWrapper, View remoteView, String remoteId, String data) throws ListenerException { //...}
@Override
public void onStartedPublishingMedia(OTWrapper otWrapper, boolean screensharing) throws ListenerException { //...}
//...
});
```
```java
//Advanced Listener from OTWrapper
private AdvancedListener mAdvancedListener =
new PausableAdvancedListener(new AdvancedListener<OTWrapper>() {
@Override
public void onCameraChanged(OTWrapper otWrapper) throws ListenerException { //... }
@Override
public void onReconnecting(OTWrapper otWrapper) throws ListenerException { //... }
@Override
public void onReconnected(OTWrapper otWrapper) throws ListenerException { //... }
@Override
public void onVideoQualityWarning(OTWrapper otWrapper, String remoteId) throws ListenerException { //... }
//...
});
```
### User interface
As described in [Class design](#class-design), the following classes set up and manage the UI fragments for the local and remote controls:
- `PreviewControlFragment`
- `RemoteControlFragment`
- `PreviewCameraFragment`
These classes work with the following `MainActivity` methods, which manage the views as the publisher and subscriber participate in the session.
## Requirements
To develop your one-to-one communication app:
1. Install [Android Studio](http://developer.android.com/intl/es/sdk/index.html)
1. Review the [OpenTok Android SDK Requirements](https://tokbox.com/developer/sdks/android/#developerandclientrequirements)
================================================
FILE: iOS/.gitignore
================================================
.DS_Store
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
*.xcworkspace
!default.xcworkspace
xcuserdata
profile
*.moved-aside
DerivedData
*.xcdatamodeld
.idea/
# Pods - for those of you who use CocoaPods
Pods
*.framework
*.bundle
Podfile.lock
================================================
FILE: iOS/OneToOneSample.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
70F017EE1E004A7A008EF97D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017E01E004A7A008EF97D /* AppDelegate.m */; };
70F017EF1E004A7A008EF97D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 70F017E11E004A7A008EF97D /* Assets.xcassets */; };
70F017F01E004A7A008EF97D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70F017E21E004A7A008EF97D /* LaunchScreen.storyboard */; };
70F017F11E004A7A008EF97D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70F017E41E004A7A008EF97D /* Main.storyboard */; };
70F017F31E004A7A008EF97D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017E71E004A7A008EF97D /* main.m */; };
70F017F41E004A7A008EF97D /* MainView.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017E91E004A7A008EF97D /* MainView.m */; };
70F017F51E004A7A008EF97D /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017EB1E004A7A008EF97D /* MainViewController.m */; };
70F017F61E004A7A008EF97D /* UIView+Helper.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017ED1E004A7A008EF97D /* UIView+Helper.m */; };
A0A0E85D1E441C6C003F319C /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = A0A0E85C1E441C6C003F319C /* Podfile */; };
A0A0E85F1E441C70003F319C /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = A0A0E85E1E441C70003F319C /* README.md */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
70F017981E00458D008EF97D /* OneToOneSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OneToOneSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
70F017DF1E004A7A008EF97D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
70F017E01E004A7A008EF97D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
70F017E11E004A7A008EF97D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
70F017E31E004A7A008EF97D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
70F017E51E004A7A008EF97D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
70F017E61E004A7A008EF97D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
70F017E71E004A7A008EF97D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
70F017E81E004A7A008EF97D /* MainView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainView.h; sourceTree = "<group>"; };
70F017E91E004A7A008EF97D /* MainView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainView.m; sourceTree = "<group>"; };
70F017EA1E004A7A008EF97D /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = "<group>"; };
70F017EB1E004A7A008EF97D /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = "<group>"; };
70F017EC1E004A7A008EF97D /* UIView+Helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Helper.h"; sourceTree = "<group>"; };
70F017ED1E004A7A008EF97D /* UIView+Helper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Helper.m"; sourceTree = "<group>"; };
A0A0E85C1E441C6C003F319C /* Podfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Podfile; sourceTree = "<group>"; };
A0A0E85E1E441C70003F319C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
70F017951E00458D008EF97D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
70F0178F1E00458D008EF97D = {
isa = PBXGroup;
children = (
A0A0E85E1E441C70003F319C /* README.md */,
A0A0E85C1E441C6C003F319C /* Podfile */,
70F017DE1E004A7A008EF97D /* SampleApp */,
70F017991E00458D008EF97D /* Products */,
);
sourceTree = "<group>";
};
70F017991E00458D008EF97D /* Products */ = {
isa = PBXGroup;
children = (
70F017981E00458D008EF97D /* OneToOneSample.app */,
);
name = Products;
sourceTree = "<group>";
};
70F017DE1E004A7A008EF97D /* SampleApp */ = {
isa = PBXGroup;
children = (
70F017DF1E004A7A008EF97D /* AppDelegate.h */,
70F017E01E004A7A008EF97D /* AppDelegate.m */,
70F017F71E004A88008EF97D /* Resources */,
70F017E71E004A7A008EF97D /* main.m */,
70F017E81E004A7A008EF97D /* MainView.h */,
70F017E91E004A7A008EF97D /* MainView.m */,
70F017EA1E004A7A008EF97D /* MainViewController.h */,
70F017EB1E004A7A008EF97D /* MainViewController.m */,
70F017EC1E004A7A008EF97D /* UIView+Helper.h */,
70F017ED1E004A7A008EF97D /* UIView+Helper.m */,
);
path = SampleApp;
sourceTree = "<group>";
};
70F017F71E004A88008EF97D /* Resources */ = {
isa = PBXGroup;
children = (
70F017E11E004A7A008EF97D /* Assets.xcassets */,
70F017E21E004A7A008EF97D /* LaunchScreen.storyboard */,
70F017E41E004A7A008EF97D /* Main.storyboard */,
70F017E61E004A7A008EF97D /* Info.plist */,
);
name = Resources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
70F017971E00458D008EF97D /* OneToOneSample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 70F017BA1E00458D008EF97D /* Build configuration list for PBXNativeTarget "OneToOneSample" */;
buildPhases = (
70F017941E00458D008EF97D /* Sources */,
70F017951E00458D008EF97D /* Frameworks */,
70F017961E00458D008EF97D /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = OneToOneSample;
productName = OneToOneSample;
productReference = 70F017981E00458D008EF97D /* OneToOneSample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
70F017901E00458D008EF97D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0810;
ORGANIZATIONNAME = "Tokbox, Inc.";
TargetAttributes = {
70F017971E00458D008EF97D = {
CreatedOnToolsVersion = 8.1;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 70F017931E00458D008EF97D /* Build configuration list for PBXProject "OneToOneSample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 70F0178F1E00458D008EF97D;
productRefGroup = 70F017991E00458D008EF97D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
70F017971E00458D008EF97D /* OneToOneSample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
70F017961E00458D008EF97D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
70F017F11E004A7A008EF97D /* Main.storyboard in Resources */,
70F017F01E004A7A008EF97D /* LaunchScreen.storyboard in Resources */,
A0A0E85D1E441C6C003F319C /* Podfile in Resources */,
70F017EF1E004A7A008EF97D /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
70F017941E00458D008EF97D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A0A0E85F1E441C70003F319C /* README.md in Sources */,
70F017EE1E004A7A008EF97D /* AppDelegate.m in Sources */,
70F017F61E004A7A008EF97D /* UIView+Helper.m in Sources */,
70F017F51E004A7A008EF97D /* MainViewController.m in Sources */,
70F017F41E004A7A008EF97D /* MainView.m in Sources */,
70F017F31E004A7A008EF97D /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
70F017E21E004A7A008EF97D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
70F017E31E004A7A008EF97D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
70F017E41E004A7A008EF97D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
70F017E51E004A7A008EF97D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
70F017B81E00458D008EF97D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
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_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
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.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
70F017B91E00458D008EF97D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
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_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
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.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
70F017BB1E00458D008EF97D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = SampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tokbox.OneToOneSample;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
70F017BC1E00458D008EF97D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = SampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tokbox.OneToOneSample;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
70F017931E00458D008EF97D /* Build configuration list for PBXProject "OneToOneSample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
70F017B81E00458D008EF97D /* Debug */,
70F017B91E00458D008EF97D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
70F017BA1E00458D008EF97D /* Build configuration list for PBXNativeTarget "OneToOneSample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
70F017BB1E00458D008EF97D /* Debug */,
70F017BC1E00458D008EF97D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 70F017901E00458D008EF97D /* Project object */;
}
================================================
FILE: iOS/OneToOneSample.xcodeproj/xcshareddata/xcschemes/OneToOneSample.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "70F017971E00458D008EF97D"
BuildableName = "OneToOneSample.app"
BlueprintName = "OneToOneSample"
ReferencedContainer = "container:OneToOneSample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A034743C1CCD15A800198DB4"
BuildableName = "OneToOneSampleTests.xctest"
BlueprintName = "OneToOneSampleTests"
ReferencedContainer = "container:OneToOneSample.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "70F017B01E00458D008EF97D"
BuildableName = "OneToOneSampleUITests.xctest"
BlueprintName = "OneToOneSampleUITests"
ReferencedContainer = "container:OneToOneSample.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "70F017971E00458D008EF97D"
BuildableName = "OneToOneSample.app"
BlueprintName = "OneToOneSample"
ReferencedContainer = "container:OneToOneSample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "70F017971E00458D008EF97D"
BuildableName = "OneToOneSample.app"
BlueprintName = "OneToOneSample"
ReferencedContainer = "container:OneToOneSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "70F017971E00458D008EF97D"
BuildableName = "OneToOneSample.app"
BlueprintName = "OneToOneSample"
ReferencedContainer = "container:OneToOneSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: iOS/Podfile
================================================
platform :ios, '9.0'
target 'OneToOneSample' do
pod 'OTAcceleratorCore', '1.0.3'
pod 'SVProgressHUD'
end
================================================
FILE: iOS/README.md
================================================

# OpenTok One-to-One Communication Sample App for iOS<br/>Version 1.3
## Quick start
This section shows you how to prepare, build, and run the sample application. The app is built by the [Accelerator Core iOS](https://github.com/opentok/accelerator-core-ios).
### Install the project files
Use CocoaPods to install the project files and dependencies.
1. Install CocoaPods as described in [CocoaPods Getting Started](https://guides.cocoapods.org/using/getting-started.html#getting-started).
1. In Terminal, `cd` to your project directory and type `pod install`.
1. Reopen your project in Xcode using the new `*.xcworkspace` file.
### Configure and build the app
Configure the sample app code. Then, build and run the app.
1. Get values for **API Key**, **Session ID**, and **Token**. See [OpenTok One-to-One Communication Sample App home page](../README.md) for important information.
1. Replace the following empty strings with the corresponding **API Key**, **Session ID**, and **Token** values:
```objc
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[OTAcceleratorSession setOpenTokApiKey:@""
sessionId:@""
token:@""];
return YES;
}
```
1. Use Xcode to build and run the app on an iOS simulator or device.
## Exploring the code
For detail about the APIs used to develop this sample, see the [OpenTok iOS SDK Reference](https://tokbox.com/developer/sdks/ios/reference/).
_**NOTE:** This sample app collects anonymous usage data for internal TokBox purposes only. Please do not modify or remove any logging code from this sample application._
### Session and stream management
The `OTOneToOneCommunicator` class is the backbone of the one-to-one communication features for the app. This class conforms to the protocols that initiate the client connection to the OpenTok session and sets up the listeners for the publisher and subscriber streams:
```objc
[self.oneToOneCommunicator connectWithHandler:^(OTOneToOneCommunicationSignal signal, NSError *error) {
if (!error) {
if (signal == OTSessionDidConnect) {
// publisher view is available, now you can add subscriber view to your desired view
}
else if (signal == OTSubscriberDidConnect) {
// subscriber view is available, now you can add subscriber view to your desired view
}
}
else {
}
}];
```
The following enum notifies the main controller of all session, publisher, and subscriber events:
```objc
typedef NS_ENUM(NSUInteger, OTOneToOneCommunicationSignal) {
OTSessionDidConnect = 0,
OTSessionDidDisconnect,
OTSessionDidFail,
OTSessionStreamCreated,
OTSessionStreamDestroyed,
OTSessionDidBeginReconnecting,
OTSessionDidReconnect,
OTPublisherDidFail,
OTPublisherStreamCreated,
OTPublisherStreamDestroyed,
OTSubscriberDidConnect,
OTSubscriberDidFail,
OTSubscriberVideoDisabledByPublisher,
OTSubscriberVideoDisabledBySubscriber,
OTSubscriberVideoDisabledByBadQuality,
OTSubscriberVideoEnabledByPublisher,
OTSubscriberVideoEnabledBySubscriber,
OTSubscriberVideoEnabledByGoodQuality,
OTSubscriberVideoDisableWarning,
OTSubscriberVideoDisableWarningLifted,
};
```
## Requirements
To develop your one-to-one communication app:
1. Install Xcode version 5 or later.
2. Review the [OpenTok iOS SDK Requirements](https://tokbox.com/developer/sdks/ios/).
================================================
FILE: iOS/SampleApp/AppDelegate.h
================================================
//
// AppDelegate.h
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
@class OTAcceleratorSession;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, strong, readonly) OTAcceleratorSession* acceleratorSession;
@end
================================================
FILE: iOS/SampleApp/AppDelegate.m
================================================
//
// AppDelegate.m
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import "AppDelegate.h"
#import "OTAcceleratorSession.h"
@interface AppDelegate ()
@property (nonatomic, strong) OTAcceleratorSession* acceleratorSession;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.acceleratorSession = [[OTAcceleratorSession alloc] initWithOpenTokApiKey:@"<# Replace #>"
sessionId:@"<# Replace #>"
token:@"<# Replace #>"];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
}
@end
================================================
FILE: iOS/SampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-40@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-40@3x-1.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-40@2x-2.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40@2x-3.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-83.5@2x.png",
"scale" : "2x"
},
{
"size" : "24x24",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-Small@2x.png",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-Small@3x.png",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/audio.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "audio.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "audio@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "audio@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/hangUp.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "hangUp.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "hangUp@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "hangUp@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/mic.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "mic.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mic@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "mic@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/mutedMic.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "mutedMicLineCopy.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mutedMicLineCopy@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "mutedMicLineCopy@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/noAudio.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "noSoundCopy.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "noSoundCopy@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "noSoundCopy@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/noVideo.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "noVideoIcon.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "noVideoIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "noVideoIcon@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/reverse cameras.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "reverse cameras.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "reverse cameras@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "reverse cameras@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/startCall.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "startCall.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "startCall@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "startCall@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Assets.xcassets/video.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "videoIcon.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "videoIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "videoIcon@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: iOS/SampleApp/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="nPX-gN-iCp"/>
<viewControllerLayoutGuide type="bottom" id="s0L-bH-Ejt"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="OneToOneSample" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e8h-0e-daP">
<rect key="frame" x="67.5" y="315.5" width="240" height="36"/>
<fontDescription key="fontDescription" type="system" pointSize="30"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="e8h-0e-daP" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="Xte-x5-DI8"/>
<constraint firstItem="e8h-0e-daP" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="aXa-6M-aml"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: iOS/SampleApp/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Main View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController autoresizesArchivedViewToFullSize="NO" id="BYZ-38-t0r" customClass="MainViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="ibB-sB-NO2"/>
<viewControllerLayoutGuide type="bottom" id="g1w-3N-XuV"/>
</layoutGuides>
<view key="view" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="HtV-zK-mFJ" customClass="MainView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zJt-pm-lIX">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
</view>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="EoV-Kh-ae4">
<rect key="frame" x="265" y="471" width="90" height="90"/>
<constraints>
<constraint firstAttribute="width" constant="90" id="2ZZ-pD-0bi"/>
<constraint firstAttribute="width" secondItem="EoV-Kh-ae4" secondAttribute="height" multiplier="1:1" id="HVT-by-pPc"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QaT-qb-Mgd">
<rect key="frame" x="84.5" y="581" width="206" height="66"/>
<subviews>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lI4-Iv-Bhv" userLabel="publisherVideoButton">
<rect key="frame" x="8" y="8" width="50" height="50"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="WHA-xY-mGi"/>
<constraint firstAttribute="width" secondItem="lI4-Iv-Bhv" secondAttribute="height" multiplier="1:1" id="atV-3z-CfA"/>
</constraints>
<state key="normal" image="video"/>
<connections>
<action selector="publisherVideoButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="2mt-7G-8tP"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8fW-9l-wlb" userLabel="publisherCallButton">
<rect key="frame" x="78" y="8" width="50" height="50"/>
<color key="backgroundColor" red="0.41568627450000001" green="0.67843137249999996" blue="0.74901960779999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="7Lw-oa-woS"/>
<constraint firstAttribute="width" secondItem="8fW-9l-wlb" secondAttribute="height" multiplier="1:1" id="DuI-oP-9Bu"/>
</constraints>
<state key="normal" image="startCall"/>
<connections>
<action selector="publisherCallButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="SO2-eY-tdE"/>
</connections>
</button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Bwb-uu-g2a" userLabel="publisherMicButton">
<rect key="frame" x="148" y="8" width="50" height="50"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="ip2-ay-ESy"/>
<constraint firstAttribute="width" secondItem="Bwb-uu-g2a" secondAttribute="height" multiplier="1:1" id="vkb-t2-Kh2"/>
</constraints>
<state key="normal" image="mic"/>
<connections>
<action selector="publisherAudioButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="pxR-Us-IdT"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="8fW-9l-wlb" firstAttribute="centerX" secondItem="QaT-qb-Mgd" secondAttribute="centerX" id="5Lu-9t-BnR"/>
<constraint firstItem="lI4-Iv-Bhv" firstAttribute="top" secondItem="QaT-qb-Mgd" secondAttribute="top" constant="8" id="CvT-GV-vFr"/>
<constraint firstAttribute="trailing" secondItem="Bwb-uu-g2a" secondAttribute="trailing" constant="8" id="JF3-ro-Bkg"/>
<constraint firstItem="lI4-Iv-Bhv" firstAttribute="leading" secondItem="QaT-qb-Mgd" secondAttribute="leading" constant="8" id="JGU-Xi-nK9"/>
<constraint firstItem="Bwb-uu-g2a" firstAttribute="top" secondItem="QaT-qb-Mgd" secondAttribute="top" constant="8" id="NKW-13-1oS"/>
<constraint firstItem="8fW-9l-wlb" firstAttribute="centerY" secondItem="QaT-qb-Mgd" secondAttribute="centerY" id="PHM-ze-gCT"/>
<constraint firstAttribute="bottom" secondItem="lI4-Iv-Bhv" secondAttribute="bottom" constant="8" id="RQX-rI-B3e"/>
<constraint firstItem="Bwb-uu-g2a" firstAttribute="leading" secondItem="8fW-9l-wlb" secondAttribute="trailing" constant="20" id="V8L-hw-qhh"/>
<constraint firstItem="8fW-9l-wlb" firstAttribute="leading" secondItem="lI4-Iv-Bhv" secondAttribute="trailing" constant="20" id="fmd-Pm-bEV"/>
<constraint firstAttribute="bottom" secondItem="Bwb-uu-g2a" secondAttribute="bottom" constant="8" id="m7x-ZQ-Ppg"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SKX-dv-Vnc" userLabel="subscriberAudioButton">
<rect key="frame" x="20" y="60" width="25" height="25"/>
<constraints>
<constraint firstAttribute="width" secondItem="SKX-dv-Vnc" secondAttribute="height" multiplier="1:1" id="qV6-yn-B90"/>
</constraints>
<state key="normal" image="audio"/>
<connections>
<action selector="subscriberAudioButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="COG-Jp-EKl"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TaE-Jx-zZR" userLabel="subscriberVideoButton">
<rect key="frame" x="20" y="105" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" secondItem="TaE-Jx-zZR" secondAttribute="height" multiplier="1:1" id="5UO-AX-cjB"/>
</constraints>
<state key="normal" image="video"/>
<connections>
<action selector="subscriberVideoButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="XWM-IL-u0J"/>
</connections>
</button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xR7-mn-zpN" userLabel="publisherCameraButton">
<rect key="frame" x="325" y="60" width="30" height="21"/>
<constraints>
<constraint firstAttribute="width" secondItem="xR7-mn-zpN" secondAttribute="height" multiplier="55:39" id="E2O-Tz-PqK"/>
<constraint firstAttribute="width" constant="30" id="Xky-0a-JDU"/>
</constraints>
<state key="normal" image="reverse cameras"/>
<connections>
<action selector="publisherCameraButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="rUd-XL-ngb"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="EoV-Kh-ae4" secondAttribute="trailing" constant="20" id="9Fu-eZ-Rs1"/>
<constraint firstItem="TaE-Jx-zZR" firstAttribute="leading" secondItem="HtV-zK-mFJ" secondAttribute="leading" constant="20" id="C2K-eN-JaA"/>
<constraint firstAttribute="trailing" secondItem="zJt-pm-lIX" secondAttribute="trailing" id="Hyy-01-kNd"/>
<constraint firstItem="SKX-dv-Vnc" firstAttribute="top" secondItem="HtV-zK-mFJ" secondAttribute="top" constant="60" id="O4w-5G-XL8"/>
<constraint firstItem="QaT-qb-Mgd" firstAttribute="centerX" secondItem="HtV-zK-mFJ" secondAttribute="centerX" id="Pf6-T6-PHm"/>
<constraint firstItem="QaT-qb-Mgd" firstAttribute="top" secondItem="EoV-Kh-ae4" secondAttribute="bottom" constant="20" id="RxN-kP-cKg"/>
<constraint firstItem="g1w-3N-XuV" firstAttribute="top" secondItem="QaT-qb-Mgd" secondAttribute="bottom" constant="20" id="U8e-tl-f28"/>
<constraint firstItem="zJt-pm-lIX" firstAttribute="top" secondItem="HtV-zK-mFJ" secondAttribute="top" id="VE2-60-yEH"/>
<constraint firstItem="TaE-Jx-zZR" firstAttribute="top" secondItem="SKX-dv-Vnc" secondAttribute="bottom" constant="20" id="VsL-68-FSY"/>
<constraint firstAttribute="bottom" secondItem="zJt-pm-lIX" secondAttribute="bottom" id="aa2-ub-2be"/>
<constraint firstItem="zJt-pm-lIX" firstAttribute="leading" secondItem="HtV-zK-mFJ" secondAttribute="leading" id="bWH-Z0-RpJ"/>
<constraint firstItem="SKX-dv-Vnc" firstAttribute="leading" secondItem="HtV-zK-mFJ" secondAttribute="leading" constant="20" id="p9N-b2-8WU"/>
<constraint firstItem="xR7-mn-zpN" firstAttribute="top" secondItem="HtV-zK-mFJ" secondAttribute="top" constant="60" id="sIb-ZZ-Zsj"/>
<constraint firstAttribute="trailing" secondItem="xR7-mn-zpN" secondAttribute="trailing" constant="20" id="wyq-rV-OBQ"/>
</constraints>
<connections>
<outlet property="callButton" destination="8fW-9l-wlb" id="uSS-1W-0Ln"/>
<outlet property="publisherAudioButton" destination="Bwb-uu-g2a" id="FaO-jl-Nwv"/>
<outlet property="publisherVideoButton" destination="lI4-Iv-Bhv" id="GAY-eu-ff1"/>
<outlet property="publisherView" destination="EoV-Kh-ae4" id="WWF-wf-5KH"/>
<outlet property="reverseCameraButton" destination="xR7-mn-zpN" id="sd4-AJ-Rur"/>
<outlet property="subscriberAudioButton" destination="SKX-dv-Vnc" id="oVI-Jt-h1h"/>
<outlet property="subscriberVideoButton" destination="TaE-Jx-zZR" id="hHd-ZY-atr"/>
<outlet property="subscriberView" destination="zJt-pm-lIX" id="mYB-hs-kpy"/>
</connections>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="565" y="357.99999999999989"/>
</scene>
</scenes>
<resources>
<image name="audio" width="24" height="25"/>
<image name="mic" width="21" height="26"/>
<image name="reverse cameras" width="55" height="39"/>
<image name="startCall" width="15" height="30"/>
<image name="video" width="24" height="14"/>
</resources>
</document>
================================================
FILE: iOS/SampleApp/Info.plist
================================================
<?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>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) uses camera</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) uses microphone</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
================================================
FILE: iOS/SampleApp/MainView.h
================================================
//
// MainView.h
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MainView : UIView
// publisher view
- (void)addPublisherView:(UIView *)publisherView;
- (void)removePublisherView;
- (void)connectCallHolder:(BOOL)connected;
- (void)updatePublisherAudio:(BOOL)connected;
- (void)updatePublisherVideo:(BOOL)connected;
// subscriber view
- (void)addSubscribeView:(UIView *)subscriberView;
- (void)removeSubscriberView;
- (void)updateSubscriberAudioButton:(BOOL)connected;
- (void)updateSubscriberVideoButton:(BOOL)connected;
- (void)showSubscriberControls:(BOOL)shown;
// other controls
- (void)enableControlButtonsForCall:(BOOL)enabled;
- (void)showReverseCameraButton;
- (void)resetAllControl;
@end
================================================
FILE: iOS/SampleApp/MainView.m
================================================
//
// MainView.m
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import "MainView.h"
#import "UIView+Helper.h"
@interface MainView()
@property (weak, nonatomic) IBOutlet UIView *publisherView;
@property (weak, nonatomic) IBOutlet UIView *subscriberView;
// 3 action buttons at the bottom of the view
@property (weak, nonatomic) IBOutlet UIButton *publisherVideoButton;
@property (weak, nonatomic) IBOutlet UIButton *callButton;
@property (weak, nonatomic) IBOutlet UIButton *publisherAudioButton;
@property (weak, nonatomic) IBOutlet UIButton *reverseCameraButton;
@property (weak, nonatomic) IBOutlet UIButton *subscriberVideoButton;
@property (weak, nonatomic) IBOutlet UIButton *subscriberAudioButton;
@end
@implementation MainView
- (void)awakeFromNib {
[super awakeFromNib];
self.publisherView.hidden = YES;
self.publisherView.alpha = 1;
self.publisherView.layer.borderWidth = 1;
self.publisherView.layer.borderColor = [UIColor whiteColor].CGColor;
self.publisherView.layer.backgroundColor = [UIColor grayColor].CGColor;
self.publisherView.layer.cornerRadius = 3;
[self drawBorderOn:self.publisherAudioButton withWhiteBorder:YES];
[self drawBorderOn:self.callButton withWhiteBorder:NO];
[self drawBorderOn:self.publisherVideoButton withWhiteBorder:YES];
[self showSubscriberControls:NO];
}
- (void)drawBorderOn:(UIView *)view
withWhiteBorder:(BOOL)withWhiteBorder {
view.layer.cornerRadius = (view.bounds.size.width / 2);
if (withWhiteBorder) {
view.layer.borderWidth = 1;
view.layer.borderColor = [UIColor whiteColor].CGColor;
}
}
#pragma mark - publisher view
- (void)addPublisherView:(UIView *)publisherView {
[self.publisherView setHidden:NO];
[self.publisherView addSubview:publisherView];
publisherView.translatesAutoresizingMaskIntoConstraints = NO;
[publisherView addAttachedLayoutConstantsToSuperview];
}
- (void)removePublisherView {
[self.publisherView setHidden:YES];
[self.publisherView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
}
- (void)connectCallHolder:(BOOL)connected {
[self.callButton setImage:connected ? [UIImage imageNamed:@"hangUp"] : [UIImage imageNamed:@"startCall"] forState:UIControlStateNormal];
self.callButton.layer.backgroundColor = connected ? [UIColor colorWithRed:(205/255.0) green:(32/255.0) blue:(40/255.0) alpha:1.0].CGColor : [UIColor colorWithRed:(106/255.0) green:(173/255.0) blue:(191/255.0) alpha:1.0].CGColor;
}
- (void)updatePublisherAudio:(BOOL)connected {
[self.publisherAudioButton setImage:connected ? [UIImage imageNamed:@"mic"] : [UIImage imageNamed:@"mutedMic"] forState:UIControlStateNormal];
}
- (void)updatePublisherVideo:(BOOL)connected {
[self.publisherVideoButton setImage:connected ? [UIImage imageNamed:@"video"] : [UIImage imageNamed:@"noVideo"] forState:UIControlStateNormal];
}
#pragma mark - subscriber view
- (void)addSubscribeView:(UIView *)subscriberView {
[self.subscriberView addSubview:subscriberView];
subscriberView.translatesAutoresizingMaskIntoConstraints = NO;
[subscriberView addAttachedLayoutConstantsToSuperview];
}
- (void)removeSubscriberView {
[self.subscriberView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
}
- (void)updateSubscriberAudioButton:(BOOL)connected {
[self.subscriberAudioButton setImage:connected ? [UIImage imageNamed:@"audio"] : [UIImage imageNamed:@"noAudio"] forState:UIControlStateNormal];
}
- (void)updateSubscriberVideoButton:(BOOL)connected {
[self.subscriberVideoButton setImage:connected ? [UIImage imageNamed:@"video"] : [UIImage imageNamed:@"noVideo"] forState:UIControlStateNormal];
}
- (void)showSubscriberControls:(BOOL)shown {
[self.subscriberAudioButton setHidden:!shown];
[self.subscriberVideoButton setHidden:!shown];
}
#pragma mark - other controls
- (void)enableControlButtonsForCall:(BOOL)enabled {
[self.subscriberAudioButton setEnabled:enabled];
[self.subscriberVideoButton setEnabled:enabled];
[self.publisherVideoButton setEnabled:enabled];
[self.publisherAudioButton setEnabled:enabled];
}
- (void)showReverseCameraButton; {
self.reverseCameraButton.hidden = NO;
}
- (void)resetAllControl {
[self removePublisherView];
[self connectCallHolder:NO];
[self updatePublisherAudio:YES];
[self updatePublisherVideo:YES];
[self updateSubscriberAudioButton:YES];
[self updateSubscriberVideoButton:YES];
[self enableControlButtonsForCall:NO];
}
@end
================================================
FILE: iOS/SampleApp/MainViewController.h
================================================
//
// MainViewController.h
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MainViewController : UIViewController
@end
================================================
FILE: iOS/SampleApp/MainViewController.m
================================================
//
// MainViewController.m
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import "MainView.h"
#import "MainViewController.h"
#import "OTOneToOneCommunicator.h"
#import "AppDelegate.h"
#import <SVProgressHUD/SVProgressHUD.h>
#define MAKE_WEAK(self) __weak typeof(self) weak##self = self
#define MAKE_STRONG(self) __strong typeof(weak##self) strong##self = weak##self
@interface MainViewController () <OTOneToOneCommunicatorDataSource>
@property (nonatomic) MainView *mainView;
@property (nonatomic) OTOneToOneCommunicator *oneToOneCommunicator;
@end
@implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mainView = (MainView *)self.view;
self.oneToOneCommunicator = [[OTOneToOneCommunicator alloc] init];
self.oneToOneCommunicator.dataSource = self;
#if !(TARGET_OS_SIMULATOR)
[self.mainView showReverseCameraButton];
#endif
}
- (IBAction)publisherCallButtonPressed:(UIButton *)sender {
if (!self.oneToOneCommunicator.isCallEnabled) {
[SVProgressHUD show];
MAKE_WEAK(self);
[self.oneToOneCommunicator connectWithHandler:^(OTCommunicationSignal signal, NSError *error) {
MAKE_STRONG(self);
strongself.oneToOneCommunicator.publisherView.showAudioVideoControl = NO;
if (!error) {
[strongself handleCommunicationSignal:signal];
}
else {
[SVProgressHUD showErrorWithStatus:error.localizedDescription];
}
}];
}
else {
[SVProgressHUD popActivity];
[self.oneToOneCommunicator disconnect];
[self.mainView resetAllControl];
}
}
- (void)handleCommunicationSignal:(OTCommunicationSignal)signal {
switch (signal) {
case OTPublisherCreated: {
[SVProgressHUD popActivity];
[self.mainView connectCallHolder:self.oneToOneCommunicator.isCallEnabled];
[self.mainView enableControlButtonsForCall:YES];
[self.mainView addPublisherView:self.oneToOneCommunicator.publisherView];
break;
}
case OTPublisherDestroyed: {
[self.mainView removePublisherView];
NSLog(@"Your publishing feed stops streaming in OpenTok");
break;
}
case OTSubscriberCreated: {
[SVProgressHUD show];
}
case OTSubscriberReady: {
[SVProgressHUD popActivity];
[self.mainView addSubscribeView:self.oneToOneCommunicator.subscriberView];
break;
}
case OTSubscriberDestroyed:{
[self.mainView removeSubscriberView];
break;
}
case OTSessionDidBeginReconnecting: {
[SVProgressHUD showInfoWithStatus:@"Reconnecting"];
break;
}
case OTSessionDidReconnect: {
[SVProgressHUD popActivity];
break;
}
case OTSubscriberVideoDisabledByBadQuality:
case OTSubscriberVideoDisabledBySubscriber: {
NSLog(@"The remote has disabled the video");
break;
}
case OTSubscriberVideoDisabledByPublisher:{
self.oneToOneCommunicator.subscribeToVideo = NO;
break;
}
case OTSubscriberVideoEnabledByGoodQuality:
case OTSubscriberVideoEnabledBySubscriber:{
NSLog(@"The remote has enabled the video");
break;
}
case OTSubscriberVideoEnabledByPublisher:{
self.oneToOneCommunicator.subscribeToVideo = YES;
break;
}
case OTSubscriberVideoDisableWarning:{
self.oneToOneCommunicator.subscribeToVideo = NO;
[SVProgressHUD showErrorWithStatus:@"Network connection is unstable."];
break;
}
case OTSubscriberVideoDisableWarningLifted:{
self.oneToOneCommunicator.subscribeToVideo = YES;
break;
}
default: break;
}
}
- (IBAction)publisherAudioButtonPressed:(UIButton *)sender {
self.oneToOneCommunicator.publishAudio = !self.oneToOneCommunicator.publishAudio;
[self.mainView updatePublisherAudio:self.oneToOneCommunicator.publishAudio];
}
- (IBAction)publisherVideoButtonPressed:(UIButton *)sender {
self.oneToOneCommunicator.publishVideo = !self.oneToOneCommunicator.publishVideo;
[self.mainView updatePublisherVideo:self.oneToOneCommunicator.publishVideo];
}
- (IBAction)publisherCameraButtonPressed:(UIButton *)sender {
self.oneToOneCommunicator.cameraPosition = self.oneToOneCommunicator.cameraPosition == AVCaptureDevicePositionBack ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack
}
- (IBAction)subscriberVideoButtonPressed:(UIButton *)sender {
self.oneToOneCommunicator.subscribeToVideo = !self.oneToOneCommunicator.subscribeToVideo;
[self.mainView updateSubscriberVideoButton:self.oneToOneCommunicator.subscribeToVideo];
}
- (IBAction)subscriberAudioButtonPressed:(UIButton *)sender {
self.oneToOneCommunicator.subscribeToAudio = !self.oneToOneCommunicator.subscribeToAudio;
[self.mainView updateSubscriberAudioButton:self.oneToOneCommunicator.subscribeToAudio];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if (self.oneToOneCommunicator.subscriberView){
[self.mainView showSubscriberControls:YES];
}
[self.mainView performSelector:@selector(showSubscriberControls:)
withObject:@(NO)
afterDelay:7.0];
}
- (OTAcceleratorSession *)sessionOfOTOneToOneCommunicator:(OTOneToOneCommunicator *)oneToOneCommunicator {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
return appDelegate.acceleratorSession;
}
@end
================================================
FILE: iOS/SampleApp/UIView+Helper.h
================================================
//
// UIView+Helper.h
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIView (Helper)
- (void)addAttachedLayoutConstantsToSuperview;
@end
================================================
FILE: iOS/SampleApp/UIView+Helper.m
================================================
//
// UIView+Helper.m
//
// Copyright © 2016 Tokbox, Inc. All rights reserved.
//
#import "UIView+Helper.h"
@implementation UIView (Helper)
- (void)addAttachedLayoutConstantsToSuperview {
if (!self.superview) {
return;
}
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.superview
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0];
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.superview
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[NSLayoutConstraint activateConstraints:@[top, leading, trailing, bottom]];
}
@end
================================================
FILE: iOS/SampleApp/main.m
================================================
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
================================================
FILE: js/.eslintrc.json
================================================
{
"extends": "airbnb",
"env": {
"browser": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 5
},
"plugins": [
"react"
],
"rules": {
"no-underscore-dangle": 0,
"vars-on-top": 0,
"padded-blocks": 0,
"no-var": 0,
"comma-dangle": 0,
"func-names": 0,
"prefer-arrow-callback": 0,
"object-shorthand": 0,
"no-unused-expressions": [2, {
"allowTernary": true,
"allowShortCircuit": true
}],
"global-require": 0
},
"globals": {
"$": true,
"_": true
}
}
================================================
FILE: js/.gitignore
================================================
node_modules
================================================
FILE: js/.jsbeautifyrc
================================================
{
"js":{
"indent_size": 2,
"space_after_anon_function": true,
"end_with_newline": true,
"brace_style": "collaps-preserve-inline"
}
}
================================================
FILE: js/Procfile
================================================
web: node server.js
================================================
FILE: js/README.md
================================================

# OpenTok One-to-One Communication Sample App for JavaScript<br/>Version 1.3
## Quick start
This section shows you how to prepare and run the sample application. The app is built by the [Accelerator Core JS](https://github.com/opentok/accelerator-core-js).
### Configuring the app
Configure the sample app code. Then, build and run the app.
1. Get values for **API Key**, **Session ID**, and **Token**. See [OpenTok One-to-One Communication Sample App home page](../README.md) for important information.
2. In **app.js**, replace the following empty strings with the corresponding **API Key**, **Session ID**, and **Token** values:
```javascript
apiKey: '', // Replace with your OpenTok API Key
sessionId: '', // Replace with a generated Session ID
token: '', // Replace with a generated token (from the dashboard or using an OpenTok server SDK)
```
### Deploying and running the app
```javascript
$ npm install
$ npm run build
$ node server.js
```
The web page that loads the sample app for JavaScript must be served over HTTP/HTTPS. Browser security limitations prevent you from publishing video using a `file://` path, as discussed in the OpenTok.js [Release Notes](https://www.tokbox.com/developer/sdks/js/release-notes.html#knownIssues). To support clients running [Chrome 47 or later](https://groups.google.com/forum/#!topic/discuss-webrtc/sq5CVmY69sc), HTTPS is required. A [Node](https://nodejs.org/en/) server will work, as will [MAMP](https://www.mamp.info/) or [XAMPP](https://www.apachefriends.org/index.html). You can also use a cloud service such as [Heroku](https://www.heroku.com/) to host the application.
## Exploring the code
For details about how to use the Accelerator Core in the sample app, see [here](https://github.com/opentok/accelerator-core-js#sample-applications).
================================================
FILE: js/package.json
================================================
{
"name": "one-to-one-sample",
"version": "1.0.4",
"description": "One to One sample app",
"main": "server.js",
"repository": {
"type": "git",
"url": "https://github.com/opentok/one-to-one-sample-apps"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "cp ./node_modules/opentok-accelerator-core/browser/opentok-acc-core.js ./public/js/components/opentok-acc-core.js",
"start": "node server.js"
},
"author": "adrice727@gmail.com",
"license": "MIT",
"dependencies": {
"express": "^4.14.1",
"body-parser": "^1.16.0",
"opentok-accelerator-core": "*"
}
}
================================================
FILE: js/public/css/style.css
================================================
html,
body {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: sans-serif;
}
.clickable {
cursor: pointer;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.App-header {
background-color: #222;
height: 40px;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
}
.App-header h1 {
font-size: 16px;
font-weight: 200;
}
.App-logo {
height: 60%;
width: auto;
}
.App-main {
position: relative;
width: 75vw;
height: calc(75vw * .6);
margin: 10px auto;
border: 1px solid lightblue;
}
.App-control-container {
position: absolute;
height: 100%;
width: 60px;
left: -60px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.App-control-container.hidden {
display: none;
}
.App-control-container .ots-video-control {
width: 50px;
height: 50px;
margin: 20px 0 !important;
border: 2px solid white;
border-radius: 50%;
background-position: center;
background-color: rgba(27, 134, 144, 0.4);
background-color: lightgrey;
background-repeat: no-repeat;
cursor: pointer;
}
.App-control-container .ots-video-control.audio {
background-image: url(https://assets.tokbox.com/solutions/images/icon-mic.png);
}
.App-control-container .ots-video-control.audio:hover, .App-control-container .ots-video-control.audio.muted {
background-image: url(https://assets.tokbox.com/solutions/images/icon-muted-mic.png);
}
.App-control-container .ots-video-control.video {
background-image: url(https://assets.tokbox.com/solutions/images/icon-video.png);
}
.App-control-container .ots-video-control.video.muted {
background-image: url(https://assets.tokbox.com/solutions/images/icon-no-video.png);
}
.App-control-container .ots-video-control.end-call {
background-image: url(https://assets.tokbox.com/solutions/images/icon-hang-up.png);
background-color: red;
}
.App-video-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.App-mask {
width: 100%;
height: 100%;
position: relative;
color: white;
background: rgba(27, 134, 144, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
.App-mask.hidden {
display: none;
}
.App-mask .react-spinner {
position: absolute;
}
.App-mask .message {
font-weight: 200;
}
.App-mask .message.with-spinner {
position: absolute;
top: 57.5%;
}
.App-mask .message.button {
border: 1px solid white;
padding: 20px 40px;
border-radius: 6px;
}
.App-video-container .video-container {
width: 100%;
height: 100%;
display: flex;
}
.App-video-container .video-container.small {
position: absolute;
top: 20px;
right: 20px;
width: 160px;
height: 96px;
border: 1px solid #fcba00;
z-index: 2;
}
.App-video-container .video-container.small.left {
left: 20px;
border: 1px solid #00fcc2;
}
.App-video-container .video-container.hidden {
display: none;
}
.App-video-container .video-container.active-2 .OT_subscriber {
width: 50%;
}
.App-video-container .video-container.active-3 .OT_subscriber {
width: calc(100%/3) !important;
}
.App-video-container .video-container.active-4 {
flex-wrap: wrap;
}
.App-video-container .video-container.active-4 .OT_subscriber {
width: 50% !important;
height: 50% !important;
}
progress-spinner {
display: inline-block;
width: 1em;
height: 1em;
border: 1px solid transparent;
border-top-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
-webkit-animation: rotate 800ms linear infinite;
animation: rotate 800ms linear infinite;
}
progress-spinner[dark] {
border-top-color: rgba(255, 255, 255, 0.6);
}
progress-spinner[dotted] {
border-width: 0;
border-style: dotted;
border-top-width: 2px;
}
@-webkit-keyframes rotate {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes rotate {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
================================================
FILE: js/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OT Accelerator Core</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://assets.tokbox.com/solutions/css/style.css">
<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script src="js/components/opentok-acc-core.js"></script>
<script src="js/app.js"></script>
</head>
<body>
<div class="App">
<div class="App-header">
<img src="./images/logo.svg" class="App-logo" alt="logo" />
<h1>OpenTok Accelerator Core</h1>
</div>
<div class="App-main">
<div id="controls" class='App-control-container hidden'>
<div class="ots-video-control circle audio" id="toggleLocalAudio"></div>
<div class="ots-video-control circle video" id="toggleLocalVideo"></div>
<div class="ots-video-control circle end-call" id="toggleEndCall"></div>
</div>
<div class="App-video-container" id="appVideoContainer">
<div class="App-mask" id="connecting-mask">
<progress-spinner dark style="font-size:50px"></progress-spinner>
<div class="message with-spinner">Connecting</div>
</div>
<div class="App-mask hidden" id="start-mask">
<div class="message button clickable" id="start">Click to Start Call</div>
</div>
<div id="cameraPublisherContainer" class="video-container hidden"></div>
<div id="screenPublisherContainer" class="video-container hidden"></div>
<div id="cameraSubscriberContainer" class="video-container-hidden"></div>
</div>
</div>
</div>
</body>
</html>
================================================
FILE: js/public/js/app.js
================================================
/* global otCore */
const options = {
credentials: {
apiKey: "", //Replace with your OpenTok API key
sessionId: "", //Replace with a generated Session ID
token: "", //Replace with a generated token (from the dashboard or using an OpenTok server SDK)
},
// A container can either be a query selector or an HTMLElement
streamContainers: function streamContainers(pubSub, type, data) {
return {
publisher: {
camera: '#cameraPublisherContainer',
},
subscriber: {
camera: '#cameraSubscriberContainer',
},
}[pubSub][type];
},
controlsContainer: '#controls',
packages: [],
communication: {
callProperites: null, // Using default
}
};
/** Application Logic */
const app = () => {
const state = {
connected: false,
active: false,
publishers: null,
subscribers: null,
meta: null,
localAudioEnabled: true,
localVideoEnabled: true,
};
/**
* Update the size and position of video containers based on the number of
* publishers and subscribers specified in the meta property returned by otCore.
*/
const updateVideoContainers = () => {
const { meta } = state;
const activeCameraSubscribers = meta ? meta.subscriber.camera : 0;
const videoContainerClass = `App-video-container ${''}`;
document.getElementById('appVideoContainer').setAttribute('class', videoContainerClass);
const cameraPublisherClass =
`video-container ${!!activeCameraSubscribers? 'small' : ''} ${!!activeCameraSubscribers? 'small' : ''}`;
document.getElementById('cameraPublisherContainer').setAttribute('class', cameraPublisherClass);
const cameraSubscriberClass =
`video-container ${!activeCameraSubscribers ? 'hidden' : ''} active-${activeCameraSubscribers}`;
document.getElementById('cameraSubscriberContainer').setAttribute('class', cameraSubscriberClass);
};
/**
* Update the UI
* @param {String} update - 'connected', 'active', or 'meta'
*/
const updateUI = (update) => {
const { connected, active } = state;
switch (update) {
case 'connected':
if (connected) {
document.getElementById('connecting-mask').classList.add('hidden');
document.getElementById('start-mask').classList.remove('hidden');
}
break;
case 'active':
if (active) {
document.getElementById('cameraPublisherContainer').classList.remove('hidden');
document.getElementById('start-mask').classList.add('hidden');
document.getElementById('controls').classList.remove('hidden');
}
else {
document.getElementById('start-mask').classList.remove('hidden');
document.getElementById('controls').classList.add('hidden');
document.getElementById('cameraPublisherContainer').classList.add('hidden');
document.getElementById('toggleLocalVideo').classList.remove('muted');
document.getElementById('toggleLocalAudio').classList.remove('muted');
}
break;
case 'meta':
updateVideoContainers();
break;
default:
console.log('nothing to do, nowhere to go');
}
};
/**
* Update the state and UI
*/
const updateState = (updates) => {
Object.assign(state, updates);
Object.keys(updates).forEach(update => updateUI(update));
};
/**
* Start publishing video/audio and subscribe to streams
*/
const startCall = () => {
otCore.startCall()
.then(({ publishers, subscribers, meta }) => {
updateState({ publishers, subscribers, meta, active: true });
}).catch(error => console.log(error));
};
/**
* Toggle publishing local audio
*/
const toggleLocalAudio = () => {
const enabled = state.localAudioEnabled;
otCore.toggleLocalAudio(!enabled);
updateState({ localAudioEnabled: !enabled });
const action = enabled ? 'add' : 'remove';
document.getElementById('toggleLocalAudio').classList[action]('muted');
};
/**
* Toggle publishing local video
*/
const toggleLocalVideo = () => {
const enabled = state.localVideoEnabled;
otCore.toggleLocalVideo(!enabled);
updateState({ localVideoEnabled: !enabled });
const action = enabled ? 'add' : 'remove';
document.getElementById('toggleLocalVideo').classList[action]('muted');
};
/**
* Toggle end call
*/
const toggleEndCall = () => {
updateState({ active: false });
otCore.endCall();
};
/**
* Subscribe to otCore and UI events
*/
const createEventListeners = () => {
const events = [
'subscribeToCamera',
'unsubscribeFromCamera',
];
events.forEach(event => otCore.on(event, ({ publishers, subscribers, meta }) => {
updateState({ publishers, subscribers, meta });
}));
document.getElementById('start').addEventListener('click', startCall);
document.getElementById('toggleLocalAudio').addEventListener('click', toggleLocalAudio);
document.getElementById('toggleLocalVideo').addEventListener('click', toggleLocalVideo);
document.getElementById('toggleEndCall').addEventListener('click', toggleEndCall);
};
/**
* Initialize otCore, connect to the session, and listen to events
*/
const init = () => {
otCore.init(options);
otCore.connect().then(() => updateState({ connected: true }));
createEventListeners();
};
init();
};
document.addEventListener('DOMContentLoaded', app);
================================================
FILE: js/public/js/components/opentok-acc-core.js
================================================
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
function _classCallCheck(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}var _this=this,_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},_createClass=function(){function n(n,e){for(var t=0;t<e.length;t++){var o=e[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(n,o.key,o)}}return function(e,t,o){return t&&n(e.prototype,t),o&&n(e,o),e}}();!function(){var n=function(n,e,t){var o="",r=void 0;t&&(r=new Date,r.setTime(r.getTime()+24*t*60*60*1e3),o=["; expires=",r.toGMTString()].join(""));var i=[n,"=",e,o,"; path=/"].join("");return document.cookie=i,e},e=function(n){for(var e=n+"=",t=document.cookie.split(";"),o=void 0,r=0;r<t.length;r++){for(o=t[r];" "===o.charAt(0);)o=o.substring(1,o.length);if(0===o.indexOf(e))return o.substring(e.length,o.length)}return null},t=function(){for(var n=[],e="0123456789abcdef",t=0;t<36;t++)n.push(e.substr(Math.floor(16*Math.random()),1));return n[14]="4",n[19]=e.substr(3&n[19]|8,1),n[8]=n[13]=n[18]=n[23]="-",n.join("")},o=function(o){return e(o)||n(o,t(),7)},r=function(n){if(!n.clientVersion)throw console.log("Error. The clientVersion field cannot be null in the log entry"),new Error("The clientVersion field cannot be null in the log entry");if(!n.source)throw console.log("Error. The source field cannot be null in the log entry"),new Error("The source field cannot be null in the log entry");if(!n.componentId)throw console.log("Error. The componentId field cannot be null in the log entry"),new Error("The componentId field cannot be null in the log entry");if(!n.name)throw console.log("Error. The name field cannot be null in the log entry"),new Error("The guid field cannot be null in the log entry");var e=n.logVersion||"2",t=n.clientSystemTime||(new Date).getTime();return Object.assign({},n,{logVersion:e,clientSystemTime:t})},i=function(n){var e=r(n),t="https://hlg.tokbox.com/prod/logging/ClientEvent",o=new XMLHttpRequest;o.open("POST",t,!0),o.setRequestHeader("Content-type","application/json"),o.send(JSON.stringify(e))},l=function(){function n(e){_classCallCheck(this,n),this.analyticsData=e,this.analyticsData.guid=o(e.name)}return _createClass(n,[{key:"addSessionInfo",value:function(n){if(!n.sessionId)throw console.log("Error. The sessionId field cannot be null in the log entry"),new Error("The sessionId field cannot be null in the log entry");if(this.analyticsData.sessionId=n.sessionId,!n.connectionId)throw console.log("Error. The connectionId field cannot be null in the log entry"),new Error("The connectionId field cannot be null in the log entry");if(this.analyticsData.connectionId=n.connectionId,0===n.partnerId)throw console.log("Error. The partnerId field cannot be null in the log entry"),new Error("The partnerId field cannot be null in the log entry");this.analyticsData.partnerId=n.partnerId}},{key:"logEvent",value:function(n){this.analyticsData.action=n.action,this.analyticsData.variation=n.variation,this.analyticsData.clientSystemTime=(new Date).getTime(),i(this.analyticsData)}}]),n}();"object"===("undefined"==typeof exports?"undefined":_typeof(exports))?module.exports=l:"function"==typeof define&&define.amd?define(function(){return l}):_this.OTKAnalytics=l}(this);
},{}],2:[function(require,module,exports){
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
/* global OT */
/** Dependencies */
var state = require('./state');
var _require = require('./errors'),
CoreError = _require.CoreError;
var _require2 = require('./util'),
dom = _require2.dom,
path = _require2.path,
pathOr = _require2.pathOr,
properCase = _require2.properCase;
var _require3 = require('./logging'),
message = _require3.message,
logAnalytics = _require3.logAnalytics,
logAction = _require3.logAction,
logVariation = _require3.logVariation;
/** Module variables */
var session = void 0;
var accPack = void 0;
var callProperties = void 0;
var screenProperties = void 0;
var streamContainers = void 0;
var autoSubscribe = void 0;
var connectionLimit = void 0;
var active = false;
/**
* Default UI propties
* https://tokbox.com/developer/guides/customize-ui/js/
*/
var defaultCallProperties = {
insertMode: 'append',
width: '100%',
height: '100%',
showControls: false,
style: {
buttonDisplayMode: 'off'
}
};
/**
* Trigger an event through the API layer
* @param {String} event - The name of the event
* @param {*} [data]
*/
var triggerEvent = function triggerEvent(event, data) {
return accPack.triggerEvent(event, data);
};
/**
* Determine whether or not the party is able to join the call based on
* the specified connection limit, if any.
* @return {Boolean}
*/
var ableToJoin = function ableToJoin() {
if (!connectionLimit) {
return true;
}
// Not using the session here since we're concerned with number of active publishers
var connections = Object.values(state.getStreams()).filter(function (s) {
return s.videoType === 'camera';
});
return connections.length < connectionLimit;
};
/**
* Create a camera publisher object
* @param {Object} publisherProperties
* @returns {Promise} <resolve: Object, reject: Error>
*/
var createPublisher = function createPublisher(publisherProperties) {
return new Promise(function (resolve, reject) {
// TODO: Handle adding 'name' option to props
var props = Object.assign({}, callProperties, publisherProperties);
// TODO: Figure out how to handle common vs package-specific options
// ^^^ This may already be available through package options
var container = dom.element(streamContainers('publisher', 'camera'));
var publisher = OT.initPublisher(container, props, function (error) {
error ? reject(error) : resolve(publisher);
});
});
};
/**
* Publish the local camera stream and update state
* @param {Object} publisherProperties
* @returns {Promise} <resolve: empty, reject: Error>
*/
var publish = function publish(publisherProperties) {
return new Promise(function (resolve, reject) {
var onPublish = function onPublish(publisher) {
return function (error) {
if (error) {
reject(error);
logAnalytics(logAction.startCall, logVariation.fail);
} else {
logAnalytics(logAction.startCall, logVariation.success);
state.addPublisher('camera', publisher);
resolve(publisher);
}
};
};
var publishToSession = function publishToSession(publisher) {
return session.publish(publisher, onPublish(publisher));
};
var handleError = function handleError(error) {
logAnalytics(logAction.startCall, logVariation.fail);
var errorMessage = error.code === 1010 ? 'Check your network connection' : error.message;
triggerEvent('error', errorMessage);
reject(error);
};
createPublisher(publisherProperties).then(publishToSession).catch(handleError);
});
};
/**
* Subscribe to a stream and update the state
* @param {Object} stream - An OpenTok stream object
* @returns {Promise} <resolve: empty reject: Error >
*/
var subscribe = function subscribe(stream) {
return new Promise(function (resolve, reject) {
logAnalytics(logAction.subscribe, logVariation.attempt);
var streamMap = state.getStreamMap();
if (streamMap[stream.id]) {
// Are we already subscribing to the stream?
resolve();
} else {
(function () {
// No videoType indicates SIP https://tokbox.com/developer/guides/sip/
var type = pathOr('sip', 'videoType', stream);
var connectionData = JSON.parse(path(['connection', 'data'], stream) || null);
var container = dom.query(streamContainers('subscriber', type, connectionData));
var options = type === 'camera' ? callProperties : screenProperties;
var subscriber = session.subscribe(stream, container, options, function (error) {
if (error) {
logAnalytics(logAction.subscribe, logVariation.fail);
reject(error);
} else {
state.addSubscriber(subscriber);
triggerEvent('subscribeTo' + properCase(type), Object.assign({}, { subscriber: subscriber }, state.all()));
type === 'screen' && triggerEvent('startViewingSharedScreen', subscriber); // Legacy event
logAnalytics(logAction.subscribe, logVariation.success);
resolve();
}
});
})();
}
});
};
/**
* Unsubscribe from a stream and update the state
* @param {Object} subscriber - An OpenTok subscriber object
* @returns {Promise} <resolve: empty>
*/
var unsubscribe = function unsubscribe(subscriber) {
return new Promise(function (resolve) {
logAnalytics(logAction.unsubscribe, logVariation.attempt);
var type = path('stream.videoType', subscriber);
state.removeSubscriber(type, subscriber);
session.unsubscribe(subscriber);
logAnalytics(logAction.unsubscribe, logVariation.success);
resolve();
});
};
/**
* Ensure all required options are received
* @param {Object} options
*/
var validateOptions = function validateOptions(options) {
var requiredOptions = ['accPack'];
requiredOptions.forEach(function (option) {
if (!options[option]) {
throw new CoreError(option + ' is a required option.', 'invalidParameters');
}
});
accPack = options.accPack;
streamContainers = options.streamContainers;
callProperties = options.callProperties || defaultCallProperties;
connectionLimit = options.connectionLimit || null;
autoSubscribe = options.hasOwnProperty('autoSubscribe') ? options.autoSubscribe : true;
screenProperties = options.screenProperties || Object.assign({}, defaultCallProperties, { videoSource: 'window' });
};
/**
* Set session in module scope
*/
var setSession = function setSession() {
session = state.getSession();
};
/**
* Subscribe to new stream unless autoSubscribe is set to false
* @param {Object} stream
*/
var onStreamCreated = function onStreamCreated(_ref) {
var stream = _ref.stream;
return active && autoSubscribe && subscribe(stream);
};
/**
* Update state and trigger corresponding event(s) when stream is destroyed
* @param {Object} stream
*/
var onStreamDestroyed = function onStreamDestroyed(_ref2) {
var stream = _ref2.stream;
state.removeStream(stream);
var type = pathOr('sip', 'videoType', stream);
type === 'screen' && triggerEvent('endViewingSharedScreen'); // Legacy event
triggerEvent('unsubscribeFrom' + properCase(type), state.getPubSub());
};
/**
* Listen for API-level events
*/
var createEventListeners = function createEventListeners() {
accPack.on('streamCreated', onStreamCreated);
accPack.on('streamDestroyed', onStreamDestroyed);
};
/**
* Start publishing the local camera feed and subscribing to streams in the session
* @param {Object} publisherProperties
* @returns {Promise} <resolve: Object, reject: Error>
*/
var startCall = function startCall(publisherProperties) {
return new Promise(function (resolve, reject) {
// eslint-disable-line consistent-return
logAnalytics(logAction.startCall, logVariation.attempt);
/**
* Determine if we're able to join the session based on an existing connection limit
*/
if (!ableToJoin()) {
var errorMessage = 'Session has reached its connection limit';
triggerEvent('error', errorMessage);
logAnalytics(logAction.startCall, logVariation.fail);
return reject(new CoreError(errorMessage, 'connectionLimit'));
}
/**
* Subscribe to any streams that existed before we start the call from our side.
*/
var subscribeToInitialStreams = function subscribeToInitialStreams(publisher) {
// Get an array of initial subscription promises
var initialSubscriptions = function initialSubscriptions() {
if (autoSubscribe) {
var _ret2 = function () {
var streams = state.getStreams();
return {
v: Object.keys(streams).map(function (id) {
return subscribe(streams[id]);
})
};
}();
if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === "object") return _ret2.v;
}
return [Promise.resolve()];
};
// Handle success
var onSubscribeToAll = function onSubscribeToAll() {
var pubSubData = Object.assign({}, state.getPubSub(), { publisher: publisher });
triggerEvent('startCall', pubSubData);
active = true;
resolve(pubSubData);
};
// Handle error
var onError = function onError(reason) {
message('Failed to subscribe to all existing streams: ' + reason);
// We do not reject here in case we still successfully publish to the session
resolve(Object.assign({}, state.getPubSub(), { publisher: publisher }));
};
Promise.all(initialSubscriptions()).then(onSubscribeToAll).catch(onError);
};
publish(publisherProperties).then(subscribeToInitialStreams).catch(reject);
});
};
/**
* Stop publishing and unsubscribe from all streams
*/
var endCall = function endCall() {
logAnalytics(logAction.endCall, logVariation.attempt);
var _state$getPubSub = state.getPubSub(),
publishers = _state$getPubSub.publishers,
subscribers = _state$getPubSub.subscribers;
var unpublish = function unpublish(publisher) {
return session.unpublish(publisher);
};
Object.values(publishers.camera).forEach(unpublish);
Object.values(publishers.screen).forEach(unpublish);
// TODO Promise.all for unsubsribing
Object.values(subscribers.camera).forEach(unsubscribe);
Object.values(subscribers.screen).forEach(unsubscribe);
state.removeAllPublishers();
active = false;
triggerEvent('endCall');
logAnalytics(logAction.endCall, logVariation.success);
};
/**
* Enable/disable local audio or video
* @param {String} source - 'audio' or 'video'
* @param {Boolean} enable
*/
var enableLocalAV = function enableLocalAV(id, source, enable) {
var method = 'publish' + properCase(source);
var _state$getPubSub2 = state.getPubSub(),
publishers = _state$getPubSub2.publishers;
publishers.camera[id][method](enable);
};
/**
* Enable/disable remote audio or video
* @param {String} subscriberId
* @param {String} source - 'audio' or 'video'
* @param {Boolean} enable
*/
var enableRemoteAV = function enableRemoteAV(subscriberId, source, enable) {
var method = 'subscribeTo' + properCase(source);
var _state$getPubSub3 = state.getPubSub(),
subscribers = _state$getPubSub3.subscribers;
subscribers.camera[subscriberId][method](enable);
};
/**
* Initialize the communication component
* @param {Object} options
* @param {Object} options.accPack
* @param {Number} options.connectionLimit
* @param {Function} options.streamContainer
*/
var init = function init(options) {
return new Promise(function (resolve) {
validateOptions(options);
setSession();
createEventListeners();
resolve();
});
};
/** Exports */
module.exports = {
init: init,
startCall: startCall,
endCall: endCall,
subscribe: subscribe,
unsubscribe: unsubscribe,
enableLocalAV: enableLocalAV,
enableRemoteAV: enableRemoteAV
};
},{"./errors":4,"./logging":6,"./state":10,"./util":11}],3:[function(require,module,exports){
(function (global){
'use strict';
var _arguments = arguments;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
/* global OT */
/**
* Dependencies
*/
var util = require('./util');
var internalState = require('./state');
var accPackEvents = require('./events');
var communication = require('./communication');
var OpenTokSDK = require('./sdk-wrapper/sdkWrapper');
var _require = require('./errors'),
CoreError = _require.CoreError;
var _require2 = require('./logging'),
message = _require2.message,
initLogAnalytics = _require2.initLogAnalytics,
logAnalytics = _require2.logAnalytics,
logAction = _require2.logAction,
logVariation = _require2.logVariation,
updateLogAnalytics = _require2.updateLogAnalytics;
/**
* Helper methods
*/
var dom = util.dom,
path = util.path,
pathOr = util.pathOr,
properCase = util.properCase;
/**
* Individual Accelerator Packs
*/
var textChat = void 0; // eslint-disable-line no-unused-vars
var screenSharing = void 0; // eslint-disable-line no-unused-vars
var annotation = void 0;
var archiving = void 0; // eslint-disable-line no-unused-vars
/**
* Get access to an accelerator pack
* @param {String} packageName - textChat, screenSharing, annotation, or archiving
* @returns {Object} The instance of the accelerator pack
*/
var getAccPack = function getAccPack(packageName) {
logAnalytics(logAction.getAccPack, logVariation.attempt);
var packages = {
textChat: textChat,
screenSharing: screenSharing,
annotation: annotation,
archiving: archiving
};
logAnalytics(logAction.getAccPack, logVariation.success);
return packages[packageName];
};
/** Eventing */
var eventListeners = {};
/**
* Register events that can be listened to be other components/modules
* @param {array | string} events - A list of event names. A single event may
* also be passed as a string.
*/
var registerEvents = function registerEvents(events) {
var eventList = Array.isArray(events) ? events : [events];
eventList.forEach(function (event) {
if (!eventListeners[event]) {
eventListeners[event] = new Set();
}
});
};
/**
* Register a callback for a specific event or pass an object with
* with event => callback key/value pairs to register listeners for
* multiple events.
* @param {String | Object} event - The name of the event
* @param {Function} callback
*/
var on = function on(event, callback) {
// logAnalytics(logAction.on, logVariation.attempt);
if ((typeof event === 'undefined' ? 'undefined' : _typeof(event)) === 'object') {
Object.keys(event).forEach(function (eventName) {
on(eventName, event[eventName]);
});
return;
}
var eventCallbacks = eventListeners[event];
if (!eventCallbacks) {
message(event + ' is not a registered event.');
// logAnalytics(logAction.on, logVariation.fail);
} else {
eventCallbacks.add(callback);
// logAnalytics(logAction.on, logVariation.success);
}
};
/**
* Remove a callback for a specific event. If no parameters are passed,
* all event listeners will be removed.
* @param {String} event - The name of the event
* @param {Function} callback
*/
var off = function off(event, callback) {
// logAnalytics(logAction.off, logVariation.attempt);
if (_arguments.lenth === 0) {
Object.keys(eventListeners).forEach(function (eventType) {
eventListeners[eventType].clear();
});
}
var eventCallbacks = eventListeners[event];
if (!eventCallbacks) {
// logAnalytics(logAction.off, logVariation.fail);
message(event + ' is not a registered event.');
} else {
eventCallbacks.delete(callback);
// logAnalytics(logAction.off, logVariation.success);
}
};
/**
* Trigger an event and fire all registered callbacks
* @param {String} event - The name of the event
* @param {*} data - Data to be passed to callback functions
*/
var triggerEvent = function triggerEvent(event, data) {
var eventCallbacks = eventListeners[event];
if (!eventCallbacks) {
registerEvents(event);
message(event + ' has been registered as a new event.');
} else {
eventCallbacks.forEach(function (callback) {
return callback(data, event);
});
}
};
/**
* Get the current OpenTok session object
* @returns {Object}
*/
var getSession = internalState.getSession;
/**
* Returns the current OpenTok session credentials
* @returns {Object}
*/
var getCredentials = internalState.getCredentials;
/**
* Returns the options used for initialization
* @returns {Object}
*/
var getOptions = internalState.getOptions;
var createEventListeners = function createEventListeners(session, options) {
Object.keys(accPackEvents).forEach(function (type) {
return registerEvents(accPackEvents[type]);
});
/**
* If using screen sharing + annotation in an external window, the screen sharing
* package will take care of calling annotation.start() and annotation.linkCanvas()
*/
var usingAnnotation = path('screenSharing.annotation', options);
var internalAnnotation = usingAnnotation && !path('screenSharing.externalWindow', options);
/**
* Wrap session events and update internalState when streams are created
* or destroyed
*/
accPackEvents.session.forEach(function (eventName) {
session.on(eventName, function (event) {
if (eventName === 'streamCreated') {
internalState.addStream(event.stream);
}
if (eventName === 'streamDestroyed') {
internalState.removeStream(event.stream);
}
triggerEvent(eventName, event);
});
});
if (usingAnnotation) {
on('subscribeToScreen', function (_ref) {
var subscriber = _ref.subscriber;
annotation.start(getSession()).then(funct
gitextract_smhvk9sm/
├── .github/
│ ├── CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE.md
│ └── PULL_REQUEST_TEMPLATE.md
├── LICENSE
├── README.md
├── android/
│ ├── .gitignore
│ ├── OneToOneSample/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── tokbox/
│ │ │ │ └── android/
│ │ │ │ └── onetoonesample/
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── config/
│ │ │ │ │ └── OpenTokConfig.java
│ │ │ │ └── ui/
│ │ │ │ ├── PreviewCameraFragment.java
│ │ │ │ ├── PreviewControlFragment.java
│ │ │ │ └── RemoteControlFragment.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── bckg_audio_only.xml
│ │ │ │ ├── bckg_icon.xml
│ │ │ │ ├── end_call_button.xml
│ │ │ │ ├── gradient_audionly.xml
│ │ │ │ ├── gradient_backg.xml
│ │ │ │ ├── initiate_call_button.xml
│ │ │ │ └── preview.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── preview_actionbar_fragment.xml
│ │ │ │ ├── preview_camera_fragment.xml
│ │ │ │ └── remote_actionbar_fragment.xml
│ │ │ ├── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── values-v21/
│ │ │ │ └── styles.xml
│ │ │ └── values-w820dp/
│ │ │ └── dimens.xml
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ └── README.md
├── iOS/
│ ├── .gitignore
│ ├── OneToOneSample.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── OneToOneSample.xcscheme
│ ├── Podfile
│ ├── README.md
│ └── SampleApp/
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── audio.imageset/
│ │ │ └── Contents.json
│ │ ├── hangUp.imageset/
│ │ │ └── Contents.json
│ │ ├── mic.imageset/
│ │ │ └── Contents.json
│ │ ├── mutedMic.imageset/
│ │ │ └── Contents.json
│ │ ├── noAudio.imageset/
│ │ │ └── Contents.json
│ │ ├── noVideo.imageset/
│ │ │ └── Contents.json
│ │ ├── reverse cameras.imageset/
│ │ │ └── Contents.json
│ │ ├── startCall.imageset/
│ │ │ └── Contents.json
│ │ └── video.imageset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── MainView.h
│ ├── MainView.m
│ ├── MainViewController.h
│ ├── MainViewController.m
│ ├── UIView+Helper.h
│ ├── UIView+Helper.m
│ └── main.m
└── js/
├── .eslintrc.json
├── .gitignore
├── .jsbeautifyrc
├── Procfile
├── README.md
├── package.json
├── public/
│ ├── css/
│ │ └── style.css
│ ├── index.html
│ └── js/
│ ├── app.js
│ └── components/
│ └── opentok-acc-core.js
└── server.js
SYMBOL INDEX (108 symbols across 6 files)
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/MainActivity.java
class MainActivity (line 43) | public class MainActivity extends AppCompatActivity implements PreviewCo...
method onCreate (line 83) | @Override
method onConfigurationChanged (line 143) | @Override
method onPause (line 149) | @Override
method onResume (line 157) | @Override
method onBackPressed (line 165) | @Override
method onRequestPermissionsResult (line 173) | @Override
method showRemoteControlBar (line 206) | public void showRemoteControlBar(View v) {
method isCallInProgress (line 212) | public boolean isCallInProgress() {
method getWrapper (line 216) | public OTWrapper getWrapper() {
method initPreviewFragment (line 222) | private void initPreviewFragment() {
method initRemoteFragment (line 229) | private void initRemoteFragment(String remoteId) {
method initCameraFragment (line 241) | private void initCameraFragment() {
method cleanViewsAndControls (line 248) | private void cleanViewsAndControls() {
method reloadViews (line 265) | private void reloadViews(){
method checkRemotes (line 274) | private void checkRemotes(){
method setLocalView (line 289) | private void setLocalView(View localView){
method setRemoteView (line 313) | private void setRemoteView(View remoteView) {
method onRemoteAudioOnly (line 337) | private void onRemoteAudioOnly(boolean enabled) {
method dpToPx (line 350) | private int dpToPx(int dp) {
method onConnected (line 358) | @Override
method onDisconnected (line 365) | @Override
method onPreviewViewReady (line 374) | @Override
method onPreviewViewDestroyed (line 380) | @Override
method onRemoteViewReady (line 386) | @Override
method onRemoteViewDestroyed (line 397) | @Override
method onStartedPublishingMedia (line 405) | @Override
method onStoppedPublishingMedia (line 412) | @Override
method onRemoteJoined (line 417) | @Override
method onRemoteLeft (line 429) | @Override
method onRemoteVideoChanged (line 437) | @Override
method onError (line 462) | @Override
method onCameraChanged (line 477) | @Override
method onReconnecting (line 482) | @Override
method onReconnected (line 488) | @Override
method onVideoQualityWarning (line 494) | @Override
method onVideoQualityWarningLifted (line 508) | @Override
method onError (line 513) | @Override
method onDisableLocalAudio (line 523) | @Override
method onDisableLocalVideo (line 531) | @Override
method onDisableRemoteAudio (line 557) | @Override
method onDisableRemoteVideo (line 564) | @Override
method onCameraSwap (line 572) | @Override
method onCall (line 579) | @Override
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/config/OpenTokConfig.java
class OpenTokConfig (line 3) | public class OpenTokConfig {
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewCameraFragment.java
class PreviewCameraFragment (line 19) | public class PreviewCameraFragment extends Fragment {
type PreviewCameraCallbacks (line 27) | public interface PreviewCameraCallbacks {
method onCameraSwap (line 28) | void onCameraSwap();
method onCameraSwap (line 32) | @Override
method onClick (line 38) | public void onClick(View v) {
method onAttach (line 43) | @Override
method onAttach (line 51) | @SuppressWarnings("deprecation")
method onDetach (line 61) | @Override
method onCreateView (line 70) | @Nullable
method cameraSwap (line 82) | public void cameraSwap() {
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewControlFragment.java
class PreviewControlFragment (line 22) | public class PreviewControlFragment extends Fragment {
type PreviewControlCallbacks (line 38) | public interface PreviewControlCallbacks {
method onDisableLocalAudio (line 40) | void onDisableLocalAudio(boolean audio);
method onDisableLocalVideo (line 42) | void onDisableLocalVideo(boolean video);
method onCall (line 44) | void onCall();
method onDisableLocalAudio (line 48) | @Override
method onDisableLocalVideo (line 51) | @Override
method onCall (line 54) | @Override
method onClick (line 60) | public void onClick(View v) {
method onAttach (line 77) | @Override
method onAttach (line 87) | @SuppressWarnings("deprecation")
method onDetach (line 99) | @Override
method onCreate (line 108) | @Override
method onCreateView (line 116) | @Nullable
method updateLocalAudio (line 155) | public void updateLocalAudio() {
method updateLocalVideo (line 165) | public void updateLocalVideo() {
method updateCall (line 175) | public void updateCall() {
method setEnabled (line 188) | public void setEnabled(boolean enabled) {
method restart (line 202) | public void restart() {
FILE: android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/RemoteControlFragment.java
class RemoteControlFragment (line 20) | public class RemoteControlFragment extends Fragment {
type RemoteControlCallbacks (line 35) | public interface RemoteControlCallbacks {
method onDisableRemoteAudio (line 36) | void onDisableRemoteAudio(boolean audio);
method onDisableRemoteVideo (line 38) | void onDisableRemoteVideo(boolean video);
method onDisableRemoteAudio (line 42) | @Override
method onDisableRemoteVideo (line 45) | @Override
method onClick (line 50) | public void onClick(View v) {
method onAttach (line 63) | @Override
method onAttach (line 73) | @SuppressWarnings("deprecation")
method onDetach (line 88) | @Override
method onCreateView (line 95) | @Nullable
method updateRemoteAudio (line 111) | public void updateRemoteAudio(){
method updateRemoteVideo (line 122) | public void updateRemoteVideo(){
method show (line 133) | public void show(){
method setEnabled (line 144) | private void setEnabled(boolean enabled) {
method restart (line 153) | public void restart() {
FILE: js/public/js/components/opentok-acc-core.js
function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
function _classCallCheck (line 2) | function _classCallCheck(n,e){if(!(n instanceof e))throw new TypeError("...
function n (line 2) | function n(n,e){for(var t=0;t<e.length;t++){var o=e[t];o.enumerable=o.en...
function n (line 2) | function n(e){_classCallCheck(this,n),this.analyticsData=e,this.analytic...
function _classCallCheck (line 1061) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
function _possibleConstructorReturn (line 1063) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
function _inherits (line 1065) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
function CoreError (line 1071) | function CoreError(errorMessage, errorType) {
function _classCallCheck (line 1185) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
function _possibleConstructorReturn (line 1187) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
function _inherits (line 1189) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
function SDKError (line 1195) | function SDKError(errorMessage, errorType) {
function defineProperties (line 1217) | function defineProperties(target, props) { for (var i = 0; i < props.len...
function _classCallCheck (line 1219) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
function OpenTokSDK (line 1322) | function OpenTokSDK(credentials) {
function defineProperties (line 1705) | function defineProperties(target, props) { for (var i = 0; i < props.len...
function _classCallCheck (line 1707) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
function State (line 1710) | function State() {
Condensed preview — 80 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (258K chars).
[
{
"path": ".github/CONDUCT.md",
"chars": 3242,
"preview": "**opentok/one-to-one-sample-apps** Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming en"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 2136,
"preview": "# Contributing to OpenTok One-to-One Communication Sample Apps\n\n## Code of Conduct\n\nPlease read our [Code of Conduct](ht"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 771,
"preview": "## New issue checklist\n<!-- Before submitting this issue, make sure you have done the following -->\n\n- [ ] I have read a"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 209,
"preview": "## Pull request checklist\n\n- [ ] All tests pass. Demo project builds and runs.\n- [ ] I have resolved any merge conflicts"
},
{
"path": "LICENSE",
"chars": 1090,
"preview": "LICENSE\n\nThe MIT License (MIT)\n\nCopyright (c) 2016 TokBox, Inc.\n\nPermission is hereby granted, free of charge, to any pe"
},
{
"path": "README.md",
"chars": 4143,
"preview": "# DEPRECATED: OpenTok One-to-One Communication Sample App<br/>Version 1.3\n\n<img src=\"https://assets.tokbox.com/img/vonag"
},
{
"path": "android/.gitignore",
"chars": 298,
"preview": "# built components files\n*.jar\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\n\n#"
},
{
"path": "android/OneToOneSample/.gitignore",
"chars": 97,
"preview": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n"
},
{
"path": "android/OneToOneSample/app/.gitignore",
"chars": 13,
"preview": "/build\n/libs\n"
},
{
"path": "android/OneToOneSample/app/build.gradle",
"chars": 1076,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 25\n buildToolsVersion \"25.0.0\"\n\n defaultC"
},
{
"path": "android/OneToOneSample/app/proguard-rules.pro",
"chars": 658,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "android/OneToOneSample/app/src/main/AndroidManifest.xml",
"chars": 1286,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:to"
},
{
"path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/MainActivity.java",
"chars": 24302,
"preview": "package com.tokbox.android.onetoonesample;\n\nimport android.Manifest;\nimport android.app.ProgressDialog;\nimport android.c"
},
{
"path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/config/OpenTokConfig.java",
"chars": 593,
"preview": "package com.tokbox.android.onetoonesample.config;\n\npublic class OpenTokConfig {\n\n // *** Fill the following variables"
},
{
"path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewCameraFragment.java",
"chars": 2422,
"preview": "package com.tokbox.android.onetoonesample.ui;\n\nimport android.app.Activity;\nimport android.support.v4.app.Fragment;\nimpo"
},
{
"path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewControlFragment.java",
"chars": 6760,
"preview": "package com.tokbox.android.onetoonesample.ui;\n\nimport android.app.Activity;\nimport android.support.graphics.drawable.Vec"
},
{
"path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/RemoteControlFragment.java",
"chars": 5036,
"preview": "package com.tokbox.android.onetoonesample.ui;\n\nimport android.app.Activity;\nimport android.support.v4.app.Fragment;\nimpo"
},
{
"path": "android/OneToOneSample/app/src/main/res/drawable/bckg_audio_only.xml",
"chars": 228,
"preview": "<shape\n xmlns:android=\"http://schemas.android.com/apk/res/android\">\n android:shape=\"rectangle\">\n <solid android"
},
{
"path": "android/OneToOneSample/app/src/main/res/drawable/bckg_icon.xml",
"chars": 432,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"106dp\"\n android:height="
},
{
"path": "android/OneToOneSample/app/src/main/res/drawable/end_call_button.xml",
"chars": 422,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"105dp\"\n android:height=\"105dp\"\n"
},
{
"path": "android/OneToOneSample/app/src/main/res/drawable/gradient_audionly.xml",
"chars": 238,
"preview": "<shape\n xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <gradient\n android:startColor=\"@color/"
},
{
"path": "android/OneToOneSample/app/src/main/res/drawable/gradient_backg.xml",
"chars": 220,
"preview": "<shape\n xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <gradient\n android:startColor=\"@color/"
},
{
"path": "android/OneToOneSample/app/src/main/res/drawable/initiate_call_button.xml",
"chars": 440,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"105dp\"\n android:height="
},
{
"path": "android/OneToOneSample/app/src/main/res/drawable/preview.xml",
"chars": 173,
"preview": "<shape\n xmlns:android=\"http://schemas.android.com/apk/res/android\">\n android:shape=\"rectangle\">\n <stroke androi"
},
{
"path": "android/OneToOneSample/app/src/main/res/layout/activity_main.xml",
"chars": 4617,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout xmlns:android=\"http://schemas.an"
},
{
"path": "android/OneToOneSample/app/src/main/res/layout/preview_actionbar_fragment.xml",
"chars": 1844,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "android/OneToOneSample/app/src/main/res/layout/preview_camera_fragment.xml",
"chars": 625,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "android/OneToOneSample/app/src/main/res/layout/remote_actionbar_fragment.xml",
"chars": 1235,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "android/OneToOneSample/app/src/main/res/values/colors.xml",
"chars": 634,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"gradientStart\">#5B5B5B</color>\n <color name=\"grad"
},
{
"path": "android/OneToOneSample/app/src/main/res/values/dimens.xml",
"chars": 600,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"border\">1dp</dimen>"
},
{
"path": "android/OneToOneSample/app/src/main/res/values/strings.xml",
"chars": 656,
"preview": "<resources>\n <string name=\"app_name\">OneToOneSample</string>\n <string name=\"network_quality\">Network connection is"
},
{
"path": "android/OneToOneSample/app/src/main/res/values/styles.xml",
"chars": 475,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "android/OneToOneSample/app/src/main/res/values-v21/styles.xml",
"chars": 327,
"preview": "<resources>\n\n <style name=\"AppTheme.NoActionBar\">\n <item name=\"windowActionBar\">false</item>\n <item nam"
},
{
"path": "android/OneToOneSample/app/src/main/res/values-w820dp/dimens.xml",
"chars": 358,
"preview": "<resources>\n <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n (such as s"
},
{
"path": "android/OneToOneSample/build.gradle",
"chars": 574,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n r"
},
{
"path": "android/OneToOneSample/gradle/wrapper/gradle-wrapper.properties",
"chars": 233,
"preview": "#Thu Jan 19 16:41:06 CET 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "android/OneToOneSample/gradle.properties",
"chars": 855,
"preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
},
{
"path": "android/OneToOneSample/gradlew",
"chars": 4971,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "android/OneToOneSample/gradlew.bat",
"chars": 2404,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "android/OneToOneSample/settings.gradle",
"chars": 15,
"preview": "include ':app'\n"
},
{
"path": "android/README.md",
"chars": 6872,
"preview": "\n\n# OpenTok One-to-One Communication Sample App for Android\n\n## Quick start\n\nThis section sho"
},
{
"path": "iOS/.gitignore",
"chars": 324,
"preview": ".DS_Store\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!defau"
},
{
"path": "iOS/OneToOneSample.xcodeproj/project.pbxproj",
"chars": 14348,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "iOS/OneToOneSample.xcodeproj/xcshareddata/xcschemes/OneToOneSample.xcscheme",
"chars": 4293,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0800\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "iOS/Podfile",
"chars": 112,
"preview": "\nplatform :ios, '9.0'\n\ntarget 'OneToOneSample' do \n pod 'OTAcceleratorCore', '1.0.3'\n pod 'SVProgressHUD'\nend\n"
},
{
"path": "iOS/README.md",
"chars": 3659,
"preview": "\n\n# OpenTok One-to-One Communication Sample App for iOS<br/>Version 1.3\n\n## Quick start\n\nThis"
},
{
"path": "iOS/SampleApp/AppDelegate.h",
"chars": 335,
"preview": "//\n// AppDelegate.h\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@class OTAcce"
},
{
"path": "iOS/SampleApp/AppDelegate.m",
"chars": 2465,
"preview": "//\n// AppDelegate.m\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"AppDelegate.h\"\n#import \"OTAcc"
},
{
"path": "iOS/SampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 3007,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"20x20\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\""
},
{
"path": "iOS/SampleApp/Assets.xcassets/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "iOS/SampleApp/Assets.xcassets/audio.imageset/Contents.json",
"chars": 372,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"audio.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "iOS/SampleApp/Assets.xcassets/hangUp.imageset/Contents.json",
"chars": 375,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"hangUp.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "iOS/SampleApp/Assets.xcassets/mic.imageset/Contents.json",
"chars": 366,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"mic.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "iOS/SampleApp/Assets.xcassets/mutedMic.imageset/Contents.json",
"chars": 405,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"mutedMicLineCopy.png\",\n \"scale\" : \"1x\"\n "
},
{
"path": "iOS/SampleApp/Assets.xcassets/noAudio.imageset/Contents.json",
"chars": 390,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"noSoundCopy.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "iOS/SampleApp/Assets.xcassets/noVideo.imageset/Contents.json",
"chars": 390,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"noVideoIcon.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "iOS/SampleApp/Assets.xcassets/reverse cameras.imageset/Contents.json",
"chars": 402,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"reverse cameras.png\",\n \"scale\" : \"1x\"\n }"
},
{
"path": "iOS/SampleApp/Assets.xcassets/startCall.imageset/Contents.json",
"chars": 384,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"startCall.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "iOS/SampleApp/Assets.xcassets/video.imageset/Contents.json",
"chars": 384,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"videoIcon.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "iOS/SampleApp/Base.lproj/LaunchScreen.storyboard",
"chars": 3086,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "iOS/SampleApp/Base.lproj/Main.storyboard",
"chars": 15674,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "iOS/SampleApp/Info.plist",
"chars": 1845,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "iOS/SampleApp/MainView.h",
"chars": 753,
"preview": "//\n// MainView.h\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface MainV"
},
{
"path": "iOS/SampleApp/MainView.m",
"chars": 4574,
"preview": "//\n// MainView.m\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"MainView.h\"\n#import \"UIView+Help"
},
{
"path": "iOS/SampleApp/MainViewController.h",
"chars": 169,
"preview": "//\n// MainViewController.h\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@inter"
},
{
"path": "iOS/SampleApp/MainViewController.m",
"chars": 5789,
"preview": "//\n// MainViewController.m\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"MainView.h\"\n#import \"M"
},
{
"path": "iOS/SampleApp/UIView+Helper.h",
"chars": 191,
"preview": "//\n// UIView+Helper.h\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface"
},
{
"path": "iOS/SampleApp/UIView+Helper.m",
"chars": 2756,
"preview": "//\n// UIView+Helper.m\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"UIView+Helper.h\"\n\n@impleme"
},
{
"path": "iOS/SampleApp/main.m",
"chars": 201,
"preview": "#import <UIKit/UIKit.h>\n#import \"AppDelegate.h\"\n\nint main(int argc, char * argv[]) {\n @autoreleasepool {\n return U"
},
{
"path": "js/.eslintrc.json",
"chars": 550,
"preview": "{\n \"extends\": \"airbnb\",\n \"env\": {\n \"browser\": true,\n \"node\": true\n },\n \"parserOptions\": {\n \"ecmaVersion\": 5"
},
{
"path": "js/.gitignore",
"chars": 12,
"preview": "node_modules"
},
{
"path": "js/.jsbeautifyrc",
"chars": 172,
"preview": "{\n \"js\":{\n \"indent_size\": 2,\n \"space_after_anon_function\": true,\n \"end_with_newline\": true,\n "
},
{
"path": "js/Procfile",
"chars": 19,
"preview": "web: node server.js"
},
{
"path": "js/README.md",
"chars": 1863,
"preview": "\n\n# OpenTok One-to-One Communication Sample App for JavaScript<br/>Version 1.3\n\n## Quick star"
},
{
"path": "js/package.json",
"chars": 637,
"preview": "{\n \"name\": \"one-to-one-sample\",\n \"version\": \"1.0.4\",\n \"description\": \"One to One sample app\",\n \"main\": \"server.js\",\n"
},
{
"path": "js/public/css/style.css",
"chars": 4391,
"preview": "html,\nbody {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n font-family: sans-serif;\n}\n\n.clickable {\n c"
},
{
"path": "js/public/index.html",
"chars": 2022,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>OT Accelerator Core</title>\n\n <link re"
},
{
"path": "js/public/js/app.js",
"chars": 5470,
"preview": "/* global otCore */\nconst options = {\n credentials: {\n apiKey: \"\", //Replace with your OpenTok API key \n session"
},
{
"path": "js/public/js/components/opentok-acc-core.js",
"chars": 74085,
"preview": "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0)"
},
{
"path": "js/server.js",
"chars": 476,
"preview": "/* eslint-env es6 */\n\n/*\n * Dependencies\n */\nconst express = require('express');\nconst bodyParser = require('body-parser"
}
]
About this extraction
This page contains the full source code of the opentok/one-to-one-sample-apps GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 80 files (234.8 KB), approximately 60.0k tokens, and a symbol index with 108 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.