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