1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.incallui.incall.impl; 18 19 import android.graphics.drawable.AnimationDrawable; 20 import android.support.annotation.CallSuper; 21 import android.support.annotation.DrawableRes; 22 import android.support.annotation.NonNull; 23 import android.support.annotation.StringRes; 24 import android.telecom.CallAudioState; 25 import android.text.TextUtils; 26 import android.view.View; 27 import android.view.View.OnClickListener; 28 import com.android.dialer.common.Assert; 29 import com.android.incallui.incall.impl.CheckableLabeledButton.OnCheckedChangeListener; 30 import com.android.incallui.incall.protocol.InCallButtonIds; 31 import com.android.incallui.incall.protocol.InCallButtonUiDelegate; 32 import com.android.incallui.incall.protocol.InCallScreenDelegate; 33 import com.android.incallui.speakerbuttonlogic.SpeakerButtonInfo; 34 35 /** Manages a single button. */ 36 interface ButtonController { 37 isEnabled()38 boolean isEnabled(); 39 setEnabled(boolean isEnabled)40 void setEnabled(boolean isEnabled); 41 isAllowed()42 boolean isAllowed(); 43 setAllowed(boolean isAllowed)44 void setAllowed(boolean isAllowed); 45 setChecked(boolean isChecked)46 void setChecked(boolean isChecked); 47 48 @InCallButtonIds getInCallButtonId()49 int getInCallButtonId(); 50 setButton(CheckableLabeledButton button)51 void setButton(CheckableLabeledButton button); 52 53 final class Controllers { 54 resetButton(CheckableLabeledButton button)55 private static void resetButton(CheckableLabeledButton button) { 56 if (button != null) { 57 button.setOnCheckedChangeListener(null); 58 button.setOnClickListener(null); 59 } 60 } 61 } 62 63 abstract class CheckableButtonController implements ButtonController, OnCheckedChangeListener { 64 65 @NonNull protected final InCallButtonUiDelegate delegate; 66 @InCallButtonIds protected final int buttonId; 67 @StringRes protected final int checkedDescription; 68 @StringRes protected final int uncheckedDescription; 69 protected boolean isEnabled; 70 protected boolean isAllowed; 71 protected boolean isChecked; 72 protected CheckableLabeledButton button; 73 CheckableButtonController( @onNull InCallButtonUiDelegate delegate, @InCallButtonIds int buttonId, @StringRes int checkedContentDescription, @StringRes int uncheckedContentDescription)74 protected CheckableButtonController( 75 @NonNull InCallButtonUiDelegate delegate, 76 @InCallButtonIds int buttonId, 77 @StringRes int checkedContentDescription, 78 @StringRes int uncheckedContentDescription) { 79 Assert.isNotNull(delegate); 80 this.delegate = delegate; 81 this.buttonId = buttonId; 82 this.checkedDescription = checkedContentDescription; 83 this.uncheckedDescription = uncheckedContentDescription; 84 } 85 86 @Override isEnabled()87 public boolean isEnabled() { 88 return isEnabled; 89 } 90 91 @Override setEnabled(boolean isEnabled)92 public void setEnabled(boolean isEnabled) { 93 this.isEnabled = isEnabled; 94 if (button != null) { 95 button.setEnabled(isEnabled); 96 } 97 } 98 99 @Override isAllowed()100 public boolean isAllowed() { 101 return isAllowed; 102 } 103 104 @Override setAllowed(boolean isAllowed)105 public void setAllowed(boolean isAllowed) { 106 this.isAllowed = isAllowed; 107 if (button != null) { 108 button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE); 109 } 110 } 111 112 @Override setChecked(boolean isChecked)113 public void setChecked(boolean isChecked) { 114 this.isChecked = isChecked; 115 if (button != null) { 116 button.setChecked(isChecked); 117 } 118 } 119 120 @Override 121 @InCallButtonIds getInCallButtonId()122 public int getInCallButtonId() { 123 return buttonId; 124 } 125 126 @Override 127 @CallSuper setButton(CheckableLabeledButton button)128 public void setButton(CheckableLabeledButton button) { 129 Controllers.resetButton(this.button); 130 131 this.button = button; 132 if (button != null) { 133 button.setEnabled(isEnabled); 134 button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE); 135 button.setChecked(isChecked); 136 button.setOnClickListener(null); 137 button.setOnCheckedChangeListener(this); 138 button.setContentDescription( 139 button.getContext().getText(isChecked ? checkedDescription : uncheckedDescription)); 140 button.setShouldShowMoreIndicator(false); 141 } 142 } 143 144 @Override onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked)145 public void onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked) { 146 button.setContentDescription( 147 button.getContext().getText(isChecked ? checkedDescription : uncheckedDescription)); 148 doCheckedChanged(isChecked); 149 } 150 doCheckedChanged(boolean isChecked)151 protected abstract void doCheckedChanged(boolean isChecked); 152 } 153 154 abstract class SimpleCheckableButtonController extends CheckableButtonController { 155 156 @StringRes private final int label; 157 @DrawableRes private final int icon; 158 SimpleCheckableButtonController( @onNull InCallButtonUiDelegate delegate, @InCallButtonIds int buttonId, @StringRes int checkedContentDescription, @StringRes int uncheckedContentDescription, @StringRes int label, @DrawableRes int icon)159 protected SimpleCheckableButtonController( 160 @NonNull InCallButtonUiDelegate delegate, 161 @InCallButtonIds int buttonId, 162 @StringRes int checkedContentDescription, 163 @StringRes int uncheckedContentDescription, 164 @StringRes int label, 165 @DrawableRes int icon) { 166 super( 167 delegate, 168 buttonId, 169 checkedContentDescription == 0 ? label : checkedContentDescription, 170 uncheckedContentDescription == 0 ? label : uncheckedContentDescription); 171 this.label = label; 172 this.icon = icon; 173 } 174 175 @Override 176 @CallSuper setButton(CheckableLabeledButton button)177 public void setButton(CheckableLabeledButton button) { 178 super.setButton(button); 179 if (button != null) { 180 button.setLabelText(label); 181 button.setIconDrawable(icon); 182 } 183 } 184 } 185 186 abstract class NonCheckableButtonController implements ButtonController, OnClickListener { 187 188 protected final InCallButtonUiDelegate delegate; 189 @InCallButtonIds protected final int buttonId; 190 @StringRes protected final int contentDescription; 191 protected boolean isEnabled; 192 protected boolean isAllowed; 193 protected CheckableLabeledButton button; 194 NonCheckableButtonController( InCallButtonUiDelegate delegate, @InCallButtonIds int buttonId, @StringRes int contentDescription)195 protected NonCheckableButtonController( 196 InCallButtonUiDelegate delegate, 197 @InCallButtonIds int buttonId, 198 @StringRes int contentDescription) { 199 this.delegate = delegate; 200 this.buttonId = buttonId; 201 this.contentDescription = contentDescription; 202 } 203 204 @Override isEnabled()205 public boolean isEnabled() { 206 return isEnabled; 207 } 208 209 @Override setEnabled(boolean isEnabled)210 public void setEnabled(boolean isEnabled) { 211 this.isEnabled = isEnabled; 212 if (button != null) { 213 button.setEnabled(isEnabled); 214 } 215 } 216 217 @Override isAllowed()218 public boolean isAllowed() { 219 return isAllowed; 220 } 221 222 @Override setAllowed(boolean isAllowed)223 public void setAllowed(boolean isAllowed) { 224 this.isAllowed = isAllowed; 225 if (button != null) { 226 button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE); 227 } 228 } 229 230 @Override setChecked(boolean isChecked)231 public void setChecked(boolean isChecked) { 232 Assert.fail(); 233 } 234 235 @Override 236 @InCallButtonIds getInCallButtonId()237 public int getInCallButtonId() { 238 return buttonId; 239 } 240 241 @Override 242 @CallSuper setButton(CheckableLabeledButton button)243 public void setButton(CheckableLabeledButton button) { 244 Controllers.resetButton(this.button); 245 246 this.button = button; 247 if (button != null) { 248 button.setEnabled(isEnabled); 249 button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE); 250 button.setChecked(false); 251 button.setOnCheckedChangeListener(null); 252 button.setOnClickListener(this); 253 button.setContentDescription(button.getContext().getText(contentDescription)); 254 button.setShouldShowMoreIndicator(false); 255 } 256 } 257 } 258 259 abstract class SimpleNonCheckableButtonController extends NonCheckableButtonController { 260 261 @StringRes private final int label; 262 @DrawableRes private final int icon; 263 SimpleNonCheckableButtonController( InCallButtonUiDelegate delegate, @InCallButtonIds int buttonId, @StringRes int contentDescription, @StringRes int label, @DrawableRes int icon)264 protected SimpleNonCheckableButtonController( 265 InCallButtonUiDelegate delegate, 266 @InCallButtonIds int buttonId, 267 @StringRes int contentDescription, 268 @StringRes int label, 269 @DrawableRes int icon) { 270 super(delegate, buttonId, contentDescription == 0 ? label : contentDescription); 271 this.label = label; 272 this.icon = icon; 273 } 274 275 @Override 276 @CallSuper setButton(CheckableLabeledButton button)277 public void setButton(CheckableLabeledButton button) { 278 super.setButton(button); 279 if (button != null) { 280 button.setLabelText(label); 281 button.setIconDrawable(icon); 282 } 283 } 284 } 285 286 class MuteButtonController extends SimpleCheckableButtonController { 287 MuteButtonController(InCallButtonUiDelegate delegate)288 public MuteButtonController(InCallButtonUiDelegate delegate) { 289 super( 290 delegate, 291 InCallButtonIds.BUTTON_MUTE, 292 R.string.incall_content_description_muted, 293 R.string.incall_content_description_unmuted, 294 R.string.incall_label_mute, 295 R.drawable.quantum_ic_mic_off_vd_theme_24); 296 } 297 298 @Override doCheckedChanged(boolean isChecked)299 public void doCheckedChanged(boolean isChecked) { 300 delegate.muteClicked(isChecked, true /* clickedByUser */); 301 } 302 } 303 304 class SpeakerButtonController 305 implements ButtonController, OnCheckedChangeListener, OnClickListener { 306 307 @NonNull private final InCallButtonUiDelegate delegate; 308 private boolean isEnabled; 309 private boolean isAllowed; 310 private boolean isChecked; 311 private CheckableLabeledButton button; 312 313 @StringRes private int label = R.string.incall_label_speaker; 314 @DrawableRes private int icon = R.drawable.quantum_ic_volume_up_vd_theme_24; 315 private boolean nonBluetoothMode; 316 private CharSequence contentDescription; 317 private CharSequence isOnContentDescription; 318 private CharSequence isOffContentDescription; 319 SpeakerButtonController(@onNull InCallButtonUiDelegate delegate)320 public SpeakerButtonController(@NonNull InCallButtonUiDelegate delegate) { 321 this.delegate = delegate; 322 } 323 324 @Override isEnabled()325 public boolean isEnabled() { 326 return isEnabled; 327 } 328 329 @Override setEnabled(boolean isEnabled)330 public void setEnabled(boolean isEnabled) { 331 this.isEnabled = isEnabled; 332 if (button != null) { 333 button.setEnabled(isEnabled && isAllowed); 334 } 335 } 336 337 @Override isAllowed()338 public boolean isAllowed() { 339 return isAllowed; 340 } 341 342 @Override setAllowed(boolean isAllowed)343 public void setAllowed(boolean isAllowed) { 344 this.isAllowed = isAllowed; 345 if (button != null) { 346 button.setEnabled(isEnabled && isAllowed); 347 } 348 } 349 350 @Override setChecked(boolean isChecked)351 public void setChecked(boolean isChecked) { 352 this.isChecked = isChecked; 353 if (button != null) { 354 button.setChecked(isChecked); 355 } 356 } 357 358 @Override getInCallButtonId()359 public int getInCallButtonId() { 360 return InCallButtonIds.BUTTON_AUDIO; 361 } 362 363 @Override setButton(CheckableLabeledButton button)364 public void setButton(CheckableLabeledButton button) { 365 this.button = button; 366 if (button != null) { 367 button.setEnabled(isEnabled && isAllowed); 368 button.setVisibility(View.VISIBLE); 369 button.setChecked(isChecked); 370 button.setOnClickListener(nonBluetoothMode ? null : this); 371 button.setOnCheckedChangeListener(nonBluetoothMode ? this : null); 372 button.setLabelText(label); 373 button.setIconDrawable(icon); 374 button.setContentDescription( 375 (nonBluetoothMode && !isChecked) ? isOffContentDescription : isOnContentDescription); 376 button.setShouldShowMoreIndicator(!nonBluetoothMode); 377 } 378 } 379 setAudioState(CallAudioState audioState)380 public void setAudioState(CallAudioState audioState) { 381 SpeakerButtonInfo info = new SpeakerButtonInfo(audioState); 382 383 nonBluetoothMode = info.nonBluetoothMode; 384 isChecked = info.isChecked; 385 label = info.label; 386 icon = info.icon; 387 @StringRes int contentDescriptionResId = info.contentDescription; 388 389 contentDescription = delegate.getContext().getText(contentDescriptionResId); 390 isOnContentDescription = 391 TextUtils.concat( 392 contentDescription, 393 delegate.getContext().getText(R.string.incall_talkback_speaker_on)); 394 isOffContentDescription = 395 TextUtils.concat( 396 contentDescription, 397 delegate.getContext().getText(R.string.incall_talkback_speaker_off)); 398 setButton(button); 399 } 400 401 @Override onClick(View v)402 public void onClick(View v) { 403 delegate.showAudioRouteSelector(); 404 } 405 406 @Override onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked)407 public void onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked) { 408 checkableLabeledButton.setContentDescription( 409 isChecked ? isOnContentDescription : isOffContentDescription); 410 delegate.toggleSpeakerphone(); 411 } 412 } 413 414 class DialpadButtonController extends SimpleCheckableButtonController { 415 DialpadButtonController(@onNull InCallButtonUiDelegate delegate)416 public DialpadButtonController(@NonNull InCallButtonUiDelegate delegate) { 417 super( 418 delegate, 419 InCallButtonIds.BUTTON_DIALPAD, 420 0, 421 0, 422 R.string.incall_label_dialpad, 423 R.drawable.quantum_ic_dialpad_vd_theme_24); 424 } 425 426 @Override doCheckedChanged(boolean isChecked)427 public void doCheckedChanged(boolean isChecked) { 428 delegate.showDialpadClicked(isChecked); 429 } 430 } 431 432 class HoldButtonController extends SimpleCheckableButtonController { 433 HoldButtonController(@onNull InCallButtonUiDelegate delegate)434 public HoldButtonController(@NonNull InCallButtonUiDelegate delegate) { 435 super( 436 delegate, 437 InCallButtonIds.BUTTON_HOLD, 438 R.string.incall_content_description_unhold, 439 R.string.incall_content_description_hold, 440 R.string.incall_label_hold, 441 R.drawable.quantum_ic_pause_vd_theme_24); 442 } 443 444 @Override doCheckedChanged(boolean isChecked)445 public void doCheckedChanged(boolean isChecked) { 446 delegate.holdClicked(isChecked); 447 } 448 } 449 450 class AddCallButtonController extends SimpleNonCheckableButtonController { 451 AddCallButtonController(@onNull InCallButtonUiDelegate delegate)452 public AddCallButtonController(@NonNull InCallButtonUiDelegate delegate) { 453 super( 454 delegate, 455 InCallButtonIds.BUTTON_ADD_CALL, 456 0, 457 R.string.incall_label_add_call, 458 R.drawable.ic_addcall_white); 459 Assert.isNotNull(delegate); 460 } 461 462 @Override onClick(View view)463 public void onClick(View view) { 464 delegate.addCallClicked(); 465 } 466 } 467 468 class SwapButtonController extends SimpleNonCheckableButtonController { 469 SwapButtonController(@onNull InCallButtonUiDelegate delegate)470 public SwapButtonController(@NonNull InCallButtonUiDelegate delegate) { 471 super( 472 delegate, 473 InCallButtonIds.BUTTON_SWAP, 474 R.string.incall_content_description_swap_calls, 475 R.string.incall_label_swap, 476 R.drawable.quantum_ic_swap_calls_vd_theme_24); 477 Assert.isNotNull(delegate); 478 } 479 480 @Override onClick(View view)481 public void onClick(View view) { 482 delegate.swapClicked(); 483 } 484 } 485 486 class MergeButtonController extends SimpleNonCheckableButtonController { 487 MergeButtonController(@onNull InCallButtonUiDelegate delegate)488 public MergeButtonController(@NonNull InCallButtonUiDelegate delegate) { 489 super( 490 delegate, 491 InCallButtonIds.BUTTON_MERGE, 492 R.string.incall_content_description_merge_calls, 493 R.string.incall_label_merge, 494 R.drawable.quantum_ic_call_merge_vd_theme_24); 495 Assert.isNotNull(delegate); 496 } 497 498 @Override onClick(View view)499 public void onClick(View view) { 500 delegate.mergeClicked(); 501 } 502 } 503 504 class UpgradeToVideoButtonController extends SimpleNonCheckableButtonController { 505 UpgradeToVideoButtonController(@onNull InCallButtonUiDelegate delegate)506 public UpgradeToVideoButtonController(@NonNull InCallButtonUiDelegate delegate) { 507 super( 508 delegate, 509 InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, 510 0, 511 R.string.incall_label_videocall, 512 R.drawable.quantum_ic_videocam_vd_theme_24); 513 Assert.isNotNull(delegate); 514 } 515 516 @Override onClick(View view)517 public void onClick(View view) { 518 delegate.changeToVideoClicked(); 519 } 520 } 521 522 class UpgradeToRttButtonController extends SimpleNonCheckableButtonController { 523 UpgradeToRttButtonController(@onNull InCallButtonUiDelegate delegate)524 public UpgradeToRttButtonController(@NonNull InCallButtonUiDelegate delegate) { 525 super( 526 delegate, 527 InCallButtonIds.BUTTON_UPGRADE_TO_RTT, 528 0, 529 R.string.incall_label_rttcall, 530 R.drawable.quantum_ic_rtt_vd_theme_24); 531 Assert.isNotNull(delegate); 532 } 533 534 @Override onClick(View view)535 public void onClick(View view) { 536 delegate.changeToRttClicked(); 537 } 538 } 539 540 class ManageConferenceButtonController extends SimpleNonCheckableButtonController { 541 542 private final InCallScreenDelegate inCallScreenDelegate; 543 ManageConferenceButtonController(@onNull InCallScreenDelegate inCallScreenDelegate)544 public ManageConferenceButtonController(@NonNull InCallScreenDelegate inCallScreenDelegate) { 545 super( 546 null, 547 InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE, 548 R.string.a11y_description_incall_label_manage_content, 549 R.string.incall_label_manage, 550 R.drawable.quantum_ic_group_vd_theme_24); 551 Assert.isNotNull(inCallScreenDelegate); 552 this.inCallScreenDelegate = inCallScreenDelegate; 553 } 554 555 @Override onClick(View view)556 public void onClick(View view) { 557 inCallScreenDelegate.onManageConferenceClicked(); 558 } 559 } 560 561 class SwitchToSecondaryButtonController extends SimpleNonCheckableButtonController { 562 563 private final InCallScreenDelegate inCallScreenDelegate; 564 SwitchToSecondaryButtonController(InCallScreenDelegate inCallScreenDelegate)565 public SwitchToSecondaryButtonController(InCallScreenDelegate inCallScreenDelegate) { 566 super( 567 null, 568 InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY, 569 R.string.incall_content_description_swap_calls, 570 R.string.incall_label_swap, 571 R.drawable.quantum_ic_swap_calls_vd_theme_24); 572 Assert.isNotNull(inCallScreenDelegate); 573 this.inCallScreenDelegate = inCallScreenDelegate; 574 } 575 576 @Override onClick(View view)577 public void onClick(View view) { 578 inCallScreenDelegate.onSecondaryInfoClicked(); 579 } 580 } 581 582 class SwapSimButtonController extends SimpleNonCheckableButtonController { 583 SwapSimButtonController(InCallButtonUiDelegate delegate)584 public SwapSimButtonController(InCallButtonUiDelegate delegate) { 585 super( 586 delegate, 587 InCallButtonIds.BUTTON_SWAP_SIM, 588 R.string.incall_content_description_swap_sim, 589 R.string.incall_label_swap_sim, 590 R.drawable.ic_sim_change_white); 591 } 592 593 @Override onClick(View view)594 public void onClick(View view) { 595 AnimationDrawable drawable = (AnimationDrawable) button.getIconDrawable(); 596 drawable.stop(); // animation is one shot, stop it so it can be started again. 597 drawable.start(); 598 delegate.swapSimClicked(); 599 } 600 } 601 } 602