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