[
  {
    "path": ".github/CONDUCT.md",
    "content": "**opentok/one-to-one-sample-apps** Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\nadvances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at [INSERT EMAIL ADDRESS]. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing to OpenTok One-to-One Communication Sample Apps\n\n## Code of Conduct\n\nPlease 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.\n\n## Opening a new issue\n\n1. Read *the entire* [README](https://github.com/opentok/one-to-one-sample-apps/blob/master/README.md).\n* 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.\n* If your issue exists and you have some new information to contribute, you may add a comment to its thread.\n* Otherwise, open a new issue with a clear title and description.\n* Provide **all** of the following information:\n  - Library version(s)\n  - iOS, Android, or browser version(s)\n  - Devices, simulators, or machines affected\n  - Expected behavior vs actual behavior\n  - Complete steps to reproduce the issue\n  - Link to a project that exhibits the issue. It is recommended that you fork the repo and modify the demo project.\n  - Screenshots, GIFs, or videos depicting the issue, if applicable.\n  - Full crash log, if applicable.\n  - A list of all possibly related issues.\n\n## Submitting a pull request\n\n1. Link to the issue that the pull request resolves. If the issue does not exist, create one.\n2. Write unit tests that test your changes, if applicable.\n3. Update header documentation as needed.\n4. Follow the existing coding style. For more information, see [style guidelines](https://github.com/NYTimes/objective-c-style-guide).\n5. Resolve any merge conflicts.\n6. Squash your commits into a single commit.\n\n## Did you read all of this?\n\nBe sure you have visited all the links in this document.\n\n### New issue checklist\n\nWhen 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:\n\n> - [x] I have reviewed the contributing guidelines. Confirmation: :muscle::sunglasses::facepunch:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "## New issue checklist\n<!-- Before submitting this issue, make sure you have done the following -->\n\n- [ ] I have read all of the [`README`](https://github.com/opentok/one-to-one-sample-apps/blob/master/README.md) \n- [ ] 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**.\n\n### General information\n\n- Library version(s):\n- iOS/Android/Browser version(s):\n- Devices/Simulators/Machine affected:\n- Reproducible in the demo project? (Yes/No): \n- Related issues:\n\n## Bug report\n\n#### Expected behavior\n\n> ...\n\n#### Actual behavior\n\n> ...\n\n#### Steps to reproduce\n\n> ...\n\n#### Crash log? Screenshots? Videos? Sample project?\n\n>...\n\n## Question or Feature Request\n\n> ...\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## Pull request checklist\n\n- [ ] All tests pass. Demo project builds and runs.\n- [ ] I have resolved any merge conflicts. Confirmation: ____\n\n#### This fixes issue #___.\n\n## What's in this pull request?\n\n>...\n"
  },
  {
    "path": "LICENSE",
    "content": "LICENSE\n\nThe MIT License (MIT)\n\nCopyright (c) 2016 TokBox, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n\n"
  },
  {
    "path": "README.md",
    "content": "# DEPRECATED: OpenTok One-to-One Communication Sample App<br/>Version 1.3\n\n<img src=\"https://assets.tokbox.com/img/vonage/Vonage_VideoAPI_black.svg\" height=\"48px\" alt=\"Tokbox is now known as Vonage\" />\n\n> This repository has been deprecated and is superseded by the following repositories:\n>\n> [accelerator-sample-apps-js](https://github.com/opentok/accelerator-sample-apps-js)\n>\n> [accelerator-sample-apps-android](https://github.com/opentok/accelerator-sample-apps-android)\n>\n> [accelerator-sample-apps-ios](https://github.com/opentok/accelerator-sample-apps-ios)\n\n---\n\nThe 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.\n\nWith this sample app, you can:\n\n- Start and end audio/visual communication between two users.\n- Achieve interoperability between web and mobile devices.\n- Mute or unmute audio.\n- Enable or disable video.\n- Control the camera to point in the forward direction or in the reverse direction (selfie mode).\n- Customize the UI features and layout.\n\nYou 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:\n\n- [OpenTok One-to-One Communication Sample App for Android](./android)\n- [OpenTok One-to-One Communication Sample App for iOS](./iOS)\n- [OpenTok One-to-One Communication Sample App for JavaScript](./js)\n\n# Device interoperability with One-to-One communication\n\nThe 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.\n\nThis 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/).\n\nFor 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.\n\nUse the following approach to try this out:\n\n1. 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.\n\n2. Start the web and mobile apps. You will observe the following interactions:\n\n   - Both apps connect to the session.\n   - Both apps start publishing and subscribing to each other’s streams.\n\n3. Observe what happens for each user when you:\n\n   - Enable or disable local audio/video on the mobile app.\n   - Enable or disable local audio/video on the web app.\n   - Enable or disable remote audio/video on the mobile app.\n   - Enable or disable remote audio/video on the web app.\n\nAs 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.\n\n## Getting Help\n\nWe love to hear from you so if you have questions or comments, let us know! You can either:\n\n- See <https://support.tokbox.com/> for support options\n- Tweet at us! We're [@VonageDev on Twitter](https://twitter.com/VonageDev)\n- Or [join the Vonage Developer Community Slack](https://developer.nexmo.com/community/slack)\n"
  },
  {
    "path": "android/.gitignore",
    "content": "# 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# Local configuration file (sdk path, etc)\nlocal.properties\n\n# OSX files\n.DS_Store\n\n# Android Studio files\n*.iml\n.idea/\n.idea/workspace.xml\n.gradle\nbuild/\n*.ipr\n*.iws\n\n#libs\n*.so\n"
  },
  {
    "path": "android/OneToOneSample/.gitignore",
    "content": "*.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",
    "content": "/build\n/libs\n"
  },
  {
    "path": "android/OneToOneSample/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"25.0.0\"\n\n    defaultConfig {\n        applicationId \"com.tokbox.android.onetosample\"\n        minSdkVersion 16\n        targetSdkVersion 25\n        versionCode 1\n        versionName \"1.3\"\n        archivesBaseName = \"OneToOneSample-$versionName\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    packagingOptions {\n        exclude 'META-INF/ASL2.0'\n        exclude 'META-INF/LICENSE'\n    }\n    configurations.all {\n\n        resolutionStrategy {\n            cacheChangingModulesFor 0, 'seconds'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    testCompile 'junit:junit:4.12'\n    compile 'com.android.support:appcompat-v7:25.+'\n    compile 'com.android.support:design:25.+'\n    compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'\n    compile 'com.opentok.android:opentok-accelerator-core:1.0.+'\n}"
  },
  {
    "path": "android/OneToOneSample/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/mserrano/android-sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.tokbox.android.onetoonesample\">\n\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"23\" />\n\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n\n    <uses-feature android:name=\"android.hardware.camera\" />\n    <uses-feature android:name=\"android.hardware.camera.autofocus\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme.NoActionBar\"\n        android:icon=\"@mipmap/ic_launcher\"\n        tools:replace=\"android:theme\">\n\n        <activity\n            android:name=\"com.tokbox.android.onetoonesample.MainActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/MainActivity.java",
    "content": "package com.tokbox.android.onetoonesample;\n\nimport android.Manifest;\nimport android.app.ProgressDialog;\nimport android.content.DialogInterface;\nimport android.content.pm.PackageManager;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentTransaction;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.app.AlertDialog;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.opentok.android.OpentokError;\nimport com.tokbox.android.onetoonesample.config.OpenTokConfig;\nimport com.tokbox.android.onetoonesample.ui.PreviewCameraFragment;\nimport com.tokbox.android.onetoonesample.ui.PreviewControlFragment;\nimport com.tokbox.android.onetoonesample.ui.RemoteControlFragment;\nimport com.tokbox.android.otsdkwrapper.listeners.AdvancedListener;\nimport com.tokbox.android.otsdkwrapper.listeners.BasicListener;\nimport com.tokbox.android.otsdkwrapper.listeners.ListenerException;\nimport com.tokbox.android.otsdkwrapper.listeners.PausableAdvancedListener;\nimport com.tokbox.android.otsdkwrapper.listeners.PausableBasicListener;\nimport com.tokbox.android.otsdkwrapper.utils.MediaType;\nimport com.tokbox.android.otsdkwrapper.utils.OTConfig;\nimport com.tokbox.android.otsdkwrapper.utils.PreviewConfig;\nimport com.tokbox.android.otsdkwrapper.wrapper.OTWrapper;\n\nimport java.util.UUID;\nimport android.widget.FrameLayout;\n\n\nimport java.util.UUID;\n\npublic class MainActivity extends AppCompatActivity implements PreviewControlFragment.PreviewControlCallbacks,\n        RemoteControlFragment.RemoteControlCallbacks, PreviewCameraFragment.PreviewCameraCallbacks {\n\n    private final String LOG_TAG = MainActivity.class.getSimpleName();\n\n    private final String[] permissions = {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA};\n    private final int permsRequestCode = 200;\n\n    private RelativeLayout mPreviewViewContainer;\n    private RelativeLayout mRemoteViewContainer;\n    private RelativeLayout.LayoutParams mLayoutParamsPreview;\n    private String mRemoteId;\n    private View mRemoteView;\n    private TextView mAlert;\n\n    //Audio only views\n    private RelativeLayout mLocalAudioOnlyView;\n    private RelativeLayout mRemoteAudioOnlyView;\n    private ImageView mLocalAudioOnlyImage;\n\n    //UI control bars fragments\n    private PreviewControlFragment mPreviewFragment;\n    private RemoteControlFragment mRemoteFragment;\n    private PreviewCameraFragment mCameraFragment;\n    private FragmentTransaction mFragmentTransaction;\n\n    //Loading dialog\n    ProgressDialog mProgressDialog;\n\n    //Permissions\n    private boolean mAudioPermission = false;\n    private boolean mVideoPermission = false;\n\n    //Communication status\n    private boolean isConnected = false;\n    private boolean isLocal = false;\n    private boolean isCallInProgress = false;\n\n    private OTWrapper mWrapper;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        Log.i(LOG_TAG, \"onCreate\");\n\n        super.onCreate(savedInstanceState);\n\n        setContentView(R.layout.activity_main);\n\n        mPreviewViewContainer = (RelativeLayout) findViewById(R.id.publisherview);\n        mRemoteViewContainer = (RelativeLayout) findViewById(R.id.subscriberview);\n        mAlert = (TextView) findViewById(R.id.quality_warning);\n\n        //remote and local audio only view\n        mRemoteAudioOnlyView = (RelativeLayout) findViewById(R.id.remoteAudioOnlyView);\n        mLocalAudioOnlyView = (RelativeLayout) findViewById(R.id.localAudioOnlyView);\n\n        //request Marshmallow camera permission\n        if (ContextCompat.checkSelfPermission(this, permissions[1]) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                requestPermissions(permissions, permsRequestCode);\n            }\n        } else {\n            mVideoPermission = true;\n            mAudioPermission = true;\n        }\n\n        //init the wrapper\n        OTConfig config =\n                new OTConfig.OTConfigBuilder(OpenTokConfig.SESSION_ID, OpenTokConfig.TOKEN,\n                        OpenTokConfig.API_KEY).name(\"one-to-one-sample-app\").subscribeAutomatically(false).subscribeToSelf(false).build();\n        if ( config != null ) {\n            mWrapper = new OTWrapper(MainActivity.this, config);\n            mWrapper.addBasicListener(mBasicListener);\n            mWrapper.addAdvancedListener(mAdvancedListener);\n\n            if (mWrapper != null) {\n                mWrapper.connect();\n            }\n\n            //init controls fragments\n            if (savedInstanceState == null) {\n                mFragmentTransaction = getSupportFragmentManager().beginTransaction();\n                initCameraFragment(); //to swap camera\n                initPreviewFragment(); //to enable/disable local media\n                mFragmentTransaction.commitAllowingStateLoss();\n            }\n\n            //show connecting dialog\n            mProgressDialog = new ProgressDialog(this);\n            mProgressDialog.setTitle(\"Please wait\");\n            mProgressDialog.setMessage(\"Connecting...\");\n            mProgressDialog.show();\n        }\n        else {\n            Log.e(LOG_TAG, \"OpenTok credentials are invalid\");\n            Toast.makeText(MainActivity.this, \"Credentials are invalid\", Toast.LENGTH_LONG).show();\n            this.finish();\n        }\n    }\n\n    @Override\n    public void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n        reloadViews();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        if (mWrapper != null) {\n            mWrapper.pause();\n        }\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        if ( mWrapper != null ){\n            mWrapper.resume(true);\n        }\n    }\n\n    @Override\n    public void onBackPressed() {\n        super.onBackPressed();\n        if ( mWrapper != null && isConnected ){\n            mWrapper.disconnect();\n        }\n    }\n\n    @Override\n    public void onRequestPermissionsResult(final int permsRequestCode, final String[] permissions,\n                                           int[] grantResults) {\n        switch (permsRequestCode) {\n            case 200:\n                mVideoPermission = grantResults[0] == PackageManager.PERMISSION_GRANTED;\n                mAudioPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED;\n\n                if (!mVideoPermission || !mAudioPermission) {\n                    final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);\n                    builder.setTitle(getResources().getString(R.string.permissions_denied_title));\n                    builder.setMessage(getResources().getString(R.string.alert_permissions_denied));\n                    builder.setPositiveButton(\"I'M SURE\", new DialogInterface.OnClickListener() {\n                        @Override\n                        public void onClick(DialogInterface dialog, int which) {\n                            dialog.dismiss();\n                        }\n                    });\n                    builder.setNegativeButton(\"RE-TRY\", new DialogInterface.OnClickListener() {\n                        @Override\n                        public void onClick(DialogInterface dialog, int which) {\n                            dialog.dismiss();\n                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                                requestPermissions(permissions, permsRequestCode);\n                            }\n                        }\n                    });\n                    builder.show();\n                }\n                break;\n        }\n    }\n\n    public void showRemoteControlBar(View v) {\n        if ( mRemoteFragment != null && mRemoteId != null ) {\n            mRemoteFragment.show();\n        }\n    }\n\n    public boolean isCallInProgress() {\n        return isCallInProgress;\n    }\n\n    public OTWrapper getWrapper() {\n        return mWrapper;\n    }\n\n    //Private methods\n    //Init the local fragment\n    private void initPreviewFragment() {\n        mPreviewFragment = new PreviewControlFragment();\n        getSupportFragmentManager().beginTransaction()\n                .add(R.id.actionbar_preview_fragment_container, mPreviewFragment).commit();\n    }\n\n    //Inti the remote fragment\n    private void initRemoteFragment(String remoteId) {\n        mRemoteFragment = new RemoteControlFragment();\n\n        Bundle args = new Bundle();\n        args.putString(\"remoteId\", remoteId);\n        mRemoteFragment.setArguments(args);\n\n        getSupportFragmentManager().beginTransaction()\n                .add(R.id.actionbar_remote_fragment_container, mRemoteFragment).commit();\n    }\n\n    //Init the local camera fragment\n    private void initCameraFragment() {\n        mCameraFragment = new PreviewCameraFragment();\n        getSupportFragmentManager().beginTransaction()\n                .add(R.id.camera_preview_fragment_container, mCameraFragment).commit();\n    }\n\n    //Clean views\n    private void cleanViewsAndControls() {\n        if ( mRemoteId != null ) {\n            mWrapper.removeRemote(mRemoteId);\n            mRemoteView = null;\n            setRemoteView(null);\n        }\n        if (isLocal) {\n            isLocal = false;\n            setLocalView(null);\n        }\n        if (mPreviewFragment != null)\n            mPreviewFragment.restart();\n        if (mRemoteFragment != null)\n            mRemoteFragment.restart();\n    }\n\n    //Reload views\n    private void reloadViews(){\n        mRemoteViewContainer.removeAllViews();\n\n        if (mRemoteId != null){\n            setRemoteView(mWrapper.getRemoteStreamStatus(mRemoteId).getView());\n        }\n    }\n\n    //Check if there are some connected remotes in the session\n    private void checkRemotes(){\n        if (mRemoteId != null){\n            //add the remote participant to the communication\n            mWrapper.addRemote(mRemoteId);\n            //check the status of the remote video stream\n            if (!mWrapper.isReceivedMediaEnabled(mRemoteId, MediaType.VIDEO)){\n                onRemoteAudioOnly(true);\n            }\n            else {\n                setRemoteView(mWrapper.getRemoteStreamStatus(mRemoteId).getView());\n            }\n        }\n    }\n\n    //Set the local participant view\n    private void setLocalView(View localView){\n        if (localView != null) {\n            mPreviewViewContainer.removeAllViews();\n            isLocal = true;\n            mLayoutParamsPreview = new RelativeLayout.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            if (mRemoteId != null) {\n                mLayoutParamsPreview.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,\n                        RelativeLayout.TRUE);\n                mLayoutParamsPreview.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,\n                        RelativeLayout.TRUE);\n                mLayoutParamsPreview.width = (int) getResources().getDimension(R.dimen.preview_width);\n                mLayoutParamsPreview.height = (int) getResources().getDimension(R.dimen.preview_height);\n                mLayoutParamsPreview.rightMargin = (int) getResources().getDimension(R.dimen.preview_rightMargin);\n                mLayoutParamsPreview.bottomMargin = (int) getResources().getDimension(R.dimen.preview_bottomMargin);\n            }\n            mPreviewViewContainer.addView(localView, mLayoutParamsPreview);\n        }\n        else {\n            mPreviewViewContainer.removeAllViews();\n        }\n    }\n\n    //Set the remote participant view\n    private void setRemoteView(View remoteView) {\n        if (mPreviewViewContainer.getChildCount() > 0) {\n            setLocalView(mPreviewViewContainer.getChildAt(0)); //main preview view\n        }\n        if (remoteView != null) {\n            //show remote view\n            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(\n                    this.getResources().getDisplayMetrics().widthPixels, this.getResources()\n                    .getDisplayMetrics().heightPixels);\n            mRemoteViewContainer.removeView(remoteView);\n            mRemoteViewContainer.addView(remoteView, layoutParams);\n            mRemoteViewContainer.setClickable(true);\n            if (mRemoteFragment != null)\n                mRemoteFragment.show();\n        } else { //view null --> remove view\n            if (mRemoteViewContainer.getChildCount() > 0) {\n                mRemoteViewContainer.removeAllViews();\n            }\n            mRemoteViewContainer.setClickable(false);\n            mRemoteAudioOnlyView.setVisibility(View.GONE);\n        }\n    }\n\n    //Set the remote audio only view\n    private void onRemoteAudioOnly(boolean enabled) {\n        if (mRemoteView != null) {\n            if (enabled) {\n                mRemoteView.setVisibility(View.GONE);\n                mRemoteAudioOnlyView.setVisibility(View.VISIBLE);\n            } else {\n                mRemoteAudioOnlyView.setVisibility(View.GONE);\n                mRemoteView.setVisibility(View.VISIBLE);\n            }\n        }\n    }\n\n    //Converts dp to real pixels, according to the screen density.\n    private int dpToPx(int dp) {\n        double screenDensity = this.getResources().getDisplayMetrics().density;\n        return (int) (screenDensity * (double) dp);\n    }\n\n    //Basic Listener from OTWrapper\n    private BasicListener mBasicListener =\n            new PausableBasicListener(new BasicListener<OTWrapper>() {\n                @Override\n                public void onConnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException {\n                    Log.i(LOG_TAG, \"Connected to the session. Number of participants: \"+participantsCount);\n                    isConnected = true;\n                    mProgressDialog.dismiss();\n                }\n\n                @Override\n                public void onDisconnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException {\n                    Log.i(LOG_TAG, \"Connection dropped: \"+connId);\n                    if ( connId == mWrapper.getOwnConnId() ) {\n                        Log.i(LOG_TAG, \"Disconnected to the session\");\n                        cleanViewsAndControls();\n                    }\n                }\n\n                @Override\n                public void onPreviewViewReady(OTWrapper otWrapper, View localView) throws ListenerException {\n                    Log.i(LOG_TAG, \"Local preview view is ready\");\n                    setLocalView(localView);\n                }\n\n                @Override\n                public void onPreviewViewDestroyed(OTWrapper otWrapper, View localView) throws ListenerException {\n                    Log.i(LOG_TAG, \"Local preview view is destroyed\");\n                    setLocalView(null);\n                }\n\n                @Override\n                public void onRemoteViewReady(OTWrapper otWrapper, View remoteView, String remoteId, String data) throws ListenerException {\n                    Log.i(LOG_TAG, \"Remove view is ready\");\n                    if ( remoteId == mRemoteId ) {\n                        if (isCallInProgress()) {\n                            setRemoteView(remoteView);\n                        }\n                        mRemoteView = remoteView;\n                    }\n                }\n\n                @Override\n                public void onRemoteViewDestroyed(OTWrapper otWrapper, View remoteView, String remoteId) throws ListenerException {\n                    Log.i(LOG_TAG, \"Remote view is destroyed\");\n                    setRemoteView(null);\n                    mRemoteView = null;\n                }\n\n\n                @Override\n                public void onStartedPublishingMedia(OTWrapper otWrapper, boolean screensharing) throws ListenerException {\n                    Log.i(LOG_TAG, \"Local started streaming video.\");\n                    //Check if there are some connected remotes\n                    checkRemotes();\n                }\n\n                @Override\n                public void onStoppedPublishingMedia(OTWrapper otWrapper, boolean screensharing) throws ListenerException {\n                    Log.i(LOG_TAG, \"Local stopped streaming video.\");\n                }\n\n                @Override\n                public void onRemoteJoined(OTWrapper otWrapper, String remoteId) throws ListenerException {\n                    Log.i(LOG_TAG, \"A new remote joined.\");\n                    if (mRemoteId == null){ //one-to-one, the first to arrive, will be the used\n                        MainActivity.this.mRemoteId = remoteId;\n                        initRemoteFragment(remoteId);\n                        if (mWrapper.isPublishing()){\n                            mWrapper.addRemote(mRemoteId);\n                        }\n                    }\n                }\n\n                @Override\n                public void onRemoteLeft(OTWrapper otWrapper, String remoteId) throws ListenerException {\n                    Log.i(LOG_TAG, \"A new remote left.\");\n                    if ( mRemoteId != null && remoteId == mRemoteId ) { //one-to-one\n                        mRemoteId = null;\n                    }\n                }\n\n                @Override\n                public void onRemoteVideoChanged(OTWrapper otWrapper, String remoteId, String reason, boolean videoActive, boolean subscribed) throws ListenerException {\n                    Log.i(LOG_TAG, \"Remote video changed\");\n                    if (isCallInProgress) {\n                        if (reason.equals(\"quality\")) {\n                            //network quality alert\n                            mAlert.setBackgroundResource(R.color.quality_alert);\n                            mAlert.setTextColor(MainActivity.this.getResources().getColor(R.color.white));\n                            mAlert.bringToFront();\n                            mAlert.setVisibility(View.VISIBLE);\n                            mAlert.postDelayed(new Runnable() {\n                                public void run() {\n                                    mAlert.setVisibility(View.GONE);\n                                }\n                            }, 7000);\n                        }\n\n                        if (!videoActive) {\n                            onRemoteAudioOnly(true); //video is not active\n                        } else {\n                            onRemoteAudioOnly(false);\n                        }\n                    }\n                }\n\n                @Override\n                public void onError(OTWrapper otWrapper, OpentokError error) throws ListenerException {\n                    Log.i(LOG_TAG, \"Error \"+error.getErrorCode()+\"-\"+error.getMessage());\n\n                    Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();\n                    mWrapper.disconnect(); //end communication\n                    mProgressDialog.dismiss();\n                    cleanViewsAndControls(); //restart views\n                }\n            });\n\n    //Advanced Listener from OTWrapper\n    private AdvancedListener mAdvancedListener =\n            new PausableAdvancedListener(new AdvancedListener<OTWrapper>() {\n\n                @Override\n                public void onCameraChanged(OTWrapper otWrapper) throws ListenerException {\n                    Log.i(LOG_TAG, \"The camera changed\");\n                }\n\n                @Override\n                public void onReconnecting(OTWrapper otWrapper) throws ListenerException {\n                    Log.i(LOG_TAG, \"The session is reconnecting.\");\n                    Toast.makeText(MainActivity.this, R.string.reconnecting, Toast.LENGTH_LONG).show();\n                }\n\n                @Override\n                public void onReconnected(OTWrapper otWrapper) throws ListenerException {\n                    Log.i(LOG_TAG, \"The session reconnected.\");\n                    Toast.makeText(MainActivity.this, R.string.reconnected, Toast.LENGTH_LONG).show();\n                }\n\n                @Override\n                public void onVideoQualityWarning(OTWrapper otWrapper, String remoteId) throws ListenerException {\n                    Log.i(LOG_TAG, \"The quality has degraded\");\n                    mAlert.setBackgroundResource(R.color.quality_warning);\n                    mAlert.setTextColor(MainActivity.this.getResources().getColor(R.color.warning_text));\n                    mAlert.bringToFront();\n                    mAlert.setVisibility(View.VISIBLE);\n                    mAlert.postDelayed(new Runnable() {\n                        public void run() {\n                            mAlert.setVisibility(View.GONE);\n                        }\n                    }, 7000);\n                }\n\n                @Override\n                public void onVideoQualityWarningLifted(OTWrapper otWrapper, String remoteId) throws ListenerException {\n                    Log.i(LOG_TAG, \"The quality has improved\");\n                }\n\n                @Override\n                public void onError(OTWrapper otWrapper, OpentokError error) throws ListenerException {\n                    Log.i(LOG_TAG, \"Error \" + error.getErrorCode() + \"-\" + error.getMessage());\n                    Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();\n                    mWrapper.disconnect(); //end communication\n                    mProgressDialog.dismiss();\n                    cleanViewsAndControls(); //restart views\n                }\n            });\n    //Audio local button event\n    @Override\n    public void onDisableLocalAudio(boolean audio) {\n        if (mWrapper != null) {\n            mWrapper.enableLocalMedia(MediaType.AUDIO, audio);\n        }\n    }\n\n    //Video local button event\n    @Override\n    public void onDisableLocalVideo(boolean video) {\n        if (mWrapper != null) {\n            mWrapper.enableLocalMedia(MediaType.VIDEO, video);\n            if (mRemoteId != null) {\n                if (!video) {\n                    mLocalAudioOnlyImage = new ImageView(this);\n                    mLocalAudioOnlyImage.setImageResource(R.drawable.avatar);\n                    mLocalAudioOnlyImage.setBackgroundResource(R.drawable.bckg_audio_only);\n                    mPreviewViewContainer.addView(mLocalAudioOnlyImage, mLayoutParamsPreview);\n                } else {\n                    mPreviewViewContainer.removeView(mLocalAudioOnlyImage);\n                }\n            } else {\n                if (!video) {\n                    mLocalAudioOnlyView.setVisibility(View.VISIBLE);\n                    mPreviewViewContainer.addView(mLocalAudioOnlyView);\n                } else {\n                    mLocalAudioOnlyView.setVisibility(View.GONE);\n                    mPreviewViewContainer.removeView(mLocalAudioOnlyView);\n                }\n            }\n        }\n    }\n\n    //Remote control callbacks\n    @Override\n    public void onDisableRemoteAudio(boolean audio) {\n        if (mWrapper != null) {\n            mWrapper.enableReceivedMedia(mRemoteId, MediaType.AUDIO, audio);\n        }\n    }\n\n    @Override\n    public void onDisableRemoteVideo(boolean video) {\n        if (mWrapper != null) {\n            mWrapper.enableReceivedMedia(mRemoteId, MediaType.VIDEO, video);\n        }\n    }\n\n    //Camera control callback\n    @Override\n    public void onCameraSwap() {\n        if (mWrapper != null) {\n            mWrapper.cycleCamera();\n        }\n    }\n\n    @Override\n    public void onCall() {\n        if (mWrapper != null && isConnected) {\n            if (!isCallInProgress) {\n                mWrapper.startPublishingMedia(new PreviewConfig.PreviewConfigBuilder().\n                        name(\"Tokboxer\").build(), false);\n                if ( mPreviewFragment != null ) {\n                    mPreviewFragment.setEnabled(true);\n                }\n                isCallInProgress = true;\n            } else {\n                mWrapper.stopPublishingMedia(false);\n                isCallInProgress = false;\n                cleanViewsAndControls();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/config/OpenTokConfig.java",
    "content": "package com.tokbox.android.onetoonesample.config;\n\npublic class OpenTokConfig {\n\n    // *** Fill the following variables using your own Project info from the OpenTok dashboard  ***\n    // ***                      https://dashboard.tokbox.com/projects                           ***\n    // Replace with a generated Session ID\n    public static final String SESSION_ID = \"\";\n    // Replace with a generated token (from the dashboard or using an OpenTok server SDK)\n    public static final String TOKEN = \"\";\n    // Replace with your OpenTok API key\n    public static final String API_KEY = \"\";\n\n}"
  },
  {
    "path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewCameraFragment.java",
    "content": "package com.tokbox.android.onetoonesample.ui;\n\nimport android.app.Activity;\nimport android.support.v4.app.Fragment;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageButton;\nimport android.widget.RelativeLayout;\n\nimport com.tokbox.android.onetoonesample.MainActivity;\nimport com.tokbox.android.onetoonesample.R;\n\npublic class PreviewCameraFragment extends Fragment {\n    private static final String LOGTAG = PreviewCameraFragment.class.getSimpleName();\n\n    private View mRootView;\n    private ImageButton mCameraBtn;\n\n    private PreviewCameraCallbacks mCameraCallbacks = cameraCallbacks;\n\n    public interface PreviewCameraCallbacks {\n        void onCameraSwap();\n    }\n\n    private static PreviewCameraCallbacks cameraCallbacks = new PreviewCameraCallbacks() {\n        @Override\n        public void onCameraSwap() {}\n\n    };\n\n    private View.OnClickListener mBtnClickListener = new View.OnClickListener() {\n        public void onClick(View v) {\n            cameraSwap();\n        }\n    };\n\n    @Override\n    public void onAttach(Context context) {\n        Log.i(LOGTAG, \"OnAttach PreviewCameraFragment\");\n\n        super.onAttach(context);\n        this.mCameraCallbacks = (PreviewCameraCallbacks) context;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public void onAttach(Activity activity) {\n        super.onAttach(activity);\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            this.mCameraCallbacks = (PreviewCameraCallbacks) activity;\n        }\n    }\n\n    @Override\n    public void onDetach() {\n        Log.i(LOGTAG, \"OnDetach PreviewCameraFragment\");\n\n        super.onDetach();\n\n        mCameraCallbacks = cameraCallbacks;\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        Log.i(LOGTAG, \"onCreate PreviewCameraFragment\");\n\n        mRootView = inflater.inflate(R.layout.preview_camera_fragment, container, false);\n        mCameraBtn = (ImageButton) mRootView.findViewById(R.id.camera);\n        mCameraBtn.setOnClickListener(mBtnClickListener);\n\n        return mRootView;\n    }\n\n    public void cameraSwap() {\n        mCameraCallbacks.onCameraSwap();\n    }\n\n}\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/PreviewControlFragment.java",
    "content": "package com.tokbox.android.onetoonesample.ui;\n\nimport android.app.Activity;\nimport android.support.graphics.drawable.VectorDrawableCompat;\nimport android.support.v4.app.Fragment;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageButton;\nimport android.widget.RelativeLayout;\n\nimport com.tokbox.android.onetoonesample.MainActivity;\nimport com.tokbox.android.onetoonesample.R;\nimport com.tokbox.android.otsdkwrapper.utils.MediaType;\n\n\npublic class PreviewControlFragment extends Fragment {\n    private static final String LOGTAG = MainActivity.class.getName();\n\n    private MainActivity mActivity;\n\n    private View rootView;\n    private ImageButton mAudioBtn;\n    private ImageButton mVideoBtn;\n    private ImageButton mCallBtn;\n\n    private VectorDrawableCompat drawableStartCall;\n    private VectorDrawableCompat drawableEndCall;\n    private VectorDrawableCompat drawableBckBtn;\n\n    private PreviewControlCallbacks mControlCallbacks = previewCallbacks;\n\n    public interface PreviewControlCallbacks {\n\n        void onDisableLocalAudio(boolean audio);\n\n        void onDisableLocalVideo(boolean video);\n\n        void onCall();\n    }\n\n    private static PreviewControlCallbacks previewCallbacks = new PreviewControlCallbacks() {\n        @Override\n        public void onDisableLocalAudio(boolean audio) { }\n\n        @Override\n        public void onDisableLocalVideo(boolean video) { }\n\n        @Override\n        public void onCall() { }\n\n    };\n\n    private View.OnClickListener mBtnClickListener = new View.OnClickListener() {\n        public void onClick(View v) {\n            switch (v.getId()) {\n                case R.id.localAudio:\n                    updateLocalAudio();\n                    break;\n\n                case R.id.localVideo:\n                    updateLocalVideo();\n                    break;\n\n                case R.id.call:\n                    updateCall();\n                    break;\n            }\n        }\n    };\n\n    @Override\n    public void onAttach(Context context) {\n        Log.i(LOGTAG, \"OnAttach PreviewControlFragment\");\n\n        super.onAttach(context);\n\n        this.mActivity = (MainActivity) context;\n        this.mControlCallbacks = (PreviewControlCallbacks) context;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public void onAttach(Activity activity) {\n        super.onAttach(activity);\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n\n            this.mActivity = (MainActivity) activity;\n            this.mControlCallbacks = (PreviewControlCallbacks) activity;\n        }\n    }\n\n    @Override\n    public void onDetach() {\n        Log.i(LOGTAG, \"onDetach PreviewControlFragment\");\n\n        super.onDetach();\n\n        mControlCallbacks = previewCallbacks;\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        // Retain this fragment across configuration changes.\n        setRetainInstance(true);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        Log.i(LOGTAG, \"OnCreate PreviewControlFragment\");\n\n        rootView = inflater.inflate(R.layout.preview_actionbar_fragment, container, false);\n        mAudioBtn = (ImageButton) rootView.findViewById(R.id.localAudio);\n        mVideoBtn = (ImageButton) rootView.findViewById(R.id.localVideo);\n        mCallBtn = (ImageButton) rootView.findViewById(R.id.call);\n\n        drawableStartCall = VectorDrawableCompat.create(getResources(), R.drawable.initiate_call_button, null);\n        drawableEndCall = VectorDrawableCompat.create(getResources(), R.drawable.end_call_button, null);\n        drawableBckBtn = VectorDrawableCompat.create(getResources(), R.drawable.bckg_icon, null);\n\n        mAudioBtn.setImageResource(mActivity.getWrapper().isLocalMediaEnabled(MediaType.AUDIO)\n                ? R.drawable.mic_icon\n                : R.drawable.muted_mic_icon);\n        mAudioBtn.setBackground(drawableBckBtn);\n\n        mVideoBtn.setImageResource(mActivity.getWrapper().isLocalMediaEnabled(MediaType.VIDEO)\n                ? R.drawable.video_icon\n                : R.drawable.no_video_icon);\n        mVideoBtn.setBackground(drawableBckBtn);\n\n        mCallBtn.setImageResource(mActivity.isCallInProgress()\n                ? R.drawable.hang_up\n                : R.drawable.start_call);\n\n        mCallBtn.setBackground(mActivity.isCallInProgress()\n                ? drawableEndCall\n                : drawableStartCall);\n\n        mCallBtn.setOnClickListener(mBtnClickListener);\n\n        setEnabled(mActivity.isCallInProgress());\n\n        return rootView;\n    }\n\n    public void updateLocalAudio() {\n        if (!mActivity.getWrapper().isLocalMediaEnabled(MediaType.AUDIO)) {\n            mControlCallbacks.onDisableLocalAudio(true);\n            mAudioBtn.setImageResource(R.drawable.mic_icon);\n        } else {\n            mControlCallbacks.onDisableLocalAudio(false);\n            mAudioBtn.setImageResource(R.drawable.muted_mic_icon);\n        }\n    }\n\n    public void updateLocalVideo() {\n        if (!mActivity.getWrapper().isLocalMediaEnabled(MediaType.VIDEO)){\n            mControlCallbacks.onDisableLocalVideo(true);\n            mVideoBtn.setImageResource(R.drawable.video_icon);\n        } else {\n            mControlCallbacks.onDisableLocalVideo(false);\n            mVideoBtn.setImageResource(R.drawable.no_video_icon);\n        }\n    }\n\n    public void updateCall() {\n        mCallBtn.setImageResource(!mActivity.isCallInProgress()\n                ? R.drawable.hang_up\n                : R.drawable.start_call);\n\n        mCallBtn.setBackground(!mActivity.isCallInProgress()\n                ? drawableEndCall\n                : drawableStartCall);\n\n        if ( mControlCallbacks != null )\n            mControlCallbacks.onCall();\n    }\n\n    public void setEnabled(boolean enabled) {\n        if (mVideoBtn != null && mAudioBtn != null) {\n            if (enabled) {\n                mAudioBtn.setOnClickListener(mBtnClickListener);\n                mVideoBtn.setOnClickListener(mBtnClickListener);\n            } else {\n                mAudioBtn.setOnClickListener(null);\n                mVideoBtn.setOnClickListener(null);\n                mAudioBtn.setImageResource(R.drawable.mic_icon);\n                mVideoBtn.setImageResource(R.drawable.video_icon);\n            }\n        }\n    }\n\n    public void restart() {\n        setEnabled(false);\n        mCallBtn.setBackground(drawableStartCall);\n        mCallBtn.setImageResource(R.drawable.start_call);\n\n    }\n}"
  },
  {
    "path": "android/OneToOneSample/app/src/main/java/com/tokbox/android/onetoonesample/ui/RemoteControlFragment.java",
    "content": "package com.tokbox.android.onetoonesample.ui;\n\nimport android.app.Activity;\nimport android.support.v4.app.Fragment;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageButton;\nimport android.widget.RelativeLayout;\n\nimport com.tokbox.android.onetoonesample.MainActivity;\nimport com.tokbox.android.onetoonesample.R;\nimport com.tokbox.android.otsdkwrapper.utils.MediaType;\n\npublic class RemoteControlFragment extends Fragment {\n    private static final String LOGTAG = RemoteControlFragment.class.getSimpleName();\n    private static final int ANIMATION_DURATION = 7000;\n\n    private MainActivity mActivity;\n\n    private RelativeLayout mContainer;\n    private View mRootView;\n    private ImageButton mAudioBtn;\n    private ImageButton mVideoBtn;\n\n    private RemoteControlCallbacks mControlCallbacks = remoteCallbacks;\n\n    private String mRemoteId;\n\n    public interface RemoteControlCallbacks {\n        void onDisableRemoteAudio(boolean audio);\n\n        void onDisableRemoteVideo(boolean video);\n    }\n\n    private static RemoteControlCallbacks remoteCallbacks = new RemoteControlCallbacks() {\n        @Override\n        public void onDisableRemoteAudio(boolean audio) { }\n\n        @Override\n        public void onDisableRemoteVideo(boolean video) { }\n    };\n\n    private View.OnClickListener mBtnClickListener = new View.OnClickListener() {\n        public void onClick(View v) {\n            switch (v.getId()) {\n                case R.id.remoteAudio:\n                    updateRemoteAudio();\n                    break;\n\n                case R.id.remoteVideo:\n                    updateRemoteVideo();\n                    break;\n            }\n        }\n    };\n\n    @Override\n    public void onAttach(Context context) {\n        Log.i(LOGTAG, \"OnAttach RemoteControlFragment\");\n\n        super.onAttach(context);\n\n        this.mActivity = (MainActivity) context;\n        this.mControlCallbacks = (RemoteControlCallbacks) context;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public void onAttach(Activity activity) {\n        super.onAttach(activity);\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            this.mActivity = (MainActivity) activity;\n            this.mControlCallbacks = (RemoteControlCallbacks) activity;\n        }\n\n        if ( mRemoteId == null ) {\n            mRemoteId = getArguments().getString(\"remoteId\");\n        }\n    }\n\n    @Override\n    public void onDetach() {\n        Log.i(LOGTAG, \"OnDetach RemoteControlFragment\");\n        super.onDetach();\n        mControlCallbacks = remoteCallbacks;\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        Log.i(LOGTAG, \"OnCreate RemoteControlFragment\");\n\n        mRootView = inflater.inflate(R.layout.remote_actionbar_fragment, container, false);\n\n        mContainer = (RelativeLayout) this.mActivity.findViewById(R.id.actionbar_remote_fragment_container);\n        mAudioBtn = (ImageButton) mRootView.findViewById(R.id.remoteAudio);\n        mVideoBtn = (ImageButton) mRootView.findViewById(R.id.remoteVideo);\n\n        mAudioBtn.setOnClickListener(mBtnClickListener);\n        mVideoBtn.setOnClickListener(mBtnClickListener);\n        return mRootView;\n    }\n\n    public void updateRemoteAudio(){\n        if(mRemoteId != null && !mActivity.getWrapper().isReceivedMediaEnabled(mRemoteId, MediaType.AUDIO)){\n            mControlCallbacks.onDisableRemoteAudio(true);\n            mAudioBtn.setImageResource(R.drawable.audio);\n        }\n        else {\n            mControlCallbacks.onDisableRemoteAudio(false);\n            mAudioBtn.setImageResource(R.drawable.no_audio);\n        }\n    }\n\n    public void updateRemoteVideo(){\n        if(mRemoteId != null && !mActivity.getWrapper().isReceivedMediaEnabled(mRemoteId, MediaType.VIDEO)){\n            mControlCallbacks.onDisableRemoteVideo(true);\n            mVideoBtn.setImageResource(R.drawable.video_icon);\n        }\n        else {\n            mControlCallbacks.onDisableRemoteVideo(false);\n            mVideoBtn.setImageResource(R.drawable.no_video_icon);\n        }\n    }\n\n    public void show(){\n        mContainer.setVisibility(View.VISIBLE);\n        mRootView.setVisibility(View.VISIBLE);\n\n        mContainer.postDelayed(new Runnable() {\n            public void run() {\n                mContainer.setVisibility(View.INVISIBLE);\n            }\n        }, ANIMATION_DURATION);\n    }\n\n    private void setEnabled(boolean enabled) {\n        if (mVideoBtn != null && mAudioBtn != null) {\n            if (!enabled) {\n                mAudioBtn.setImageResource(R.drawable.audio);\n                mVideoBtn.setImageResource(R.drawable.video_icon);\n            }\n        }\n    }\n\n    public void restart() {\n        setEnabled(false);\n        mContainer.setVisibility(View.INVISIBLE);\n    }\n\n}\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/drawable/bckg_audio_only.xml",
    "content": "<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    android:shape=\"rectangle\">\n    <solid android:color=\"@color/audiOnlyBackground\"/>\n    <stroke android:color=\"@color/white\" android:width=\"1dp\"/>\n</shape>"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/drawable/bckg_icon.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:width=\"106dp\"\n        android:height=\"106dp\"\n        android:viewportWidth=\"106.0\"\n        android:viewportHeight=\"106.0\">\n    <path\n        android:pathData=\"M1.26,52.61a51.93,51.91 0,1 0,103.87 0a51.93,51.91 0,1 0,-103.87 0z\"\n        android:strokeWidth=\"1\"\n        android:fillColor=\"#66000000\"\n        android:strokeColor=\"#979797\" />\n</vector>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/drawable/end_call_button.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"105dp\"\n    android:height=\"105dp\"\n    android:viewportWidth=\"105.0\"\n    android:viewportHeight=\"105.0\">\n    <path\n        android:pathData=\"M0.25,52.68a51.93,51.91 0,1 0,103.87 0a51.93,51.91 0,1 0,-103.87 0z\"\n        android:strokeWidth=\"1\"\n        android:fillColor=\"@color/endCall\"\n        android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/drawable/gradient_audionly.xml",
    "content": "<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <gradient\n        android:startColor=\"@color/gradientAudioOnlyStart\"\n        android:endColor=\"@color/gradientAudioOnlyEnd\"\n        android:angle=\"270\" />\n</shape>"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/drawable/gradient_backg.xml",
    "content": "<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <gradient\n        android:startColor=\"@color/gradientStart\"\n        android:endColor=\"@color/gradientEnd\"\n        android:angle=\"270\" />\n</shape>"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/drawable/initiate_call_button.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:width=\"105dp\"\n        android:height=\"105dp\"\n        android:viewportWidth=\"105.0\"\n        android:viewportHeight=\"105.0\">\n    <path\n        android:pathData=\"M0.25,52.68a51.93,51.91 0,1 0,103.87 0a51.93,51.91 0,1 0,-103.87 0z\"\n        android:strokeWidth=\"1\"\n        android:fillColor=\"@color/startCall\"\n        android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/drawable/preview.xml",
    "content": "<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    android:shape=\"rectangle\">\n    <stroke android:color=\"@color/white\" android:width=\"1dp\"/>\n</shape>"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/gradient_backg\"\n    android:keepScreenOn=\"true\"\n    tools:context=\"com.tokbox.android.onetoonesample.MainActivity\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n        <TextView\n            android:id=\"@+id/quality_warning\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/alert_bar_height\"\n            android:background=\"@color/quality_warning\"\n            android:gravity=\"center\"\n            android:text=\"@string/network_quality\"\n            android:textColor=\"@color/warning_text\"\n            android:textSize=\"@dimen/alert_text\"\n            android:visibility=\"gone\"></TextView>\n\n        <RelativeLayout\n            android:id=\"@+id/subscriberview\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clickable=\"false\"\n            android:gravity=\"center_horizontal\"\n            android:onClick=\"showRemoteControlBar\">\n\n            <RelativeLayout\n                android:id=\"@+id/remoteAudioOnlyView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:layout_alignParentLeft=\"true\"\n                android:layout_alignParentTop=\"true\"\n                android:background=\"@drawable/gradient_audionly\"\n                android:visibility=\"gone\" >\n\n                <ImageView\n                    android:id=\"@+id/avatar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentBottom=\"true\"\n                    android:layout_centerHorizontal=\"true\"\n                    android:layout_marginBottom=\"30dp\"\n                    android:src=\"@drawable/avatar\" />\n\n            </RelativeLayout>\n\n        </RelativeLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/publisherview\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <RelativeLayout\n                android:id=\"@+id/localAudioOnlyView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:layout_alignParentLeft=\"true\"\n                android:layout_alignParentTop=\"true\"\n                android:background=\"@drawable/gradient_audionly\"\n                android:visibility=\"gone\" >\n\n                <ImageView\n                    android:id=\"@+id/localAvatar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentBottom=\"true\"\n                    android:layout_centerHorizontal=\"true\"\n                    android:layout_marginBottom=\"30dp\"\n                    android:src=\"@drawable/avatar\" />\n\n            </RelativeLayout>\n        </RelativeLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/actionbar_preview_fragment_container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/action_bar_height\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_centerHorizontal=\"true\"\n            android:visibility=\"visible\"></RelativeLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/camera_preview_fragment_container\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_alignParentTop=\"true\"\n            android:layout_marginRight=\"21.5dp\"\n            android:layout_marginTop=\"37.5dp\"\n            android:visibility=\"visible\"></RelativeLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/actionbar_remote_fragment_container\"\n            android:layout_width=\"@dimen/action_bar_width\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_alignParentTop=\"true\"\n            android:layout_marginLeft=\"21.5dp\"\n            android:layout_marginTop=\"37.5dp\"\n            android:visibility=\"visible\"></RelativeLayout>\n    </RelativeLayout>\n\n\n</android.support.design.widget.CoordinatorLayout>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/layout/preview_actionbar_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/layoutPreviewActionBar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:id=\"@+id/previewFragment\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"@dimen/action_bar_height\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\">\n\n        <ImageButton\n            android:id=\"@+id/localVideo\"\n            android:layout_width=\"@dimen/icon_width\"\n            android:layout_height=\"@dimen/icon_height\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginRight=\"14.1dp\"\n            android:layout_toLeftOf=\"@+id/call\"\n            android:background=\"@drawable/bckg_icon\"\n            android:src=\"@drawable/video_icon\" />\n\n        <ImageButton\n            android:id=\"@+id/call\"\n            android:layout_width=\"@dimen/icon_width\"\n            android:layout_height=\"@dimen/icon_height\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"@drawable/initiate_call_button\"\n            android:clickable=\"true\"\n            android:src=\"@drawable/start_call\" />\n\n        <ImageButton\n            android:id=\"@+id/localAudio\"\n            android:layout_width=\"@dimen/icon_width\"\n            android:layout_height=\"@dimen/icon_height\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginLeft=\"14.1dp\"\n            android:layout_toRightOf=\"@+id/call\"\n            android:background=\"@drawable/bckg_icon\"\n            android:src=\"@drawable/mic_icon\" />\n\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/layout/preview_camera_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/layoutPreviewCamera\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/action_bar_height\"\n    android:layout_alignParentTop=\"true\">\n\n    <ImageButton\n        android:id=\"@+id/camera\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:background=\"@null\"\n        android:src=\"@drawable/camera\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/layout/remote_actionbar_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/layoutPreviewActionBar\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"match_parent\"\n    android:visibility=\"gone\">\n\n    <RelativeLayout\n        android:id=\"@+id/remoteFragment\"\n        android:layout_width=\"@dimen/action_bar_width\"\n        android:layout_height=\"match_parent\"\n        android:layout_alignParentLeft=\"true\">\n\n        <ImageButton\n            android:id=\"@+id/remoteVideo\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@null\"\n            android:enabled=\"false\"\n            android:src=\"@drawable/video_icon\" />\n\n        <ImageButton\n            android:id=\"@+id/remoteAudio\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@+id/remoteVideo\"\n            android:layout_marginTop=\"25dp\"\n            android:background=\"@null\"\n            android:enabled=\"false\"\n            android:src=\"@drawable/audio\" />\n\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"gradientStart\">#5B5B5B</color>\n    <color name=\"gradientEnd\">#393939</color>\n    <color name=\"gradientAudioOnlyStart\">#343434</color>\n    <color name=\"gradientAudioOnlyEnd\">#6e6e6e</color>\n    <color name=\"audiOnlyBackground\">#7e7e7e</color>\n    <color name=\"white\">#ffffff</color>\n    <color name=\"black\">#000000</color>\n    <color name=\"startCall\">#6aadbf</color>\n    <color name=\"endCall\">#cb1e28</color>\n    <color name=\"quality_alert\">#cb1e28</color>\n    <color name=\"quality_warning\">#fef9c8</color>\n    <color name=\"warning_text\">#ad7212</color>\n</resources>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"border\">1dp</dimen>\n    <dimen name=\"action_bar_height\">71dp</dimen>\n    <dimen name=\"action_bar_width\">71dp</dimen>\n    <dimen name=\"icon_width\">52dp</dimen>\n    <dimen name=\"icon_height\">52dp</dimen>\n    <dimen name=\"alert_bar_height\">30dp</dimen>\n    <dimen name=\"alert_text\">14sp</dimen>\n    <dimen name=\"preview_width\">90dp</dimen>\n    <dimen name=\"preview_height\">78dp</dimen>\n    <dimen name=\"preview_rightMargin\">24dp</dimen>\n    <dimen name=\"preview_bottomMargin\">75dp</dimen>\n</resources>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">OneToOneSample</string>\n    <string name=\"network_quality\">Network connection is unstable.</string>\n    <string name=\"reconnecting\">The session is reconnecting</string>\n    <string name=\"reconnected\">The session reconnected</string>\n    <string name=\"permissions_denied_title\">Permissions Denied</string>\n    <string name=\"permissions_denied\">Cannot use this app without requested permission. Please, grant audio and video permissions</string>\n    <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>\n</resources>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    </style>\n\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n\n    <style name=\"AppTheme.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\" />\n\n    <style name=\"AppTheme.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\" />\n\n</resources>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/values-v21/styles.xml",
    "content": "<resources>\n\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/OneToOneSample/app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "android/OneToOneSample/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.2.3'\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        maven {\n            url  \"http://tokbox.bintray.com/maven\"\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "android/OneToOneSample/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Jan 19 16:41:06 CET 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.14.1-all.zip\n"
  },
  {
    "path": "android/OneToOneSample/gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "android/OneToOneSample/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "android/OneToOneSample/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "android/OneToOneSample/settings.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "android/README.md",
    "content": "![logo](../tokbox-logo.png)\n\n# OpenTok One-to-One Communication Sample App for Android\n\n## Quick start\n\nThis section shows you how to prepare, build, and run the sample application.\n\n### Install the project files\n\n1. 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.\n1. Start Android Studio.\n1. In the **Quick Start** panel, click **Open an existing Android Studio Project**.\n1. Navigate to the **android** folder, select the **OnetoOneSample** folder, and click **Choose**.\n\n\n### Add the Accelerator Core Android\n\nThere are two options for installing the OpenTok SDK included in the Accelerator Pack Common for Android:\n\n\n#### Using the repository\n\n1. Clone the [OpenTok Accelerator Core repo](https://github.com/opentok/accelerator-core-android).\n2. From your app project, right-click the app name and select **New > Module > Import Gradle Project**.\n3. Navigate to the directory in which you cloned **OpenTok Accelerator Pack**, select **accelerator-core**, and click **Finish**.\n4. Open the **build.gradle** file for the app and ensure the following lines have been added to the `dependencies` section:\n\n```\ncompile project(':accelerator-core-android')\n\n```\n\n#### Using Maven\n\n1. Modify the `build.gradle` for your solution and add the following code snippet to the section labeled `repositories`:\n\n  ```gradle\n    maven { url  \"http://tokbox.bintray.com/maven\" }\n  ```\n\n1. Modify the `build.gradle` for your activity and add the following code snippet to the section labeled `dependencies`:\n  \n  ```gradle\n    compile 'com.opentok.android:accelerator-core-android:+'\n  ```\n\n### Configure and build the app\n\nConfigure the sample app code. Then, build and run the app.\n\n1. Get values for **API Key**, **Session ID**, and **Token**. See [OpenTok One-to-One Communication Sample App home page](../README.md) for important information.\n\nIn Android Studio, open **OpenTokConfig.java** and replace the following empty strings with the corresponding **API Key**, **Session ID**, and **Token** values:\n\n  ```java\n    // Replace with a generated Session ID\n    public static final String SESSION_ID = \"\";\n\n    // Replace with a generated token\n    public static final String TOKEN = \"\";\n\n    // Replace with your OpenTok API key\n    public static final String API_KEY = \"\";\n  ```\n\n  ```java\n    //init the wrapper\n    OTConfig config =\n          new OTConfig.OTConfigBuilder(OpenTokConfig.SESSION_ID, OpenTokConfig.TOKEN,\n            OpenTokConfig.API_KEY).name(\"one-to-one-sample-app\").subscribeAutomatically(true).subscribeToSelf(false).build();\n    \n    if ( config != null ) {\n      mWrapper = new OTWrapper(MainActivity.this, config);\n      mWrapper.addBasicListener(mBasicListener);\n      mWrapper.addAdvancedListener(mAdvancedListener);\n\n      //...\n    }\n  ```\n\n## Exploring the code\n\nThis section describes best practices the sample app code uses to implement the one-to-one communication features.\n\nFor 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).\n\n\n### Class design\n\nThis 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).\n\n| Class        | Description  |\n| ------------- | ------------- |\n| `MainActivity`    | Implements the UI and media control callbacks. |\n| `OpenTokConfig`   | Stores the information required to configure the session and authorize the app to make requests to the backend server.   |\n| `PreviewControlFragment`   | Manages the toolbar for the local audio and video controls, and the start/end call button. |\n| `RemoteControlFragment`   | Manages the icons to enable/disable the audio and video of the remote subscriber. |\n| `PreviewCameraFragment `   | Manages the camera control. |\n\n\n### Session and stream management\n\nThe `OTWrapper` class, included in the Accelerator Core for Android, is the backbone of the one-to-one communication features for the app.\n\nThis class uses the OpenTok API to initiate the client connection to the OpenTok session and manage the audio and video streams.\n```java\n  \n  mWrapper.connect();\n  \n  mWrapper.startPublishingMedia(new PreviewConfig.PreviewConfigBuilder().\n                        name(\"Tokboxer\").build(), false);\n\n  mWrapper.enableLocalMedia(MediaType.AUDIO, audio);\n  \n  mWrapper.disconnect();\n\n```\n\nThe BasicListener and AdvancedListener interface monitor state changes in the communication, and defines the following methods:\n\n```java\n  //Basic Listener from OTWrapper\n  private BasicListener mBasicListener =\n    new PausableBasicListener(new BasicListener<OTWrapper>() {\n    @Override\n    public void onConnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException { //...}\n    @Override\n    public void onDisconnected(OTWrapper otWrapper, int participantsCount, String connId, String data) throws ListenerException { //...}\n    @Override\n    public void onPreviewViewReady(OTWrapper otWrapper, View localView) throws ListenerException { //...}\n    @Override\n    public void onRemoteViewReady(OTWrapper otWrapper, View remoteView, String remoteId, String data) throws ListenerException { //...}\n    @Override\n    public void onStartedPublishingMedia(OTWrapper otWrapper, boolean screensharing) throws ListenerException { //...}\n    //...\n  });\n\n```\n```java\n  //Advanced Listener from OTWrapper\n  private AdvancedListener mAdvancedListener =\n    new PausableAdvancedListener(new AdvancedListener<OTWrapper>() {\n    @Override\n    public void onCameraChanged(OTWrapper otWrapper) throws ListenerException { //... }\n    @Override\n    public void onReconnecting(OTWrapper otWrapper) throws ListenerException { //... }\n    @Override\n    public void onReconnected(OTWrapper otWrapper) throws ListenerException { //... }\n    @Override\n    public void onVideoQualityWarning(OTWrapper otWrapper, String remoteId) throws ListenerException { //... }\n    //...\n  });\n```\n### User interface\n\nAs described in [Class design](#class-design), the following classes set up and manage the UI fragments for the local and remote controls:\n\n   - `PreviewControlFragment`\n   - `RemoteControlFragment`\n   - `PreviewCameraFragment`\n\n\nThese classes work with the following `MainActivity` methods, which manage the views as the publisher and subscriber participate in the session.\n\n## Requirements\n\nTo develop your one-to-one communication app:\n\n1. Install [Android Studio](http://developer.android.com/intl/es/sdk/index.html)\n1. Review the [OpenTok Android SDK Requirements](https://tokbox.com/developer/sdks/android/#developerandclientrequirements)\n"
  },
  {
    "path": "iOS/.gitignore",
    "content": ".DS_Store\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\n*.xcworkspace\n!default.xcworkspace\nxcuserdata\nprofile\n*.moved-aside\nDerivedData\n*.xcdatamodeld\n.idea/\n# Pods - for those of you who use CocoaPods\nPods\n\n*.framework\n*.bundle\nPodfile.lock\n\n"
  },
  {
    "path": "iOS/OneToOneSample.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t70F017EE1E004A7A008EF97D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017E01E004A7A008EF97D /* AppDelegate.m */; };\n\t\t70F017EF1E004A7A008EF97D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 70F017E11E004A7A008EF97D /* Assets.xcassets */; };\n\t\t70F017F01E004A7A008EF97D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70F017E21E004A7A008EF97D /* LaunchScreen.storyboard */; };\n\t\t70F017F11E004A7A008EF97D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70F017E41E004A7A008EF97D /* Main.storyboard */; };\n\t\t70F017F31E004A7A008EF97D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017E71E004A7A008EF97D /* main.m */; };\n\t\t70F017F41E004A7A008EF97D /* MainView.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017E91E004A7A008EF97D /* MainView.m */; };\n\t\t70F017F51E004A7A008EF97D /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017EB1E004A7A008EF97D /* MainViewController.m */; };\n\t\t70F017F61E004A7A008EF97D /* UIView+Helper.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F017ED1E004A7A008EF97D /* UIView+Helper.m */; };\n\t\tA0A0E85D1E441C6C003F319C /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = A0A0E85C1E441C6C003F319C /* Podfile */; };\n\t\tA0A0E85F1E441C70003F319C /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = A0A0E85E1E441C70003F319C /* README.md */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t70F017981E00458D008EF97D /* OneToOneSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OneToOneSample.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t70F017DF1E004A7A008EF97D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\t70F017E01E004A7A008EF97D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\t70F017E11E004A7A008EF97D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t70F017E31E004A7A008EF97D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t70F017E51E004A7A008EF97D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t70F017E61E004A7A008EF97D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t70F017E71E004A7A008EF97D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\t70F017E81E004A7A008EF97D /* MainView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainView.h; sourceTree = \"<group>\"; };\n\t\t70F017E91E004A7A008EF97D /* MainView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainView.m; sourceTree = \"<group>\"; };\n\t\t70F017EA1E004A7A008EF97D /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = \"<group>\"; };\n\t\t70F017EB1E004A7A008EF97D /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = \"<group>\"; };\n\t\t70F017EC1E004A7A008EF97D /* UIView+Helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"UIView+Helper.h\"; sourceTree = \"<group>\"; };\n\t\t70F017ED1E004A7A008EF97D /* UIView+Helper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"UIView+Helper.m\"; sourceTree = \"<group>\"; };\n\t\tA0A0E85C1E441C6C003F319C /* Podfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Podfile; sourceTree = \"<group>\"; };\n\t\tA0A0E85E1E441C70003F319C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t70F017951E00458D008EF97D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t70F0178F1E00458D008EF97D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tA0A0E85E1E441C70003F319C /* README.md */,\n\t\t\t\tA0A0E85C1E441C6C003F319C /* Podfile */,\n\t\t\t\t70F017DE1E004A7A008EF97D /* SampleApp */,\n\t\t\t\t70F017991E00458D008EF97D /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t70F017991E00458D008EF97D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t70F017981E00458D008EF97D /* OneToOneSample.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t70F017DE1E004A7A008EF97D /* SampleApp */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t70F017DF1E004A7A008EF97D /* AppDelegate.h */,\n\t\t\t\t70F017E01E004A7A008EF97D /* AppDelegate.m */,\n\t\t\t\t70F017F71E004A88008EF97D /* Resources */,\n\t\t\t\t70F017E71E004A7A008EF97D /* main.m */,\n\t\t\t\t70F017E81E004A7A008EF97D /* MainView.h */,\n\t\t\t\t70F017E91E004A7A008EF97D /* MainView.m */,\n\t\t\t\t70F017EA1E004A7A008EF97D /* MainViewController.h */,\n\t\t\t\t70F017EB1E004A7A008EF97D /* MainViewController.m */,\n\t\t\t\t70F017EC1E004A7A008EF97D /* UIView+Helper.h */,\n\t\t\t\t70F017ED1E004A7A008EF97D /* UIView+Helper.m */,\n\t\t\t);\n\t\t\tpath = SampleApp;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t70F017F71E004A88008EF97D /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t70F017E11E004A7A008EF97D /* Assets.xcassets */,\n\t\t\t\t70F017E21E004A7A008EF97D /* LaunchScreen.storyboard */,\n\t\t\t\t70F017E41E004A7A008EF97D /* Main.storyboard */,\n\t\t\t\t70F017E61E004A7A008EF97D /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t70F017971E00458D008EF97D /* OneToOneSample */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 70F017BA1E00458D008EF97D /* Build configuration list for PBXNativeTarget \"OneToOneSample\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t70F017941E00458D008EF97D /* Sources */,\n\t\t\t\t70F017951E00458D008EF97D /* Frameworks */,\n\t\t\t\t70F017961E00458D008EF97D /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = OneToOneSample;\n\t\t\tproductName = OneToOneSample;\n\t\t\tproductReference = 70F017981E00458D008EF97D /* OneToOneSample.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t70F017901E00458D008EF97D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0810;\n\t\t\t\tORGANIZATIONNAME = \"Tokbox, Inc.\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t70F017971E00458D008EF97D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 8.1;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 70F017931E00458D008EF97D /* Build configuration list for PBXProject \"OneToOneSample\" */;\n\t\t\tcompatibilityVersion = \"Xcode 3.2\";\n\t\t\tdevelopmentRegion = English;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 70F0178F1E00458D008EF97D;\n\t\t\tproductRefGroup = 70F017991E00458D008EF97D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t70F017971E00458D008EF97D /* OneToOneSample */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t70F017961E00458D008EF97D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t70F017F11E004A7A008EF97D /* Main.storyboard in Resources */,\n\t\t\t\t70F017F01E004A7A008EF97D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tA0A0E85D1E441C6C003F319C /* Podfile in Resources */,\n\t\t\t\t70F017EF1E004A7A008EF97D /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t70F017941E00458D008EF97D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tA0A0E85F1E441C70003F319C /* README.md in Sources */,\n\t\t\t\t70F017EE1E004A7A008EF97D /* AppDelegate.m in Sources */,\n\t\t\t\t70F017F61E004A7A008EF97D /* UIView+Helper.m in Sources */,\n\t\t\t\t70F017F51E004A7A008EF97D /* MainViewController.m in Sources */,\n\t\t\t\t70F017F41E004A7A008EF97D /* MainView.m in Sources */,\n\t\t\t\t70F017F31E004A7A008EF97D /* main.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t70F017E21E004A7A008EF97D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t70F017E31E004A7A008EF97D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t70F017E41E004A7A008EF97D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t70F017E51E004A7A008EF97D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t70F017B81E00458D008EF97D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVES = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.1;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t70F017B91E00458D008EF97D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVES = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.1;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t70F017BB1E00458D008EF97D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = SampleApp/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tokbox.OneToOneSample;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t70F017BC1E00458D008EF97D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = SampleApp/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tokbox.OneToOneSample;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t70F017931E00458D008EF97D /* Build configuration list for PBXProject \"OneToOneSample\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t70F017B81E00458D008EF97D /* Debug */,\n\t\t\t\t70F017B91E00458D008EF97D /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t70F017BA1E00458D008EF97D /* Build configuration list for PBXNativeTarget \"OneToOneSample\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t70F017BB1E00458D008EF97D /* Debug */,\n\t\t\t\t70F017BC1E00458D008EF97D /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 70F017901E00458D008EF97D /* Project object */;\n}\n"
  },
  {
    "path": "iOS/OneToOneSample.xcodeproj/xcshareddata/xcschemes/OneToOneSample.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0800\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"70F017971E00458D008EF97D\"\n               BuildableName = \"OneToOneSample.app\"\n               BlueprintName = \"OneToOneSample\"\n               ReferencedContainer = \"container:OneToOneSample.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"A034743C1CCD15A800198DB4\"\n               BuildableName = \"OneToOneSampleTests.xctest\"\n               BlueprintName = \"OneToOneSampleTests\"\n               ReferencedContainer = \"container:OneToOneSample.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"70F017B01E00458D008EF97D\"\n               BuildableName = \"OneToOneSampleUITests.xctest\"\n               BlueprintName = \"OneToOneSampleUITests\"\n               ReferencedContainer = \"container:OneToOneSample.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"70F017971E00458D008EF97D\"\n            BuildableName = \"OneToOneSample.app\"\n            BlueprintName = \"OneToOneSample\"\n            ReferencedContainer = \"container:OneToOneSample.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"70F017971E00458D008EF97D\"\n            BuildableName = \"OneToOneSample.app\"\n            BlueprintName = \"OneToOneSample\"\n            ReferencedContainer = \"container:OneToOneSample.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"70F017971E00458D008EF97D\"\n            BuildableName = \"OneToOneSample.app\"\n            BlueprintName = \"OneToOneSample\"\n            ReferencedContainer = \"container:OneToOneSample.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/Podfile",
    "content": "\nplatform :ios, '9.0'\n\ntarget 'OneToOneSample' do \n  pod 'OTAcceleratorCore', '1.0.3'\n  pod 'SVProgressHUD'\nend\n"
  },
  {
    "path": "iOS/README.md",
    "content": "![logo](../tokbox-logo.png)\n\n# OpenTok One-to-One Communication Sample App for iOS<br/>Version 1.3\n\n## Quick start\n\nThis 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).\n\n### Install the project files\n\nUse CocoaPods to install the project files and dependencies.\n\n1. Install CocoaPods as described in [CocoaPods Getting Started](https://guides.cocoapods.org/using/getting-started.html#getting-started).\n1. In Terminal, `cd` to your project directory and type `pod install`.\n1. Reopen your project in Xcode using the new `*.xcworkspace` file.\n\n### Configure and build the app\n\nConfigure the sample app code. Then, build and run the app.\n\n1. Get values for **API Key**, **Session ID**, and **Token**. See [OpenTok One-to-One Communication Sample App home page](../README.md) for important information.\n\n1. Replace the following empty strings with the corresponding **API Key**, **Session ID**, and **Token** values:\n\n    ```objc\n    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n\n        // Override point for customization after application launch.    \n        [OTAcceleratorSession setOpenTokApiKey:@\"\"\n                                     sessionId:@\"\"\n                                         token:@\"\"];\n        return YES;\n    }\n    ```\n\n1. Use Xcode to build and run the app on an iOS simulator or device.\n\n## Exploring the code\n\nFor detail about the APIs used to develop this sample, see the [OpenTok iOS SDK Reference](https://tokbox.com/developer/sdks/ios/reference/).\n\n_**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._\n\n### Session and stream management\n\nThe `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:\n\n```objc\n[self.oneToOneCommunicator connectWithHandler:^(OTOneToOneCommunicationSignal signal, NSError *error) {\n    if (!error) {\n        if (signal == OTSessionDidConnect) {\n            // publisher view is available, now you can add subscriber view to your desired view\n        }\n        else if (signal == OTSubscriberDidConnect) {\n            // subscriber view is available, now you can add subscriber view to your desired view\n        }\n    }\n    else {\n        \n    }\n}];\n```\n\nThe following enum notifies the main controller of all session, publisher, and subscriber events:\n\n```objc\ntypedef NS_ENUM(NSUInteger, OTOneToOneCommunicationSignal) {\n    OTSessionDidConnect = 0,\n    OTSessionDidDisconnect,\n    OTSessionDidFail,\n    OTSessionStreamCreated,\n    OTSessionStreamDestroyed,\n    OTSessionDidBeginReconnecting,\n    OTSessionDidReconnect,\n    OTPublisherDidFail,\n    OTPublisherStreamCreated,\n    OTPublisherStreamDestroyed,\n    OTSubscriberDidConnect,\n    OTSubscriberDidFail,\n    OTSubscriberVideoDisabledByPublisher,\n    OTSubscriberVideoDisabledBySubscriber,\n    OTSubscriberVideoDisabledByBadQuality,\n    OTSubscriberVideoEnabledByPublisher,\n    OTSubscriberVideoEnabledBySubscriber,\n    OTSubscriberVideoEnabledByGoodQuality,\n    OTSubscriberVideoDisableWarning,\n    OTSubscriberVideoDisableWarningLifted,\n};\n```\n## Requirements\n\nTo develop your one-to-one communication app:\n\n1. Install Xcode version 5 or later.\n2. Review the [OpenTok iOS SDK Requirements](https://tokbox.com/developer/sdks/ios/).\n"
  },
  {
    "path": "iOS/SampleApp/AppDelegate.h",
    "content": "//\n//  AppDelegate.h\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@class OTAcceleratorSession;\n\n@interface AppDelegate : UIResponder <UIApplicationDelegate>\n\n@property (strong, nonatomic) UIWindow *window;\n@property (nonatomic, strong, readonly) OTAcceleratorSession* acceleratorSession;\n\n@end\n"
  },
  {
    "path": "iOS/SampleApp/AppDelegate.m",
    "content": "//\n//  AppDelegate.m\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"AppDelegate.h\"\n#import \"OTAcceleratorSession.h\"\n\n@interface AppDelegate ()\n@property (nonatomic, strong) OTAcceleratorSession* acceleratorSession;\n@end\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n    // Override point for customization after application launch.\n    self.acceleratorSession = [[OTAcceleratorSession alloc] initWithOpenTokApiKey:@\"<# Replace #>\"\n                                                                        sessionId:@\"<# Replace #>\"\n                                                                            token:@\"<# Replace #>\"];\n    return YES;\n}\n\n- (void)applicationWillResignActive:(UIApplication *)application {\n  // 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.\n  // 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.\n}\n\n- (void)applicationDidEnterBackground:(UIApplication *)application {\n  // 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.\n  // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n}\n\n- (void)applicationWillEnterForeground:(UIApplication *)application {\n  // 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.\n}\n\n- (void)applicationDidBecomeActive:(UIApplication *)application {\n  // 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.\n}\n\n- (void)applicationWillTerminate:(UIApplication *)application {\n  // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n  // Saves changes in the application's managed object context before the application terminates.\n}\n\n@end\n"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-40@2x-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-40@3x-1.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-Small.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-40@2x-2.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-40.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-40@2x-3.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-76.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"24x24\",\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"role\" : \"notificationCenter\",\n      \"subtype\" : \"38mm\"\n    },\n    {\n      \"size\" : \"27.5x27.5\",\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"role\" : \"notificationCenter\",\n      \"subtype\" : \"42mm\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"watch\",\n      \"filename\" : \"Icon-Small@2x.png\",\n      \"role\" : \"companionSettings\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"watch\",\n      \"filename\" : \"Icon-Small@3x.png\",\n      \"role\" : \"companionSettings\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"role\" : \"appLauncher\",\n      \"subtype\" : \"38mm\"\n    },\n    {\n      \"size\" : \"86x86\",\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"role\" : \"quickLook\",\n      \"subtype\" : \"38mm\"\n    },\n    {\n      \"size\" : \"98x98\",\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"role\" : \"quickLook\",\n      \"subtype\" : \"42mm\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/audio.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"audio.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"audio@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"audio@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/hangUp.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"hangUp.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"hangUp@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"hangUp@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/mic.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"mic.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"mic@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"mic@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/mutedMic.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"mutedMicLineCopy.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"mutedMicLineCopy@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"mutedMicLineCopy@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/noAudio.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"noSoundCopy.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"noSoundCopy@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"noSoundCopy@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/noVideo.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"noVideoIcon.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"noVideoIcon@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"noVideoIcon@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/reverse cameras.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"reverse cameras.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"reverse cameras@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"reverse cameras@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/startCall.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"startCall.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"startCall@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"startCall@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Assets.xcassets/video.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"videoIcon.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"videoIcon@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"videoIcon@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/SampleApp/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"11757\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"nPX-gN-iCp\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"s0L-bH-Ejt\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <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\">\n                                <rect key=\"frame\" x=\"67.5\" y=\"315.5\" width=\"240\" height=\"36\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"30\"/>\n                                <color key=\"textColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"e8h-0e-daP\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"Xte-x5-DI8\"/>\n                            <constraint firstItem=\"e8h-0e-daP\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"aXa-6M-aml\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/SampleApp/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"11757\"/>\n        <capability name=\"Aspect ratio constraints\" minToolsVersion=\"5.1\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Main View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController autoresizesArchivedViewToFullSize=\"NO\" id=\"BYZ-38-t0r\" customClass=\"MainViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"ibB-sB-NO2\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"g1w-3N-XuV\"/>\n                    </layoutGuides>\n                    <view key=\"view\" clearsContextBeforeDrawing=\"NO\" contentMode=\"scaleToFill\" id=\"HtV-zK-mFJ\" customClass=\"MainView\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"zJt-pm-lIX\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                            </view>\n                            <view contentMode=\"scaleAspectFit\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"EoV-Kh-ae4\">\n                                <rect key=\"frame\" x=\"265\" y=\"471\" width=\"90\" height=\"90\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"width\" constant=\"90\" id=\"2ZZ-pD-0bi\"/>\n                                    <constraint firstAttribute=\"width\" secondItem=\"EoV-Kh-ae4\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"HVT-by-pPc\"/>\n                                </constraints>\n                            </view>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"QaT-qb-Mgd\">\n                                <rect key=\"frame\" x=\"84.5\" y=\"581\" width=\"206\" height=\"66\"/>\n                                <subviews>\n                                    <button opaque=\"NO\" alpha=\"0.5\" contentMode=\"scaleToFill\" enabled=\"NO\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"lI4-Iv-Bhv\" userLabel=\"publisherVideoButton\">\n                                        <rect key=\"frame\" x=\"8\" y=\"8\" width=\"50\" height=\"50\"/>\n                                        <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"50\" id=\"WHA-xY-mGi\"/>\n                                            <constraint firstAttribute=\"width\" secondItem=\"lI4-Iv-Bhv\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"atV-3z-CfA\"/>\n                                        </constraints>\n                                        <state key=\"normal\" image=\"video\"/>\n                                        <connections>\n                                            <action selector=\"publisherVideoButtonPressed:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"2mt-7G-8tP\"/>\n                                        </connections>\n                                    </button>\n                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8fW-9l-wlb\" userLabel=\"publisherCallButton\">\n                                        <rect key=\"frame\" x=\"78\" y=\"8\" width=\"50\" height=\"50\"/>\n                                        <color key=\"backgroundColor\" red=\"0.41568627450000001\" green=\"0.67843137249999996\" blue=\"0.74901960779999999\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"50\" id=\"7Lw-oa-woS\"/>\n                                            <constraint firstAttribute=\"width\" secondItem=\"8fW-9l-wlb\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"DuI-oP-9Bu\"/>\n                                        </constraints>\n                                        <state key=\"normal\" image=\"startCall\"/>\n                                        <connections>\n                                            <action selector=\"publisherCallButtonPressed:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"SO2-eY-tdE\"/>\n                                        </connections>\n                                    </button>\n                                    <button opaque=\"NO\" alpha=\"0.5\" contentMode=\"scaleToFill\" enabled=\"NO\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Bwb-uu-g2a\" userLabel=\"publisherMicButton\">\n                                        <rect key=\"frame\" x=\"148\" y=\"8\" width=\"50\" height=\"50\"/>\n                                        <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"50\" id=\"ip2-ay-ESy\"/>\n                                            <constraint firstAttribute=\"width\" secondItem=\"Bwb-uu-g2a\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"vkb-t2-Kh2\"/>\n                                        </constraints>\n                                        <state key=\"normal\" image=\"mic\"/>\n                                        <connections>\n                                            <action selector=\"publisherAudioButtonPressed:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"pxR-Us-IdT\"/>\n                                        </connections>\n                                    </button>\n                                </subviews>\n                                <constraints>\n                                    <constraint firstItem=\"8fW-9l-wlb\" firstAttribute=\"centerX\" secondItem=\"QaT-qb-Mgd\" secondAttribute=\"centerX\" id=\"5Lu-9t-BnR\"/>\n                                    <constraint firstItem=\"lI4-Iv-Bhv\" firstAttribute=\"top\" secondItem=\"QaT-qb-Mgd\" secondAttribute=\"top\" constant=\"8\" id=\"CvT-GV-vFr\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"Bwb-uu-g2a\" secondAttribute=\"trailing\" constant=\"8\" id=\"JF3-ro-Bkg\"/>\n                                    <constraint firstItem=\"lI4-Iv-Bhv\" firstAttribute=\"leading\" secondItem=\"QaT-qb-Mgd\" secondAttribute=\"leading\" constant=\"8\" id=\"JGU-Xi-nK9\"/>\n                                    <constraint firstItem=\"Bwb-uu-g2a\" firstAttribute=\"top\" secondItem=\"QaT-qb-Mgd\" secondAttribute=\"top\" constant=\"8\" id=\"NKW-13-1oS\"/>\n                                    <constraint firstItem=\"8fW-9l-wlb\" firstAttribute=\"centerY\" secondItem=\"QaT-qb-Mgd\" secondAttribute=\"centerY\" id=\"PHM-ze-gCT\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"lI4-Iv-Bhv\" secondAttribute=\"bottom\" constant=\"8\" id=\"RQX-rI-B3e\"/>\n                                    <constraint firstItem=\"Bwb-uu-g2a\" firstAttribute=\"leading\" secondItem=\"8fW-9l-wlb\" secondAttribute=\"trailing\" constant=\"20\" id=\"V8L-hw-qhh\"/>\n                                    <constraint firstItem=\"8fW-9l-wlb\" firstAttribute=\"leading\" secondItem=\"lI4-Iv-Bhv\" secondAttribute=\"trailing\" constant=\"20\" id=\"fmd-Pm-bEV\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"Bwb-uu-g2a\" secondAttribute=\"bottom\" constant=\"8\" id=\"m7x-ZQ-Ppg\"/>\n                                </constraints>\n                            </view>\n                            <button opaque=\"NO\" contentMode=\"scaleToFill\" enabled=\"NO\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"SKX-dv-Vnc\" userLabel=\"subscriberAudioButton\">\n                                <rect key=\"frame\" x=\"20\" y=\"60\" width=\"25\" height=\"25\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"width\" secondItem=\"SKX-dv-Vnc\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"qV6-yn-B90\"/>\n                                </constraints>\n                                <state key=\"normal\" image=\"audio\"/>\n                                <connections>\n                                    <action selector=\"subscriberAudioButtonPressed:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"COG-Jp-EKl\"/>\n                                </connections>\n                            </button>\n                            <button opaque=\"NO\" contentMode=\"scaleToFill\" enabled=\"NO\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"TaE-Jx-zZR\" userLabel=\"subscriberVideoButton\">\n                                <rect key=\"frame\" x=\"20\" y=\"105\" width=\"24\" height=\"24\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"width\" secondItem=\"TaE-Jx-zZR\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"5UO-AX-cjB\"/>\n                                </constraints>\n                                <state key=\"normal\" image=\"video\"/>\n                                <connections>\n                                    <action selector=\"subscriberVideoButtonPressed:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"XWM-IL-u0J\"/>\n                                </connections>\n                            </button>\n                            <button hidden=\"YES\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"xR7-mn-zpN\" userLabel=\"publisherCameraButton\">\n                                <rect key=\"frame\" x=\"325\" y=\"60\" width=\"30\" height=\"21\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"width\" secondItem=\"xR7-mn-zpN\" secondAttribute=\"height\" multiplier=\"55:39\" id=\"E2O-Tz-PqK\"/>\n                                    <constraint firstAttribute=\"width\" constant=\"30\" id=\"Xky-0a-JDU\"/>\n                                </constraints>\n                                <state key=\"normal\" image=\"reverse cameras\"/>\n                                <connections>\n                                    <action selector=\"publisherCameraButtonPressed:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"rUd-XL-ngb\"/>\n                                </connections>\n                            </button>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"0.66666666666666663\" green=\"0.66666666666666663\" blue=\"0.66666666666666663\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"EoV-Kh-ae4\" secondAttribute=\"trailing\" constant=\"20\" id=\"9Fu-eZ-Rs1\"/>\n                            <constraint firstItem=\"TaE-Jx-zZR\" firstAttribute=\"leading\" secondItem=\"HtV-zK-mFJ\" secondAttribute=\"leading\" constant=\"20\" id=\"C2K-eN-JaA\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"zJt-pm-lIX\" secondAttribute=\"trailing\" id=\"Hyy-01-kNd\"/>\n                            <constraint firstItem=\"SKX-dv-Vnc\" firstAttribute=\"top\" secondItem=\"HtV-zK-mFJ\" secondAttribute=\"top\" constant=\"60\" id=\"O4w-5G-XL8\"/>\n                            <constraint firstItem=\"QaT-qb-Mgd\" firstAttribute=\"centerX\" secondItem=\"HtV-zK-mFJ\" secondAttribute=\"centerX\" id=\"Pf6-T6-PHm\"/>\n                            <constraint firstItem=\"QaT-qb-Mgd\" firstAttribute=\"top\" secondItem=\"EoV-Kh-ae4\" secondAttribute=\"bottom\" constant=\"20\" id=\"RxN-kP-cKg\"/>\n                            <constraint firstItem=\"g1w-3N-XuV\" firstAttribute=\"top\" secondItem=\"QaT-qb-Mgd\" secondAttribute=\"bottom\" constant=\"20\" id=\"U8e-tl-f28\"/>\n                            <constraint firstItem=\"zJt-pm-lIX\" firstAttribute=\"top\" secondItem=\"HtV-zK-mFJ\" secondAttribute=\"top\" id=\"VE2-60-yEH\"/>\n                            <constraint firstItem=\"TaE-Jx-zZR\" firstAttribute=\"top\" secondItem=\"SKX-dv-Vnc\" secondAttribute=\"bottom\" constant=\"20\" id=\"VsL-68-FSY\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"zJt-pm-lIX\" secondAttribute=\"bottom\" id=\"aa2-ub-2be\"/>\n                            <constraint firstItem=\"zJt-pm-lIX\" firstAttribute=\"leading\" secondItem=\"HtV-zK-mFJ\" secondAttribute=\"leading\" id=\"bWH-Z0-RpJ\"/>\n                            <constraint firstItem=\"SKX-dv-Vnc\" firstAttribute=\"leading\" secondItem=\"HtV-zK-mFJ\" secondAttribute=\"leading\" constant=\"20\" id=\"p9N-b2-8WU\"/>\n                            <constraint firstItem=\"xR7-mn-zpN\" firstAttribute=\"top\" secondItem=\"HtV-zK-mFJ\" secondAttribute=\"top\" constant=\"60\" id=\"sIb-ZZ-Zsj\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"xR7-mn-zpN\" secondAttribute=\"trailing\" constant=\"20\" id=\"wyq-rV-OBQ\"/>\n                        </constraints>\n                        <connections>\n                            <outlet property=\"callButton\" destination=\"8fW-9l-wlb\" id=\"uSS-1W-0Ln\"/>\n                            <outlet property=\"publisherAudioButton\" destination=\"Bwb-uu-g2a\" id=\"FaO-jl-Nwv\"/>\n                            <outlet property=\"publisherVideoButton\" destination=\"lI4-Iv-Bhv\" id=\"GAY-eu-ff1\"/>\n                            <outlet property=\"publisherView\" destination=\"EoV-Kh-ae4\" id=\"WWF-wf-5KH\"/>\n                            <outlet property=\"reverseCameraButton\" destination=\"xR7-mn-zpN\" id=\"sd4-AJ-Rur\"/>\n                            <outlet property=\"subscriberAudioButton\" destination=\"SKX-dv-Vnc\" id=\"oVI-Jt-h1h\"/>\n                            <outlet property=\"subscriberVideoButton\" destination=\"TaE-Jx-zZR\" id=\"hHd-ZY-atr\"/>\n                            <outlet property=\"subscriberView\" destination=\"zJt-pm-lIX\" id=\"mYB-hs-kpy\"/>\n                        </connections>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"565\" y=\"357.99999999999989\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"audio\" width=\"24\" height=\"25\"/>\n        <image name=\"mic\" width=\"21\" height=\"26\"/>\n        <image name=\"reverse cameras\" width=\"55\" height=\"39\"/>\n        <image name=\"startCall\" width=\"15\" height=\"30\"/>\n        <image name=\"video\" width=\"24\" height=\"14\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "iOS/SampleApp/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSCameraUsageDescription</key>\n\t<string>$(PRODUCT_NAME) uses camera</string>\n\t<key>NSMicrophoneUsageDescription</key>\n\t<string>$(PRODUCT_NAME) uses microphone</string>\n\t<key>UIBackgroundModes</key>\n\t<array>\n\t\t<string>audio</string>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIStatusBarHidden</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/SampleApp/MainView.h",
    "content": "//\n//  MainView.h\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface MainView : UIView\n\n// publisher view\n- (void)addPublisherView:(UIView *)publisherView;\n- (void)removePublisherView;\n\n- (void)connectCallHolder:(BOOL)connected;\n- (void)updatePublisherAudio:(BOOL)connected;\n- (void)updatePublisherVideo:(BOOL)connected;\n\n// subscriber view\n- (void)addSubscribeView:(UIView *)subscriberView;\n- (void)removeSubscriberView;\n\n- (void)updateSubscriberAudioButton:(BOOL)connected;\n- (void)updateSubscriberVideoButton:(BOOL)connected;\n- (void)showSubscriberControls:(BOOL)shown;\n\n// other controls\n- (void)enableControlButtonsForCall:(BOOL)enabled;\n- (void)showReverseCameraButton;\n\n- (void)resetAllControl;\n@end\n"
  },
  {
    "path": "iOS/SampleApp/MainView.m",
    "content": "//\n//  MainView.m\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"MainView.h\"\n#import \"UIView+Helper.h\"\n\n@interface MainView()\n@property (weak, nonatomic) IBOutlet UIView *publisherView;\n@property (weak, nonatomic) IBOutlet UIView *subscriberView;\n\n// 3 action buttons at the bottom of the view\n@property (weak, nonatomic) IBOutlet UIButton *publisherVideoButton;\n@property (weak, nonatomic) IBOutlet UIButton *callButton;\n@property (weak, nonatomic) IBOutlet UIButton *publisherAudioButton;\n\n@property (weak, nonatomic) IBOutlet UIButton *reverseCameraButton;\n\n@property (weak, nonatomic) IBOutlet UIButton *subscriberVideoButton;\n@property (weak, nonatomic) IBOutlet UIButton *subscriberAudioButton;\n\n@end\n\n@implementation MainView\n\n- (void)awakeFromNib {\n    [super awakeFromNib];\n    \n    self.publisherView.hidden = YES;\n    self.publisherView.alpha = 1;\n    self.publisherView.layer.borderWidth = 1;\n    self.publisherView.layer.borderColor = [UIColor whiteColor].CGColor;\n    self.publisherView.layer.backgroundColor = [UIColor grayColor].CGColor;\n    self.publisherView.layer.cornerRadius = 3;\n    \n    [self drawBorderOn:self.publisherAudioButton withWhiteBorder:YES];\n    [self drawBorderOn:self.callButton withWhiteBorder:NO];\n    [self drawBorderOn:self.publisherVideoButton withWhiteBorder:YES];\n    [self showSubscriberControls:NO];\n}\n\n- (void)drawBorderOn:(UIView *)view\n     withWhiteBorder:(BOOL)withWhiteBorder {\n    \n    view.layer.cornerRadius = (view.bounds.size.width / 2);\n    if (withWhiteBorder) {\n        view.layer.borderWidth = 1;\n        view.layer.borderColor = [UIColor whiteColor].CGColor;\n    }\n}\n\n#pragma mark - publisher view\n- (void)addPublisherView:(UIView *)publisherView {\n    \n    [self.publisherView setHidden:NO];\n    [self.publisherView addSubview:publisherView];\n    publisherView.translatesAutoresizingMaskIntoConstraints = NO;\n    [publisherView addAttachedLayoutConstantsToSuperview];\n}\n\n- (void)removePublisherView {\n    [self.publisherView setHidden:YES];\n    [self.publisherView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];\n}\n\n- (void)connectCallHolder:(BOOL)connected {\n    [self.callButton setImage:connected ? [UIImage imageNamed:@\"hangUp\"] : [UIImage imageNamed:@\"startCall\"]  forState:UIControlStateNormal];\n    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;\n}\n- (void)updatePublisherAudio:(BOOL)connected {\n    [self.publisherAudioButton setImage:connected ? [UIImage imageNamed:@\"mic\"] : [UIImage imageNamed:@\"mutedMic\"] forState:UIControlStateNormal];\n}\n\n- (void)updatePublisherVideo:(BOOL)connected {\n    [self.publisherVideoButton setImage:connected ? [UIImage imageNamed:@\"video\"] : [UIImage imageNamed:@\"noVideo\"] forState:UIControlStateNormal];\n}\n\n#pragma mark - subscriber view\n- (void)addSubscribeView:(UIView *)subscriberView {\n    [self.subscriberView addSubview:subscriberView];\n    subscriberView.translatesAutoresizingMaskIntoConstraints = NO;\n    [subscriberView addAttachedLayoutConstantsToSuperview];\n}\n\n- (void)removeSubscriberView {\n    [self.subscriberView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];\n}\n\n- (void)updateSubscriberAudioButton:(BOOL)connected {\n    [self.subscriberAudioButton setImage:connected ? [UIImage imageNamed:@\"audio\"] : [UIImage imageNamed:@\"noAudio\"] forState:UIControlStateNormal];\n}\n\n- (void)updateSubscriberVideoButton:(BOOL)connected {\n    [self.subscriberVideoButton setImage:connected ? [UIImage imageNamed:@\"video\"] : [UIImage imageNamed:@\"noVideo\"] forState:UIControlStateNormal];\n}\n\n- (void)showSubscriberControls:(BOOL)shown {\n    [self.subscriberAudioButton setHidden:!shown];\n    [self.subscriberVideoButton setHidden:!shown];\n}\n\n#pragma mark - other controls\n- (void)enableControlButtonsForCall:(BOOL)enabled {\n    [self.subscriberAudioButton setEnabled:enabled];\n    [self.subscriberVideoButton setEnabled:enabled];\n    [self.publisherVideoButton setEnabled:enabled];\n    [self.publisherAudioButton setEnabled:enabled];\n}\n\n- (void)showReverseCameraButton; {\n    self.reverseCameraButton.hidden = NO;\n}\n\n- (void)resetAllControl {\n    [self removePublisherView];\n    [self connectCallHolder:NO];\n    [self updatePublisherAudio:YES];\n    [self updatePublisherVideo:YES];\n    [self updateSubscriberAudioButton:YES];\n    [self updateSubscriberVideoButton:YES];\n    [self enableControlButtonsForCall:NO];\n}\n\n@end\n"
  },
  {
    "path": "iOS/SampleApp/MainViewController.h",
    "content": "//\n//  MainViewController.h\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface MainViewController : UIViewController\n\n@end\n"
  },
  {
    "path": "iOS/SampleApp/MainViewController.m",
    "content": "//\n//  MainViewController.m\n//\n// Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"MainView.h\"\n#import \"MainViewController.h\"\n#import \"OTOneToOneCommunicator.h\"\n#import \"AppDelegate.h\"\n\n#import <SVProgressHUD/SVProgressHUD.h>\n\n#define MAKE_WEAK(self) __weak typeof(self) weak##self = self\n#define MAKE_STRONG(self) __strong typeof(weak##self) strong##self = weak##self\n\n@interface MainViewController () <OTOneToOneCommunicatorDataSource>\n@property (nonatomic) MainView *mainView;\n@property (nonatomic) OTOneToOneCommunicator *oneToOneCommunicator;\n@end\n\n@implementation MainViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    \n    self.mainView = (MainView *)self.view;\n    \n    self.oneToOneCommunicator = [[OTOneToOneCommunicator alloc] init];\n    self.oneToOneCommunicator.dataSource = self;\n    \n#if !(TARGET_OS_SIMULATOR)\n    [self.mainView showReverseCameraButton];\n#endif\n}\n\n- (IBAction)publisherCallButtonPressed:(UIButton *)sender {\n    \n    if (!self.oneToOneCommunicator.isCallEnabled) {\n        [SVProgressHUD show];\n\n        MAKE_WEAK(self);\n        [self.oneToOneCommunicator connectWithHandler:^(OTCommunicationSignal signal, NSError *error) {\n            MAKE_STRONG(self);\n            strongself.oneToOneCommunicator.publisherView.showAudioVideoControl = NO;\n            if (!error) {\n                [strongself handleCommunicationSignal:signal];\n            }\n            else {\n                [SVProgressHUD showErrorWithStatus:error.localizedDescription];\n            }\n        }];\n    }\n    else {\n        [SVProgressHUD popActivity];\n        [self.oneToOneCommunicator disconnect];\n        [self.mainView resetAllControl];\n    }\n}\n\n- (void)handleCommunicationSignal:(OTCommunicationSignal)signal {\n    \n    switch (signal) {\n        case OTPublisherCreated: {\n            [SVProgressHUD popActivity];\n            [self.mainView connectCallHolder:self.oneToOneCommunicator.isCallEnabled];\n            [self.mainView enableControlButtonsForCall:YES];\n            [self.mainView addPublisherView:self.oneToOneCommunicator.publisherView];\n            break;\n        }\n        case OTPublisherDestroyed: {\n            [self.mainView removePublisherView];\n            NSLog(@\"Your publishing feed stops streaming in OpenTok\");\n            break;\n        }\n        case OTSubscriberCreated: {\n            [SVProgressHUD show];\n        }\n        case OTSubscriberReady: {\n            [SVProgressHUD popActivity];\n            [self.mainView addSubscribeView:self.oneToOneCommunicator.subscriberView];\n            break;\n        }\n        case OTSubscriberDestroyed:{\n            [self.mainView removeSubscriberView];\n            break;\n        }\n        case OTSessionDidBeginReconnecting: {\n            [SVProgressHUD showInfoWithStatus:@\"Reconnecting\"];\n            break;\n        }\n        case OTSessionDidReconnect: {\n            [SVProgressHUD popActivity];\n            break;\n        }\n        case OTSubscriberVideoDisabledByBadQuality:\n        case OTSubscriberVideoDisabledBySubscriber: {\n            NSLog(@\"The remote has disabled the video\");\n            break;\n        }\n        case OTSubscriberVideoDisabledByPublisher:{\n            self.oneToOneCommunicator.subscribeToVideo = NO;\n            break;\n        }\n        case OTSubscriberVideoEnabledByGoodQuality:\n        case OTSubscriberVideoEnabledBySubscriber:{\n            NSLog(@\"The remote has enabled the video\");\n            break;\n        }\n        case OTSubscriberVideoEnabledByPublisher:{\n            self.oneToOneCommunicator.subscribeToVideo = YES;\n            break;\n        }\n        case OTSubscriberVideoDisableWarning:{\n            self.oneToOneCommunicator.subscribeToVideo = NO;\n            [SVProgressHUD showErrorWithStatus:@\"Network connection is unstable.\"];\n            break;\n        }\n        case OTSubscriberVideoDisableWarningLifted:{\n            self.oneToOneCommunicator.subscribeToVideo = YES;\n            break;\n        }\n        default: break;\n    }\n}\n\n- (IBAction)publisherAudioButtonPressed:(UIButton *)sender {\n    self.oneToOneCommunicator.publishAudio = !self.oneToOneCommunicator.publishAudio;\n    [self.mainView updatePublisherAudio:self.oneToOneCommunicator.publishAudio];\n}\n\n- (IBAction)publisherVideoButtonPressed:(UIButton *)sender {\n    self.oneToOneCommunicator.publishVideo = !self.oneToOneCommunicator.publishVideo;\n    [self.mainView updatePublisherVideo:self.oneToOneCommunicator.publishVideo];\n}\n\n- (IBAction)publisherCameraButtonPressed:(UIButton *)sender {\n    self.oneToOneCommunicator.cameraPosition = self.oneToOneCommunicator.cameraPosition == AVCaptureDevicePositionBack ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack\n}\n\n- (IBAction)subscriberVideoButtonPressed:(UIButton *)sender {\n    self.oneToOneCommunicator.subscribeToVideo = !self.oneToOneCommunicator.subscribeToVideo;\n    [self.mainView updateSubscriberVideoButton:self.oneToOneCommunicator.subscribeToVideo];\n}\n\n- (IBAction)subscriberAudioButtonPressed:(UIButton *)sender {\n    self.oneToOneCommunicator.subscribeToAudio = !self.oneToOneCommunicator.subscribeToAudio;\n    [self.mainView updateSubscriberAudioButton:self.oneToOneCommunicator.subscribeToAudio];\n}\n\n- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{\n    if (self.oneToOneCommunicator.subscriberView){\n        [self.mainView showSubscriberControls:YES];\n    }\n    [self.mainView performSelector:@selector(showSubscriberControls:)\n                        withObject:@(NO)\n                        afterDelay:7.0];\n}\n\n- (OTAcceleratorSession *)sessionOfOTOneToOneCommunicator:(OTOneToOneCommunicator *)oneToOneCommunicator {\n    AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];\n    return appDelegate.acceleratorSession;\n}\n\n@end\n"
  },
  {
    "path": "iOS/SampleApp/UIView+Helper.h",
    "content": "//\n//  UIView+Helper.h\n//\n//  Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface UIView (Helper)\n\n- (void)addAttachedLayoutConstantsToSuperview;\n\n@end\n"
  },
  {
    "path": "iOS/SampleApp/UIView+Helper.m",
    "content": "//\n//  UIView+Helper.m\n//\n//  Copyright © 2016 Tokbox, Inc. All rights reserved.\n//\n\n#import \"UIView+Helper.h\"\n\n@implementation UIView (Helper)\n\n- (void)addAttachedLayoutConstantsToSuperview {\n    \n    if (!self.superview) {\n        return;\n    }\n    \n    NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self\n                                                           attribute:NSLayoutAttributeTop\n                                                           relatedBy:NSLayoutRelationEqual\n                                                              toItem:self.superview\n                                                           attribute:NSLayoutAttributeTop\n                                                          multiplier:1.0\n                                                            constant:0.0];\n    NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:self\n                                                               attribute:NSLayoutAttributeLeading\n                                                               relatedBy:NSLayoutRelationEqual\n                                                                  toItem:self.superview\n                                                               attribute:NSLayoutAttributeLeading\n                                                              multiplier:1.0\n                                                                constant:0.0];\n    NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:self\n                                                                attribute:NSLayoutAttributeTrailing\n                                                                relatedBy:NSLayoutRelationEqual\n                                                                   toItem:self.superview\n                                                                attribute:NSLayoutAttributeTrailing\n                                                               multiplier:1.0\n                                                                 constant:0.0];\n    NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:self\n                                                              attribute:NSLayoutAttributeBottom\n                                                              relatedBy:NSLayoutRelationEqual\n                                                                 toItem:self.superview\n                                                              attribute:NSLayoutAttributeBottom\n                                                             multiplier:1.0\n                                                               constant:0.0];\n    [NSLayoutConstraint activateConstraints:@[top, leading, trailing, bottom]];\n}\n\n@end\n"
  },
  {
    "path": "iOS/SampleApp/main.m",
    "content": "#import <UIKit/UIKit.h>\n#import \"AppDelegate.h\"\n\nint main(int argc, char * argv[]) {\n  @autoreleasepool {\n      return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));\n  }\n}\n"
  },
  {
    "path": "js/.eslintrc.json",
    "content": "{\n  \"extends\": \"airbnb\",\n  \"env\": {\n    \"browser\": true,\n    \"node\": true\n  },\n  \"parserOptions\": {\n    \"ecmaVersion\": 5\n  },\n  \"plugins\": [\n    \"react\"\n  ],\n  \"rules\": {\n    \"no-underscore-dangle\": 0,\n    \"vars-on-top\": 0,\n    \"padded-blocks\": 0,\n    \"no-var\": 0,\n    \"comma-dangle\": 0,\n    \"func-names\": 0,\n    \"prefer-arrow-callback\": 0,\n    \"object-shorthand\": 0,\n    \"no-unused-expressions\": [2, {\n      \"allowTernary\": true,\n      \"allowShortCircuit\": true\n    }],\n    \"global-require\": 0\n  },\n  \"globals\": {\n    \"$\": true,\n    \"_\": true\n  }\n}\n"
  },
  {
    "path": "js/.gitignore",
    "content": "node_modules"
  },
  {
    "path": "js/.jsbeautifyrc",
    "content": "{\n    \"js\":{\n        \"indent_size\": 2,\n        \"space_after_anon_function\": true,\n        \"end_with_newline\": true,\n        \"brace_style\": \"collaps-preserve-inline\"\n    }\n}"
  },
  {
    "path": "js/Procfile",
    "content": "web: node server.js"
  },
  {
    "path": "js/README.md",
    "content": "![logo](../tokbox-logo.png)\n\n# OpenTok One-to-One Communication Sample App for JavaScript<br/>Version 1.3\n\n## Quick start\n\nThis 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).\n\n### Configuring the app\n\nConfigure the sample app code. Then, build and run the app.\n\n1. Get values for **API Key**, **Session ID**, and **Token**. See [OpenTok One-to-One Communication Sample App home page](../README.md) for important information.\n\n2. In **app.js**, replace the following empty strings with the corresponding **API Key**, **Session ID**, and **Token** values:\n\n   ```javascript\n    apiKey: '',    // Replace with your OpenTok API Key\n    sessionId: '', // Replace with a generated Session ID\n    token: '',     // Replace with a generated token (from the dashboard or using an OpenTok server SDK)\n   ```\n\n### Deploying and running the app\n\n```javascript\n$ npm install\n$ npm run build\n$ node server.js\n```\n\nThe 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.\n\n\n## Exploring the code\n\nFor details about how to use the Accelerator Core in the sample app, see [here](https://github.com/opentok/accelerator-core-js#sample-applications).\n\n"
  },
  {
    "path": "js/package.json",
    "content": "{\n  \"name\": \"one-to-one-sample\",\n  \"version\": \"1.0.4\",\n  \"description\": \"One to One sample app\",\n  \"main\": \"server.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/opentok/one-to-one-sample-apps\"\n  },\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"build\": \"cp ./node_modules/opentok-accelerator-core/browser/opentok-acc-core.js ./public/js/components/opentok-acc-core.js\",\n    \"start\": \"node server.js\"\n  },\n  \"author\": \"adrice727@gmail.com\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"express\": \"^4.14.1\",\n    \"body-parser\": \"^1.16.0\",\n    \"opentok-accelerator-core\": \"*\"\n  }\n}\n"
  },
  {
    "path": "js/public/css/style.css",
    "content": "html,\nbody {\n    margin: 0;\n    padding: 0;\n    box-sizing: border-box;\n    font-family: sans-serif;\n}\n\n.clickable {\n  cursor: pointer;\n}\n\n*,\n*:before,\n*:after {\n    box-sizing: inherit;\n}\n\n.App-header {\n    background-color: #222;\n    height: 40px;\n    color: white;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 0 20px;\n}\n\n.App-header h1 {\n    font-size: 16px;\n    font-weight: 200;\n}\n\n.App-logo {\n    height: 60%;\n    width: auto;\n}\n\n.App-main {\n    position: relative;\n    width: 75vw;\n    height: calc(75vw * .6);\n    margin: 10px auto;\n    border: 1px solid lightblue;\n}\n\n.App-control-container {\n    position: absolute;\n    height: 100%;\n    width: 60px;\n    left: -60px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n}\n\n.App-control-container.hidden {\n    display: none;\n}\n\n.App-control-container .ots-video-control {\n    width: 50px;\n    height: 50px;\n    margin: 20px 0 !important;\n    border: 2px solid white;\n    border-radius: 50%;\n    background-position: center;\n    background-color: rgba(27, 134, 144, 0.4);\n    background-color: lightgrey;\n    background-repeat: no-repeat;\n    cursor: pointer;\n}\n\n.App-control-container .ots-video-control.audio {\n    background-image: url(https://assets.tokbox.com/solutions/images/icon-mic.png);\n}\n\n.App-control-container .ots-video-control.audio:hover, .App-control-container .ots-video-control.audio.muted {\n    background-image: url(https://assets.tokbox.com/solutions/images/icon-muted-mic.png);\n}\n\n.App-control-container .ots-video-control.video {\n    background-image: url(https://assets.tokbox.com/solutions/images/icon-video.png);\n}\n\n.App-control-container .ots-video-control.video.muted {\n    background-image: url(https://assets.tokbox.com/solutions/images/icon-no-video.png);\n}\n\n.App-control-container .ots-video-control.end-call {\n    background-image: url(https://assets.tokbox.com/solutions/images/icon-hang-up.png);\n    background-color: red;\n}\n   \n.App-video-container {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n}\n\n.App-mask {\n    width: 100%;\n    height: 100%;\n    position: relative;\n    color: white;\n    background: rgba(27, 134, 144, 0.4);\n    display: flex;\n    justify-content: center;\n    align-items: center;\n}\n\n.App-mask.hidden {\n    display: none;\n}\n\n.App-mask .react-spinner {\n    position: absolute;\n}\n\n.App-mask .message {\n    font-weight: 200;\n}\n\n.App-mask .message.with-spinner {\n    position: absolute;\n    top: 57.5%;\n}\n\n.App-mask .message.button {\n    border: 1px solid white;\n    padding: 20px 40px;\n    border-radius: 6px;\n}\n\n.App-video-container .video-container {\n    width: 100%;\n    height: 100%;\n    display: flex;\n}\n\n.App-video-container .video-container.small {\n    position: absolute;\n    top: 20px;\n    right: 20px;\n    width: 160px;\n    height: 96px;\n    border: 1px solid #fcba00;\n    z-index: 2;\n}\n\n.App-video-container .video-container.small.left {\n    left: 20px;\n    border: 1px solid #00fcc2;\n}\n\n.App-video-container .video-container.hidden {\n    display: none;\n}\n\n.App-video-container .video-container.active-2 .OT_subscriber {\n    width: 50%;\n}\n\n.App-video-container .video-container.active-3 .OT_subscriber {\n    width: calc(100%/3) !important;\n}\n\n.App-video-container .video-container.active-4 {\n    flex-wrap: wrap;\n}\n\n.App-video-container .video-container.active-4 .OT_subscriber {\n    width: 50% !important;\n    height: 50% !important;\n}\n\nprogress-spinner {\n  display: inline-block;\n  width: 1em;\n  height: 1em;\n  border: 1px solid transparent;\n  border-top-color: rgba(0, 0, 0, 0.6);\n  border-radius: 50%;\n  -webkit-animation: rotate 800ms linear infinite;\n          animation: rotate 800ms linear infinite;\n}\nprogress-spinner[dark] {\n  border-top-color: rgba(255, 255, 255, 0.6);\n}\nprogress-spinner[dotted] {\n  border-width: 0;\n  border-style: dotted;\n  border-top-width: 2px;\n}\n@-webkit-keyframes rotate {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg);\n  }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg);\n  }\n}\n@keyframes rotate {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg);\n  }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg);\n  }\n}"
  },
  {
    "path": "js/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <title>OT Accelerator Core</title>\n\n    <link rel=\"stylesheet\" href=\"css/style.css\">\n    <link rel=\"stylesheet\" href=\"https://assets.tokbox.com/solutions/css/style.css\">\n    <script src=\"https://static.opentok.com/v2/js/opentok.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js\"></script>\n    <script src=\"js/components/opentok-acc-core.js\"></script>\n    <script src=\"js/app.js\"></script>\n</head>\n\n<body>\n    <div class=\"App\">\n        <div class=\"App-header\">\n            <img src=\"./images/logo.svg\" class=\"App-logo\" alt=\"logo\" />\n            <h1>OpenTok Accelerator Core</h1>\n        </div>\n        <div class=\"App-main\">\n            <div id=\"controls\" class='App-control-container hidden'>\n                <div class=\"ots-video-control circle audio\" id=\"toggleLocalAudio\"></div>\n                <div class=\"ots-video-control circle video\" id=\"toggleLocalVideo\"></div>\n                <div class=\"ots-video-control circle end-call\" id=\"toggleEndCall\"></div>\n            </div>\n            <div class=\"App-video-container\" id=\"appVideoContainer\">\n                <div class=\"App-mask\" id=\"connecting-mask\">\n                    <progress-spinner dark style=\"font-size:50px\"></progress-spinner>\n                    <div class=\"message with-spinner\">Connecting</div>\n                </div>\n                <div class=\"App-mask hidden\" id=\"start-mask\">\n                    <div class=\"message button clickable\" id=\"start\">Click to Start Call</div>\n                </div>\n                <div id=\"cameraPublisherContainer\" class=\"video-container hidden\"></div>\n                <div id=\"screenPublisherContainer\" class=\"video-container hidden\"></div>\n                <div id=\"cameraSubscriberContainer\" class=\"video-container-hidden\"></div>\n            </div>\n        </div>\n    </div>\n\n</body>\n\n</html>"
  },
  {
    "path": "js/public/js/app.js",
    "content": "/* global otCore */\nconst options = {\n  credentials: {\n    apiKey: \"\",  //Replace with your OpenTok API key \n    sessionId: \"\", //Replace with a generated Session ID\n    token: \"\", //Replace with a generated token (from the dashboard or using an OpenTok server SDK)\n  },\n   // A container can either be a query selector or an HTMLElement\n  streamContainers: function streamContainers(pubSub, type, data) {\n    return {\n      publisher: {\n        camera: '#cameraPublisherContainer',\n      },\n      subscriber: {\n        camera: '#cameraSubscriberContainer',\n      },\n    }[pubSub][type];\n  },\n  controlsContainer: '#controls',\n  packages: [],\n  communication: {\n    callProperites: null, // Using default\n  }\n};\n\n/** Application Logic */\nconst app = () => {\n  const state = {\n    connected: false,\n    active: false,\n    publishers: null,\n    subscribers: null,\n    meta: null,\n    localAudioEnabled: true,\n    localVideoEnabled: true,\n  };\n\n  /**\n   * Update the size and position of video containers based on the number of\n   * publishers and subscribers specified in the meta property returned by otCore.\n   */\n  const updateVideoContainers = () => {\n    const { meta } = state;\n    const activeCameraSubscribers = meta ? meta.subscriber.camera : 0;\n   \n    const videoContainerClass = `App-video-container ${''}`;\n    document.getElementById('appVideoContainer').setAttribute('class', videoContainerClass);\n\n    const cameraPublisherClass =\n      `video-container ${!!activeCameraSubscribers? 'small' : ''} ${!!activeCameraSubscribers? 'small' : ''}`;\n    document.getElementById('cameraPublisherContainer').setAttribute('class', cameraPublisherClass);\n\n    const cameraSubscriberClass =\n      `video-container ${!activeCameraSubscribers ? 'hidden' : ''} active-${activeCameraSubscribers}`;\n    document.getElementById('cameraSubscriberContainer').setAttribute('class', cameraSubscriberClass);\n  };\n\n\n  /**\n   * Update the UI\n   * @param {String} update - 'connected', 'active', or 'meta'\n   */\n  const updateUI = (update) => {\n    const { connected, active } = state;\n\n    switch (update) {\n      case 'connected':\n        if (connected) {\n          document.getElementById('connecting-mask').classList.add('hidden');\n          document.getElementById('start-mask').classList.remove('hidden');\n        }\n        break;\n      case 'active':\n        if (active) {\n          document.getElementById('cameraPublisherContainer').classList.remove('hidden');\n          document.getElementById('start-mask').classList.add('hidden');\n          document.getElementById('controls').classList.remove('hidden');\n        }\n        else {\n          document.getElementById('start-mask').classList.remove('hidden');\n          document.getElementById('controls').classList.add('hidden');\n          document.getElementById('cameraPublisherContainer').classList.add('hidden');\n          document.getElementById('toggleLocalVideo').classList.remove('muted');\n          document.getElementById('toggleLocalAudio').classList.remove('muted');\n        }\n        break;\n      case 'meta':\n        updateVideoContainers();\n        break;\n      default:\n        console.log('nothing to do, nowhere to go');\n    }\n  };\n\n  /**\n   * Update the state and UI\n   */\n  const updateState = (updates) => {\n    Object.assign(state, updates);\n    Object.keys(updates).forEach(update => updateUI(update));\n  };\n\n  /**\n   * Start publishing video/audio and subscribe to streams\n   */\n  const startCall = () => {\n    otCore.startCall()\n      .then(({ publishers, subscribers, meta }) => {\n        updateState({ publishers, subscribers, meta, active: true });\n      }).catch(error => console.log(error));\n  };\n\n  /**\n   * Toggle publishing local audio\n   */\n  const toggleLocalAudio = () => {\n    const enabled = state.localAudioEnabled;\n    otCore.toggleLocalAudio(!enabled);\n    updateState({ localAudioEnabled: !enabled });\n    const action = enabled ? 'add' : 'remove';\n    document.getElementById('toggleLocalAudio').classList[action]('muted');\n  };\n\n  /**\n   * Toggle publishing local video\n   */\n  const toggleLocalVideo = () => {\n    const enabled = state.localVideoEnabled;\n    otCore.toggleLocalVideo(!enabled);\n    updateState({ localVideoEnabled: !enabled });\n    const action = enabled ? 'add' : 'remove';\n    document.getElementById('toggleLocalVideo').classList[action]('muted');\n  };\n\n  /**\n   * Toggle end call\n   */\n  const toggleEndCall = () => {\n    updateState({ active: false });\n    otCore.endCall();\n  };\n\n  /**\n   * Subscribe to otCore and UI events\n   */\n  const createEventListeners = () => {\n    const events = [\n      'subscribeToCamera',\n      'unsubscribeFromCamera',\n    ];\n    events.forEach(event => otCore.on(event, ({ publishers, subscribers, meta }) => {\n      updateState({ publishers, subscribers, meta });\n    }));\n\n    document.getElementById('start').addEventListener('click', startCall);\n    document.getElementById('toggleLocalAudio').addEventListener('click', toggleLocalAudio);\n    document.getElementById('toggleLocalVideo').addEventListener('click', toggleLocalVideo);\n    document.getElementById('toggleEndCall').addEventListener('click', toggleEndCall);\n  };\n\n  /**\n   * Initialize otCore, connect to the session, and listen to events\n   */\n  const init = () => {\n    otCore.init(options);\n    otCore.connect().then(() => updateState({ connected: true }));\n    createEventListeners();\n  };\n\n  init();\n};\n\ndocument.addEventListener('DOMContentLoaded', app);\n"
  },
  {
    "path": "js/public/js/components/opentok-acc-core.js",
    "content": "(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){\nfunction _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);\n},{}],2:[function(require,module,exports){\n'use strict';\n\nvar _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; };\n\n/* global OT */\n\n/** Dependencies */\nvar state = require('./state');\n\nvar _require = require('./errors'),\n    CoreError = _require.CoreError;\n\nvar _require2 = require('./util'),\n    dom = _require2.dom,\n    path = _require2.path,\n    pathOr = _require2.pathOr,\n    properCase = _require2.properCase;\n\nvar _require3 = require('./logging'),\n    message = _require3.message,\n    logAnalytics = _require3.logAnalytics,\n    logAction = _require3.logAction,\n    logVariation = _require3.logVariation;\n\n/** Module variables */\n\n\nvar session = void 0;\nvar accPack = void 0;\nvar callProperties = void 0;\nvar screenProperties = void 0;\nvar streamContainers = void 0;\nvar autoSubscribe = void 0;\nvar connectionLimit = void 0;\nvar active = false;\n\n/**\n * Default UI propties\n * https://tokbox.com/developer/guides/customize-ui/js/\n */\nvar defaultCallProperties = {\n  insertMode: 'append',\n  width: '100%',\n  height: '100%',\n  showControls: false,\n  style: {\n    buttonDisplayMode: 'off'\n  }\n};\n\n/**\n * Trigger an event through the API layer\n * @param {String} event - The name of the event\n * @param {*} [data]\n */\nvar triggerEvent = function triggerEvent(event, data) {\n  return accPack.triggerEvent(event, data);\n};\n\n/**\n * Determine whether or not the party is able to join the call based on\n * the specified connection limit, if any.\n * @return {Boolean}\n */\nvar ableToJoin = function ableToJoin() {\n  if (!connectionLimit) {\n    return true;\n  }\n  // Not using the session here since we're concerned with number of active publishers\n  var connections = Object.values(state.getStreams()).filter(function (s) {\n    return s.videoType === 'camera';\n  });\n  return connections.length < connectionLimit;\n};\n\n/**\n * Create a camera publisher object\n * @param {Object} publisherProperties\n * @returns {Promise} <resolve: Object, reject: Error>\n */\nvar createPublisher = function createPublisher(publisherProperties) {\n  return new Promise(function (resolve, reject) {\n    // TODO: Handle adding 'name' option to props\n    var props = Object.assign({}, callProperties, publisherProperties);\n    // TODO: Figure out how to handle common vs package-specific options\n    // ^^^ This may already be available through package options\n    var container = dom.element(streamContainers('publisher', 'camera'));\n    var publisher = OT.initPublisher(container, props, function (error) {\n      error ? reject(error) : resolve(publisher);\n    });\n  });\n};\n\n/**\n * Publish the local camera stream and update state\n * @param {Object} publisherProperties\n * @returns {Promise} <resolve: empty, reject: Error>\n */\nvar publish = function publish(publisherProperties) {\n  return new Promise(function (resolve, reject) {\n    var onPublish = function onPublish(publisher) {\n      return function (error) {\n        if (error) {\n          reject(error);\n          logAnalytics(logAction.startCall, logVariation.fail);\n        } else {\n          logAnalytics(logAction.startCall, logVariation.success);\n          state.addPublisher('camera', publisher);\n          resolve(publisher);\n        }\n      };\n    };\n\n    var publishToSession = function publishToSession(publisher) {\n      return session.publish(publisher, onPublish(publisher));\n    };\n\n    var handleError = function handleError(error) {\n      logAnalytics(logAction.startCall, logVariation.fail);\n      var errorMessage = error.code === 1010 ? 'Check your network connection' : error.message;\n      triggerEvent('error', errorMessage);\n      reject(error);\n    };\n\n    createPublisher(publisherProperties).then(publishToSession).catch(handleError);\n  });\n};\n\n/**\n * Subscribe to a stream and update the state\n * @param {Object} stream - An OpenTok stream object\n * @returns {Promise} <resolve: empty reject: Error >\n */\nvar subscribe = function subscribe(stream) {\n  return new Promise(function (resolve, reject) {\n    logAnalytics(logAction.subscribe, logVariation.attempt);\n    var streamMap = state.getStreamMap();\n    if (streamMap[stream.id]) {\n      // Are we already subscribing to the stream?\n      resolve();\n    } else {\n      (function () {\n        // No videoType indicates SIP https://tokbox.com/developer/guides/sip/\n        var type = pathOr('sip', 'videoType', stream);\n        var connectionData = JSON.parse(path(['connection', 'data'], stream) || null);\n        var container = dom.query(streamContainers('subscriber', type, connectionData));\n        var options = type === 'camera' ? callProperties : screenProperties;\n        var subscriber = session.subscribe(stream, container, options, function (error) {\n          if (error) {\n            logAnalytics(logAction.subscribe, logVariation.fail);\n            reject(error);\n          } else {\n            state.addSubscriber(subscriber);\n            triggerEvent('subscribeTo' + properCase(type), Object.assign({}, { subscriber: subscriber }, state.all()));\n            type === 'screen' && triggerEvent('startViewingSharedScreen', subscriber); // Legacy event\n            logAnalytics(logAction.subscribe, logVariation.success);\n            resolve();\n          }\n        });\n      })();\n    }\n  });\n};\n\n/**\n * Unsubscribe from a stream and update the state\n * @param {Object} subscriber - An OpenTok subscriber object\n * @returns {Promise} <resolve: empty>\n */\nvar unsubscribe = function unsubscribe(subscriber) {\n  return new Promise(function (resolve) {\n    logAnalytics(logAction.unsubscribe, logVariation.attempt);\n    var type = path('stream.videoType', subscriber);\n    state.removeSubscriber(type, subscriber);\n    session.unsubscribe(subscriber);\n    logAnalytics(logAction.unsubscribe, logVariation.success);\n    resolve();\n  });\n};\n\n/**\n * Ensure all required options are received\n * @param {Object} options\n */\nvar validateOptions = function validateOptions(options) {\n  var requiredOptions = ['accPack'];\n  requiredOptions.forEach(function (option) {\n    if (!options[option]) {\n      throw new CoreError(option + ' is a required option.', 'invalidParameters');\n    }\n  });\n\n  accPack = options.accPack;\n  streamContainers = options.streamContainers;\n  callProperties = options.callProperties || defaultCallProperties;\n  connectionLimit = options.connectionLimit || null;\n  autoSubscribe = options.hasOwnProperty('autoSubscribe') ? options.autoSubscribe : true;\n\n  screenProperties = options.screenProperties || Object.assign({}, defaultCallProperties, { videoSource: 'window' });\n};\n\n/**\n * Set session in module scope\n */\nvar setSession = function setSession() {\n  session = state.getSession();\n};\n\n/**\n * Subscribe to new stream unless autoSubscribe is set to false\n * @param {Object} stream\n */\nvar onStreamCreated = function onStreamCreated(_ref) {\n  var stream = _ref.stream;\n  return active && autoSubscribe && subscribe(stream);\n};\n\n/**\n * Update state and trigger corresponding event(s) when stream is destroyed\n * @param {Object} stream\n */\nvar onStreamDestroyed = function onStreamDestroyed(_ref2) {\n  var stream = _ref2.stream;\n\n  state.removeStream(stream);\n  var type = pathOr('sip', 'videoType', stream);\n  type === 'screen' && triggerEvent('endViewingSharedScreen'); // Legacy event\n  triggerEvent('unsubscribeFrom' + properCase(type), state.getPubSub());\n};\n\n/**\n * Listen for API-level events\n */\nvar createEventListeners = function createEventListeners() {\n  accPack.on('streamCreated', onStreamCreated);\n  accPack.on('streamDestroyed', onStreamDestroyed);\n};\n\n/**\n * Start publishing the local camera feed and subscribing to streams in the session\n * @param {Object} publisherProperties\n * @returns {Promise} <resolve: Object, reject: Error>\n */\nvar startCall = function startCall(publisherProperties) {\n  return new Promise(function (resolve, reject) {\n    // eslint-disable-line consistent-return\n    logAnalytics(logAction.startCall, logVariation.attempt);\n\n    /**\n     * Determine if we're able to join the session based on an existing connection limit\n     */\n    if (!ableToJoin()) {\n      var errorMessage = 'Session has reached its connection limit';\n      triggerEvent('error', errorMessage);\n      logAnalytics(logAction.startCall, logVariation.fail);\n      return reject(new CoreError(errorMessage, 'connectionLimit'));\n    }\n\n    /**\n     * Subscribe to any streams that existed before we start the call from our side.\n     */\n    var subscribeToInitialStreams = function subscribeToInitialStreams(publisher) {\n      // Get an array of initial subscription promises\n      var initialSubscriptions = function initialSubscriptions() {\n        if (autoSubscribe) {\n          var _ret2 = function () {\n            var streams = state.getStreams();\n            return {\n              v: Object.keys(streams).map(function (id) {\n                return subscribe(streams[id]);\n              })\n            };\n          }();\n\n          if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === \"object\") return _ret2.v;\n        }\n        return [Promise.resolve()];\n      };\n\n      // Handle success\n      var onSubscribeToAll = function onSubscribeToAll() {\n        var pubSubData = Object.assign({}, state.getPubSub(), { publisher: publisher });\n        triggerEvent('startCall', pubSubData);\n        active = true;\n        resolve(pubSubData);\n      };\n\n      // Handle error\n      var onError = function onError(reason) {\n        message('Failed to subscribe to all existing streams: ' + reason);\n        // We do not reject here in case we still successfully publish to the session\n        resolve(Object.assign({}, state.getPubSub(), { publisher: publisher }));\n      };\n\n      Promise.all(initialSubscriptions()).then(onSubscribeToAll).catch(onError);\n    };\n\n    publish(publisherProperties).then(subscribeToInitialStreams).catch(reject);\n  });\n};\n\n/**\n * Stop publishing and unsubscribe from all streams\n */\nvar endCall = function endCall() {\n  logAnalytics(logAction.endCall, logVariation.attempt);\n\n  var _state$getPubSub = state.getPubSub(),\n      publishers = _state$getPubSub.publishers,\n      subscribers = _state$getPubSub.subscribers;\n\n  var unpublish = function unpublish(publisher) {\n    return session.unpublish(publisher);\n  };\n  Object.values(publishers.camera).forEach(unpublish);\n  Object.values(publishers.screen).forEach(unpublish);\n  // TODO Promise.all for unsubsribing\n  Object.values(subscribers.camera).forEach(unsubscribe);\n  Object.values(subscribers.screen).forEach(unsubscribe);\n  state.removeAllPublishers();\n  active = false;\n  triggerEvent('endCall');\n  logAnalytics(logAction.endCall, logVariation.success);\n};\n\n/**\n * Enable/disable local audio or video\n * @param {String} source - 'audio' or 'video'\n * @param {Boolean} enable\n */\nvar enableLocalAV = function enableLocalAV(id, source, enable) {\n  var method = 'publish' + properCase(source);\n\n  var _state$getPubSub2 = state.getPubSub(),\n      publishers = _state$getPubSub2.publishers;\n\n  publishers.camera[id][method](enable);\n};\n\n/**\n * Enable/disable remote audio or video\n * @param {String} subscriberId\n * @param {String} source - 'audio' or 'video'\n * @param {Boolean} enable\n */\nvar enableRemoteAV = function enableRemoteAV(subscriberId, source, enable) {\n  var method = 'subscribeTo' + properCase(source);\n\n  var _state$getPubSub3 = state.getPubSub(),\n      subscribers = _state$getPubSub3.subscribers;\n\n  subscribers.camera[subscriberId][method](enable);\n};\n\n/**\n * Initialize the communication component\n * @param {Object} options\n * @param {Object} options.accPack\n * @param {Number} options.connectionLimit\n * @param {Function} options.streamContainer\n */\nvar init = function init(options) {\n  return new Promise(function (resolve) {\n    validateOptions(options);\n    setSession();\n    createEventListeners();\n    resolve();\n  });\n};\n\n/** Exports */\nmodule.exports = {\n  init: init,\n  startCall: startCall,\n  endCall: endCall,\n  subscribe: subscribe,\n  unsubscribe: unsubscribe,\n  enableLocalAV: enableLocalAV,\n  enableRemoteAV: enableRemoteAV\n};\n\n},{\"./errors\":4,\"./logging\":6,\"./state\":10,\"./util\":11}],3:[function(require,module,exports){\n(function (global){\n'use strict';\n\nvar _arguments = arguments;\n\nvar _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; };\n\n/* global OT */\n/**\n * Dependencies\n */\nvar util = require('./util');\nvar internalState = require('./state');\nvar accPackEvents = require('./events');\nvar communication = require('./communication');\nvar OpenTokSDK = require('./sdk-wrapper/sdkWrapper');\n\nvar _require = require('./errors'),\n    CoreError = _require.CoreError;\n\nvar _require2 = require('./logging'),\n    message = _require2.message,\n    initLogAnalytics = _require2.initLogAnalytics,\n    logAnalytics = _require2.logAnalytics,\n    logAction = _require2.logAction,\n    logVariation = _require2.logVariation,\n    updateLogAnalytics = _require2.updateLogAnalytics;\n\n/**\n * Helper methods\n */\n\n\nvar dom = util.dom,\n    path = util.path,\n    pathOr = util.pathOr,\n    properCase = util.properCase;\n\n/**\n * Individual Accelerator Packs\n */\n\nvar textChat = void 0; // eslint-disable-line no-unused-vars\nvar screenSharing = void 0; // eslint-disable-line no-unused-vars\nvar annotation = void 0;\nvar archiving = void 0; // eslint-disable-line no-unused-vars\n\n/**\n * Get access to an accelerator pack\n * @param {String} packageName - textChat, screenSharing, annotation, or archiving\n * @returns {Object} The instance of the accelerator pack\n */\nvar getAccPack = function getAccPack(packageName) {\n  logAnalytics(logAction.getAccPack, logVariation.attempt);\n  var packages = {\n    textChat: textChat,\n    screenSharing: screenSharing,\n    annotation: annotation,\n    archiving: archiving\n  };\n  logAnalytics(logAction.getAccPack, logVariation.success);\n  return packages[packageName];\n};\n\n/** Eventing */\n\nvar eventListeners = {};\n\n/**\n * Register events that can be listened to be other components/modules\n * @param {array | string} events - A list of event names. A single event may\n * also be passed as a string.\n */\nvar registerEvents = function registerEvents(events) {\n  var eventList = Array.isArray(events) ? events : [events];\n  eventList.forEach(function (event) {\n    if (!eventListeners[event]) {\n      eventListeners[event] = new Set();\n    }\n  });\n};\n\n/**\n * Register a callback for a specific event or pass an object with\n * with event => callback key/value pairs to register listeners for\n * multiple events.\n * @param {String | Object} event - The name of the event\n * @param {Function} callback\n */\nvar on = function on(event, callback) {\n  // logAnalytics(logAction.on, logVariation.attempt);\n  if ((typeof event === 'undefined' ? 'undefined' : _typeof(event)) === 'object') {\n    Object.keys(event).forEach(function (eventName) {\n      on(eventName, event[eventName]);\n    });\n    return;\n  }\n  var eventCallbacks = eventListeners[event];\n  if (!eventCallbacks) {\n    message(event + ' is not a registered event.');\n    // logAnalytics(logAction.on, logVariation.fail);\n  } else {\n    eventCallbacks.add(callback);\n    // logAnalytics(logAction.on, logVariation.success);\n  }\n};\n\n/**\n * Remove a callback for a specific event.  If no parameters are passed,\n * all event listeners will be removed.\n * @param {String} event - The name of the event\n * @param {Function} callback\n */\nvar off = function off(event, callback) {\n  // logAnalytics(logAction.off, logVariation.attempt);\n  if (_arguments.lenth === 0) {\n    Object.keys(eventListeners).forEach(function (eventType) {\n      eventListeners[eventType].clear();\n    });\n  }\n  var eventCallbacks = eventListeners[event];\n  if (!eventCallbacks) {\n    // logAnalytics(logAction.off, logVariation.fail);\n    message(event + ' is not a registered event.');\n  } else {\n    eventCallbacks.delete(callback);\n    // logAnalytics(logAction.off, logVariation.success);\n  }\n};\n\n/**\n * Trigger an event and fire all registered callbacks\n * @param {String} event - The name of the event\n * @param {*} data - Data to be passed to callback functions\n */\nvar triggerEvent = function triggerEvent(event, data) {\n  var eventCallbacks = eventListeners[event];\n  if (!eventCallbacks) {\n    registerEvents(event);\n    message(event + ' has been registered as a new event.');\n  } else {\n    eventCallbacks.forEach(function (callback) {\n      return callback(data, event);\n    });\n  }\n};\n\n/**\n * Get the current OpenTok session object\n * @returns {Object}\n */\nvar getSession = internalState.getSession;\n\n/**\n * Returns the current OpenTok session credentials\n * @returns {Object}\n */\nvar getCredentials = internalState.getCredentials;\n\n/**\n * Returns the options used for initialization\n * @returns {Object}\n */\nvar getOptions = internalState.getOptions;\n\nvar createEventListeners = function createEventListeners(session, options) {\n  Object.keys(accPackEvents).forEach(function (type) {\n    return registerEvents(accPackEvents[type]);\n  });\n\n  /**\n   * If using screen sharing + annotation in an external window, the screen sharing\n   * package will take care of calling annotation.start() and annotation.linkCanvas()\n   */\n  var usingAnnotation = path('screenSharing.annotation', options);\n  var internalAnnotation = usingAnnotation && !path('screenSharing.externalWindow', options);\n\n  /**\n   * Wrap session events and update internalState when streams are created\n   * or destroyed\n   */\n  accPackEvents.session.forEach(function (eventName) {\n    session.on(eventName, function (event) {\n      if (eventName === 'streamCreated') {\n        internalState.addStream(event.stream);\n      }\n      if (eventName === 'streamDestroyed') {\n        internalState.removeStream(event.stream);\n      }\n      triggerEvent(eventName, event);\n    });\n  });\n\n  if (usingAnnotation) {\n    on('subscribeToScreen', function (_ref) {\n      var subscriber = _ref.subscriber;\n\n      annotation.start(getSession()).then(function () {\n        var absoluteParent = dom.query(path('annotation.absoluteParent.subscriber', options));\n        var linkOptions = absoluteParent ? { absoluteParent: absoluteParent } : null;\n        annotation.linkCanvas(subscriber, subscriber.element.parentElement, linkOptions);\n      });\n    });\n\n    on('unsubscribeFromScreen', function () {\n      annotation.end();\n    });\n  }\n\n  on('startScreenSharing', function (publisher) {\n    internalState.addPublisher('screen', publisher);\n    triggerEvent('startScreenShare', Object.assign({}, { publisher: publisher }, internalState.getPubSub()));\n    if (internalAnnotation) {\n      annotation.start(getSession()).then(function () {\n        var absoluteParent = dom.query(path('annotation.absoluteParent.publisher', options));\n        var linkOptions = absoluteParent ? { absoluteParent: absoluteParent } : null;\n        annotation.linkCanvas(publisher, publisher.element.parentElement, linkOptions);\n      });\n    }\n  });\n\n  on('endScreenSharing', function (publisher) {\n    // delete publishers.screen[publisher.id];\n    internalState.removePublisher('screen', publisher);\n    triggerEvent('endScreenShare', internalState.getPubSub());\n    if (internalAnnotation) {\n      annotation.end();\n    }\n  });\n};\n\nvar setupExternalAnnotation = function setupExternalAnnotation() {\n  return annotation.start(getSession(), {\n    screensharing: true\n  });\n};\n\nvar linkAnnotation = function linkAnnotation(pubSub, annotationContainer, externalWindow) {\n  annotation.linkCanvas(pubSub, annotationContainer, {\n    externalWindow: externalWindow\n  });\n\n  if (externalWindow) {\n    (function () {\n      // Add subscribers to the external window\n      var streams = internalState.getStreams();\n      var cameraStreams = Object.keys(streams).reduce(function (acc, streamId) {\n        var stream = streams[streamId];\n        return stream.videoType === 'camera' ? acc.concat(stream) : acc;\n      }, []);\n      cameraStreams.forEach(annotation.addSubscriberToExternalWindow);\n    })();\n  }\n};\n\nvar initPackages = function initPackages() {\n  logAnalytics(logAction.initPackages, logVariation.attempt);\n  var session = getSession();\n  var options = getOptions();\n  /**\n   * Try to require a package.  If 'require' is unavailable, look for\n   * the package in global scope.  A switch internalStatement is used because\n   * webpack and Browserify aren't able to resolve require internalStatements\n   * that use variable names.\n   * @param {String} packageName - The name of the npm package\n   * @param {String} globalName - The name of the package if exposed on global/window\n   * @returns {Object}\n   */\n  var optionalRequire = function optionalRequire(packageName, globalName) {\n    var result = void 0;\n    /* eslint-disable global-require, import/no-extraneous-dependencies, import/no-unresolved */\n    try {\n      switch (packageName) {\n        case 'opentok-text-chat':\n          result = require('opentok-text-chat');\n          break;\n        case 'opentok-screen-sharing':\n          result = require('opentok-screen-sharing');\n          break;\n        case 'opentok-annotation':\n          result = require('opentok-annotation');\n          break;\n        case 'opentok-archiving':\n          result = require('opentok-archiving');\n          break;\n        default:\n          break;\n      }\n      /* eslint-enable global-require */\n    } catch (error) {\n      result = window[globalName];\n    }\n    if (!result) {\n      logAnalytics(logAction.initPackages, logVariation.fail);\n      throw new CoreError('Could not load ' + packageName, 'missingDependency');\n    }\n    return result;\n  };\n\n  var availablePackages = {\n    textChat: function textChat() {\n      return optionalRequire('opentok-text-chat', 'TextChatAccPack');\n    },\n    screenSharing: function screenSharing() {\n      return optionalRequire('opentok-screen-sharing', 'ScreenSharingAccPack');\n    },\n    annotation: function annotation() {\n      return optionalRequire('opentok-annotation', 'AnnotationAccPack');\n    },\n    archiving: function archiving() {\n      return optionalRequire('opentok-archiving', 'ArchivingAccPack');\n    }\n  };\n\n  var packages = {};\n  (path('packages', options) || []).forEach(function (acceleratorPack) {\n    if (availablePackages[acceleratorPack]) {\n      // eslint-disable-next-line no-param-reassign\n      packages[properCase(acceleratorPack)] = availablePackages[acceleratorPack]();\n    } else {\n      message(acceleratorPack + ' is not a valid accelerator pack');\n    }\n  });\n\n  /**\n   * Get containers for streams, controls, and the chat widget\n   */\n  var getDefaultContainer = function getDefaultContainer(pubSub) {\n    return document.getElementById(pubSub + 'Container');\n  };\n  var getContainerElements = function getContainerElements() {\n    // Need to use path to check for null values\n    var controls = pathOr('#videoControls', 'controlsContainer', options);\n    var chat = pathOr('#chat', 'textChat.container', options);\n    var stream = pathOr(getDefaultContainer, 'streamContainers', options);\n    return { stream: stream, controls: controls, chat: chat };\n  };\n  /** *** *** *** *** */\n\n  /**\n   * Return options for the specified package\n   * @param {String} packageName\n   * @returns {Object}\n   */\n  var packageOptions = function packageOptions(packageName) {\n    /**\n     * Methods to expose to accelerator packs\n     */\n    var accPack = {\n      registerEventListener: on, // Legacy option\n      on: on,\n      registerEvents: registerEvents,\n      triggerEvent: triggerEvent,\n      setupExternalAnnotation: setupExternalAnnotation,\n      linkAnnotation: linkAnnotation\n    };\n\n    /**\n     * If options.controlsContainer/containers.controls is null,\n     * accelerator packs should not append their controls.\n     */\n    var containers = getContainerElements();\n    var appendControl = !!containers.controls;\n    var controlsContainer = containers.controls; // Legacy option\n    var streamContainers = containers.stream;\n    var baseOptions = { session: session, accPack: accPack, controlsContainer: controlsContainer, appendControl: appendControl, streamContainers: streamContainers };\n\n    switch (packageName) {\n      /* beautify ignore:start */\n      case 'communication':\n        {\n          return Object.assign({}, baseOptions, options.communication);\n        }\n      case 'textChat':\n        {\n          var textChatOptions = {\n            textChatContainer: options.textChat.container,\n            waitingMessage: options.textChat.waitingMessage,\n            sender: { alias: options.textChat.name }\n          };\n          return Object.assign({}, baseOptions, textChatOptions);\n        }\n      case 'screenSharing':\n        {\n          var screenSharingContainer = { screenSharingContainer: streamContainers };\n          return Object.assign({}, baseOptions, screenSharingContainer, options.screenSharing);\n        }\n      case 'annotation':\n        {\n          return Object.assign({}, baseOptions, options.annotation);\n        }\n      case 'archiving':\n        {\n          return Object.assign({}, baseOptions, options.archiving);\n        }\n      default:\n        return {};\n      /* beautify ignore:end */\n    }\n  };\n\n  /** Create instances of each package */\n  // eslint-disable-next-line global-require,import/no-extraneous-dependencies\n  communication.init(packageOptions('communication'));\n  textChat = packages.TextChat ? new packages.TextChat(packageOptions('textChat')) : null;\n  screenSharing = packages.ScreenSharing ? new packages.ScreenSharing(packageOptions('screenSharing')) : null;\n  annotation = packages.Annotation ? new packages.Annotation(packageOptions('annotation')) : null;\n  archiving = packages.Archiving ? new packages.Archiving(packageOptions('archiving')) : null;\n\n  logAnalytics(logAction.initPackages, logVariation.success);\n};\n\n/**\n * Ensures that we have the required credentials\n * @param {Object} credentials\n * @param {String} credentials.apiKey\n * @param {String} credentials.sessionId\n * @param {String} credentials.token\n */\nvar validateCredentials = function validateCredentials() {\n  var credentials = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n\n  var required = ['apiKey', 'sessionId', 'token'];\n  required.forEach(function (credential) {\n    if (!credentials[credential]) {\n      throw new CoreError(credential + ' is a required credential', 'invalidParameters');\n    }\n  });\n};\n\n/**\n * Connect to the session\n * @returns {Promise} <resolve: -, reject: Error>\n */\nvar connect = function connect() {\n  return new Promise(function (resolve, reject) {\n    logAnalytics(logAction.connect, logVariation.attempt);\n    var session = getSession();\n\n    var _getCredentials = getCredentials(),\n        token = _getCredentials.token;\n\n    session.connect(token, function (error) {\n      if (error) {\n        message(error);\n        logAnalytics(logAction.connect, logVariation.fail);\n        return reject(error);\n      }\n      var sessionId = session.sessionId,\n          apiKey = session.apiKey;\n\n      updateLogAnalytics(sessionId, path('connection.connectionId', session), apiKey);\n      logAnalytics(logAction.connect, logVariation.success);\n      initPackages();\n      triggerEvent('connected', session);\n      return resolve({ connections: session.connections.length() });\n    });\n  });\n};\n\n/**\n * Disconnect from the session\n * @returns {Promise} <resolve: -, reject: Error>\n */\nvar disconnect = function disconnect() {\n  logAnalytics(logAction.disconnect, logVariation.attempt);\n  getSession().disconnect();\n  internalState.reset();\n  logAnalytics(logAction.disconnect, logVariation.success);\n};\n\n/**\n * Force a remote connection to leave the session\n * @param {Object} connection\n * @returns {Promise} <resolve: empty, reject: Error>\n */\nvar forceDisconnect = function forceDisconnect(connection) {\n  return new Promise(function (resolve, reject) {\n    logAnalytics(logAction.forceDisconnect, logVariation.attempt);\n    getSession().forceDisconnect(connection, function (error) {\n      if (error) {\n        logAnalytics(logAction.forceDisconnect, logVariation.fail);\n        reject(error);\n      } else {\n        logAnalytics(logAction.forceDisconnect, logVariation.success);\n        resolve();\n      }\n    });\n  });\n};\n\n/**\n * Force the publisher of a stream to stop publishing the stream\n * @param {Object} stream\n * @returns {Promise} <resolve: empty, reject: Error>\n */\nvar forceUnpublish = function forceUnpublish(stream) {\n  return new Promise(function (resolve, reject) {\n    logAnalytics(logAction.forceUnpublish, logVariation.attempt);\n    getSession().forceUnpublish(stream, function (error) {\n      if (error) {\n        logAnalytics(logAction.forceUnpublish, logVariation.fail);\n        reject(error);\n      } else {\n        logAnalytics(logAction.forceUnpublish, logVariation.success);\n        resolve();\n      }\n    });\n  });\n};\n\n/**\n * Get the local publisher object for a stream\n * @param {Object} stream - An OpenTok stream object\n * @returns {Object} - The publisher object\n */\nvar getPublisherForStream = function getPublisherForStream(stream) {\n  return getSession().getPublisherForStream(stream);\n};\n\n/**\n * Get the local subscriber objects for a stream\n * @param {Object} stream - An OpenTok stream object\n * @returns {Array} - An array of subscriber object\n */\nvar getSubscribersForStream = function getSubscribersForStream(stream) {\n  return getSession().getSubscribersForStream(stream);\n};\n\n/**\n * Send a signal using the OpenTok signaling apiKey\n * @param {String} type\n * @param {*} [data]\n * @param {Object} to - An OpenTok connection object\n * @returns {Promise} <resolve: empty, reject: Error>\n */\nvar signal = function signal(type, signalData, to) {\n  return new Promise(function (resolve, reject) {\n    logAnalytics(logAction.signal, logVariation.attempt);\n    var session = getSession();\n    var data = JSON.stringify(signalData);\n    var signalObj = to ? { type: type, data: data, to: to } : { type: type, data: data };\n    session.signal(signalObj, function (error) {\n      if (error) {\n        logAnalytics(logAction.signal, logVariation.fail);\n        reject(error);\n      } else {\n        logAnalytics(logAction.signal, logVariation.success);\n        resolve();\n      }\n    });\n  });\n};\n\n/**\n * Enable or disable local audio\n * @param {Boolean} enable\n */\nvar toggleLocalAudio = function toggleLocalAudio(enable) {\n  logAnalytics(logAction.toggleLocalAudio, logVariation.attempt);\n\n  var _internalState$getPub = internalState.getPubSub(),\n      publishers = _internalState$getPub.publishers;\n\n  var toggleAudio = function toggleAudio(id) {\n    return communication.enableLocalAV(id, 'audio', enable);\n  };\n  Object.keys(publishers.camera).forEach(toggleAudio);\n  logAnalytics(logAction.toggleLocalAudio, logVariation.success);\n};\n\n/**\n * Enable or disable local video\n * @param {Boolean} enable\n */\nvar toggleLocalVideo = function toggleLocalVideo(enable) {\n  logAnalytics(logAction.toggleLocalVideo, logVariation.attempt);\n\n  var _internalState$getPub2 = internalState.getPubSub(),\n      publishers = _internalState$getPub2.publishers;\n\n  var toggleVideo = function toggleVideo(id) {\n    return communication.enableLocalAV(id, 'video', enable);\n  };\n  Object.keys(publishers.camera).forEach(toggleVideo);\n  logAnalytics(logAction.toggleLocalVideo, logVariation.success);\n};\n\n/**\n * Enable or disable remote audio\n * @param {String} id - Subscriber id\n * @param {Boolean} enable\n */\nvar toggleRemoteAudio = function toggleRemoteAudio(id, enable) {\n  logAnalytics(logAction.toggleRemoteAudio, logVariation.attempt);\n  communication.enableRemoteAV(id, 'audio', enable);\n  logAnalytics(logAction.toggleRemoteAudio, logVariation.success);\n};\n\n/**\n * Enable or disable remote video\n * @param {String} id - Subscriber id\n * @param {Boolean} enable\n */\nvar toggleRemoteVideo = function toggleRemoteVideo(id, enable) {\n  logAnalytics(logAction.toggleRemoteVideo, logVariation.attempt);\n  communication.enableRemoteAV(id, 'video', enable);\n  logAnalytics(logAction.toggleRemoteVideo, logVariation.success);\n};\n\n/**\n * Initialize the accelerator pack\n * @param {Object} options\n * @param {Object} options.credentials\n * @param {Array} [options.packages]\n * @param {Object} [options.containers]\n */\nvar init = function init(options) {\n  if (!options) {\n    throw new CoreError('Missing options required for initialization', 'invalidParameters');\n  }\n  var credentials = options.credentials;\n\n  validateCredentials(options.credentials);\n\n  // Init analytics\n  initLogAnalytics(window.location.origin, credentials.sessionId, null, credentials.apiKey);\n  logAnalytics(logAction.init, logVariation.attempt);\n  var session = OT.initSession(credentials.apiKey, credentials.sessionId);\n  createEventListeners(session, options);\n  internalState.setSession(session);\n  internalState.setCredentials(credentials);\n  internalState.setOptions(options);\n  logAnalytics(logAction.init, logVariation.success);\n};\n\nvar opentokCore = {\n  init: init,\n  connect: connect,\n  disconnect: disconnect,\n  forceDisconnect: forceDisconnect,\n  forceUnpublish: forceUnpublish,\n  getAccPack: getAccPack,\n  getOptions: getOptions,\n  getSession: getSession,\n  getPublisherForStream: getPublisherForStream,\n  getSubscribersForStream: getSubscribersForStream,\n  on: on,\n  off: off,\n  registerEventListener: on,\n  triggerEvent: triggerEvent,\n  signal: signal,\n  state: internalState.all,\n  startCall: communication.startCall,\n  endCall: communication.endCall,\n  OpenTokSDK: OpenTokSDK,\n  toggleLocalAudio: toggleLocalAudio,\n  toggleLocalVideo: toggleLocalVideo,\n  toggleRemoteAudio: toggleRemoteAudio,\n  toggleRemoteVideo: toggleRemoteVideo,\n  subscribe: communication.subscribe,\n  unsubscribe: communication.unsubscribe,\n  util: util\n};\n\nif (global === window) {\n  window.otCore = opentokCore;\n}\n\nmodule.exports = opentokCore;\n\n}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? window : {})\n},{\"./communication\":2,\"./errors\":4,\"./events\":5,\"./logging\":6,\"./sdk-wrapper/sdkWrapper\":8,\"./state\":10,\"./util\":11,\"opentok-annotation\":undefined,\"opentok-archiving\":undefined,\"opentok-screen-sharing\":undefined,\"opentok-text-chat\":undefined}],4:[function(require,module,exports){\n\"use strict\";\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\n/** Errors */\nvar CoreError = function (_Error) {\n  _inherits(CoreError, _Error);\n\n  function CoreError(errorMessage, errorType) {\n    _classCallCheck(this, CoreError);\n\n    var _this = _possibleConstructorReturn(this, (CoreError.__proto__ || Object.getPrototypeOf(CoreError)).call(this, \"otAccCore: \" + errorMessage));\n\n    _this.type = errorType;\n    return _this;\n  }\n\n  return CoreError;\n}(Error);\n\nmodule.exports = {\n  CoreError: CoreError\n};\n\n},{}],5:[function(require,module,exports){\n'use strict';\n\nvar events = {\n  session: ['archiveStarted', 'archiveStopped', 'connectionCreated', 'connectionDestroyed', 'sessionConnected', 'sessionDisconnected', 'sessionReconnected', 'sessionReconnecting', 'signal', 'streamCreated', 'streamDestroyed', 'streamPropertyChanged'],\n  core: ['connected', 'startScreenShare', 'endScreenShare', 'error'],\n  communication: ['startCall', 'endCall', 'callPropertyChanged', 'subscribeToCamera', 'subscribeToScreen', 'subscribeToSip', 'unsubscribeFromCamera', 'unsubscribeFromScreen', 'startViewingSharedScreen', 'endViewingSharedScreen'],\n  textChat: ['showTextChat', 'hideTextChat', 'messageSent', 'errorSendingMessage', 'messageReceived'],\n  screenSharing: ['startScreenSharing', 'endScreenSharing', 'screenSharingError'],\n  annotation: ['startAnnotation', 'linkAnnotation', 'resizeCanvas', 'annotationWindowClosed', 'endAnnotation'],\n  archiving: ['startArchive', 'stopArchive', 'archiveReady', 'archiveError']\n};\n\nmodule.exports = events;\n\n},{}],6:[function(require,module,exports){\n'use strict';\n\nvar OTKAnalytics = require('opentok-solutions-logging');\n\n// eslint-disable-next-line no-console\nvar message = function message(messageText) {\n  return console.log('otAccCore: ' + messageText);\n};\n\n/** Analytics */\n\nvar analytics = null;\n\nvar logVariation = {\n  attempt: 'Attempt',\n  success: 'Success',\n  fail: 'Fail'\n};\n\nvar logAction = {\n  // vars for the analytics logs. Internal use\n  init: 'Init',\n  initPackages: 'InitPackages',\n  connect: 'ConnectCoreAcc',\n  disconnect: 'DisconnectCoreAcc',\n  forceDisconnect: 'ForceDisconnectCoreAcc',\n  forceUnpublish: 'ForceUnpublishCoreAcc',\n  getAccPack: 'GetAccPack',\n  signal: 'SignalCoreAcc',\n  startCall: 'StartCallCoreAcc',\n  endCall: 'EndCallCoreAcc',\n  toggleLocalAudio: 'ToggleLocalAudio',\n  toggleLocalVideo: 'ToggleLocalVideo',\n  toggleRemoteAudio: 'ToggleRemoteAudio',\n  toggleRemoteVideo: 'ToggleRemoteVideo',\n  subscribe: 'SubscribeCoreAcc',\n  unsubscribe: 'UnsubscribeCoreAcc'\n};\n\nvar updateLogAnalytics = function updateLogAnalytics(sessionId, connectionId, apiKey) {\n  if (sessionId && connectionId && apiKey) {\n    var sessionInfo = {\n      sessionId: sessionId,\n      connectionId: connectionId,\n      partnerId: apiKey\n    };\n    analytics.addSessionInfo(sessionInfo);\n  }\n};\n\nvar initLogAnalytics = function initLogAnalytics(source, sessionId, connectionId, apikey) {\n  var otkanalyticsData = {\n    clientVersion: 'js-vsol-1.0.0',\n    source: source,\n    componentId: 'coreAccelerator',\n    name: 'coreAccelerator',\n    partnerId: apikey\n  };\n\n  analytics = new OTKAnalytics(otkanalyticsData);\n\n  if (connectionId) {\n    updateLogAnalytics(sessionId, connectionId, apikey);\n  }\n};\n\nvar logAnalytics = function logAnalytics(action, variation) {\n  analytics.logEvent({ action: action, variation: variation });\n};\n\nmodule.exports = {\n  message: message,\n  logAction: logAction,\n  logVariation: logVariation,\n  initLogAnalytics: initLogAnalytics,\n  updateLogAnalytics: updateLogAnalytics,\n  logAnalytics: logAnalytics\n};\n\n},{\"opentok-solutions-logging\":1}],7:[function(require,module,exports){\n\"use strict\";\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\n/** Errors */\nvar SDKError = function (_Error) {\n  _inherits(SDKError, _Error);\n\n  function SDKError(errorMessage, errorType) {\n    _classCallCheck(this, SDKError);\n\n    var _this = _possibleConstructorReturn(this, (SDKError.__proto__ || Object.getPrototypeOf(SDKError)).call(this, \"otSDK: \" + errorMessage));\n\n    _this.type = errorType;\n    return _this;\n  }\n\n  return SDKError;\n}(Error);\n\nmodule.exports = {\n  SDKError: SDKError\n};\n\n},{}],8:[function(require,module,exports){\n(function (global){\n'use strict';\n\nvar _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; };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n/* global OT */\n\n/* Dependencies */\nvar State = require('./state');\n\nvar _require = require('./errors'),\n    SDKError = _require.SDKError;\n\n/* Internal variables */\n\nvar stateMap = new WeakMap();\n\n/* Internal methods */\n\n/**\n * Ensures that we have the required credentials\n * @param {Object} credentials\n * @param {String} credentials.apiKey\n * @param {String} credentials.sessionId\n * @param {String} credentials.token\n * @returns {Object}\n */\nvar validateCredentials = function validateCredentials() {\n  var credentials = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n  var required = ['apiKey', 'sessionId', 'token'];\n  required.forEach(function (credential) {\n    if (!credentials[credential]) {\n      throw new SDKError(credential + ' is a required credential', 'invalidParameters');\n    }\n  });\n  return credentials;\n};\n\n/**\n * Initialize an OpenTok publisher object\n * @param {String | Object} element - The target element\n * @param {Object} properties - The publisher properties\n * @returns {Promise} <resolve: Object, reject: Error>\n */\nvar initPublisher = function initPublisher(element, properties) {\n  return new Promise(function (resolve, reject) {\n    var publisher = OT.initPublisher(element, properties, function (error) {\n      error ? reject(error) : resolve(publisher);\n    });\n  });\n};\n\n/**\n * Binds and sets a single event listener on the OpenTok session\n * @param {String} event - The name of the event\n * @param {Function} callback\n */\nvar bindListener = function bindListener(target, context, event, callback) {\n  var paramsError = '\\'on\\' requires a string and a function to create an event listener.';\n  if (typeof event !== 'string' || typeof callback !== 'function') {\n    throw new SDKError(paramsError, 'invalidParameters');\n  }\n  target.on(event, callback.bind(context));\n};\n\n/**\n * Bind and set event listeners\n * @param {Object} target - An OpenTok session, publisher, or subscriber object\n * @param {Object} context - The context to which to bind event listeners\n * @param {Object | Array} listeners - An object (or array of objects) with\n *        eventName/callback k/v pairs\n */\nvar bindListeners = function bindListeners(target, context, listeners) {\n  /**\n   * Create listeners from an object with event/callback k/v pairs\n   * @param {Object} listeners\n   */\n  var createListenersFromObject = function createListenersFromObject(eventListeners) {\n    Object.keys(eventListeners).forEach(function (event) {\n      bindListener(target, context, event, eventListeners[event]);\n    });\n  };\n\n  if (Array.isArray(listeners)) {\n    listeners.forEach(function (listener) {\n      return createListenersFromObject(listener);\n    });\n  } else {\n    createListenersFromObject(listeners);\n  }\n};\n\n/**\n * @class\n * Represents an OpenTok SDK Wrapper\n */\n\nvar OpenTokSDK = function () {\n  /**\n   * Create an SDK Wrapper\n   * @param {Object} credentials\n   * @param {String} credentials.apiKey\n   * @param {String} credentials.sessionId\n   * @param {String} credentials.token\n   */\n  function OpenTokSDK(credentials) {\n    _classCallCheck(this, OpenTokSDK);\n\n    this.credentials = validateCredentials(credentials);\n    stateMap.set(this, new State());\n    this.session = OT.initSession(credentials.apiKey, credentials.sessionId);\n    this.setInternalListeners();\n  }\n\n  /**\n   * Determines if a connection object is my local connection\n   * @param {Object} connection - An OpenTok connection object\n   * @returns {Boolean}\n   */\n\n\n  _createClass(OpenTokSDK, [{\n    key: 'isMe',\n    value: function isMe(connection) {\n      var session = this.session;\n\n      return session && session.connection.connectionId === connection.connectionId;\n    }\n\n    /**\n     * Wrap OpenTok session events\n     */\n\n  }, {\n    key: 'setInternalListeners',\n    value: function setInternalListeners() {\n      /**\n       * Wrap session events and update state when streams are created\n       * or destroyed\n       */\n      var state = stateMap.get(this);\n      this.session.on('streamCreated', function (_ref) {\n        var stream = _ref.stream;\n        return state.addStream(stream);\n      });\n      this.session.on('streamDestroyed', function (_ref2) {\n        var stream = _ref2.stream;\n        return state.removeStream(stream);\n      });\n    }\n\n    /**\n     * Register a callback for a specific event, pass an object\n     * with event => callback key/values (or an array of objects)\n     * to register callbacks for multiple events.\n     * @param {String | Object | Array} [events] - The name of the events\n     * @param {Function} [callback]\n     * https://tokbox.com/developer/sdks/js/reference/Session.html#on\n     */\n\n  }, {\n    key: 'on',\n    value: function on() {\n      if (arguments.length === 1 && _typeof(arguments.length <= 0 ? undefined : arguments[0]) === 'object') {\n        bindListeners(this.session, this, arguments.length <= 0 ? undefined : arguments[0]);\n      } else if (arguments.length === 2) {\n        bindListener(this.session, this, arguments.length <= 0 ? undefined : arguments[0], arguments.length <= 1 ? undefined : arguments[1]);\n      }\n    }\n\n    /**\n     * Remove a callback for a specific event. If no parameters are passed,\n     * all callbacks for the session will be removed.\n     * @param {String} [events] - The name of the events\n     * @param {Function} [callback]\n     * https://tokbox.com/developer/sdks/js/reference/Session.html#off\n     */\n\n  }, {\n    key: 'off',\n    value: function off() {\n      var _session;\n\n      (_session = this.session).off.apply(_session, arguments);\n    }\n\n    /**\n     * Enable or disable local publisher audio\n     * @param {Boolean} enable\n     */\n\n  }, {\n    key: 'enablePublisherAudio',\n    value: function enablePublisherAudio(enable) {\n      var _stateMap$get$getPubS = stateMap.get(this).getPubSub(),\n          publishers = _stateMap$get$getPubS.publishers;\n\n      Object.keys(publishers.camera).forEach(function (publisherId) {\n        publishers.camera[publisherId].publishAudio(enable);\n      });\n    }\n\n    /**\n     * Enable or disable local publisher video\n     * @param {Boolean} enable\n     */\n\n  }, {\n    key: 'enablePublisherVideo',\n    value: function enablePublisherVideo(enable) {\n      var _stateMap$get$getPubS2 = stateMap.get(this).getPubSub(),\n          publishers = _stateMap$get$getPubS2.publishers;\n\n      Object.keys(publishers.camera).forEach(function (publisherId) {\n        publishers.camera[publisherId].publishVideo(enable);\n      });\n    }\n\n    /**\n     * Enable or disable local subscriber audio\n     * @param {String} streamId\n     * @param {Boolean} enable\n     */\n\n  }, {\n    key: 'enableSubscriberAudio',\n    value: function enableSubscriberAudio(streamId, enable) {\n      var _stateMap$get$all = stateMap.get(this).all(),\n          streamMap = _stateMap$get$all.streamMap,\n          subscribers = _stateMap$get$all.subscribers;\n\n      var subscriberId = streamMap[streamId];\n      var subscriber = subscribers.camera[subscriberId] || subscribers.screen[subscriberId];\n      subscriber && subscriber.subscribeToVideo(enable);\n    }\n\n    /**\n     * Enable or disable local subscriber video\n     * @param {String} streamId\n     * @param {Boolean} enable\n     */\n\n  }, {\n    key: 'enableSubscriberVideo',\n    value: function enableSubscriberVideo(streamId, enable) {\n      var _stateMap$get$all2 = stateMap.get(this).all(),\n          streamMap = _stateMap$get$all2.streamMap,\n          subscribers = _stateMap$get$all2.subscribers;\n\n      var subscriberId = streamMap[streamId];\n      var subscriber = subscribers.camera[subscriberId] || subscribers.screen[subscriberId];\n      subscriber && subscriber.subscribeToAudio(enable);\n    }\n\n    /**\n     * Create and publish a stream\n     * @param {String | Object} element - The target element\n     * @param {Object} properties - The publisher properties\n     * @param {Array | Object} [eventListeners] - An object (or array of objects) with\n     *        eventName/callback k/v pairs\n     * @param {Boolean} [preview] - Create a publisher with publishing to the session\n     * @returns {Promise} <resolve: Object, reject: Error>\n     */\n\n  }, {\n    key: 'publish',\n    value: function publish(element, properties) {\n      var _this = this;\n\n      var eventListeners = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n      var preview = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n\n      return new Promise(function (resolve, reject) {\n        initPublisher(element, properties) // eslint-disable-next-line no-confusing-arrow\n        .then(function (publisher) {\n          eventListeners && bindListeners(publisher, _this, eventListeners);\n          if (preview) {\n            resolve(publisher);\n          } else {\n            _this.publishPreview(publisher).then(resolve).catch(reject);\n          }\n        }).catch(reject);\n      });\n    }\n\n    /**\n     * Publish a 'preview' stream to the session\n     * @param {Object} publisher - An OpenTok publisher object\n     * @returns {Promise} <resolve: empty, reject: Error>\n     */\n\n  }, {\n    key: 'publishPreview',\n    value: function publishPreview(publisher) {\n      var _this2 = this;\n\n      return new Promise(function (resolve, reject) {\n        var state = stateMap.get(_this2);\n        _this2.session.publish(publisher, function (error) {\n          error && reject(error);\n          var type = publisher.stream.videoType;\n          state.addPublisher(type, publisher);\n          resolve(publisher);\n        });\n      });\n    }\n\n    /**\n     * Stop publishing a stream\n     * @param {Object} publisher - An OpenTok publisher object\n     */\n\n  }, {\n    key: 'unpublish',\n    value: function unpublish(publisher) {\n      var type = publisher.stream.videoType;\n      var state = stateMap.get(this);\n      this.session.unpublish(publisher);\n      state.removePublisher(type, publisher);\n    }\n\n    /**\n     * Subscribe to stream\n     * @param {Object} stream\n     * @param {String | Object} container - The id of the container or a reference to the element\n     * @param {Object} [properties]\n     * @param {Array | Object} [eventListeners] - An object (or array of objects) with\n     *        eventName/callback k/v pairs\n     * @returns {Promise} <resolve: empty, reject: Error>\n     * https://tokbox.com/developer/sdks/js/reference/Session.html#subscribe\n     */\n\n  }, {\n    key: 'subscribe',\n    value: function subscribe(stream, container, properties, eventListeners) {\n      var _this3 = this;\n\n      var state = stateMap.get(this);\n      return new Promise(function (resolve, reject) {\n        var subscriber = _this3.session.subscribe(stream, container, properties, function (error) {\n          if (error) {\n            reject(error);\n          } else {\n            state.addSubscriber(subscriber);\n            eventListeners && bindListeners(subscriber, _this3, eventListeners);\n            resolve(subscriber);\n          }\n        });\n      });\n    }\n\n    /**\n     * Unsubscribe from a stream and update the state\n     * @param {Object} subscriber - An OpenTok subscriber object\n     * @returns {Promise} <resolve: empty>\n     */\n\n  }, {\n    key: 'unsubscribe',\n    value: function unsubscribe(subscriber) {\n      var _this4 = this;\n\n      var state = stateMap.get(this);\n      return new Promise(function (resolve) {\n        _this4.session.unsubscribe(subscriber);\n        state.removeSubscriber(subscriber);\n        resolve();\n      });\n    }\n\n    /**\n     * Connect to the OpenTok session\n     * @param {Array | Object} [eventListeners] - An object (or array of objects) with\n     *        eventName/callback k/v pairs\n     * @returns {Promise} <resolve: empty, reject: Error>\n     */\n\n  }, {\n    key: 'connect',\n    value: function connect(eventListeners) {\n      var _this5 = this;\n\n      this.off();\n      eventListeners && this.on(eventListeners);\n      return new Promise(function (resolve, reject) {\n        var token = _this5.credentials.token;\n\n        _this5.session.connect(token, function (error) {\n          error ? reject(error) : resolve();\n        });\n      });\n    }\n\n    /**\n     * Force a remote connection to leave the session\n     * @param {Object} connection\n     * @returns {Promise} <resolve: empty, reject: Error>\n     */\n\n  }, {\n    key: 'forceDisconnect',\n    value: function forceDisconnect(connection) {\n      var _this6 = this;\n\n      return new Promise(function (resolve, reject) {\n        _this6.session.forceDisconnect(connection, function (error) {\n          error ? reject(error) : resolve();\n        });\n      });\n    }\n\n    /**\n     * Force the publisher of a stream to stop publishing the stream\n     * @param {Object} stream\n     * @returns {Promise} <resolve: empty, reject: Error>\n     */\n\n  }, {\n    key: 'forceUnpublish',\n    value: function forceUnpublish(stream) {\n      var _this7 = this;\n\n      return new Promise(function (resolve, reject) {\n        _this7.session.forceUnpublish(stream, function (error) {\n          error ? reject(error) : resolve();\n        });\n      });\n    }\n\n    /**\n     * Send a signal using the OpenTok signaling apiKey\n     * @param {String} type\n     * @param {*} signalData\n     * @param {Object} [to] - An OpenTok connection object\n     * @returns {Promise} <resolve: empty, reject: Error>\n     * https://tokbox.com/developer/guides/signaling/js/\n     */\n\n  }, {\n    key: 'signal',\n    value: function signal(type, signalData, to) {\n      var _this8 = this;\n\n      var data = JSON.stringify(signalData);\n      var signal = to ? { type: type, data: data, to: to } : { type: type, data: data };\n      return new Promise(function (resolve, reject) {\n        _this8.session.signal(signal, function (error) {\n          error ? reject(error) : resolve();\n        });\n      });\n    }\n\n    /**\n     * Disconnect from the OpenTok session\n     */\n\n  }, {\n    key: 'disconnect',\n    value: function disconnect() {\n      this.session.disconnect();\n      stateMap.get(this).reset();\n    }\n\n    /**\n     * Return the state of the OpenTok session\n     * @returns {Object} Streams, publishers, subscribers, and stream map\n     */\n\n  }, {\n    key: 'state',\n    value: function state() {\n      return stateMap.get(this).all();\n    }\n  }]);\n\n  return OpenTokSDK;\n}();\n\nif (global === window) {\n  window.OpenTokSDK = OpenTokSDK;\n}\n\nmodule.exports = OpenTokSDK;\n\n}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? window : {})\n},{\"./errors\":7,\"./state\":9}],9:[function(require,module,exports){\n\"use strict\";\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar State = function () {\n  function State() {\n    _classCallCheck(this, State);\n\n    this.publishers = {\n      camera: {},\n      screen: {}\n    };\n\n    this.subscribers = {\n      camera: {},\n      screen: {}\n    };\n\n    this.streams = {};\n\n    // Map stream ids to subscriber/publisher ids\n    this.streamMap = {};\n\n    // OpenTok session\n    this.session = null;\n\n    // OpenTok credentials\n    this.credentials = null;\n  }\n\n  // Get the current OpenTok session\n\n\n  _createClass(State, [{\n    key: \"getSession\",\n    value: function getSession() {\n      return this.session;\n    }\n\n    // Set the current OpenTok session\n\n  }, {\n    key: \"setSession\",\n    value: function setSession(session) {\n      this.session = session;\n    }\n\n    // Get the current OpenTok credentials\n\n  }, {\n    key: \"getCredentials\",\n    value: function getCredentials() {\n      return this.credentials;\n    }\n    // Set the current OpenTok credentials\n\n  }, {\n    key: \"setCredentials\",\n    value: function setCredentials(credentials) {\n      this.credentials = credentials;\n    }\n\n    /**\n     * Returns the count of current publishers and subscribers by type\n     * @retuns {Object}\n     *    {\n     *      publishers: {\n     *        camera: 1,\n     *        screen: 1,\n     *        total: 2\n     *      },\n     *      subscribers: {\n     *        camera: 3,\n     *        screen: 1,\n     *        total: 4\n     *      }\n     *   }\n     */\n\n  }, {\n    key: \"pubSubCount\",\n    value: function pubSubCount() {\n      var publishers = this.publishers,\n          subscribers = this.subscribers;\n      /* eslint-disable no-param-reassign */\n\n      var pubs = Object.keys(publishers).reduce(function (acc, source) {\n        acc[source] = Object.keys(publishers[source]).length;\n        acc.total += acc[source];\n        return acc;\n      }, { camera: 0, screen: 0, total: 0 });\n\n      var subs = Object.keys(subscribers).reduce(function (acc, source) {\n        acc[source] = Object.keys(subscribers[source]).length;\n        acc.total += acc[source];\n        return acc;\n      }, { camera: 0, screen: 0, total: 0 });\n      /* eslint-enable no-param-reassign */\n      return { publisher: pubs, subscriber: subs };\n    }\n\n    /**\n     * Returns the current publishers and subscribers, along with a count of each\n     */\n\n  }, {\n    key: \"getPubSub\",\n    value: function getPubSub() {\n      var publishers = this.publishers,\n          subscribers = this.subscribers;\n\n      return { publishers: publishers, subscribers: subscribers, meta: this.pubSubCount() };\n    }\n  }, {\n    key: \"addPublisher\",\n    value: function addPublisher(type, publisher) {\n      this.streamMap[publisher.streamId] = publisher.id;\n      this.publishers[type][publisher.id] = publisher;\n    }\n  }, {\n    key: \"removePublisher\",\n    value: function removePublisher(type, publisher) {\n      var id = publisher.id || this.streamMap[publisher.streamId];\n      delete this.publishers[type][id];\n    }\n  }, {\n    key: \"removeAllPublishers\",\n    value: function removeAllPublishers() {\n      this.publishers.camera = {};\n      this.publishers.screen = {};\n    }\n  }, {\n    key: \"addSubscriber\",\n    value: function addSubscriber(subscriber) {\n      var type = subscriber.stream.videoType;\n      var streamId = subscriber.stream.id;\n      this.subscribers[type][subscriber.id] = subscriber;\n      this.streamMap[streamId] = subscriber.id;\n    }\n  }, {\n    key: \"removeSubscriber\",\n    value: function removeSubscriber() {\n      var subscriber = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n      var stream = subscriber.stream;\n\n      var type = stream && stream.videoType;\n      delete this.subscribers[type][subscriber.id];\n    }\n  }, {\n    key: \"addStream\",\n    value: function addStream(stream) {\n      this.streams[stream.id] = stream;\n    }\n  }, {\n    key: \"removeStream\",\n    value: function removeStream(stream) {\n      var type = stream.videoType;\n      var subscriberId = this.streamMap[stream.id];\n      delete this.streamMap[stream.id];\n      delete this.streams[stream.id];\n      this.removeSubscriber(this.subscribers[type][subscriberId]);\n    }\n  }, {\n    key: \"getStreams\",\n    value: function getStreams() {\n      return this.streams;\n    }\n\n    /** Reset streams, publishers, and subscribers */\n\n  }, {\n    key: \"reset\",\n    value: function reset() {\n      this.streams = {};\n      this.streamMap = {};\n      this.publishers = { camera: {}, screen: {} };\n      this.subscribers = { camera: {}, screen: {} };\n    }\n  }, {\n    key: \"all\",\n    value: function all() {\n      var streams = this.streams,\n          streamMap = this.streamMap;\n\n      return Object.assign({}, this.getPubSub(), { streams: streams, streamMap: streamMap });\n    }\n  }]);\n\n  return State;\n}();\n\nmodule.exports = State;\n\n},{}],10:[function(require,module,exports){\n'use strict';\n\n/**\n * Internal variables\n */\n\n// Map publisher ids to publisher objects\nvar publishers = {\n  camera: {},\n  screen: {}\n};\n\n// Map subscriber id to subscriber objects\nvar subscribers = {\n  camera: {},\n  screen: {},\n  sip: {}\n};\n\n// Map stream ids to stream objects\nvar streams = {};\n\n// Map stream ids to subscriber/publisher ids\nvar streamMap = {};\n\nvar session = null;\nvar credentials = null;\nvar options = null;\n\n/**\n * Internal methods\n */\n\n/**\n * Returns the count of current publishers and subscribers by type\n * @retuns {Object}\n *    {\n *      publishers: {\n *        camera: 1,\n *        screen: 1,\n *        total: 2\n *      },\n *      subscribers: {\n *        camera: 3,\n *        screen: 1,\n *        total: 4\n *      }\n *   }\n */\nvar pubSubCount = function pubSubCount() {\n  /* eslint-disable no-param-reassign */\n  var pubs = Object.keys(publishers).reduce(function (acc, source) {\n    acc[source] = Object.keys(publishers[source]).length;\n    acc.total += acc[source];\n    return acc;\n  }, { camera: 0, screen: 0, total: 0 });\n\n  var subs = Object.keys(subscribers).reduce(function (acc, source) {\n    acc[source] = Object.keys(subscribers[source]).length;\n    acc.total += acc[source];\n    return acc;\n  }, { camera: 0, screen: 0, sip: 0, total: 0 });\n  /* eslint-enable no-param-reassign */\n  return { publisher: pubs, subscriber: subs };\n};\n\n/**\n * Returns the current publishers and subscribers, along with a count of each\n * @returns {Object}\n */\nvar getPubSub = function getPubSub() {\n  return { publishers: publishers, subscribers: subscribers, meta: pubSubCount() };\n};\n\n/**\n * Get streams, streamMap, publishers, and subscribers\n * @return {Object}\n */\nvar all = function all() {\n  return Object.assign({}, { streams: streams, streamMap: streamMap }, getPubSub());\n};\n\n/**\n * Get the current OpenTok session\n * @returns {Object}\n */\nvar getSession = function getSession() {\n  return session;\n};\n\n/**\n * Set the current OpenTok session\n * @param {Object} otSession\n */\nvar setSession = function setSession(otSession) {\n  session = otSession;\n};\n\n/**\n * Get the current OpenTok credentials\n * @returns {Object}\n */\nvar getCredentials = function getCredentials() {\n  return credentials;\n};\n\n/**\n * Set the current OpenTok credentials\n * @param {Object} otCredentials\n */\nvar setCredentials = function setCredentials(otCredentials) {\n  credentials = otCredentials;\n};\n\n/**\n * Get the options defined for core\n * @returns {Object}\n */\nvar getOptions = function getOptions() {\n  return options;\n};\n\n/**\n * Set the options defined for core\n * @param {Object} otOptions\n */\nvar setOptions = function setOptions(otOptions) {\n  options = otOptions;\n};\n\n/**\n * Add a stream to state\n * @param {Object} stream - An OpenTok stream object\n */\nvar addStream = function addStream(stream) {\n  streams[stream.id] = stream;\n};\n\n/**\n * Remove a stream from state and any associated subscribers\n * @param {Object} stream - An OpenTok stream object\n */\nvar removeStream = function removeStream(stream) {\n  var type = stream.videoType;\n  var subscriberId = streamMap[stream.id];\n  delete streamMap[stream.id];\n  delete subscribers[type][subscriberId];\n  delete streams[stream.id];\n};\n\n/**\n * Get all remote streams\n * @returns {Object}\n */\nvar getStreams = function getStreams() {\n  return streams;\n};\n\n/**\n * Get the map of stream ids to publisher/subscriber ids\n * @returns {Object}\n */\nvar getStreamMap = function getStreamMap() {\n  return streamMap;\n};\n\n/**\n * Add a publisher to state\n * @param {String} type - 'camera' or 'screen'\n * @param {Object} publisher - The OpenTok publisher object\n */\nvar addPublisher = function addPublisher(type, publisher) {\n  streamMap[publisher.streamId] = publisher.id;\n  publishers[type][publisher.id] = publisher;\n};\n\n/**\n * Remove a publisher from state\n * @param {String} type - 'camera' or 'screen'\n * @param {Object} publisher - The OpenTok publisher object\n */\nvar removePublisher = function removePublisher(type, publisher) {\n  var id = publisher.id || streamMap[publisher.streamId];\n  delete publishers[type][id];\n  delete streamMap[publisher.streamId];\n};\n\n/**\n * Remove all publishers from state\n */\nvar removeAllPublishers = function removeAllPublishers() {\n  ['camera', 'screen'].forEach(function (type) {\n    Object.values(publishers[type]).forEach(function (publisher) {\n      removePublisher(type, publisher);\n    });\n  });\n};\n\n/**\n * Add a subscriber to state\n * @param {Object} - An OpenTok subscriber object\n */\nvar addSubscriber = function addSubscriber(subscriber) {\n  var type = subscriber.stream.videoType;\n  var streamId = subscriber.stream.id;\n  subscribers[type][subscriber.id] = subscriber;\n  streamMap[streamId] = subscriber.id;\n};\n\n/**\n * Remove a publisher from state\n * @param {String} type - 'camera' or 'screen'\n * @param {Object} subscriber - The OpenTok subscriber object\n */\nvar removeSubscriber = function removeSubscriber(type, subscriber) {\n  var id = subscriber.id || streamMap[subscriber.streamId];\n  delete subscribers[type][id];\n  delete streamMap[subscriber.streamId];\n};\n\n/**\n * Remove all subscribers from state\n */\nvar removeAllSubscribers = function removeAllSubscribers() {\n  ['camera', 'screen', 'sip'].forEach(function (type) {\n    Object.values(subscribers[type]).forEach(function (subscriber) {\n      removeSubscriber(type, subscriber);\n    });\n  });\n};\n\n/**\n * Reset state\n */\nvar reset = function reset() {\n  removeAllPublishers();\n  removeAllSubscribers();\n  [streams, streamMap].forEach(function (streamObj) {\n    Object.keys(streamObj).forEach(function (streamId) {\n      delete streamObj[streamId]; // eslint-disable-line no-param-reassign\n    });\n  });\n};\n\n/** Exports */\nmodule.exports = {\n  all: all,\n  getSession: getSession,\n  setSession: setSession,\n  getCredentials: getCredentials,\n  setCredentials: setCredentials,\n  getOptions: getOptions,\n  setOptions: setOptions,\n  addStream: addStream,\n  removeStream: removeStream,\n  getStreams: getStreams,\n  getStreamMap: getStreamMap,\n  addPublisher: addPublisher,\n  removePublisher: removePublisher,\n  removeAllPublishers: removeAllPublishers,\n  addSubscriber: addSubscriber,\n  removeSubscriber: removeSubscriber,\n  removeAllSubscribers: removeAllSubscribers,\n  getPubSub: getPubSub,\n  reset: reset\n};\n\n},{}],11:[function(require,module,exports){\n'use strict';\n\n/** Wrap DOM selector methods:\n *  document.querySelector,\n *  document.getElementById,\n *  document.getElementsByClassName\n *  'element' checks for a string before returning an element with `query`\n */\nvar dom = {\n  query: function query(arg) {\n    return document.querySelector(arg);\n  },\n  id: function id(arg) {\n    return document.getElementById(arg);\n  },\n  class: function _class(arg) {\n    return document.getElementsByClassName(arg);\n  },\n  element: function element(el) {\n    return typeof el === 'string' ? this.query(el) : el;\n  }\n};\n\n/**\n * Returns a (nested) propery from an object, or undefined if it doesn't exist\n * @param {String | Array} props - An array of properties or a single property\n * @param {Object | Array} obj\n */\nvar path = function path(props, obj) {\n  var nested = obj;\n  var properties = typeof props === 'string' ? props.split('.') : props;\n\n  var _iteratorNormalCompletion = true;\n  var _didIteratorError = false;\n  var _iteratorError = undefined;\n\n  try {\n    for (var _iterator = properties[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n      var property = _step.value;\n\n      nested = nested[property];\n      if (nested === undefined) {\n        return nested;\n      }\n    }\n  } catch (err) {\n    _didIteratorError = true;\n    _iteratorError = err;\n  } finally {\n    try {\n      if (!_iteratorNormalCompletion && _iterator.return) {\n        _iterator.return();\n      }\n    } finally {\n      if (_didIteratorError) {\n        throw _iteratorError;\n      }\n    }\n  }\n\n  return nested;\n};\n\n/**\n * Checks for a (nested) propery in an object and returns the property if\n * it exists.  Otherwise, it returns a default value.\n * @param {*} d - Default value\n * @param {String | Array} props - An array of properties or a single property\n * @param {Object | Array} obj\n */\nvar pathOr = function pathOr(d, props, obj) {\n  var value = path(props, obj);\n  return value === undefined ? d : value;\n};\n\n/**\n * Converts a string to proper case (e.g. 'camera' => 'Camera')\n * @param {String} text\n * @returns {String}\n */\nvar properCase = function properCase(text) {\n  return '' + text[0].toUpperCase() + text.slice(1);\n};\n\nmodule.exports = {\n  dom: dom,\n  path: path,\n  pathOr: pathOr,\n  properCase: properCase\n};\n\n},{}]},{},[3]);\n"
  },
  {
    "path": "js/server.js",
    "content": "/* eslint-env es6 */\n\n/*\n * Dependencies\n */\nconst express = require('express');\nconst bodyParser = require('body-parser');\n\n/*\n * Config\n */\nconst app = express();\nconst port = process.env.PORT || 8080;\napp.use(express.static(`${__dirname}/public`));\napp.use(bodyParser.json());\n\n/*\n * User Routes\n */\n\napp.get('/', (req, res) => {\n  res.sendfile('public/index.html');\n});\n\n/*\n * Listen\n */\napp.listen(process.env.PORT || port);\nconsole.log(`app listening on port ${port}`);\n"
  }
]