1 package com.example.imsmediatestingapp; 2 3 import android.annotation.SuppressLint; 4 import android.content.Context; 5 import android.content.SharedPreferences; 6 import android.graphics.SurfaceTexture; 7 import android.hardware.radio.ims.media.AmrMode; 8 import android.hardware.radio.ims.media.CodecType; 9 import android.hardware.radio.ims.media.EvsBandwidth; 10 import android.hardware.radio.ims.media.EvsMode; 11 import android.media.AudioManager; 12 import android.net.ConnectivityManager; 13 import android.net.LinkProperties; 14 import android.net.Network; 15 import android.net.wifi.WifiInfo; 16 import android.net.wifi.WifiManager; 17 import android.os.Bundle; 18 import android.telephony.AccessNetworkConstants.AccessNetworkType; 19 import android.telephony.CallQuality; 20 import android.telephony.ims.RtpHeaderExtension; 21 import android.telephony.imsmedia.AmrParams; 22 import android.telephony.imsmedia.AudioConfig; 23 import android.telephony.imsmedia.AudioSessionCallback; 24 import android.telephony.imsmedia.EvsParams; 25 import android.telephony.imsmedia.IImsMediaCallback; 26 import android.telephony.imsmedia.ImsAudioSession; 27 import android.telephony.imsmedia.ImsMediaManager; 28 import android.telephony.imsmedia.ImsMediaSession; 29 import android.telephony.imsmedia.ImsTextSession; 30 import android.telephony.imsmedia.ImsVideoSession; 31 import android.telephony.imsmedia.MediaQualityStatus; 32 import android.telephony.imsmedia.MediaQualityThreshold; 33 import android.telephony.imsmedia.RtcpConfig; 34 import android.telephony.imsmedia.RtpConfig; 35 import android.telephony.imsmedia.TextConfig; 36 import android.telephony.imsmedia.TextSessionCallback; 37 import android.telephony.imsmedia.VideoConfig; 38 import android.telephony.imsmedia.VideoSessionCallback; 39 import android.text.format.Formatter; 40 import android.util.Log; 41 import android.view.Gravity; 42 import android.view.Menu; 43 import android.view.MenuItem; 44 import android.view.Surface; 45 import android.view.TextureView; 46 import android.view.View; 47 import android.view.ViewGroup; 48 import android.widget.AdapterView; 49 import android.widget.ArrayAdapter; 50 import android.widget.Button; 51 import android.widget.EditText; 52 import android.widget.FrameLayout; 53 import android.widget.LinearLayout; 54 import android.widget.ListView; 55 import android.widget.PopupMenu; 56 import android.widget.Spinner; 57 import android.widget.TextView; 58 import android.widget.Toast; 59 60 import androidx.annotation.NonNull; 61 import androidx.appcompat.app.AppCompatActivity; 62 import androidx.appcompat.widget.SwitchCompat; 63 64 import java.io.IOException; 65 import java.net.DatagramSocket; 66 import java.net.InetAddress; 67 import java.net.InetSocketAddress; 68 import java.net.SocketException; 69 import java.net.UnknownHostException; 70 import java.util.ArrayList; 71 import java.util.HashSet; 72 import java.util.List; 73 import java.util.Set; 74 import java.util.concurrent.Executor; 75 import java.util.concurrent.Executors; 76 77 /** 78 * The MainActivity is the main and default layout for the application. 79 */ 80 public class MainActivity extends AppCompatActivity { 81 private SharedPreferences prefs; 82 private SharedPreferences.Editor editor; 83 private SharedPrefsHandler prefsHandler; 84 public static final String PREF_NAME = "preferences"; 85 private static final String HANDSHAKE_PORT_PREF = "HANDSHAKE_PORT_OPEN"; 86 private static final String CONFIRMATION_MESSAGE = "CONNECTED"; 87 private static final String TAG = MainActivity.class.getName(); 88 89 private static final int MAX_MTU_BYTES = 1500; 90 private static final int DSCP = 0; 91 private static final int AUDIO_RX_PAYLOAD_TYPE_NUMBER = 96; 92 private static final int AUDIO_TX_PAYLOAD_TYPE_NUMBER = 96; 93 private static final int VIDEO_RX_PAYLOAD_TYPE_NUMBER = 106; 94 private static final int VIDEO_TX_PAYLOAD_TYPE_NUMBER = 106; 95 private static final int TEXT_REDUNDANT_PAYLOAD_TYPE_NUMBER = 111; 96 private static final int TEXT_RX_PAYLOAD_TYPE_NUMBER = 112; 97 private static final int TEXT_TX_PAYLOAD_TYPE_NUMBER = 112; 98 private static final int SAMPLING_RATE_KHZ = 16; 99 private static final int P_TIME_MILLIS = 20; 100 private static final int MAX_P_TIME_MILLIS = 240; 101 private static final int DTMF_PAYLOAD_TYPE_NUMBER = 100; 102 private static final int DTMF_SAMPLING_RATE_KHZ = 16; 103 private static final int DTMF_DURATION = 140; 104 private static final int IDR_INTERVAL = 1; 105 private static final int RESOLUTION_WIDTH = 480; 106 private static final int RESOLUTION_HEIGHT = 640; 107 private static final String IMAGE = "data/user_de/0/com.android.telephony.imsmedia/test.jpg"; 108 private static final float DISABLED_ALPHA = 0.3f; 109 private static final float ENABLED_ALPHA = 1.0f; 110 private static final int VIDEO_FRAMERATE = 15; 111 private static final int VIDEO_BITRATE = 384; 112 private static final int CAMERA_ID = 0; 113 private static final int CAMERA_ZOOM = 10; 114 115 private static final int[] RTP_TIMEOUT = { 10000, 20000 }; 116 private static final int RTCP_TIMEOUT = 15000; 117 private static final int RTP_HYSTERESIS_TIME = 3000; 118 private static final int RTP_PACKET_LOSS_DURATION = 3000; 119 private static final int[] PACKET_LOSS_RATE = { 1, 3 }; 120 private static final int[] JITTER_THRESHOLD = { 100, 200 }; 121 private static final boolean NOTIFY_STATUS = false; 122 private static final int VIDEO_BITRATE_THRESHOLD_BPS = 100000; 123 124 private Set<Integer> mSelectedCodecTypes = new HashSet<>(); 125 private Set<Integer> mSelectedAmrModes = new HashSet<>(); 126 private Set<Integer> mSelectedEvsBandwidths = new HashSet<>(); 127 private Set<Integer> mSelectedEvsModes = new HashSet<>(); 128 private int mSelectedVideoCodec = VideoConfig.VIDEO_CODEC_AVC; 129 private int mSelectedVideoMode = VideoConfig.VIDEO_MODE_RECORDING; 130 private int mSelectedFramerate = VIDEO_FRAMERATE; 131 private int mSelectedBitrate = VIDEO_BITRATE; 132 private int mSelectedCodecProfile = VideoConfig.AVC_PROFILE_BASELINE; 133 private int mSelectedCodecLevel = VideoConfig.AVC_LEVEL_12; 134 private int mSelectedCameraId = CAMERA_ID; 135 private int mSelectedCameraZoom = CAMERA_ZOOM; 136 private int mSelectedDeviceOrientationDegree = 0; 137 private int mSelectedCvoValue = -1; 138 private String mSelectedVideoResolution = "VGA_PR"; 139 private Set<Integer> mSelectedRtcpFbTypes = new HashSet<>(); 140 141 // The order of these values determines the priority in which they would be 142 // selected if there 143 // is a common match between the two devices' selections during the handshake 144 // process. 145 private static final int[] CODEC_ORDER = new int[] { CodecType.AMR, CodecType.AMR_WB, 146 CodecType.EVS, CodecType.PCMA, CodecType.PCMU }; 147 private static final int[] EVS_BANDWIDTH_ORDER = new int[] { EvsBandwidth.NONE, 148 EvsBandwidth.NARROW_BAND, EvsBandwidth.WIDE_BAND, EvsBandwidth.SUPER_WIDE_BAND, 149 EvsBandwidth.FULL_BAND }; 150 private static final int[] AMR_MODE_ORDER = new int[] { AmrMode.AMR_MODE_0, AmrMode.AMR_MODE_1, 151 AmrMode.AMR_MODE_2, AmrMode.AMR_MODE_3, AmrMode.AMR_MODE_4, AmrMode.AMR_MODE_5, 152 AmrMode.AMR_MODE_6, AmrMode.AMR_MODE_7, AmrMode.AMR_MODE_8 }; 153 private static final int[] EVS_MODE_ORDER = new int[] { EvsMode.EVS_MODE_0, EvsMode.EVS_MODE_1, 154 EvsMode.EVS_MODE_2, EvsMode.EVS_MODE_3, EvsMode.EVS_MODE_4, EvsMode.EVS_MODE_5, 155 EvsMode.EVS_MODE_6, EvsMode.EVS_MODE_7, EvsMode.EVS_MODE_8, EvsMode.EVS_MODE_9, 156 EvsMode.EVS_MODE_10, EvsMode.EVS_MODE_11, EvsMode.EVS_MODE_12, EvsMode.EVS_MODE_13, 157 EvsMode.EVS_MODE_14, EvsMode.EVS_MODE_15, EvsMode.EVS_MODE_16, EvsMode.EVS_MODE_17, 158 EvsMode.EVS_MODE_18, EvsMode.EVS_MODE_19, EvsMode.EVS_MODE_20 }; 159 160 private boolean mLoopbackModeEnabled = false; 161 private boolean mIsMediaManagerReady = false; 162 private boolean mIsOpenSessionSent = false; 163 private boolean mIsVideoSessionOpened = false; 164 private boolean mIsTextSessionOpened = false; 165 private boolean mIsPreviewSurfaceSet = false; 166 private boolean mIsDisplaySurfaceSet = false; 167 private boolean mVideoEnabled = false; 168 private boolean mTextEnabled = false; 169 private final StringBuilder mDtmfInput = new StringBuilder(); 170 171 private ConnectionStatus mConnectionStatus; 172 private ImsAudioSession mAudioSession; 173 private ImsVideoSession mVideoSession; 174 private ImsTextSession mTextSession; 175 private AudioConfig mAudioConfig; 176 private VideoConfig mVideoConfig; 177 private TextConfig mTextConfig; 178 private ImsMediaManager mImsMediaManager; 179 private Executor mExecutor; 180 private Thread mWaitForHandshakeThread; 181 private HandshakeReceiver mHandshakeReceptionSocket; 182 private DatagramSocket mAudioRtp; 183 private DatagramSocket mAudioRtcp; 184 private DatagramSocket mVideoRtp; 185 private DatagramSocket mVideoRtcp; 186 private DatagramSocket mTextRtp; 187 private DatagramSocket mTextRtcp; 188 private DeviceInfo mRemoteDeviceInfo; 189 private DeviceInfo mLocalDeviceInfo; 190 private BottomSheetDialer mBottomSheetDialog; 191 private BottomSheetAudioCodecSettings mBottomSheetAudioCodecSettings; 192 private TextView mLocalHandshakePortLabel; 193 private TextView mLocalRtpPortLabel; 194 private TextView mLocalRtcpPortLabel; 195 private TextView mRemoteIpLabel; 196 private TextView mRemoteHandshakePortLabel; 197 private TextView mRemoteRtpPortLabel; 198 private TextView mRemoteRtcpPortLabel; 199 private Button mAllowCallsButton; 200 private Button mConnectButton; 201 private Button mOpenSessionButton; 202 private SwitchCompat mLoopbackSwitch; 203 private LinearLayout mActiveCallToolBar; 204 private TextureView mTexturePreview; 205 private TextureView mTextureDisplay; 206 private Surface mPreviewSurface; 207 private Surface mDisplaySurface; 208 209 /** 210 * Enum of the CodecType from android.hardware.radio.ims.media.CodecType with 211 * the matching 212 * Integer value. 213 */ 214 public enum CodecTypeEnum { 215 AMR(CodecType.AMR), 216 AMR_WB(CodecType.AMR_WB), 217 EVS(CodecType.EVS), 218 PCMA(CodecType.PCMA), 219 PCMU(CodecType.PCMU); 220 221 private final int mValue; 222 CodecTypeEnum(int value)223 CodecTypeEnum(int value) { 224 mValue = value; 225 } 226 getValue()227 public int getValue() { 228 return mValue; 229 } 230 } 231 232 /** 233 * Enum of the AmrMode from android.hardware.radio.ims.media.AmrMode with the 234 * matching 235 * Integer value. 236 */ 237 public enum AmrModeEnum { 238 AMR_MODE_0(AmrMode.AMR_MODE_0), 239 AMR_MODE_1(AmrMode.AMR_MODE_1), 240 AMR_MODE_2(AmrMode.AMR_MODE_2), 241 AMR_MODE_3(AmrMode.AMR_MODE_3), 242 AMR_MODE_4(AmrMode.AMR_MODE_4), 243 AMR_MODE_5(AmrMode.AMR_MODE_5), 244 AMR_MODE_6(AmrMode.AMR_MODE_6), 245 AMR_MODE_7(AmrMode.AMR_MODE_7), 246 AMR_MODE_8(AmrMode.AMR_MODE_8); 247 248 private final int mValue; 249 AmrModeEnum(int value)250 AmrModeEnum(int value) { 251 mValue = value; 252 } 253 getValue()254 public int getValue() { 255 return mValue; 256 } 257 258 } 259 260 /** 261 * Enum of the EvsBandwidth from android.hardware.radio.ims.media.EvsBandwidth 262 * with the 263 * matching Integer value. 264 */ 265 public enum EvsBandwidthEnum { 266 NONE(EvsBandwidth.NONE), 267 NARROW_BAND(EvsBandwidth.NARROW_BAND), 268 WIDE_BAND(EvsBandwidth.WIDE_BAND), 269 SUPER_WIDE_BAND(EvsBandwidth.SUPER_WIDE_BAND), 270 FULL_BAND(EvsBandwidth.FULL_BAND); 271 272 private final int mValue; 273 EvsBandwidthEnum(int value)274 EvsBandwidthEnum(int value) { 275 mValue = value; 276 } 277 getValue()278 public int getValue() { 279 return mValue; 280 } 281 } 282 283 /** 284 * Enum of the EvsMode from android.hardware.radio.ims.media.EvsMode with the 285 * matching 286 * Integer value. 287 */ 288 public enum EvsModeEnum { 289 EVS_MODE_0(EvsMode.EVS_MODE_0), 290 EVS_MODE_1(EvsMode.EVS_MODE_1), 291 EVS_MODE_2(EvsMode.EVS_MODE_2), 292 EVS_MODE_3(EvsMode.EVS_MODE_3), 293 EVS_MODE_4(EvsMode.EVS_MODE_4), 294 EVS_MODE_5(EvsMode.EVS_MODE_5), 295 EVS_MODE_6(EvsMode.EVS_MODE_6), 296 EVS_MODE_7(EvsMode.EVS_MODE_7), 297 EVS_MODE_8(EvsMode.EVS_MODE_8), 298 EVS_MODE_9(EvsMode.EVS_MODE_9), 299 EVS_MODE_10(EvsMode.EVS_MODE_10), 300 EVS_MODE_11(EvsMode.EVS_MODE_11), 301 EVS_MODE_12(EvsMode.EVS_MODE_12), 302 EVS_MODE_13(EvsMode.EVS_MODE_13), 303 EVS_MODE_14(EvsMode.EVS_MODE_14), 304 EVS_MODE_15(EvsMode.EVS_MODE_15), 305 EVS_MODE_16(EvsMode.EVS_MODE_16), 306 EVS_MODE_17(EvsMode.EVS_MODE_17), 307 EVS_MODE_18(EvsMode.EVS_MODE_18), 308 EVS_MODE_19(EvsMode.EVS_MODE_19), 309 EVS_MODE_20(EvsMode.EVS_MODE_20); 310 311 private final int mValue; 312 EvsModeEnum(int value)313 EvsModeEnum(int value) { 314 mValue = value; 315 } 316 getValue()317 public int getValue() { 318 return mValue; 319 } 320 } 321 322 /** 323 * Enum of the video codecs from VideoConfig with the matching 324 * Integer value. 325 */ 326 public enum VideoCodecEnum { 327 AVC(VideoConfig.VIDEO_CODEC_AVC), 328 HEVC(VideoConfig.VIDEO_CODEC_HEVC); 329 330 private final int mValue; 331 VideoCodecEnum(int value)332 VideoCodecEnum(int value) { 333 mValue = value; 334 } 335 getValue()336 public int getValue() { 337 return mValue; 338 } 339 } 340 341 /** 342 * Enum of the video modes from VideoConfig with the matching 343 * Integer value. 344 */ 345 public enum VideoModeEnum { 346 VIDEO_MODE_PREVIEW(VideoConfig.VIDEO_MODE_PREVIEW), 347 VIDEO_MODE_RECORDING(VideoConfig.VIDEO_MODE_RECORDING), 348 VIDEO_MODE_PAUSE_IMAGE(VideoConfig.VIDEO_MODE_PAUSE_IMAGE); 349 350 private final int mValue; 351 VideoModeEnum(int value)352 VideoModeEnum(int value) { 353 mValue = value; 354 } 355 getValue()356 public int getValue() { 357 return mValue; 358 } 359 } 360 361 /** 362 * Enum of the video codec profiles from VideoConfig with the matching 363 * Integer value. 364 */ 365 public enum VideoCodecProfileEnum { 366 CODEC_PROFILE_NONE(VideoConfig.CODEC_PROFILE_NONE), 367 AVC_PROFILE_BASELINE(VideoConfig.AVC_PROFILE_BASELINE), 368 AVC_PROFILE_CONSTRAINED_BASELINE(VideoConfig.AVC_PROFILE_CONSTRAINED_BASELINE), 369 AVC_PROFILE_CONSTRAINED_HIGH(VideoConfig.AVC_PROFILE_CONSTRAINED_HIGH), 370 AVC_PROFILE_HIGH(VideoConfig.AVC_PROFILE_HIGH), 371 AVC_PROFILE_MAIN(VideoConfig.AVC_PROFILE_MAIN), 372 HEVC_PROFILE_MAIN(VideoConfig.HEVC_PROFILE_MAIN), 373 HEVC_PROFILE_MAIN10(VideoConfig.HEVC_PROFILE_MAIN10); 374 375 private final int mValue; 376 VideoCodecProfileEnum(int value)377 VideoCodecProfileEnum(int value) { 378 mValue = value; 379 } 380 getValue()381 public int getValue() { 382 return mValue; 383 } 384 } 385 386 /** 387 * Enum of the video codec levels from VideoConfig with the matching 388 * Integer value. 389 */ 390 public enum VideoCodecLevelEnum { 391 CODEC_LEVEL_NONE(VideoConfig.CODEC_LEVEL_NONE), 392 AVC_LEVEL_1(VideoConfig.AVC_LEVEL_1), 393 AVC_LEVEL_1B(VideoConfig.AVC_LEVEL_1B), 394 AVC_LEVEL_11(VideoConfig.AVC_LEVEL_11), 395 AVC_LEVEL_12(VideoConfig.AVC_LEVEL_12), 396 AVC_LEVEL_13(VideoConfig.AVC_LEVEL_13), 397 AVC_LEVEL_2(VideoConfig.AVC_LEVEL_2), 398 AVC_LEVEL_21(VideoConfig.AVC_LEVEL_21), 399 AVC_LEVEL_22(VideoConfig.AVC_LEVEL_22), 400 AVC_LEVEL_3(VideoConfig.AVC_LEVEL_3), 401 AVC_LEVEL_31(VideoConfig.AVC_LEVEL_31), 402 HEVC_HIGHTIER_LEVEL_1(VideoConfig.HEVC_HIGHTIER_LEVEL_1), 403 HEVC_HIGHTIER_LEVEL_2(VideoConfig.HEVC_HIGHTIER_LEVEL_2), 404 HEVC_HIGHTIER_LEVEL_21(VideoConfig.HEVC_HIGHTIER_LEVEL_21), 405 HEVC_HIGHTIER_LEVEL_3(VideoConfig.HEVC_HIGHTIER_LEVEL_3), 406 HEVC_HIGHTIER_LEVEL_31(VideoConfig.HEVC_HIGHTIER_LEVEL_31), 407 HEVC_HIGHTIER_LEVEL_4(VideoConfig.HEVC_HIGHTIER_LEVEL_4), 408 HEVC_HIGHTIER_LEVEL_41(VideoConfig.HEVC_HIGHTIER_LEVEL_41), 409 HEVC_MAINTIER_LEVEL_1(VideoConfig.HEVC_MAINTIER_LEVEL_1), 410 HEVC_MAINTIER_LEVEL_2(VideoConfig.HEVC_MAINTIER_LEVEL_2), 411 HEVC_MAINTIER_LEVEL_21(VideoConfig.HEVC_MAINTIER_LEVEL_21), 412 HEVC_MAINTIER_LEVEL_3(VideoConfig.HEVC_MAINTIER_LEVEL_3), 413 HEVC_MAINTIER_LEVEL_31(VideoConfig.HEVC_MAINTIER_LEVEL_31), 414 HEVC_MAINTIER_LEVEL_4(VideoConfig.HEVC_MAINTIER_LEVEL_4), 415 HEVC_MAINTIER_LEVEL_41(VideoConfig.HEVC_MAINTIER_LEVEL_41); 416 417 private final int mValue; 418 VideoCodecLevelEnum(int value)419 VideoCodecLevelEnum(int value) { 420 mValue = value; 421 } 422 getValue()423 public int getValue() { 424 return mValue; 425 } 426 } 427 428 /** 429 * Enum of the video camera ids from VideoConfig 430 * Integer value. 431 */ 432 public enum VideoCameraIdEnum { 433 ID_0(0), 434 ID_1(1), 435 ID_2(2), 436 ID_3(3), 437 ID_4(4); 438 439 private final int mValue; 440 VideoCameraIdEnum(int value)441 VideoCameraIdEnum(int value) { 442 mValue = value; 443 } 444 getValue()445 public int getValue() { 446 return mValue; 447 } 448 } 449 450 /** 451 * Enum of the video zoom levels from VideoConfig 452 * Integer value. 453 */ 454 public enum VideoCameraZoomEnum { 455 LEVEL_0(0), 456 LEVEL_1(1), 457 LEVEL_2(2), 458 LEVEL_3(3), 459 LEVEL_4(4), 460 LEVEL_5(5), 461 LEVEL_6(6), 462 LEVEL_7(7), 463 LEVEL_8(8), 464 LEVEL_9(9); 465 466 private final int mValue; 467 VideoCameraZoomEnum(int value)468 VideoCameraZoomEnum(int value) { 469 mValue = value; 470 } 471 getValue()472 public int getValue() { 473 return mValue; 474 } 475 } 476 477 /** 478 * Enum of the video framerate from VideoConfig 479 * Integer value. 480 */ 481 public enum VideoFramerateEnum { 482 FPS_10(10), 483 FPS_15(15), 484 FPS_20(20), 485 FPS_24(24), 486 FPS_30(30); 487 488 private final int mValue; 489 VideoFramerateEnum(int value)490 VideoFramerateEnum(int value) { 491 mValue = value; 492 } 493 getValue()494 public int getValue() { 495 return mValue; 496 } 497 } 498 499 /** 500 * Enum of the video bitrate from VideoConfig 501 * Integer value. 502 */ 503 public enum VideoBitrateEnum { 504 BITRATE_192kbps(192), 505 BITRATE_256kbps(256), 506 BITRATE_384kbps(384), 507 BITRATE_512kbps(512), 508 BITRATE_640kbps(640); 509 510 private final int mValue; 511 VideoBitrateEnum(int value)512 VideoBitrateEnum(int value) { 513 mValue = value; 514 } 515 getValue()516 public int getValue() { 517 return mValue; 518 } 519 } 520 521 /** 522 * Enum of the video device orientation from VideoConfig 523 * Integer value. 524 */ 525 public enum VideoDeviceOrientationEnum { 526 DEGREE_0(0), 527 DEGREE_90(90), 528 DEGREE_180(180), 529 DEGREE_270(270); 530 531 private final int mValue; 532 VideoDeviceOrientationEnum(int value)533 VideoDeviceOrientationEnum(int value) { 534 mValue = value; 535 } 536 getValue()537 public int getValue() { 538 return mValue; 539 } 540 } 541 542 /** 543 * Enum of the video cvo offset value from VideoConfig 544 * Integer value. 545 */ 546 public enum VideoCvoValueEnum { 547 CVO_DISABLE(-1), 548 CVO_OFFSET_1(1), 549 CVO_OFFSET_2(2), 550 CVO_OFFSET_3(3), 551 CVO_OFFSET_4(4), 552 CVO_OFFSET_5(5), 553 CVO_OFFSET_6(6), 554 CVO_OFFSET_7(7), 555 CVO_OFFSET_8(8), 556 CVO_OFFSET_9(9), 557 CVO_OFFSET_10(10), 558 CVO_OFFSET_11(11), 559 CVO_OFFSET_12(12), 560 CVO_OFFSET_13(13), 561 CVO_OFFSET_14(14); 562 563 private final int mValue; 564 VideoCvoValueEnum(int value)565 VideoCvoValueEnum(int value) { 566 mValue = value; 567 } 568 getValue()569 public int getValue() { 570 return mValue; 571 } 572 } 573 574 public String[] mVideoResolutionStrings = new String[] { 575 "HD_PR", "HD_LS", "VGA_PR", "VGA_LS", "QVGA_PR", "QVGA_LS", "SIF_PR", "SIF_LS", "CIF_PR", 576 "CIF_LS", "QCIF_PR", "QCIF_LS", 577 }; 578 579 public int[][] mVideoResolution = { 580 {720, 1280}, {1280, 720}, {480, 640}, {640, 480}, {240, 320}, {320, 240}, {240, 352}, 581 {352, 240}, {288, 352}, {352, 288}, {176, 144}, {144, 176}, 582 }; 583 getResolutionWidth(String resolution)584 public int getResolutionWidth(String resolution) { 585 for (int i = 0; i < mVideoResolutionStrings.length; i++) { 586 if (mVideoResolutionStrings[i].equals(resolution)) { 587 return mVideoResolution[i][0]; 588 } 589 } 590 return RESOLUTION_WIDTH; 591 } 592 getResolutionHeight(String resolution)593 public int getResolutionHeight(String resolution) { 594 for (int i = 0; i < mVideoResolutionStrings.length; i++) { 595 if (mVideoResolutionStrings[i].equals(resolution)) { 596 return mVideoResolution[i][1]; 597 } 598 } 599 return RESOLUTION_HEIGHT; 600 } 601 602 /** 603 * Enum of the different states the application can be in. Mainly used to decide 604 * how 605 * different features of the app UI will be styled. 606 */ 607 public enum ConnectionStatus { 608 OFFLINE(0), 609 DISCONNECTED(1), 610 CONNECTING(2), 611 CONNECTED(3), 612 ACTIVE_CALL(4); 613 614 private final int mValue; 615 ConnectionStatus(int value)616 ConnectionStatus(int value) { 617 mValue = value; 618 } 619 getValue()620 public int getValue() { 621 return mValue; 622 } 623 } 624 625 @Override onCreate(Bundle savedInstanceState)626 protected void onCreate(Bundle savedInstanceState) { 627 Log.d(TAG, "onCreate"); 628 super.onCreate(savedInstanceState); 629 setContentView(R.layout.activity_main); 630 631 prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); 632 prefsHandler = new SharedPrefsHandler(prefs); 633 editor = prefs.edit(); 634 editor.putBoolean(HANDSHAKE_PORT_PREF, false); 635 editor.apply(); 636 637 Context context = getApplicationContext(); 638 MediaManagerCallback callback = new MediaManagerCallback(); 639 mExecutor = Executors.newSingleThreadExecutor(); 640 mImsMediaManager = new ImsMediaManager(context, mExecutor, callback); 641 642 mBottomSheetDialog = new BottomSheetDialer(this); 643 mBottomSheetDialog.setContentView(R.layout.dialer); 644 645 mBottomSheetAudioCodecSettings = new BottomSheetAudioCodecSettings(this); 646 mBottomSheetAudioCodecSettings.setContentView(R.layout.audio_codec_change); 647 updateCodecSelectionFromPrefs(); 648 649 updateUI(ConnectionStatus.OFFLINE); 650 updateAdditionalMedia(); 651 652 mAudioSession = null; 653 mVideoSession = null; 654 mTextSession = null; 655 } 656 657 @Override onStart()658 protected void onStart() { 659 super.onStart(); 660 styleMainActivity(); 661 } 662 663 @Override onResume()664 protected void onResume() { 665 Log.d(TAG, "onResume"); 666 super.onResume(); 667 styleMainActivity(); 668 updateAdditionalMedia(); 669 } 670 671 @Override onDestroy()672 protected void onDestroy() { 673 super.onDestroy(); 674 if (mAudioRtp != null) { 675 mAudioRtp.close(); 676 } 677 if (mAudioRtcp != null) { 678 mAudioRtcp.close(); 679 } 680 if (mVideoRtp != null) { 681 mVideoRtp.close(); 682 } 683 if (mVideoRtcp != null) { 684 mVideoRtcp.close(); 685 } 686 if (mTextRtp != null) { 687 mTextRtp.close(); 688 } 689 if (mTextRtcp != null) { 690 mTextRtcp.close(); 691 } 692 } 693 694 @Override onCreateOptionsMenu(Menu menu)695 public boolean onCreateOptionsMenu(Menu menu) { 696 getMenuInflater().inflate(R.menu.main, menu); 697 return super.onCreateOptionsMenu(menu); 698 } 699 700 @SuppressLint("NonConstantResourceId") 701 @Override onOptionsItemSelected(@onNull MenuItem item)702 public boolean onOptionsItemSelected(@NonNull MenuItem item) { 703 switch (item.getItemId()) { 704 case R.id.homeMenuButton: 705 setContentView(R.layout.activity_main); 706 styleMainActivity(); 707 break; 708 709 case R.id.settingsMenuButton: 710 setContentView(R.layout.settings); 711 setupSettingsPage(); 712 break; 713 714 case R.id.settingsVideoMenuButton: 715 setContentView(R.layout.settings_video); 716 setupVideoSettingsPage(); 717 break; 718 719 default: 720 throw new IllegalStateException("Unexpected value: " + item.getItemId()); 721 } 722 return super.onOptionsItemSelected(item); 723 } 724 725 private class MediaManagerCallback implements ImsMediaManager.OnConnectedCallback { 726 727 @Override onConnected()728 public void onConnected() { 729 Log.d(TAG, "ImsMediaManager - connected"); 730 mIsMediaManagerReady = true; 731 } 732 733 @Override onDisconnected()734 public void onDisconnected() { 735 Log.d(TAG, "ImsMediaManager - disconnected"); 736 mIsMediaManagerReady = false; 737 updateUI(ConnectionStatus.CONNECTED); 738 } 739 } 740 741 private class RtpAudioSessionCallback extends AudioSessionCallback { 742 743 @Override onModifySessionResponse(AudioConfig config, int result)744 public void onModifySessionResponse(AudioConfig config, int result) { 745 Log.d(TAG, "onModifySessionResponse"); 746 } 747 748 @Override onOpenSessionFailure(int error)749 public void onOpenSessionFailure(int error) { 750 Log.e(TAG, "onOpenSessionFailure - error=" + error); 751 } 752 753 @Override onOpenSessionSuccess(ImsMediaSession session)754 public void onOpenSessionSuccess(ImsMediaSession session) { 755 mAudioSession = (ImsAudioSession) session; 756 Log.d(TAG, "onOpenSessionSuccess: id=" + mAudioSession.getSessionId()); 757 mIsOpenSessionSent = true; 758 759 MediaQualityThreshold threshold = createMediaQualityThreshold(RTP_TIMEOUT, 760 RTCP_TIMEOUT, RTP_HYSTERESIS_TIME, RTP_PACKET_LOSS_DURATION, PACKET_LOSS_RATE, 761 JITTER_THRESHOLD, NOTIFY_STATUS); 762 mAudioSession.setMediaQualityThreshold(threshold); 763 mAudioSession.modifySession(mAudioConfig); 764 765 AudioManager audioManager = getSystemService(AudioManager.class); 766 audioManager.setMode(AudioManager.MODE_IN_CALL); 767 updateUI(ConnectionStatus.ACTIVE_CALL); 768 } 769 770 @Override onAddConfigResponse(AudioConfig config, int result)771 public void onAddConfigResponse(AudioConfig config, int result) { 772 Log.d(TAG, "onAddConfigResponse"); 773 } 774 775 @Override onConfirmConfigResponse(AudioConfig config, int result)776 public void onConfirmConfigResponse(AudioConfig config, int result) { 777 Log.d(TAG, "onConfirmConfigResponse"); 778 } 779 780 @Override onFirstMediaPacketReceived(AudioConfig config)781 public void onFirstMediaPacketReceived(AudioConfig config) { 782 Log.d(TAG, "onFirstMediaPacketReceived"); 783 } 784 785 @Override onHeaderExtensionReceived(final List<RtpHeaderExtension> extensions)786 public void onHeaderExtensionReceived(final List<RtpHeaderExtension> extensions) { 787 Log.d(TAG, "onHeaderExtensionReceived, list size=" + extensions.size() 788 + "list=" + extensions); 789 } 790 791 @Override triggerAnbrQuery(AudioConfig config)792 public void triggerAnbrQuery(AudioConfig config) { 793 Log.d(TAG, "triggerAnbrQuery"); 794 } 795 796 @Override onDtmfReceived(char dtmfDigit, int durationMs)797 public void onDtmfReceived(char dtmfDigit, int durationMs) { 798 Log.d(TAG, "onDtmfReceived digit: " + dtmfDigit + " duration: " + durationMs); 799 } 800 801 @Override notifyMediaQualityStatus(final MediaQualityStatus status)802 public void notifyMediaQualityStatus(final MediaQualityStatus status) { 803 Log.d(TAG, "notifyMediaQualityStatus, status=" + status); 804 } 805 806 @Override onCallQualityChanged(CallQuality callQuality)807 public void onCallQualityChanged(CallQuality callQuality) { 808 Log.d(TAG, "onCallQualityChanged, callQuality=" + callQuality); 809 Log.d(TAG, "onCallQualityChanged, discardRate=" 810 + (double) callQuality.getNumDroppedRtpPackets() 811 / callQuality.getNumRtpPacketsReceived() * 100); 812 Log.d(TAG, "onCallQualityChanged, lossRate=" 813 + (double) callQuality.getNumRtpPacketsNotReceived() 814 / callQuality.getNumRtpPacketsReceived() * 100); 815 Log.d(TAG, "onCallQualityChanged, maxPlayoutDelay=" 816 + (double) callQuality.getMaxPlayoutDelayMillis()); 817 } 818 } 819 820 private class RtpVideoSessionCallback extends VideoSessionCallback { 821 @Override onOpenSessionFailure(int error)822 public void onOpenSessionFailure(int error) { 823 Log.e(TAG, "onOpenSessionFailure - error=" + error); 824 } 825 826 @Override onOpenSessionSuccess(ImsMediaSession session)827 public void onOpenSessionSuccess(ImsMediaSession session) { 828 mVideoSession = (ImsVideoSession) session; 829 Log.d(TAG, "onOpenSessionSuccess: id=" + mVideoSession.getSessionId()); 830 mIsVideoSessionOpened = true; 831 832 MediaQualityThreshold threshold = createMediaQualityThreshold(RTP_TIMEOUT, 833 RTCP_TIMEOUT, RTP_HYSTERESIS_TIME, RTP_PACKET_LOSS_DURATION, PACKET_LOSS_RATE, 834 JITTER_THRESHOLD, NOTIFY_STATUS); 835 mVideoSession.setMediaQualityThreshold(threshold); 836 837 int rtcpfbTypes = 0; 838 for (int types : mSelectedRtcpFbTypes) { 839 rtcpfbTypes |= types; 840 } 841 842 mVideoConfig = createVideoConfig(mSelectedVideoCodec, mSelectedVideoMode, 843 mSelectedFramerate, mSelectedBitrate, mSelectedCodecProfile, 844 mSelectedCodecLevel, mSelectedCameraId, mSelectedCameraZoom, 845 mSelectedDeviceOrientationDegree, 846 mSelectedCvoValue, rtcpfbTypes, 847 getResolutionWidth(mSelectedVideoResolution), 848 getResolutionHeight(mSelectedVideoResolution)); 849 850 Log.d(TAG, "VideoConfig: " + mVideoConfig.toString()); 851 mVideoSession.modifySession(mVideoConfig); 852 853 runOnUiThread(() -> { 854 if (mIsPreviewSurfaceSet) { 855 mVideoSession.setPreviewSurface(mPreviewSurface); 856 } 857 if (mIsDisplaySurfaceSet) { 858 mVideoSession.setDisplaySurface(mDisplaySurface); 859 } 860 }); 861 } 862 863 @Override onModifySessionResponse(VideoConfig config, final @ImsMediaSession.SessionOperationResult int result)864 public void onModifySessionResponse(VideoConfig config, 865 final @ImsMediaSession.SessionOperationResult int result) { 866 Log.d(TAG, "onModifySessionResponse"); 867 } 868 869 @Override onPeerDimensionChanged(final int width, final int height)870 public void onPeerDimensionChanged(final int width, final int height) { 871 Log.d(TAG, "onPeerDimensionChanged - width=" + width + ", height=" + height); 872 } 873 874 @Override notifyBitrate(final int bitrate)875 public void notifyBitrate(final int bitrate) { 876 Log.d(TAG, "notifyBitrate - bitrate=" + bitrate); 877 } 878 } 879 880 private class RtpTextSessionCallback extends TextSessionCallback { 881 @Override onOpenSessionFailure(int error)882 public void onOpenSessionFailure(int error) { 883 Log.e(TAG, "onOpenSessionFailure - error=" + error); 884 } 885 886 @Override onOpenSessionSuccess(ImsMediaSession session)887 public void onOpenSessionSuccess(ImsMediaSession session) { 888 mTextSession = (ImsTextSession) session; 889 Log.d(TAG, "onOpenSessionSuccess: id=" + mTextSession.getSessionId()); 890 mIsTextSessionOpened = true; 891 } 892 893 @Override onRttReceived(final String text)894 public void onRttReceived(final String text) { 895 Log.d(TAG, "onRttReceived: text=" + text); 896 if (mIsTextSessionOpened) { 897 runOnUiThread(() -> { 898 TextView view = findViewById(R.id.receivedText); 899 view.setText(text); 900 }); 901 } 902 } 903 } 904 updateAdditionalMedia()905 private void updateAdditionalMedia() { 906 Log.d(TAG, "updateAdditionalMedia()"); 907 ArrayList<String> listAdditionalMedia = new ArrayList<>(); 908 ArrayAdapter<String> spinnerAdaptor = new ArrayAdapter<>(this, 909 android.R.layout.simple_spinner_item, listAdditionalMedia); 910 911 listAdditionalMedia.add(getString(R.string.media_none)); 912 listAdditionalMedia.add(getString(R.string.media_video)); 913 listAdditionalMedia.add(getString(R.string.media_text)); 914 spinnerAdaptor.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 915 916 Spinner spinnerAdditionalMedia = findViewById(R.id.spinnerAdditionalMedia); 917 spinnerAdditionalMedia.setAdapter(spinnerAdaptor); 918 spinnerAdditionalMedia.setOnItemSelectedListener(mAdditionalMediaListener); 919 if (mVideoEnabled) { 920 spinnerAdditionalMedia.setSelection(1); 921 } else if (mTextEnabled) { 922 spinnerAdditionalMedia.setSelection(2); 923 } else { 924 spinnerAdditionalMedia.setSelection(0); 925 } 926 } 927 928 private AdapterView.OnItemSelectedListener mAdditionalMediaListener = 929 new AdapterView.OnItemSelectedListener() { 930 @Override 931 public void onItemSelected(AdapterView<?> adapterView, View view, 932 int i, long l) { 933 String str = adapterView.getItemAtPosition(i).toString(); 934 Log.d(TAG, "onItemSelected() str=" + str); 935 if (str.equals(getString(R.string.media_video))) { 936 mVideoEnabled = true; 937 mTextEnabled = false; 938 } else if (str.equals(getString(R.string.media_text))) { 939 mTextEnabled = true; 940 mVideoEnabled = false; 941 } else { 942 mVideoEnabled = false; 943 mTextEnabled = false; 944 } 945 updateVideoUi(mVideoEnabled); 946 updateTextUi(mTextEnabled); 947 } 948 949 @Override 950 public void onNothingSelected(AdapterView<?> adapterView) { 951 952 } 953 }; 954 retrieveNetworkConfig()955 private WifiInfo retrieveNetworkConfig() { 956 WifiManager wifiManager = (WifiManager) getApplication() 957 .getSystemService(Context.WIFI_SERVICE); 958 return wifiManager.getConnectionInfo(); 959 } 960 getLocalIpAddress()961 private String getLocalIpAddress() { 962 return Formatter.formatIpAddress(retrieveNetworkConfig().getIpAddress()); 963 } 964 getOtherDeviceIp()965 private String getOtherDeviceIp() { 966 return prefs.getString("OTHER_IP_ADDRESS", "localhost"); 967 } 968 getOtherDevicePort()969 private int getOtherDevicePort() { 970 return prefs.getInt("OTHER_HANDSHAKE_PORT", -1); 971 } 972 getRemoteDevicePortEditText()973 private int getRemoteDevicePortEditText() { 974 EditText portBox = findViewById(R.id.remotePortNumberEditText); 975 return Integer.parseInt(portBox.getText().toString()); 976 } 977 getRemoteDeviceIpEditText()978 private String getRemoteDeviceIpEditText() { 979 EditText ipBox = findViewById(R.id.remoteDeviceIpEditText); 980 return ipBox.getText().toString(); 981 } 982 getVideoRemoteDevicePortEditText()983 private int getVideoRemoteDevicePortEditText() { 984 EditText portBox = findViewById(R.id.remoteVideoPortNumberEditText); 985 return Integer.parseInt(portBox.getText().toString()); 986 } 987 getVideoRemoteDeviceIpEditText()988 private String getVideoRemoteDeviceIpEditText() { 989 EditText ipBox = findViewById(R.id.videoRemoteDeviceIpEditText); 990 return ipBox.getText().toString(); 991 } 992 createTextureView()993 private void createTextureView() { 994 Log.d(TAG, "createTextureView"); 995 mTexturePreview = (TextureView) findViewById(R.id.texturePreview); 996 assert mTexturePreview != null; 997 mTexturePreview.setSurfaceTextureListener(mPreviewListener); 998 if (mTexturePreview.isAvailable()) { 999 Log.d(TAG, "preview available"); 1000 mTexturePreview.setLayoutParams( 1001 new FrameLayout.LayoutParams(300, 400, Gravity.CENTER)); 1002 mTexturePreview.setKeepScreenOn(true); 1003 mTexturePreview.getSurfaceTexture().setDefaultBufferSize( 1004 RESOLUTION_WIDTH, RESOLUTION_HEIGHT); 1005 } 1006 mTextureDisplay = (TextureView) findViewById(R.id.textureDisplay); 1007 assert mTextureDisplay != null; 1008 mTextureDisplay.setSurfaceTextureListener(mDisplayListener); 1009 if (mTextureDisplay.isAvailable()) { 1010 Log.d(TAG, "display available"); 1011 mTextureDisplay.setLayoutParams( 1012 new FrameLayout.LayoutParams(300, 400, Gravity.CENTER)); 1013 mTextureDisplay.setKeepScreenOn(true); 1014 mTextureDisplay.getSurfaceTexture().setDefaultBufferSize( 1015 RESOLUTION_WIDTH, RESOLUTION_HEIGHT); 1016 } 1017 } 1018 1019 /** 1020 * Opens two datagram sockets for audio rtp and rtcp, and a third for the handshake between 1021 * devices if true is passed in the parameter. 1022 * 1023 * @param openHandshakePort boolean value to open a port for the handshake. 1024 */ openPorts(boolean openHandshakePort)1025 private void openPorts(boolean openHandshakePort) { 1026 Log.d(TAG, "openPorts"); 1027 mAudioRtp = createDatagramSocket(getLocalIpAddress(), 10000); 1028 mAudioRtcp = createDatagramSocket(getLocalIpAddress(), 1029 mAudioRtp.getLocalPort() + 1); 1030 mVideoRtp = createDatagramSocket(getLocalIpAddress(), 20000); 1031 mVideoRtcp = createDatagramSocket(getLocalIpAddress(), 1032 mVideoRtp.getLocalPort() + 1); 1033 mTextRtp = createDatagramSocket(getLocalIpAddress(), 30000); 1034 mTextRtcp = createDatagramSocket(getLocalIpAddress(), 1035 mTextRtp.getLocalPort() + 1); 1036 if (openHandshakePort) { 1037 mHandshakeReceptionSocket = new HandshakeReceiver(prefs); 1038 mHandshakeReceptionSocket.run(); 1039 } 1040 } 1041 1042 /** 1043 * Closes the handshake, rtp, and rtcp ports if they have been opened or instantiated. 1044 */ closePorts()1045 private void closePorts() { 1046 Log.d(TAG, "closePorts"); 1047 if (mHandshakeReceptionSocket != null) { 1048 mHandshakeReceptionSocket.close(); 1049 } 1050 closeDatagramSocket(mAudioRtp); 1051 closeDatagramSocket(mAudioRtcp); 1052 closeDatagramSocket(mVideoRtp); 1053 closeDatagramSocket(mVideoRtcp); 1054 closeDatagramSocket(mTextRtp); 1055 closeDatagramSocket(mTextRtcp); 1056 } 1057 createDatagramSocket(@onNull String address, int port)1058 private DatagramSocket createDatagramSocket(@NonNull String address, int port) { 1059 DatagramSocket socket = null; 1060 1061 try { 1062 socket = new DatagramSocket(null); 1063 1064 if (socket == null) { 1065 Log.e(TAG, "socket not found"); 1066 return null; 1067 } 1068 1069 socket.setReuseAddress(true); 1070 InetAddress inetAddress = createInetAddress(address); 1071 if (inetAddress == null) { 1072 Log.e(TAG, "inetAddress not found"); 1073 closeDatagramSocket(socket); 1074 return null; 1075 } 1076 1077 InetSocketAddress sockAddr = new InetSocketAddress(inetAddress, port); 1078 socket.bind(sockAddr); 1079 1080 Network network = getNetworkForIpAddress(inetAddress); 1081 if (network == null) { 1082 Log.e(TAG, "Network not found"); 1083 closeDatagramSocket(socket); 1084 return null; 1085 } 1086 1087 network.bindSocket(socket); 1088 } catch (SocketException e) { 1089 Log.e(TAG, "SocketException: " + e.toString()); 1090 } catch (UnknownHostException e) { 1091 Log.e(TAG, "UnknownHostException: " + e.toString()); 1092 } catch (IOException e) { 1093 Log.e(TAG, "IOException: " + e.toString()); 1094 closeDatagramSocket(socket); 1095 } catch (IllegalArgumentException e) { 1096 Log.e(TAG, "IllegalArgumentException: " + e.toString()); 1097 closeDatagramSocket(socket); 1098 } 1099 1100 Log.v(TAG, "DatagramSocket created"); 1101 return socket; 1102 } 1103 closeDatagramSocket(DatagramSocket socket)1104 public void closeDatagramSocket(DatagramSocket socket) { 1105 if (socket != null) { 1106 socket.close(); 1107 Log.v(TAG, "DatagramSocket closed"); 1108 } 1109 } 1110 getNetworkForIpAddress(InetAddress addr)1111 private Network getNetworkForIpAddress(InetAddress addr) { 1112 ConnectivityManager cm = 1113 getApplication().getSystemService(ConnectivityManager.class); 1114 1115 if (cm == null) { 1116 return null; 1117 } 1118 1119 Network[] networks = cm.getAllNetworks(); 1120 1121 if (networks == null) { 1122 return null; 1123 } 1124 1125 for (Network network : networks) { 1126 LinkProperties lp = cm.getLinkProperties(network); 1127 1128 if (lp == null) { 1129 continue; 1130 } 1131 1132 List<InetAddress> linkAddrs = lp.getAddresses(); 1133 for (InetAddress linkAddr : linkAddrs) { 1134 if (addr.equals(linkAddr)) { 1135 return network; 1136 } 1137 } 1138 } 1139 return null; 1140 } 1141 createInetAddress(String address)1142 private InetAddress createInetAddress(String address) { 1143 try { 1144 return InetAddress.getByName(address); 1145 } catch (IOException e) { 1146 Log.e(TAG, "IOException: " + e.toString()); 1147 } 1148 1149 return null; 1150 } 1151 1152 /** 1153 * texture view listener for preview 1154 */ 1155 TextureView.SurfaceTextureListener mPreviewListener = 1156 new TextureView.SurfaceTextureListener() { 1157 @Override 1158 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1159 Log.d(TAG, "onSurfaceTextureAvailable - preview, width=" + width + ",height=" + height); 1160 surface.setDefaultBufferSize(RESOLUTION_WIDTH, RESOLUTION_HEIGHT); 1161 mPreviewSurface = new Surface(surface); 1162 mIsPreviewSurfaceSet = true; 1163 } 1164 1165 @Override 1166 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1167 Log.d(TAG, "onSurfaceTextureSizeChanged, width=" + width + ", height=" + height); 1168 } 1169 1170 @Override 1171 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1172 Log.d(TAG, "onSurfaceTextureDestroyed"); 1173 return false; 1174 } 1175 1176 @Override 1177 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1178 surface.setDefaultBufferSize(RESOLUTION_WIDTH, RESOLUTION_HEIGHT); 1179 mPreviewSurface = new Surface(surface); 1180 mIsPreviewSurfaceSet = true; 1181 } 1182 }; 1183 1184 /** 1185 * texture view listener for display 1186 */ 1187 TextureView.SurfaceTextureListener mDisplayListener = 1188 new TextureView.SurfaceTextureListener() { 1189 @Override 1190 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1191 Log.d(TAG, "onSurfaceTextureAvailable - display, width=" + width + ",height=" + height); 1192 surface.setDefaultBufferSize(RESOLUTION_WIDTH, RESOLUTION_HEIGHT); 1193 mDisplaySurface = new Surface(surface); 1194 mIsDisplaySurfaceSet = true; 1195 } 1196 1197 @Override 1198 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1199 Log.d(TAG, "onSurfaceTextureSizeChanged, width=" + width + ", height=" + height); 1200 } 1201 1202 @Override 1203 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1204 Log.d(TAG, "onSurfaceTextureDestroyed"); 1205 return false; 1206 } 1207 1208 @Override 1209 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1210 surface.setDefaultBufferSize(RESOLUTION_WIDTH, RESOLUTION_HEIGHT); 1211 mDisplaySurface = new Surface(surface); 1212 mIsDisplaySurfaceSet = true; 1213 } 1214 }; 1215 1216 /** 1217 * After the ports are open this runnable is called to wait for in incoming handshake to pair 1218 * with the remote device. 1219 */ 1220 Runnable handleIncomingHandshake = new Runnable() { 1221 @Override 1222 public void run() { 1223 try { 1224 while (!mHandshakeReceptionSocket.isHandshakeReceived()) { 1225 if (Thread.currentThread().isInterrupted()) { 1226 throw new InterruptedException(); 1227 } 1228 } 1229 1230 mRemoteDeviceInfo = mHandshakeReceptionSocket.getReceivedDeviceInfo(); 1231 1232 HandshakeSender handshakeSender = new HandshakeSender( 1233 mRemoteDeviceInfo.getInetAddress(), 1234 mRemoteDeviceInfo.getHandshakePort()); 1235 mLocalDeviceInfo = createMyDeviceInfo(); 1236 handshakeSender.setData(mLocalDeviceInfo); 1237 handshakeSender.run(); 1238 1239 while (!mHandshakeReceptionSocket.isConfirmationReceived()) { 1240 if (Thread.currentThread().isInterrupted()) { 1241 throw new InterruptedException(); 1242 } 1243 } 1244 1245 handshakeSender = new HandshakeSender(mRemoteDeviceInfo.getInetAddress(), 1246 mRemoteDeviceInfo.getHandshakePort()); 1247 handshakeSender.setData(CONFIRMATION_MESSAGE); 1248 handshakeSender.run(); 1249 Log.d(TAG, "Handshake has been completed. Devices are connected."); 1250 editor.putString("OTHER_IP_ADDRESS", 1251 mRemoteDeviceInfo.getInetAddress().getHostName()); 1252 editor.putInt("OTHER_HANDSHAKE_PORT", 1253 mRemoteDeviceInfo.getAudioRtpPort()); 1254 editor.apply(); 1255 updateUI(ConnectionStatus.CONNECTED); 1256 } catch (InterruptedException e) { 1257 Log.e(TAG, e.toString()); 1258 } 1259 1260 } 1261 }; 1262 1263 /** 1264 * This runnable controls the handshake process from the user that is attempting to connect to 1265 * the remote device. First it will create and send a DeviceInfo object that contains the local 1266 * devices info, and wait until it receives the remote DeviceInfo. After it receives the remote 1267 * DeviceInfo it will save it into memory and send a conformation String back, then wait until 1268 * it receives a conformation String. 1269 */ 1270 Runnable initiateHandshake = new Runnable() { 1271 @Override 1272 public void run() { 1273 try { 1274 HandshakeSender sender = new HandshakeSender(InetAddress.getByName( 1275 getOtherDeviceIp()), getOtherDevicePort()); 1276 mLocalDeviceInfo = createMyDeviceInfo(); 1277 sender.setData(mLocalDeviceInfo); 1278 sender.run(); 1279 mRemoteDeviceInfo = mHandshakeReceptionSocket.getReceivedDeviceInfo(); 1280 sender.setData(CONFIRMATION_MESSAGE); 1281 sender.run(); 1282 Log.d(TAG, "Handshake successful, devices connected."); 1283 updateUI(ConnectionStatus.CONNECTED); 1284 } catch (Exception e) { 1285 Log.e(TAG, "initiateHandshake(): e=" + e.toString()); 1286 } 1287 } 1288 }; 1289 1290 /** 1291 * Creates and returns a DeviceInfo object with the local port, ip, and audio codec settings 1292 * 1293 * @return DeviceInfo object containing the local device's information 1294 */ createMyDeviceInfo()1295 public DeviceInfo createMyDeviceInfo() { 1296 try { 1297 return new DeviceInfo.Builder() 1298 .setInetAddress(InetAddress.getByName(getLocalIpAddress())) 1299 .setHandshakePort(mHandshakeReceptionSocket != null 1300 ? mHandshakeReceptionSocket.getBoundSocket() : 0) 1301 .setAudioRtpPort(mAudioRtp.getLocalPort()) 1302 .setVideoRtpPort(mVideoRtp.getLocalPort()) 1303 .setTextRtpPort(mTextRtp.getLocalPort()) 1304 .setAudioCodecs(mSelectedCodecTypes) 1305 .setAmrModes(mSelectedAmrModes) 1306 .setEvsBandwidths(mSelectedEvsBandwidths) 1307 .setEvsModes(mSelectedEvsModes) 1308 .setVideoCodec(mSelectedVideoCodec) 1309 .setVideoResolutionWidth(RESOLUTION_WIDTH) 1310 .setVideoResolutionHeight(RESOLUTION_HEIGHT) 1311 .setVideoCvoValue(mSelectedCvoValue) 1312 .setRtcpFbTypes(mSelectedRtcpFbTypes) 1313 .build(); 1314 1315 } catch (UnknownHostException e) { 1316 Log.e(TAG, "UnknownHostException: " + e.toString()); 1317 } 1318 return null; 1319 } 1320 1321 /** 1322 * Updates the mConnectionStatus and restyles the UI 1323 * 1324 * @param newStatus The new ConnectionStatus used to style the UI 1325 */ updateUI(ConnectionStatus newStatus)1326 public void updateUI(ConnectionStatus newStatus) { 1327 mConnectionStatus = newStatus; 1328 styleMainActivity(); 1329 } 1330 1331 /** 1332 * Creates and returns an InetSocketAddress from the remote device that is 1333 * connected to the 1334 * local device. 1335 * 1336 * @return the InetSocketAddress of the remote device 1337 */ getRemoteAudioSocketAddress()1338 private InetSocketAddress getRemoteAudioSocketAddress() { 1339 int remotePort = mRemoteDeviceInfo.getAudioRtpPort(); 1340 InetAddress remoteInetAddress = mRemoteDeviceInfo.getInetAddress(); 1341 return new InetSocketAddress(remoteInetAddress, remotePort); 1342 } 1343 1344 /** 1345 * Creates and returns an InetSocketAddress from the remote device that is 1346 * connected to the 1347 * local device. 1348 * 1349 * @return the InetSocketAddress of the remote device 1350 */ getRemoteVideoSocketAddress()1351 private InetSocketAddress getRemoteVideoSocketAddress() { 1352 int remotePort = mRemoteDeviceInfo.getVideoRtpPort(); 1353 InetAddress remoteInetAddress = mRemoteDeviceInfo.getInetAddress(); 1354 return new InetSocketAddress(remoteInetAddress, remotePort); 1355 } 1356 1357 /** 1358 * Creates and returns an InetSocketAddress from the remote device that is connected to the 1359 * local device. 1360 * 1361 * @return the InetSocketAddress of the remote device 1362 */ getRemoteTextSocketAddress()1363 private InetSocketAddress getRemoteTextSocketAddress() { 1364 int remotePort = mRemoteDeviceInfo.getTextRtpPort(); 1365 InetAddress remoteInetAddress = mRemoteDeviceInfo.getInetAddress(); 1366 return new InetSocketAddress(remoteInetAddress, remotePort); 1367 } 1368 1369 /** 1370 * Builds and returns an RtcpConfig for the remote device that is connected to the local device. 1371 * 1372 * @return the RtcpConfig for the remote device 1373 */ getRemoteAudioRtcpConfig()1374 private RtcpConfig getRemoteAudioRtcpConfig() { 1375 return new RtcpConfig.Builder() 1376 .setCanonicalName("audio rtcp config") 1377 .setTransmitPort(mRemoteDeviceInfo.getAudioRtpPort() + 1) 1378 .setIntervalSec(5) 1379 .setRtcpXrBlockTypes(RtcpConfig.FLAG_RTCPXR_STATISTICS_SUMMARY_REPORT_BLOCK 1380 | RtcpConfig.FLAG_RTCPXR_VOIP_METRICS_REPORT_BLOCK) 1381 .build(); 1382 } 1383 getRemoteVideoRtcpConfig()1384 private RtcpConfig getRemoteVideoRtcpConfig() { 1385 return new RtcpConfig.Builder() 1386 .setCanonicalName("video rtcp config") 1387 .setTransmitPort(mRemoteDeviceInfo.getVideoRtpPort() + 1) 1388 .setIntervalSec(5) 1389 .setRtcpXrBlockTypes(0) 1390 .build(); 1391 } 1392 getRemoteTextRtcpConfig()1393 private RtcpConfig getRemoteTextRtcpConfig() { 1394 return new RtcpConfig.Builder() 1395 .setCanonicalName("text rtcp config") 1396 .setTransmitPort(mRemoteDeviceInfo.getTextRtpPort() + 1) 1397 .setIntervalSec(5) 1398 .setRtcpXrBlockTypes(0) 1399 .build(); 1400 } 1401 1402 /** 1403 * Creates and returns a new AudioConfig 1404 * 1405 * @param remoteRtpAddress - InetSocketAddress of the remote device 1406 * @param rtcpConfig - RtcpConfig of the remove device 1407 * @param audioCodec - the type of AudioCodec 1408 * @param amrParams - the settings if the AudioCodec is an AMR variant 1409 * @param evsParams - the settings if the AudioCodec is EVS 1410 * @return an AudioConfig with the given params 1411 */ createAudioConfig(InetSocketAddress remoteRtpAddress, RtcpConfig rtcpConfig, int audioCodec, AmrParams amrParams, EvsParams evsParams)1412 private AudioConfig createAudioConfig(InetSocketAddress remoteRtpAddress, 1413 RtcpConfig rtcpConfig, int audioCodec, AmrParams amrParams, EvsParams evsParams) { 1414 AudioConfig config; 1415 1416 if (audioCodec == AudioConfig.CODEC_AMR) { 1417 config = new AudioConfig.Builder() 1418 .setMediaDirection(RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE) 1419 .setAccessNetwork(AccessNetworkType.EUTRAN) 1420 .setRemoteRtpAddress(remoteRtpAddress) 1421 .setRtcpConfig(rtcpConfig) 1422 .setDscp((byte) DSCP) 1423 .setRxPayloadTypeNumber((byte) AUDIO_RX_PAYLOAD_TYPE_NUMBER) 1424 .setTxPayloadTypeNumber((byte) AUDIO_TX_PAYLOAD_TYPE_NUMBER) 1425 .setSamplingRateKHz((byte) 8) 1426 .setPtimeMillis((byte) P_TIME_MILLIS) 1427 .setMaxPtimeMillis((byte) MAX_P_TIME_MILLIS) 1428 .setDtxEnabled(true) 1429 .setTxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1430 .setRxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1431 .setDtmfSamplingRateKHz((byte) 8) 1432 .setCodecType(audioCodec) 1433 .setAmrParams(amrParams) 1434 // TODO audio is currently only working when amr params are set as well 1435 .setEvsParams(evsParams) 1436 .build(); 1437 } else if (audioCodec == AudioConfig.CODEC_AMR_WB) { 1438 config = new AudioConfig.Builder() 1439 .setMediaDirection(RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE) 1440 .setAccessNetwork(AccessNetworkType.EUTRAN) 1441 .setRemoteRtpAddress(remoteRtpAddress) 1442 .setRtcpConfig(rtcpConfig) 1443 .setDscp((byte) DSCP) 1444 .setRxPayloadTypeNumber((byte) AUDIO_RX_PAYLOAD_TYPE_NUMBER) 1445 .setTxPayloadTypeNumber((byte) AUDIO_TX_PAYLOAD_TYPE_NUMBER) 1446 .setSamplingRateKHz((byte) SAMPLING_RATE_KHZ) 1447 .setPtimeMillis((byte) P_TIME_MILLIS) 1448 .setMaxPtimeMillis((byte) MAX_P_TIME_MILLIS) 1449 .setDtxEnabled(true) 1450 .setTxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1451 .setRxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1452 .setDtmfSamplingRateKHz((byte) SAMPLING_RATE_KHZ) 1453 .setCodecType(audioCodec) 1454 .setAmrParams(amrParams) 1455 // TODO audio is currently only working when amr params are set as well 1456 .setEvsParams(evsParams) 1457 .build(); 1458 } else if (audioCodec == AudioConfig.CODEC_EVS) { 1459 config = new AudioConfig.Builder() 1460 .setMediaDirection(RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE) 1461 .setAccessNetwork(AccessNetworkType.EUTRAN) 1462 .setRemoteRtpAddress(remoteRtpAddress) 1463 .setRtcpConfig(rtcpConfig) 1464 .setDscp((byte) DSCP) 1465 .setRxPayloadTypeNumber((byte) AUDIO_RX_PAYLOAD_TYPE_NUMBER) 1466 .setTxPayloadTypeNumber((byte) AUDIO_TX_PAYLOAD_TYPE_NUMBER) 1467 .setSamplingRateKHz((byte) SAMPLING_RATE_KHZ) 1468 .setPtimeMillis((byte) P_TIME_MILLIS) 1469 .setMaxPtimeMillis((byte) MAX_P_TIME_MILLIS) 1470 .setDtxEnabled(true) 1471 .setTxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1472 .setRxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1473 .setDtmfSamplingRateKHz((byte) DTMF_SAMPLING_RATE_KHZ) 1474 .setCodecType(audioCodec) 1475 // TODO audio is currently only working when amr params are set as well 1476 .setAmrParams(amrParams) 1477 .setEvsParams(evsParams) 1478 .build(); 1479 } else { 1480 config = new AudioConfig.Builder() 1481 .setMediaDirection(RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE) 1482 .setAccessNetwork(AccessNetworkType.EUTRAN) 1483 .setRemoteRtpAddress(remoteRtpAddress) 1484 .setRtcpConfig(rtcpConfig) 1485 .setDscp((byte) DSCP) 1486 .setRxPayloadTypeNumber((byte) AUDIO_RX_PAYLOAD_TYPE_NUMBER) 1487 .setTxPayloadTypeNumber((byte) AUDIO_TX_PAYLOAD_TYPE_NUMBER) 1488 .setSamplingRateKHz((byte) SAMPLING_RATE_KHZ) 1489 .setPtimeMillis((byte) P_TIME_MILLIS) 1490 .setMaxPtimeMillis((byte) MAX_P_TIME_MILLIS) 1491 .setDtxEnabled(true) 1492 .setTxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1493 .setRxDtmfPayloadTypeNumber((byte) DTMF_PAYLOAD_TYPE_NUMBER) 1494 .setDtmfSamplingRateKHz((byte) DTMF_SAMPLING_RATE_KHZ) 1495 .setCodecType(audioCodec) 1496 .build(); 1497 } 1498 return config; 1499 } 1500 createVideoConfig(InetSocketAddress remoteRtpAddress, RtcpConfig rtcpConfig, int codecType, int videoMode, int framerate, int bitrate, int profile, int level, int cameraId, int cameraZoom, int deviceOrientation, int cvo, int rtcpFbTypes, int width, int height)1501 private VideoConfig createVideoConfig(InetSocketAddress remoteRtpAddress, 1502 RtcpConfig rtcpConfig, int codecType, int videoMode, int framerate, int bitrate, 1503 int profile, int level, int cameraId, int cameraZoom, int deviceOrientation, int cvo, 1504 int rtcpFbTypes, int width, int height) { 1505 VideoConfig config = new VideoConfig.Builder() 1506 .setMediaDirection(RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE) 1507 .setAccessNetwork(AccessNetworkType.EUTRAN) 1508 .setRemoteRtpAddress(remoteRtpAddress) 1509 .setRtcpConfig(rtcpConfig) 1510 .setDscp((byte) DSCP) 1511 .setRxPayloadTypeNumber((byte) VIDEO_RX_PAYLOAD_TYPE_NUMBER) 1512 .setTxPayloadTypeNumber((byte) VIDEO_TX_PAYLOAD_TYPE_NUMBER) 1513 .setMaxMtuBytes(MAX_MTU_BYTES) 1514 .setSamplingRateKHz((byte) 90) 1515 .setCodecType(codecType) 1516 .setVideoMode(videoMode) 1517 .setFramerate(framerate) 1518 .setBitrate(bitrate) 1519 .setCodecProfile(profile) 1520 .setCodecLevel(level) 1521 .setIntraFrameIntervalSec(IDR_INTERVAL) 1522 .setPacketizationMode(VideoConfig.MODE_NON_INTERLEAVED) 1523 .setCameraId(cameraId) 1524 .setCameraZoom(cameraZoom) 1525 .setResolutionWidth(width) 1526 .setResolutionHeight(height) 1527 .setPauseImagePath(IMAGE) 1528 .setDeviceOrientationDegree(deviceOrientation) 1529 .setCvoValue(cvo) 1530 .setRtcpFbTypes(rtcpFbTypes) 1531 .build(); 1532 return config; 1533 } 1534 createTextConfig(InetSocketAddress remoteRtpAddress, RtcpConfig rtcpConfig, int codecType)1535 private TextConfig createTextConfig(InetSocketAddress remoteRtpAddress, 1536 RtcpConfig rtcpConfig, int codecType) { 1537 TextConfig config = new TextConfig.Builder() 1538 .setMediaDirection(RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE) 1539 .setAccessNetwork(AccessNetworkType.EUTRAN) 1540 .setRemoteRtpAddress(remoteRtpAddress) 1541 .setRtcpConfig(rtcpConfig) 1542 .setDscp((byte) DSCP) 1543 .setRxPayloadTypeNumber((byte) TEXT_RX_PAYLOAD_TYPE_NUMBER) 1544 .setTxPayloadTypeNumber((byte) TEXT_TX_PAYLOAD_TYPE_NUMBER) 1545 .setSamplingRateKHz((byte) 10) 1546 .setCodecType(codecType) 1547 .setBitrate(1000) 1548 .setRedundantPayload((byte) TEXT_REDUNDANT_PAYLOAD_TYPE_NUMBER) 1549 .setRedundantLevel((byte) 2) 1550 .setKeepRedundantLevel(true) 1551 .build(); 1552 return config; 1553 } 1554 createMediaQualityThreshold(int[] rtpInactivityTimerMillis, int rtcpInactivityTimerMillis, int rtpHysteresisTimeInMillis, int rtpPacketLossDurationMillis, int[] rtpPacketLossRate, int[] rtpJitterMillis, boolean notifyCurrentStatus)1555 private MediaQualityThreshold createMediaQualityThreshold(int[] rtpInactivityTimerMillis, 1556 int rtcpInactivityTimerMillis, int rtpHysteresisTimeInMillis, 1557 int rtpPacketLossDurationMillis, int[] rtpPacketLossRate, int[] rtpJitterMillis, 1558 boolean notifyCurrentStatus) { 1559 return new MediaQualityThreshold.Builder() 1560 .setRtpInactivityTimerMillis(rtpInactivityTimerMillis) 1561 .setRtcpInactivityTimerMillis(rtcpInactivityTimerMillis) 1562 .setRtpHysteresisTimeInMillis(rtpHysteresisTimeInMillis) 1563 .setRtpPacketLossDurationMillis(rtpPacketLossDurationMillis) 1564 .setRtpPacketLossRate(rtpPacketLossRate) 1565 .setRtpJitterMillis(rtpJitterMillis) 1566 .setNotifyCurrentStatus(notifyCurrentStatus) 1567 .setVideoBitrateBps(VIDEO_BITRATE_THRESHOLD_BPS) 1568 .build(); 1569 } 1570 1571 /** 1572 * @param amrMode Integer value of the AmrMode 1573 * @return AmrParams object with the passed AmrMode value 1574 */ createAmrParams(int amrMode, boolean octateAligned, int maxRed)1575 private AmrParams createAmrParams(int amrMode, boolean octateAligned, int maxRed) { 1576 return new AmrParams.Builder() 1577 .setAmrMode(amrMode) 1578 .setOctetAligned(octateAligned) 1579 .setMaxRedundancyMillis(maxRed) 1580 .build(); 1581 } 1582 1583 /** 1584 * @param evsBand Integer value of the EvsBandwidth 1585 * @param evsMode Integer value of the EvsMode 1586 * @return EvsParams object with the passed EvsBandwidth and EvsMode 1587 */ createEvsParams(int evsBand, int evsMode)1588 private EvsParams createEvsParams(int evsBand, int evsMode) { 1589 return new EvsParams.Builder() 1590 .setEvsbandwidth(evsBand) 1591 .setEvsMode(evsMode) 1592 .setChannelAwareMode((byte) 3) 1593 .setHeaderFullOnly(true) 1594 .setCodecModeRequest((byte) 15) 1595 .build(); 1596 } 1597 1598 /** 1599 * Determines the audio codec to use to configure the AudioConfig object. The 1600 * function uses 1601 * the order arrays of Integers to determine the priority of a given codec, 1602 * mode, and 1603 * bandwidth. Then creates and returns a AudioConfig object containing it. 1604 * 1605 * @param localDevice DeviceInfo object containing the local device's 1606 * information 1607 * @param remoteDevice DeviceInfo object containing the remote device's 1608 * information 1609 * @return AudioConfig containing the selected audio codec, determined by the 1610 * algorithm 1611 */ determineAudioConfig(DeviceInfo localDevice, DeviceInfo remoteDevice)1612 private AudioConfig determineAudioConfig(DeviceInfo localDevice, DeviceInfo remoteDevice) { 1613 AmrParams amrParams = null; 1614 EvsParams evsParams = null; 1615 1616 int selectedCodec = determineCommonCodecSettings(localDevice.getAudioCodecs(), 1617 remoteDevice.getAudioCodecs(), CODEC_ORDER); 1618 1619 switch (selectedCodec) { 1620 case CodecType.AMR: 1621 case CodecType.AMR_WB: 1622 int amrMode = determineCommonCodecSettings(localDevice.getAmrModes(), 1623 remoteDevice.getAmrModes(), AMR_MODE_ORDER); 1624 amrParams = createAmrParams(amrMode, false, 0); 1625 break; 1626 1627 case CodecType.EVS: 1628 int evsMode = determineCommonCodecSettings(localDevice.getEvsModes(), 1629 remoteDevice.getEvsModes(), EVS_MODE_ORDER); 1630 int evsBand = determineCommonCodecSettings(localDevice.getEvsBandwidths(), 1631 remoteDevice.getEvsBandwidths(), EVS_BANDWIDTH_ORDER); 1632 evsParams = createEvsParams(evsBand, evsMode); 1633 amrParams = createAmrParams(0, false, 0); 1634 break; 1635 1636 case -1: 1637 return createAudioConfig(CodecType.AMR_WB, 1638 createAmrParams(AmrMode.AMR_MODE_4, false, 0), null); 1639 } 1640 1641 return createAudioConfig(selectedCodec, amrParams, evsParams); 1642 } 1643 1644 /** 1645 * Helper function used to determine the highest ranking codec, mode, or 1646 * bandwidth between 1647 * two devices. 1648 * 1649 * @param localSet the set containing the local device's selection of 1650 * codecs, modes, or 1651 * bandwidths 1652 * @param remoteSet the set containing the remote device's selection of 1653 * codecs, modes, or 1654 * bandwidths 1655 * @param codecSetting the Integer array containing the ranking order of the 1656 * different values 1657 * @return highest ranking mode, codec, bandwidth, or -1 if no match is found 1658 */ determineCommonCodecSettings(Set<Integer> localSet, Set<Integer> remoteSet, int[] codecSetting)1659 private int determineCommonCodecSettings(Set<Integer> localSet, Set<Integer> remoteSet, 1660 int[] codecSetting) { 1661 Log.d(TAG, "determineCommonCodecSettings() - localSet : " + localSet); 1662 int negotiatedMode = 0; 1663 for (int setting : codecSetting) { 1664 if (localSet.contains(setting) && remoteSet.contains(setting)) { 1665 negotiatedMode |= setting; 1666 } 1667 } 1668 return negotiatedMode; 1669 } 1670 1671 /** 1672 * Creates an AudioConfig object depending on the passed parameters and returns it. 1673 * 1674 * @param audioCodec Integer value of the CodecType 1675 * @param amrParams AmrParams object to be set in the AudioConfig 1676 * @param evsParams EvsParams object to be set in the AudioConfig 1677 * @return an AudioConfig with the passed parameters and default values. 1678 */ createAudioConfig(int audioCodec, AmrParams amrParams, EvsParams evsParams)1679 private AudioConfig createAudioConfig(int audioCodec, AmrParams amrParams, 1680 EvsParams evsParams) { 1681 AudioConfig mAudioConfig = null; 1682 // TODO - evs params must be present to hear audio currently, regardless of codec 1683 EvsParams mEvs = new EvsParams.Builder() 1684 .setEvsbandwidth(EvsParams.EVS_BAND_NONE) 1685 .setEvsMode(EvsParams.EVS_MODE_0) 1686 .setChannelAwareMode((byte) 3) 1687 .setHeaderFullOnly(true) 1688 .setCodecModeRequest((byte) 15) 1689 .build(); 1690 1691 switch (audioCodec) { 1692 case CodecType.AMR: 1693 case CodecType.AMR_WB: 1694 mAudioConfig = createAudioConfig(getRemoteAudioSocketAddress(), 1695 getRemoteAudioRtcpConfig(), audioCodec, amrParams, mEvs); 1696 break; 1697 1698 case CodecType.EVS: 1699 mAudioConfig = createAudioConfig(getRemoteAudioSocketAddress(), 1700 getRemoteAudioRtcpConfig(), audioCodec, amrParams, evsParams); 1701 break; 1702 1703 case CodecType.PCMA: 1704 case CodecType.PCMU: 1705 mAudioConfig = createAudioConfig(getRemoteAudioSocketAddress(), 1706 getRemoteAudioRtcpConfig(), audioCodec, null, null); 1707 break; 1708 1709 } 1710 1711 return mAudioConfig; 1712 } 1713 1714 /** 1715 * Creates a VideoConfig object depending on the passed parameters and returns it. 1716 * 1717 * @return a VideoConfig with the passed parameters and default values. 1718 */ createVideoConfig(int codecType, int videoMode, int framerate, int bitrate, int profile, int level, int cameraId, int cameraZoom, int deviceOrientation, int cvo, int rtcpFbTypes, int width, int height)1719 private VideoConfig createVideoConfig(int codecType, int videoMode, int framerate, int bitrate, 1720 int profile, int level, int cameraId, int cameraZoom, int deviceOrientation, int cvo, 1721 int rtcpFbTypes, int width, int height) { 1722 VideoConfig videoConfig = null; 1723 1724 switch (codecType) { 1725 case VideoConfig.VIDEO_CODEC_AVC: 1726 case VideoConfig.VIDEO_CODEC_HEVC: 1727 videoConfig = createVideoConfig(getRemoteVideoSocketAddress(), 1728 getRemoteVideoRtcpConfig(), codecType, videoMode, framerate, bitrate, 1729 profile, level, cameraId, cameraZoom, deviceOrientation, cvo, rtcpFbTypes, 1730 width, height); 1731 break; 1732 } 1733 1734 return videoConfig; 1735 } 1736 1737 /** 1738 * Creates a TextConfig object depending on the passed parameters and returns it. 1739 * 1740 * @param codecType Integer value of the text codec type 1741 * @return a TextConfig with the passed parameters and default values. 1742 */ createTextConfig(int codecType)1743 private TextConfig createTextConfig(int codecType) { 1744 TextConfig textConfig = null; 1745 1746 switch (codecType) { 1747 case TextConfig.TEXT_T140: 1748 case TextConfig.TEXT_T140_RED: 1749 textConfig = createTextConfig(getRemoteTextSocketAddress(), 1750 getRemoteTextRtcpConfig(), codecType); 1751 break; 1752 } 1753 1754 return textConfig; 1755 } 1756 1757 /** 1758 * Displays the dialer BottomSheetDialog when the button is clicked 1759 * 1760 * @param view the view form the button click 1761 */ openDialer(View view)1762 public void openDialer(View view) { 1763 if (!mBottomSheetDialog.isOpen()) { 1764 mBottomSheetDialog.show(); 1765 } 1766 } 1767 1768 /** 1769 * Sends a DTMF input to the current AudioSession and updates the TextView to 1770 * display the input. 1771 * 1772 * @param view the view from the button click 1773 */ sendDtmfOnClick(View view)1774 public void sendDtmfOnClick(View view) { 1775 char digit = ((Button) view).getText().toString().charAt(0); 1776 mDtmfInput.append(digit); 1777 1778 TextView mDtmfInputBox = mBottomSheetDialog.getDtmfInput(); 1779 mDtmfInputBox.setText(mDtmfInput.toString()); 1780 1781 mAudioSession.sendDtmf(digit, DTMF_DURATION); 1782 } 1783 1784 /** 1785 * Resets the TextView containing the DTMF input 1786 * 1787 * @param view the view from the button click 1788 */ clearDtmfInputOnClick(View view)1789 public void clearDtmfInputOnClick(View view) { 1790 mDtmfInput.setLength(0); 1791 TextView mDtmfInputBox = mBottomSheetDialog.getDtmfInput(); 1792 mDtmfInputBox.setText(getString(R.string.dtmfInputPlaceholder)); 1793 } 1794 1795 /** 1796 * Set a speaker mode on/off 1797 * 1798 * @param view the view from the button click 1799 */ setSpeakModeOnClick(View view)1800 public void setSpeakModeOnClick(View view) { 1801 SwitchCompat speakerMode = findViewById(R.id.speakerModeSwitch); 1802 if (speakerMode != null) { 1803 AudioManager audioManager = getSystemService(AudioManager.class); 1804 audioManager.setSpeakerphoneOn(speakerMode.isChecked()); 1805 } 1806 } 1807 1808 /** 1809 * Calls closeSession() on ImsMediaManager and resets the flag on 1810 * mIsOpenSessionSent 1811 * 1812 * @param view the view from the button click 1813 */ closeSessionOnClick(View view)1814 public void closeSessionOnClick(View view) { 1815 Log.d(TAG, "closeSessionOnClick"); 1816 if (mIsOpenSessionSent) { 1817 mImsMediaManager.closeSession(mAudioSession); 1818 mIsOpenSessionSent = false; 1819 } 1820 if (mIsVideoSessionOpened) { 1821 mImsMediaManager.closeSession(mVideoSession); 1822 mIsVideoSessionOpened = false; 1823 TextureView texturePreview = findViewById(R.id.texturePreview); 1824 TextureView textureDisplay = findViewById(R.id.textureDisplay); 1825 texturePreview.setVisibility(View.GONE); 1826 textureDisplay.setVisibility(View.GONE); 1827 } 1828 if (mIsTextSessionOpened) { 1829 mImsMediaManager.closeSession(mTextSession); 1830 mIsTextSessionOpened = false; 1831 } 1832 } 1833 1834 /** 1835 * When the button is clicked a menu is opened containing the different media 1836 * directions and 1837 * the onMenuItemClickListener is set to handle the user's selection. 1838 * 1839 * @param view The view passed in from the button that is clicked 1840 */ 1841 @SuppressLint("NonConstantResourceId") mediaDirectionOnClick(View view)1842 public void mediaDirectionOnClick(View view) { 1843 PopupMenu mediaDirectionMenu = new PopupMenu(this, findViewById(R.id.mediaDirectionButton)); 1844 mediaDirectionMenu.getMenuInflater() 1845 .inflate(R.menu.media_direction_menu, mediaDirectionMenu.getMenu()); 1846 int[] direction = { 0 }; 1847 mediaDirectionMenu.setOnMenuItemClickListener(item -> { 1848 switch (item.getItemId()) { 1849 case R.id.noFlowDirectionMenuItem: 1850 direction[0] = RtpConfig.MEDIA_DIRECTION_NO_FLOW; 1851 break; 1852 case R.id.sendReceiveDirectionMenuItem: 1853 direction[0] = RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE; 1854 break; 1855 case R.id.receiveOnlyDirectionMenuItem: 1856 direction[0] = RtpConfig.MEDIA_DIRECTION_RECEIVE_ONLY; 1857 break; 1858 case R.id.sendOnlyDirectionMenuItem: 1859 direction[0] = RtpConfig.MEDIA_DIRECTION_SEND_ONLY; 1860 break; 1861 case R.id.inactiveDirectionMenuItem: 1862 direction[0] = RtpConfig.MEDIA_DIRECTION_INACTIVE; 1863 break; 1864 default: 1865 return false; 1866 } 1867 mAudioConfig.setMediaDirection(direction[0]); 1868 mAudioSession.modifySession(mAudioConfig); 1869 if (mIsVideoSessionOpened) { 1870 mVideoConfig.setMediaDirection(direction[0]); 1871 mVideoSession.modifySession(mVideoConfig); 1872 } 1873 if (mIsTextSessionOpened) { 1874 mTextConfig.setMediaDirection(direction[0]); 1875 mTextSession.modifySession(mTextConfig); 1876 } 1877 return true; 1878 }); 1879 mediaDirectionMenu.show(); 1880 } 1881 1882 /** 1883 * Displays the audio codec change BottomSheetDialog when the button is clicked 1884 * 1885 * @param view the view form the button click 1886 */ openChangeAudioCodecSheet(View view)1887 public void openChangeAudioCodecSheet(View view) { 1888 if (!mBottomSheetAudioCodecSettings.isOpen()) { 1889 mBottomSheetAudioCodecSettings.show(); 1890 } 1891 } 1892 1893 /** 1894 * Calls openSession() on the ImsMediaManager 1895 * 1896 * @param view the view from the button click 1897 */ openSessionOnClick(View view)1898 public void openSessionOnClick(View view) { 1899 Log.d(TAG, "openSessionOnClick()"); 1900 if (mIsMediaManagerReady && !mIsOpenSessionSent) { 1901 1902 Toast.makeText(getApplicationContext(), getString(R.string.connecting_call_toast_text), 1903 Toast.LENGTH_SHORT).show(); 1904 1905 mAudioConfig = determineAudioConfig(mLocalDeviceInfo, mRemoteDeviceInfo); 1906 Log.d(TAG, "AudioConfig: " + mAudioConfig.toString()); 1907 1908 RtpAudioSessionCallback sessionAudioCallback = new RtpAudioSessionCallback(); 1909 mImsMediaManager.openSession(mAudioRtp, mAudioRtcp, 1910 ImsMediaSession.SESSION_TYPE_AUDIO, 1911 null, mExecutor, sessionAudioCallback); 1912 Log.d(TAG, "openSession(): audio=" + mRemoteDeviceInfo.getInetAddress() + ":" 1913 + mRemoteDeviceInfo.getAudioRtpPort()); 1914 1915 if (mVideoEnabled) { 1916 RtpVideoSessionCallback sessionVideoCallback = new RtpVideoSessionCallback(); 1917 mImsMediaManager.openSession(mVideoRtp, mVideoRtcp, 1918 ImsMediaSession.SESSION_TYPE_VIDEO, 1919 null, mExecutor, sessionVideoCallback); 1920 Log.d(TAG, "openSession(): video=" + mRemoteDeviceInfo.getInetAddress() + ":" 1921 + mRemoteDeviceInfo.getVideoRtpPort()); 1922 } 1923 1924 if (mTextEnabled) { 1925 mTextConfig = createTextConfig(TextConfig.TEXT_T140_RED); 1926 Log.d(TAG, "TextConfig: " + mTextConfig.toString()); 1927 1928 RtpTextSessionCallback sessionTextCallback = new RtpTextSessionCallback(); 1929 mImsMediaManager.openSession(mTextRtp, mTextRtcp, 1930 ImsMediaSession.SESSION_TYPE_RTT, 1931 mTextConfig, mExecutor, sessionTextCallback); 1932 Log.d(TAG, "openSession(): text=" + mRemoteDeviceInfo.getInetAddress() + ":" 1933 + mRemoteDeviceInfo.getTextRtpPort()); 1934 } 1935 } 1936 } 1937 1938 /** 1939 * Saves the inputted ip address and port number to SharedPreferences. 1940 * 1941 * @param view the view from the button click 1942 */ saveSettingsOnClick(View view)1943 public void saveSettingsOnClick(View view) { 1944 int port = getRemoteDevicePortEditText(); 1945 String ip = getRemoteDeviceIpEditText(); 1946 editor.putInt("OTHER_HANDSHAKE_PORT", port); 1947 editor.putString("OTHER_IP_ADDRESS", ip); 1948 editor.apply(); 1949 Toast.makeText(getApplicationContext(), R.string.save_button_action_toast, 1950 Toast.LENGTH_SHORT).show(); 1951 } 1952 1953 /** 1954 * Saves the inputted ip address and video port number to SharedPreferences. 1955 * 1956 * @param view the view from the button click 1957 */ saveVideoSettingsOnClick(View view)1958 public void saveVideoSettingsOnClick(View view) { 1959 int port = getVideoRemoteDevicePortEditText(); 1960 String ip = getVideoRemoteDeviceIpEditText(); 1961 editor.putInt("OTHER_HANDSHAKE_VIDEO_PORT", port); 1962 editor.putString("OTHER_VIDEO_IP_ADDRESS", ip); 1963 editor.apply(); 1964 1965 Spinner videoCodecSpinner = findViewById(R.id.spinnerVideoCodecs); 1966 Spinner videoCodecProfileSpinner = findViewById(R.id.spinnerVideoCodecProfiles); 1967 Spinner videoCodecLevelSpinner = findViewById(R.id.spinnerVideoCodecLevels); 1968 Spinner videoModeSpinner = findViewById(R.id.spinnerVideoModes); 1969 Spinner videoCameraIdSpinner = findViewById(R.id.spinnerVideoCameraIds); 1970 Spinner videoCameraZoomSpinner = findViewById(R.id.spinnerVideoCameraZoom); 1971 Spinner videoFramerateSpinner = findViewById(R.id.spinnerVideoFramerates); 1972 Spinner videoBitrateSpinner = findViewById(R.id.spinnerVideoBitrates); 1973 Spinner videoDeviceOrientationSpinner = findViewById(R.id.spinnerVideoDeviceOrientations); 1974 Spinner videoCvoValueSpinner = findViewById(R.id.spinnerVideoCvoValues); 1975 Spinner videoResolutionSpinner = (Spinner) findViewById(R.id.spinnerVideoResolution); 1976 1977 mSelectedVideoCodec = 1978 ((VideoCodecEnum) videoCodecSpinner.getSelectedItem()).getValue(); 1979 mSelectedCodecProfile = 1980 ((VideoCodecProfileEnum) videoCodecProfileSpinner.getSelectedItem()).getValue(); 1981 mSelectedCodecLevel = 1982 ((VideoCodecLevelEnum) videoCodecLevelSpinner.getSelectedItem()).getValue(); 1983 mSelectedVideoMode = 1984 ((VideoModeEnum) videoModeSpinner.getSelectedItem()).getValue(); 1985 mSelectedCameraId = 1986 ((VideoCameraIdEnum) videoCameraIdSpinner.getSelectedItem()).getValue(); 1987 mSelectedCameraZoom = 1988 ((VideoCameraZoomEnum) videoCameraZoomSpinner.getSelectedItem()).getValue(); 1989 mSelectedFramerate = 1990 ((VideoFramerateEnum) videoFramerateSpinner.getSelectedItem()).getValue(); 1991 mSelectedBitrate = 1992 ((VideoBitrateEnum) videoBitrateSpinner.getSelectedItem()).getValue(); 1993 mSelectedDeviceOrientationDegree = 1994 ((VideoDeviceOrientationEnum) videoDeviceOrientationSpinner 1995 .getSelectedItem()) 1996 .getValue(); 1997 mSelectedCvoValue = ((VideoCvoValueEnum) videoCvoValueSpinner.getSelectedItem()) 1998 .getValue(); 1999 mSelectedVideoResolution = (String) videoResolutionSpinner.getSelectedItem(); 2000 Toast.makeText(getApplicationContext(), R.string.save_button_action_toast, 2001 Toast.LENGTH_SHORT).show(); 2002 } 2003 2004 /** 2005 * Calls modifySession to change the audio codec on the current AudioSession. 2006 * Also contains 2007 * the logic to create the new AudioConfig. 2008 * 2009 * @param view the view form the button click 2010 */ changeAudioCodecOnClick(View view)2011 public void changeAudioCodecOnClick(View view) { 2012 AudioConfig config = null; 2013 AmrParams amrParams; 2014 EvsParams evsParams; 2015 int audioCodec = mBottomSheetAudioCodecSettings.getAudioCodec(); 2016 2017 switch (audioCodec) { 2018 case CodecType.AMR: 2019 case CodecType.AMR_WB: 2020 2021 evsParams = new EvsParams.Builder() 2022 .setEvsbandwidth(EvsParams.EVS_BAND_NONE) 2023 .setEvsMode(EvsParams.EVS_MODE_0) 2024 .setChannelAwareMode((byte) 3) 2025 .setHeaderFullOnly(true) 2026 .setCodecModeRequest((byte) 15) 2027 .build(); 2028 2029 amrParams = createAmrParams(mBottomSheetAudioCodecSettings.getAmrMode(), false, 0); 2030 config = createAudioConfig(getRemoteAudioSocketAddress(), 2031 getRemoteAudioRtcpConfig(), audioCodec, amrParams, evsParams); 2032 Log.d(TAG, String.format("AudioConfig switched to Codec: %s\t Params: %s", 2033 mBottomSheetAudioCodecSettings.getAudioCodec(), 2034 config.getAmrParams().toString())); 2035 break; 2036 2037 case CodecType.EVS: 2038 evsParams = createEvsParams(mBottomSheetAudioCodecSettings.getEvsBand(), 2039 mBottomSheetAudioCodecSettings.getEvsMode()); 2040 amrParams = createAmrParams(0, false, 0); 2041 config = createAudioConfig(getRemoteAudioSocketAddress(), 2042 getRemoteAudioRtcpConfig(), audioCodec, amrParams, evsParams); 2043 Log.d(TAG, String.format("AudioConfig switched to Codec: %s\t Params: %s", 2044 mBottomSheetAudioCodecSettings.getAudioCodec(), 2045 config.getEvsParams().toString())); 2046 break; 2047 2048 case CodecType.PCMA: 2049 case CodecType.PCMU: 2050 config = createAudioConfig(getRemoteAudioSocketAddress(), 2051 getRemoteAudioRtcpConfig(), audioCodec, null, null); 2052 Log.d(TAG, String.format("AudioConfig switched to Codec: %s", 2053 mBottomSheetAudioCodecSettings.getAudioCodec())); 2054 break; 2055 } 2056 2057 mAudioSession.modifySession(config); 2058 mBottomSheetAudioCodecSettings.dismiss(); 2059 } 2060 2061 /** 2062 * Changes the flag of loopback mode, changes the ConnectionStatus sate, and 2063 * restyles the UI 2064 * 2065 * @param view the view from, the button click 2066 */ loopbackOnClick(View view)2067 public void loopbackOnClick(View view) { 2068 SwitchCompat mLoopbackSwitch = findViewById(R.id.loopbackModeSwitch); 2069 if (mLoopbackSwitch.isChecked()) { 2070 mLoopbackModeEnabled = true; 2071 openPorts(true); 2072 editor.putString("OTHER_IP_ADDRESS", getLocalIpAddress()).apply(); 2073 mRemoteDeviceInfo = createMyDeviceInfo(); 2074 mLocalDeviceInfo = createMyDeviceInfo(); 2075 updateUI(ConnectionStatus.CONNECTED); 2076 updateAdditionalMedia(); 2077 } else { 2078 closePorts(); 2079 mLoopbackModeEnabled = false; 2080 updateUI(ConnectionStatus.OFFLINE); 2081 updateVideoUi(false); 2082 updateTextUi(false); 2083 } 2084 } 2085 2086 /** 2087 * Send text message to the text session 2088 * 2089 * @param view the view from, the button click 2090 */ sendRttOnClick(View view)2091 public void sendRttOnClick(View view) { 2092 if (mIsTextSessionOpened) { 2093 EditText edit = findViewById(R.id.textEditTextSending); 2094 mTextSession.sendRtt(edit.getText().toString()); 2095 } 2096 } 2097 2098 /** 2099 * Opens or closes ports and starts the waiting handshake runnable depending on 2100 * the current 2101 * state of the button. 2102 * 2103 * @param view view from the button click 2104 */ allowCallsOnClick(View view)2105 public void allowCallsOnClick(View view) { 2106 if (prefs.getBoolean(HANDSHAKE_PORT_PREF, false)) { 2107 closePorts(); 2108 Log.d(TAG, "Closed handshake, rtp, and rtcp ports."); 2109 2110 Toast.makeText(getApplicationContext(), 2111 "Closing ports", 2112 Toast.LENGTH_SHORT).show(); 2113 updateUI(ConnectionStatus.OFFLINE); 2114 } else { 2115 openPorts(true); 2116 while (!prefs.getBoolean(HANDSHAKE_PORT_PREF, false)) { 2117 } 2118 Log.d(TAG, "Handshake, rtp, and rtcp ports have been bound."); 2119 2120 Toast.makeText(getApplicationContext(), getString(R.string.allowing_calls_toast_text), 2121 Toast.LENGTH_SHORT).show(); 2122 2123 mWaitForHandshakeThread = new Thread(handleIncomingHandshake); 2124 mWaitForHandshakeThread.start(); 2125 updateUI(ConnectionStatus.DISCONNECTED); 2126 } 2127 } 2128 2129 /** 2130 * Starts the handshake process runnable that attempts to connect to two device 2131 * together. 2132 * 2133 * @param view view from the button click 2134 */ initiateHandshakeOnClick(View view)2135 public void initiateHandshakeOnClick(View view) { 2136 mWaitForHandshakeThread.interrupt(); 2137 Thread initiateHandshakeThread = new Thread(initiateHandshake); 2138 initiateHandshakeThread.start(); 2139 updateUI(ConnectionStatus.CONNECTING); 2140 } 2141 2142 /** 2143 * Handles the styling of the settings layout. 2144 */ setupSettingsPage()2145 public void setupSettingsPage() { 2146 EditText ipAddress = findViewById(R.id.remoteDeviceIpEditText); 2147 EditText portNumber = findViewById(R.id.remotePortNumberEditText); 2148 ipAddress.setText(prefs.getString("OTHER_IP_ADDRESS", "")); 2149 portNumber.setText(String.valueOf(prefs.getInt("OTHER_HANDSHAKE_PORT", 0))); 2150 2151 setupAudioCodecSelectionLists(); 2152 setupCodecSelectionOnClickListeners(); 2153 } 2154 getSpinnerIndex(Spinner spinner, Object value)2155 private int getSpinnerIndex(Spinner spinner, Object value) { 2156 int index = 0; 2157 for (int i = 0; i < spinner.getCount(); i++) { 2158 if (spinner.getItemAtPosition(i).equals(value)) { 2159 index = i; 2160 } 2161 } 2162 return index; 2163 } 2164 2165 /** 2166 * Handles the styling of the video settings layout. 2167 */ setupVideoSettingsPage()2168 public void setupVideoSettingsPage() { 2169 EditText ipAddress = findViewById(R.id.videoRemoteDeviceIpEditText); 2170 EditText portNumber = findViewById(R.id.remoteVideoPortNumberEditText); 2171 ipAddress.setText(prefs.getString("OTHER_IP_ADDRESS", "")); 2172 portNumber.setText(String.valueOf(prefs.getInt("OTHER_HANDSHAKE_PORT", 0))); 2173 2174 setupVideoCodecSelectionLists(); 2175 } 2176 2177 /** 2178 * Gets the saved user selections for the audio codec settings and updates the UI's lists to 2179 * match. 2180 */ setupAudioCodecSelectionLists()2181 private void setupAudioCodecSelectionLists() { 2182 updateCodecSelectionFromPrefs(); 2183 2184 ArrayAdapter<CodecTypeEnum> codecTypeAdapter = new ArrayAdapter<>( 2185 this, android.R.layout.simple_list_item_multiple_choice, CodecTypeEnum.values()); 2186 ListView codecTypeList = findViewById(R.id.audioCodecList); 2187 codecTypeList.setAdapter(codecTypeAdapter); 2188 for (int i = 0; i < codecTypeAdapter.getCount(); i++) { 2189 CodecTypeEnum mode = (CodecTypeEnum) codecTypeList.getItemAtPosition(i); 2190 codecTypeList.setItemChecked(i, mSelectedCodecTypes.contains(mode.getValue())); 2191 } 2192 2193 ArrayAdapter<EvsBandwidthEnum> evsBandAdaptor = new ArrayAdapter<>( 2194 this, android.R.layout.simple_list_item_multiple_choice, EvsBandwidthEnum.values()); 2195 ListView evsBandwidthList = findViewById(R.id.evsBandwidthsList); 2196 evsBandwidthList.setAdapter(evsBandAdaptor); 2197 for (int i = 0; i < evsBandAdaptor.getCount(); i++) { 2198 EvsBandwidthEnum mode = (EvsBandwidthEnum) evsBandwidthList.getItemAtPosition(i); 2199 evsBandwidthList.setItemChecked(i, mSelectedEvsBandwidths.contains(mode.getValue())); 2200 } 2201 2202 ArrayAdapter<AmrModeEnum> amrModeAdapter = new ArrayAdapter<>( 2203 this, android.R.layout.simple_list_item_multiple_choice, AmrModeEnum.values()); 2204 ListView amrModesList = findViewById(R.id.amrModesList); 2205 amrModesList.setAdapter(amrModeAdapter); 2206 for (int i = 0; i < amrModeAdapter.getCount(); i++) { 2207 AmrModeEnum mode = (AmrModeEnum) amrModesList.getItemAtPosition(i); 2208 amrModesList.setItemChecked(i, mSelectedAmrModes.contains(mode.getValue())); 2209 } 2210 2211 ArrayAdapter<EvsModeEnum> evsModeAdaptor = new ArrayAdapter<>( 2212 this, android.R.layout.simple_list_item_multiple_choice, EvsModeEnum.values()); 2213 ListView evsModeList = findViewById(R.id.evsModesList); 2214 evsModeList.setAdapter(evsModeAdaptor); 2215 for (int i = 0; i < evsModeAdaptor.getCount(); i++) { 2216 EvsModeEnum mode = (EvsModeEnum) evsModeList.getItemAtPosition(i); 2217 evsModeList.setItemChecked(i, mSelectedEvsModes.contains(mode.getValue())); 2218 } 2219 } 2220 setupVideoCodecSelectionLists()2221 private void setupVideoCodecSelectionLists() { 2222 Spinner videoCodecSpinner = findViewById(R.id.spinnerVideoCodecs); 2223 ArrayAdapter<VideoCodecEnum> videoCodecAdaptor = new ArrayAdapter<>( 2224 this, android.R.layout.simple_spinner_item, VideoCodecEnum.values()); 2225 videoCodecAdaptor.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 2226 videoCodecSpinner.setAdapter(videoCodecAdaptor); 2227 videoCodecSpinner.setSelection(getSpinnerIndex(videoCodecSpinner, mSelectedVideoCodec)); 2228 2229 Spinner videoCodecProfileSpinner = findViewById(R.id.spinnerVideoCodecProfiles); 2230 ArrayAdapter<VideoCodecProfileEnum> videoCodecProfileAdaptor = new ArrayAdapter<>( 2231 this, android.R.layout.simple_spinner_item, VideoCodecProfileEnum.values()); 2232 videoCodecProfileAdaptor.setDropDownViewResource( 2233 android.R.layout.simple_spinner_dropdown_item); 2234 videoCodecProfileSpinner.setAdapter(videoCodecProfileAdaptor); 2235 videoCodecProfileSpinner.setSelection(getSpinnerIndex(videoCodecProfileSpinner, 2236 mSelectedCodecProfile)); 2237 2238 Spinner videoCodecLevelSpinner = findViewById(R.id.spinnerVideoCodecLevels); 2239 ArrayAdapter<VideoCodecLevelEnum> videoCodecLevelAdaptor = new ArrayAdapter<>( 2240 this, android.R.layout.simple_spinner_item, VideoCodecLevelEnum.values()); 2241 videoCodecLevelAdaptor.setDropDownViewResource( 2242 android.R.layout.simple_spinner_dropdown_item); 2243 videoCodecLevelSpinner.setAdapter(videoCodecLevelAdaptor); 2244 videoCodecLevelSpinner.setSelection(getSpinnerIndex(videoCodecLevelSpinner, 2245 mSelectedCodecLevel)); 2246 2247 Spinner videoModeSpinner = findViewById(R.id.spinnerVideoModes); 2248 ArrayAdapter<VideoModeEnum> videoModeAdapter = new ArrayAdapter<>( 2249 this, android.R.layout.simple_spinner_item, VideoModeEnum.values()); 2250 videoModeAdapter.setDropDownViewResource( 2251 android.R.layout.simple_spinner_dropdown_item); 2252 videoModeSpinner.setAdapter(videoModeAdapter); 2253 videoModeSpinner.setSelection(getSpinnerIndex(videoModeSpinner, mSelectedVideoMode)); 2254 2255 Spinner videoCameraIdSpinner = findViewById(R.id.spinnerVideoCameraIds); 2256 ArrayAdapter<VideoCameraIdEnum> videoCameraIdAdaptor = new ArrayAdapter<>( 2257 this, android.R.layout.simple_spinner_item, VideoCameraIdEnum.values()); 2258 videoCameraIdAdaptor.setDropDownViewResource( 2259 android.R.layout.simple_spinner_dropdown_item); 2260 videoCameraIdSpinner.setAdapter(videoCameraIdAdaptor); 2261 videoCameraIdSpinner.setSelection(getSpinnerIndex(videoCameraIdSpinner, mSelectedCameraId)); 2262 2263 Spinner videoCameraZoomSpinner = findViewById(R.id.spinnerVideoCameraZoom); 2264 ArrayAdapter<VideoCameraZoomEnum> videoCameraZoomAdaptor = new ArrayAdapter<>( 2265 this, android.R.layout.simple_spinner_item, VideoCameraZoomEnum.values()); 2266 videoCameraZoomAdaptor.setDropDownViewResource( 2267 android.R.layout.simple_spinner_dropdown_item); 2268 videoCameraZoomSpinner.setAdapter(videoCameraZoomAdaptor); 2269 videoCameraZoomSpinner.setSelection(getSpinnerIndex(videoCameraZoomSpinner, 2270 mSelectedCameraZoom)); 2271 2272 Spinner videoFramerateSpinner = findViewById(R.id.spinnerVideoFramerates); 2273 ArrayAdapter<VideoFramerateEnum> videoFramerateAdaptor = new ArrayAdapter<>( 2274 this, android.R.layout.simple_spinner_item, VideoFramerateEnum.values()); 2275 videoFramerateAdaptor.setDropDownViewResource( 2276 android.R.layout.simple_spinner_dropdown_item); 2277 videoFramerateSpinner.setAdapter(videoFramerateAdaptor); 2278 videoFramerateSpinner.setSelection(getSpinnerIndex(videoFramerateSpinner, 2279 mSelectedFramerate)); 2280 2281 Spinner videoBitrateSpinner = findViewById(R.id.spinnerVideoBitrates); 2282 ArrayAdapter<VideoBitrateEnum> videoBitrateAdaptor = new ArrayAdapter<>( 2283 this, android.R.layout.simple_spinner_item, VideoBitrateEnum.values()); 2284 videoBitrateAdaptor.setDropDownViewResource( 2285 android.R.layout.simple_spinner_dropdown_item); 2286 videoBitrateSpinner.setAdapter(videoBitrateAdaptor); 2287 videoBitrateSpinner.setSelection(getSpinnerIndex(videoBitrateSpinner, mSelectedBitrate)); 2288 2289 Spinner videoDeviceOrientationSpinner = findViewById(R.id.spinnerVideoDeviceOrientations); 2290 ArrayAdapter<VideoDeviceOrientationEnum> videoDeviceOrientationAdaptor = new ArrayAdapter<>( 2291 this, android.R.layout.simple_spinner_item, VideoDeviceOrientationEnum.values()); 2292 videoDeviceOrientationAdaptor.setDropDownViewResource( 2293 android.R.layout.simple_spinner_dropdown_item); 2294 videoDeviceOrientationSpinner.setAdapter(videoDeviceOrientationAdaptor); 2295 videoDeviceOrientationSpinner.setSelection(getSpinnerIndex(videoDeviceOrientationSpinner, 2296 mSelectedDeviceOrientationDegree)); 2297 2298 Spinner videoCvoValueSpinner = findViewById(R.id.spinnerVideoCvoValues); 2299 ArrayAdapter<VideoCvoValueEnum> videoCvoValueAdaptor = new ArrayAdapter<>( 2300 this, android.R.layout.simple_spinner_item, VideoCvoValueEnum.values()); 2301 videoCvoValueAdaptor.setDropDownViewResource( 2302 android.R.layout.simple_spinner_dropdown_item); 2303 videoCvoValueSpinner.setAdapter(videoCvoValueAdaptor); 2304 videoCvoValueSpinner.setSelection(getSpinnerIndex(videoCvoValueSpinner, 2305 mSelectedCvoValue)); 2306 2307 Spinner videoResolutionSpinner = (Spinner) findViewById(R.id.spinnerVideoResolution); 2308 ArrayAdapter<String> videoResolutionAdapter = new ArrayAdapter<String>(this, 2309 android.R.layout.simple_spinner_item, mVideoResolutionStrings); 2310 videoResolutionAdapter.setDropDownViewResource( 2311 android.R.layout.simple_spinner_dropdown_item); 2312 videoResolutionSpinner.setAdapter(videoResolutionAdapter); 2313 videoResolutionSpinner.setSelection(getSpinnerIndex(videoResolutionSpinner, 2314 mSelectedVideoResolution)); 2315 } 2316 2317 /** 2318 * Updates all of the lists containing the user's codecs selections. 2319 */ updateCodecSelectionFromPrefs()2320 private void updateCodecSelectionFromPrefs() { 2321 mSelectedCodecTypes = 2322 prefsHandler.getIntegerSetFromPrefs(SharedPrefsHandler.CODECS_PREF); 2323 mSelectedEvsBandwidths = 2324 prefsHandler.getIntegerSetFromPrefs(SharedPrefsHandler.EVS_BANDS_PREF); 2325 mSelectedAmrModes = 2326 prefsHandler.getIntegerSetFromPrefs(SharedPrefsHandler.AMR_MODES_PREF); 2327 mSelectedEvsModes = 2328 prefsHandler.getIntegerSetFromPrefs(SharedPrefsHandler.EVS_MODES_PREF); 2329 } 2330 2331 /** 2332 * Adds onClickListeners to the 4 check box lists on the settings page, to 2333 * handle the user input 2334 * of the codec, bandwidth, and mode selections. 2335 */ setupCodecSelectionOnClickListeners()2336 public void setupCodecSelectionOnClickListeners() { 2337 ListView audioCodecList, evsBandList, amrModeList, evsModeList; 2338 2339 audioCodecList = findViewById(R.id.audioCodecList); 2340 evsBandList = findViewById(R.id.evsBandwidthsList); 2341 amrModeList = findViewById(R.id.amrModesList); 2342 evsModeList = findViewById(R.id.evsModesList); 2343 2344 audioCodecList.setOnItemClickListener((adapterView, view, position, id) -> { 2345 CodecTypeEnum item = (CodecTypeEnum) audioCodecList.getItemAtPosition(position); 2346 2347 if (audioCodecList.isItemChecked(position)) { 2348 mSelectedCodecTypes.add(item.getValue()); 2349 if (item == CodecTypeEnum.AMR || item == CodecTypeEnum.AMR_WB) { 2350 amrModeList.setAlpha(ENABLED_ALPHA); 2351 amrModeList.setEnabled(true); 2352 } else if (item == CodecTypeEnum.EVS) { 2353 evsBandList.setAlpha(ENABLED_ALPHA); 2354 amrModeList.setEnabled(true); 2355 evsModeList.setAlpha(ENABLED_ALPHA); 2356 evsModeList.setEnabled(true); 2357 } 2358 } else { 2359 mSelectedCodecTypes.remove(item.getValue()); 2360 if (item == CodecTypeEnum.AMR || item == CodecTypeEnum.AMR_WB) { 2361 amrModeList.setAlpha(0.3f); 2362 amrModeList.setEnabled(false); 2363 } else if (item == CodecTypeEnum.EVS) { 2364 evsBandList.setAlpha(0.3f); 2365 evsBandList.setEnabled(false); 2366 evsModeList.setAlpha(0.3f); 2367 evsModeList.setEnabled(false); 2368 } 2369 } 2370 2371 prefsHandler.saveIntegerSetToPrefs(SharedPrefsHandler.CODECS_PREF, 2372 mSelectedCodecTypes); 2373 2374 }); 2375 evsBandList.setOnItemClickListener((adapterView, view, position, id) -> { 2376 EvsBandwidthEnum item = (EvsBandwidthEnum) evsBandList.getItemAtPosition(position); 2377 2378 if (evsBandList.isItemChecked(position)) { 2379 mSelectedEvsBandwidths.add(item.getValue()); 2380 } else { 2381 mSelectedEvsBandwidths.remove(item.getValue()); 2382 } 2383 2384 prefsHandler.saveIntegerSetToPrefs(SharedPrefsHandler.EVS_BANDS_PREF, 2385 mSelectedEvsBandwidths); 2386 }); 2387 evsModeList.setOnItemClickListener((adapterView, view, position, id) -> { 2388 EvsModeEnum item = (EvsModeEnum) evsModeList.getItemAtPosition(position); 2389 2390 if (evsModeList.isItemChecked(position)) { 2391 mSelectedEvsModes.add(item.getValue()); 2392 } else { 2393 mSelectedEvsModes.remove(item.getValue()); 2394 } 2395 2396 prefsHandler.saveIntegerSetToPrefs(SharedPrefsHandler.EVS_MODES_PREF, 2397 mSelectedEvsModes); 2398 }); 2399 amrModeList.setOnItemClickListener((adapterView, view, position, id) -> { 2400 AmrModeEnum item = (AmrModeEnum) amrModeList.getItemAtPosition(position); 2401 2402 if (amrModeList.isItemChecked(position)) { 2403 mSelectedAmrModes.add(item.getValue()); 2404 } else { 2405 mSelectedAmrModes.remove(item.getValue()); 2406 } 2407 2408 prefsHandler.saveIntegerSetToPrefs(SharedPrefsHandler.AMR_MODES_PREF, 2409 mSelectedAmrModes); 2410 }); 2411 } 2412 2413 /** 2414 * Styles the main activity UI based on the current ConnectionStatus enum state. 2415 */ styleMainActivity()2416 private void styleMainActivity() { 2417 runOnUiThread(() -> { 2418 updateUiViews(); 2419 styleDevicesInfo(); 2420 switch (mConnectionStatus) { 2421 case OFFLINE: 2422 styleOffline(); 2423 break; 2424 2425 case DISCONNECTED: 2426 styleDisconnected(); 2427 break; 2428 2429 case CONNECTING: 2430 break; 2431 2432 case CONNECTED: 2433 styleConnected(); 2434 break; 2435 2436 case ACTIVE_CALL: 2437 styleActiveCall(); 2438 break; 2439 } 2440 }); 2441 } 2442 styleDevicesInfo()2443 private void styleDevicesInfo() { 2444 TextView localIpLabel = findViewById(R.id.localIpLabel); 2445 localIpLabel.setText(getString(R.string.local_ip_label, getLocalIpAddress())); 2446 2447 mRemoteIpLabel = findViewById(R.id.remoteIpLabel); 2448 mRemoteIpLabel.setText(getString(R.string.other_device_ip_label, 2449 prefs.getString("OTHER_IP_ADDRESS", "null"))); 2450 2451 mRemoteHandshakePortLabel = findViewById(R.id.remoteHandshakePortLabel); 2452 mRemoteHandshakePortLabel.setText(getString(R.string.handshake_port_label, 2453 String.valueOf(getOtherDevicePort()))); 2454 } 2455 styleOffline()2456 private void styleOffline() { 2457 mLocalHandshakePortLabel.setText(getString(R.string.port_closed_label)); 2458 mLocalRtpPortLabel.setText(getString(R.string.port_closed_label)); 2459 mLocalRtcpPortLabel.setText(getString(R.string.port_closed_label)); 2460 mRemoteRtpPortLabel.setText(getString(R.string.port_closed_label)); 2461 mRemoteRtcpPortLabel.setText(getString(R.string.port_closed_label)); 2462 2463 mAllowCallsButton.setText(R.string.allow_calls_button_text); 2464 mAllowCallsButton.setBackgroundColor(getColor(R.color.mint_green)); 2465 styleButtonEnabled(mAllowCallsButton); 2466 2467 mConnectButton.setText(R.string.connect_button_text); 2468 mConnectButton.setBackgroundColor(getColor(R.color.mint_green)); 2469 styleButtonDisabled(mConnectButton); 2470 2471 styleLoopbackSwitchEnabled(); 2472 styleButtonDisabled(mOpenSessionButton); 2473 styleLayoutChildrenDisabled(mActiveCallToolBar); 2474 } 2475 styleDisconnected()2476 private void styleDisconnected() { 2477 mLocalHandshakePortLabel.setText(getString(R.string.handshake_port_label, 2478 String.valueOf(mHandshakeReceptionSocket.getBoundSocket()))); 2479 mLocalRtpPortLabel.setText(getString(R.string.rtp_reception_port_label, 2480 String.valueOf(mAudioRtp.getLocalPort()))); 2481 mLocalRtcpPortLabel.setText(getString(R.string.rtcp_reception_port_label, 2482 String.valueOf(mAudioRtcp.getLocalPort()))); 2483 mRemoteRtpPortLabel.setText(getString(R.string.port_closed_label)); 2484 mRemoteRtcpPortLabel.setText(getString(R.string.port_closed_label)); 2485 2486 mAllowCallsButton.setText(R.string.disable_calls_button_text); 2487 mAllowCallsButton.setBackgroundColor(getColor(R.color.coral_red)); 2488 styleButtonEnabled(mAllowCallsButton); 2489 2490 mConnectButton.setText(R.string.connect_button_text); 2491 mConnectButton.setBackgroundColor(getColor(R.color.mint_green)); 2492 styleButtonEnabled(mConnectButton); 2493 2494 styleLoopbackSwitchDisabled(); 2495 styleButtonDisabled(mOpenSessionButton); 2496 styleLayoutChildrenDisabled(mActiveCallToolBar); 2497 } 2498 styleConnected()2499 private void styleConnected() { 2500 if (mLoopbackModeEnabled) { 2501 mAllowCallsButton.setText(R.string.allow_calls_button_text); 2502 mAllowCallsButton.setBackgroundColor(getColor(R.color.mint_green)); 2503 styleButtonDisabled(mAllowCallsButton); 2504 2505 mConnectButton.setText(R.string.connect_button_text); 2506 mConnectButton.setBackgroundColor(getColor(R.color.mint_green)); 2507 styleButtonDisabled(mConnectButton); 2508 styleLoopbackSwitchEnabled(); 2509 2510 } else { 2511 mAllowCallsButton.setText(R.string.disable_calls_button_text); 2512 mAllowCallsButton.setBackgroundColor(getColor(R.color.coral_red)); 2513 styleButtonEnabled(mAllowCallsButton); 2514 2515 mConnectButton.setText(R.string.disconnect_button_text); 2516 mConnectButton.setBackgroundColor(getColor(R.color.coral_red)); 2517 styleButtonEnabled(mConnectButton); 2518 styleLoopbackSwitchDisabled(); 2519 } 2520 2521 mLocalHandshakePortLabel.setText(getString(R.string.reception_port_label, 2522 String.valueOf(mHandshakeReceptionSocket.getBoundSocket()))); 2523 mLocalRtpPortLabel.setText(getString(R.string.rtp_reception_port_label, 2524 String.valueOf(mAudioRtp.getLocalPort()))); 2525 mLocalRtcpPortLabel.setText(getString(R.string.rtcp_reception_port_label, 2526 String.valueOf(mAudioRtcp.getLocalPort()))); 2527 mRemoteRtpPortLabel.setText(getString(R.string.rtp_reception_port_label, 2528 String.valueOf(mRemoteDeviceInfo.getAudioRtpPort()))); 2529 mRemoteRtcpPortLabel.setText(getString(R.string.rtcp_reception_port_label, 2530 String.valueOf(mRemoteDeviceInfo.getAudioRtpPort() + 1))); 2531 styleButtonEnabled(mOpenSessionButton); 2532 styleLayoutChildrenDisabled(mActiveCallToolBar); 2533 } 2534 styleActiveCall()2535 private void styleActiveCall() { 2536 if (mLoopbackModeEnabled) { 2537 styleLoopbackSwitchEnabled(); 2538 2539 mAllowCallsButton.setText(R.string.allow_calls_button_text); 2540 mAllowCallsButton.setBackgroundColor(getColor(R.color.mint_green)); 2541 styleButtonDisabled(mAllowCallsButton); 2542 2543 mConnectButton.setText(R.string.connect_button_text); 2544 mConnectButton.setBackgroundColor(getColor(R.color.mint_green)); 2545 styleButtonDisabled(mConnectButton); 2546 styleLoopbackSwitchEnabled(); 2547 2548 } else { 2549 styleLoopbackSwitchDisabled(); 2550 2551 mAllowCallsButton.setText(R.string.disable_calls_button_text); 2552 mAllowCallsButton.setBackgroundColor(getColor(R.color.coral_red)); 2553 styleButtonDisabled(mAllowCallsButton); 2554 2555 mConnectButton.setText(R.string.disconnect_button_text); 2556 mConnectButton.setBackgroundColor(getColor(R.color.coral_red)); 2557 styleButtonDisabled(mConnectButton); 2558 styleLoopbackSwitchDisabled(); 2559 } 2560 2561 mLocalHandshakePortLabel 2562 .setText(getString(R.string.reception_port_label, getString(R.string.connected))); 2563 mLocalRtpPortLabel.setText(getString(R.string.rtp_reception_port_label, 2564 String.valueOf(mAudioRtp.getLocalPort()))); 2565 mLocalRtcpPortLabel.setText(getString(R.string.rtcp_reception_port_label, 2566 String.valueOf(mAudioRtcp.getLocalPort()))); 2567 mRemoteRtpPortLabel.setText(getString(R.string.rtp_reception_port_label, 2568 String.valueOf(mRemoteDeviceInfo.getAudioRtpPort()))); 2569 mRemoteRtcpPortLabel.setText(getString(R.string.rtcp_reception_port_label, 2570 String.valueOf(mRemoteDeviceInfo.getAudioRtpPort() + 1))); 2571 2572 styleButtonDisabled(mOpenSessionButton); 2573 styleLayoutChildrenEnabled(mActiveCallToolBar); 2574 } 2575 styleButtonDisabled(Button button)2576 private void styleButtonDisabled(Button button) { 2577 button.setEnabled(false); 2578 button.setClickable(false); 2579 button.setAlpha(DISABLED_ALPHA); 2580 } 2581 styleButtonEnabled(Button button)2582 private void styleButtonEnabled(Button button) { 2583 button.setEnabled(true); 2584 button.setClickable(true); 2585 button.setAlpha(ENABLED_ALPHA); 2586 } 2587 styleLayoutChildrenDisabled(LinearLayout linearLayout)2588 private void styleLayoutChildrenDisabled(LinearLayout linearLayout) { 2589 linearLayout.setAlpha(DISABLED_ALPHA); 2590 for (int x = 0; x < linearLayout.getChildCount(); x++) { 2591 linearLayout.getChildAt(x).setAlpha(DISABLED_ALPHA); 2592 linearLayout.getChildAt(x).setEnabled(false); 2593 linearLayout.getChildAt(x).setClickable(false); 2594 } 2595 } 2596 styleLayoutChildrenEnabled(LinearLayout linearLayout)2597 private void styleLayoutChildrenEnabled(LinearLayout linearLayout) { 2598 linearLayout.setAlpha(ENABLED_ALPHA); 2599 for (int x = 0; x < linearLayout.getChildCount(); x++) { 2600 linearLayout.getChildAt(x).setAlpha(ENABLED_ALPHA); 2601 linearLayout.getChildAt(x).setEnabled(true); 2602 linearLayout.getChildAt(x).setClickable(true); 2603 } 2604 } 2605 updateUiViews()2606 private void updateUiViews() { 2607 mLocalHandshakePortLabel = findViewById(R.id.localHandshakePortLabel); 2608 mLocalRtpPortLabel = findViewById(R.id.localRtpPortLabel); 2609 mLocalRtcpPortLabel = findViewById(R.id.localRtcpPortLabel); 2610 mRemoteHandshakePortLabel = findViewById(R.id.remoteHandshakePortLabel); 2611 mRemoteRtpPortLabel = findViewById(R.id.remoteRtpPortLabel); 2612 mRemoteRtcpPortLabel = findViewById(R.id.remoteRtcpPortLabel); 2613 mAllowCallsButton = findViewById(R.id.allowCallsButton); 2614 mConnectButton = findViewById(R.id.connectButton); 2615 mOpenSessionButton = findViewById(R.id.openSessionButton); 2616 mActiveCallToolBar = findViewById(R.id.activeCallActionsLayout); 2617 mLoopbackSwitch = findViewById(R.id.loopbackModeSwitch); 2618 } 2619 styleLoopbackSwitchEnabled()2620 private void styleLoopbackSwitchEnabled() { 2621 mLoopbackSwitch.setChecked(mLoopbackModeEnabled); 2622 mLoopbackSwitch.setEnabled(true); 2623 mLoopbackSwitch.setClickable(true); 2624 mLoopbackSwitch.setAlpha(ENABLED_ALPHA); 2625 } 2626 styleLoopbackSwitchDisabled()2627 private void styleLoopbackSwitchDisabled() { 2628 mLoopbackSwitch.setChecked(mLoopbackModeEnabled); 2629 mLoopbackSwitch.setEnabled(false); 2630 mLoopbackSwitch.setClickable(false); 2631 mLoopbackSwitch.setAlpha(DISABLED_ALPHA); 2632 } 2633 updateVideoUi(boolean enable)2634 private void updateVideoUi(boolean enable) { 2635 createTextureView(); 2636 TextView previewLabel = findViewById(R.id.previewLabel); 2637 TextView displayLabel = findViewById(R.id.displayLabel); 2638 FrameLayout previewFrame = findViewById(R.id.previewFrame); 2639 FrameLayout displayFrames = findViewById(R.id.displayFrames); 2640 2641 if (enable) { 2642 previewLabel.setVisibility(View.VISIBLE); 2643 displayLabel.setVisibility(View.VISIBLE); 2644 previewFrame.setVisibility(View.VISIBLE); 2645 mTexturePreview.setVisibility(View.VISIBLE); 2646 displayFrames.setVisibility(View.VISIBLE); 2647 mTextureDisplay.setVisibility(View.VISIBLE); 2648 } else { 2649 previewLabel.setVisibility(View.GONE); 2650 displayLabel.setVisibility(View.GONE); 2651 previewFrame.setVisibility(View.GONE); 2652 mTexturePreview.setVisibility(View.GONE); 2653 displayFrames.setVisibility(View.GONE); 2654 mTextureDisplay.setVisibility(View.GONE); 2655 } 2656 2657 ViewGroup viewGroup = findViewById(R.id.rootLayout); 2658 viewGroup.invalidate(); 2659 } 2660 updateTextUi(boolean enable)2661 private void updateTextUi(boolean enable) { 2662 TextView sendingTextTitle = findViewById(R.id.sendingTextTitle); 2663 EditText textEditTextSending = findViewById(R.id.textEditTextSending); 2664 TextView receivedTextTitle = findViewById(R.id.receivedTextTitle); 2665 TextView receivedText = findViewById(R.id.receivedText); 2666 TextView buttonTextSend = findViewById(R.id.buttonTextSend); 2667 2668 if (enable) { 2669 sendingTextTitle.setVisibility(View.VISIBLE); 2670 textEditTextSending.setVisibility(View.VISIBLE); 2671 receivedTextTitle.setVisibility(View.VISIBLE); 2672 receivedText.setVisibility(View.VISIBLE); 2673 buttonTextSend.setVisibility(View.VISIBLE); 2674 } else { 2675 sendingTextTitle.setVisibility(View.GONE); 2676 textEditTextSending.setVisibility(View.GONE); 2677 receivedTextTitle.setVisibility(View.GONE); 2678 receivedText.setVisibility(View.GONE); 2679 buttonTextSend.setVisibility(View.GONE); 2680 } 2681 ViewGroup viewGroup = findViewById(R.id.rootLayout); 2682 viewGroup.invalidate(); 2683 } 2684 2685 private IImsMediaCallback.Stub mMediaUtilCallback = new IImsMediaCallback.Stub() { 2686 @Override 2687 public void onVideoSpropResponse(String[] spropList) { 2688 Log.d(TAG, "[GetSprop]onVideoSprop"); 2689 for (String sprop : spropList) { 2690 Log.d(TAG, "[GetSprop]SPROP : " + sprop); 2691 } 2692 } 2693 }; 2694 generateSprop(View btn)2695 public void generateSprop(View btn) { 2696 if (!mIsMediaManagerReady) { 2697 Log.d(TAG, "[GetSprop]Media Manager not ready"); 2698 return; 2699 } 2700 2701 VideoConfig[] videoConfigList = new VideoConfig[1]; 2702 VideoConfig.Builder vcbuilder = new VideoConfig.Builder(); 2703 vcbuilder.setBitrate(384) 2704 .setCodecType(VideoConfig.VIDEO_CODEC_AVC) 2705 .setCodecProfile(VideoConfig.AVC_PROFILE_CONSTRAINED_BASELINE) 2706 .setCodecLevel(VideoConfig.AVC_LEVEL_12) 2707 .setFramerate(10) 2708 .setIntraFrameIntervalSec(1) 2709 .setPacketizationMode(VideoConfig.MODE_NON_INTERLEAVED) 2710 .setResolutionWidth(RESOLUTION_WIDTH) 2711 .setResolutionHeight(RESOLUTION_HEIGHT) 2712 .setVideoMode(VideoConfig.VIDEO_MODE_RECORDING) 2713 .setMaxMtuBytes(1500); 2714 2715 videoConfigList[0] = vcbuilder.build(); 2716 2717 try { 2718 mImsMediaManager.generateVideoSprop(videoConfigList, mMediaUtilCallback.asBinder()); 2719 } catch (Exception e) { 2720 Log.d(TAG, e.toString()); 2721 } 2722 } 2723 sendHeaderExtension(View btn)2724 public void sendHeaderExtension(View btn) { 2725 if (mAudioSession != null) { 2726 List<RtpHeaderExtension> extensions = new ArrayList<>(); 2727 byte[] testBytes1 = new byte[1]; 2728 byte[] testBytes2 = new byte[1]; 2729 testBytes1[0] = 5; 2730 testBytes2[0] = 10; 2731 RtpHeaderExtension extension1 = new RtpHeaderExtension(1, testBytes1); 2732 RtpHeaderExtension extension2 = new RtpHeaderExtension(2, testBytes2); 2733 extensions.add(extension1); 2734 extensions.add(extension2); 2735 Log.d(TAG, "[sendHeaderExtension] extension size=" + extensions.size()); 2736 mAudioSession.sendHeaderExtension(extensions); 2737 } 2738 } 2739 } 2740