From 604e481f53eccaefc6763498b525b6e88d9e08e1 Mon Sep 17 00:00:00 2001 From: wangqi Date: Thu, 1 Mar 2018 13:58:01 -0800 Subject: Implement overflow menu for RTT call. Bug: 67596257 Test: manual PiperOrigin-RevId: 187529383 Change-Id: I6ef6593a79ef0c4fb407284eede966a1eaabcd1e --- .../drawable/quantum_ic_add_call_vd_theme_24.xml | 25 +++ .../drawable/quantum_ic_dialpad_vd_theme_24.xml | 25 +++ .../android/incallui/rtt/impl/RttChatFragment.java | 33 ++- .../incallui/rtt/impl/RttCheckableButton.java | 222 +++++++++++++++++++++ .../android/incallui/rtt/impl/RttOverflowMenu.java | 78 ++++++++ .../impl/res/color/rtt_checkable_button_color.xml | 21 ++ .../impl/res/drawable/overflow_menu_background.xml | 21 ++ .../incallui/rtt/impl/res/layout/overflow_menu.xml | 49 +++++ .../android/incallui/rtt/impl/res/values/attrs.xml | 23 +++ .../incallui/rtt/impl/res/values/colors.xml | 2 + .../incallui/rtt/impl/res/values/dimens.xml | 1 + .../incallui/rtt/impl/res/values/styles.xml | 15 ++ 12 files changed, 512 insertions(+), 3 deletions(-) create mode 100644 assets/quantum/res/drawable/quantum_ic_add_call_vd_theme_24.xml create mode 100644 assets/quantum/res/drawable/quantum_ic_dialpad_vd_theme_24.xml create mode 100644 java/com/android/incallui/rtt/impl/RttCheckableButton.java create mode 100644 java/com/android/incallui/rtt/impl/RttOverflowMenu.java create mode 100644 java/com/android/incallui/rtt/impl/res/color/rtt_checkable_button_color.xml create mode 100644 java/com/android/incallui/rtt/impl/res/drawable/overflow_menu_background.xml create mode 100644 java/com/android/incallui/rtt/impl/res/layout/overflow_menu.xml create mode 100644 java/com/android/incallui/rtt/impl/res/values/attrs.xml diff --git a/assets/quantum/res/drawable/quantum_ic_add_call_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_add_call_vd_theme_24.xml new file mode 100644 index 000000000..b7d3a09b4 --- /dev/null +++ b/assets/quantum/res/drawable/quantum_ic_add_call_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/assets/quantum/res/drawable/quantum_ic_dialpad_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_dialpad_vd_theme_24.xml new file mode 100644 index 000000000..4e340edb2 --- /dev/null +++ b/assets/quantum/res/drawable/quantum_ic_dialpad_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java index deb205ce6..396b89e75 100644 --- a/java/com/android/incallui/rtt/impl/RttChatFragment.java +++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java @@ -29,6 +29,7 @@ import android.telecom.CallAudioState; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; +import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -45,6 +46,8 @@ import android.widget.TextView.OnEditorActionListener; import com.android.dialer.common.Assert; import com.android.dialer.common.FragmentUtils; import com.android.dialer.common.LogUtil; +import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment; +import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter; import com.android.incallui.call.DialerCall.State; import com.android.incallui.incall.protocol.InCallButtonUi; import com.android.incallui.incall.protocol.InCallButtonUiDelegate; @@ -68,7 +71,8 @@ public class RttChatFragment extends Fragment MessageListener, RttCallScreen, InCallScreen, - InCallButtonUi { + InCallButtonUi, + AudioRouteSelectorPresenter { private static final String ARG_CALL_ID = "call_id"; @@ -94,6 +98,7 @@ public class RttChatFragment extends Fragment private TextView nameTextView; private Chronometer chronometer; private boolean isTimerStarted; + private RttOverflowMenu overflowMenu; /** * Create a new instance of RttChatFragment. @@ -173,6 +178,10 @@ public class RttChatFragment extends Fragment inCallButtonUiDelegate.onEndCallClicked(); }); + overflowMenu = new RttOverflowMenu(getContext(), inCallButtonUiDelegate); + view.findViewById(R.id.rtt_overflow_button) + .setOnClickListener(v -> overflowMenu.showAtLocation(v, Gravity.TOP | Gravity.RIGHT, 0, 0)); + nameTextView = view.findViewById(R.id.rtt_name_or_number); chronometer = view.findViewById(R.id.rtt_timer); return view; @@ -240,6 +249,9 @@ public class RttChatFragment extends Fragment public void onStop() { LogUtil.enterBlock("RttChatFragment.onStop"); super.onStop(); + if (overflowMenu.isShowing()) { + overflowMenu.dismiss(); + } onRttScreenStop(); } @@ -360,7 +372,11 @@ public class RttChatFragment extends Fragment public void setVideoPaused(boolean isPaused) {} @Override - public void setAudioState(CallAudioState audioState) {} + public void setAudioState(CallAudioState audioState) { + LogUtil.i("RttChatFragment.setAudioState", "audioState: " + audioState); + overflowMenu.setMuteButtonChecked(audioState.isMuted()); + overflowMenu.setAudioState(audioState); + } @Override public void updateButtonStates() {} @@ -374,5 +390,16 @@ public class RttChatFragment extends Fragment } @Override - public void showAudioRouteSelector() {} + public void showAudioRouteSelector() { + AudioRouteSelectorDialogFragment.newInstance(inCallButtonUiDelegate.getCurrentAudioState()) + .show(getChildFragmentManager(), null); + } + + @Override + public void onAudioRouteSelected(int audioRoute) { + inCallButtonUiDelegate.setAudioRoute(audioRoute); + } + + @Override + public void onAudioRouteSelectorDismiss() {} } diff --git a/java/com/android/incallui/rtt/impl/RttCheckableButton.java b/java/com/android/incallui/rtt/impl/RttCheckableButton.java new file mode 100644 index 000000000..ba15ca985 --- /dev/null +++ b/java/com/android/incallui/rtt/impl/RttCheckableButton.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.incallui.rtt.impl; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.SoundEffectConstants; +import android.widget.Checkable; +import android.widget.TextView; + +/** Image button that maintains a checked state. */ +public class RttCheckableButton extends TextView implements Checkable { + + private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked}; + + /** Callback interface to notify when the button's checked state has changed */ + public interface OnCheckedChangeListener { + + void onCheckedChanged(RttCheckableButton button, boolean isChecked); + } + + private boolean broadcasting; + private boolean isChecked; + private OnCheckedChangeListener onCheckedChangeListener; + private CharSequence contentDescriptionChecked; + private CharSequence contentDescriptionUnchecked; + + public RttCheckableButton(Context context) { + this(context, null); + } + + public RttCheckableButton(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RttCheckableButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RttCheckableButton); + setChecked(typedArray.getBoolean(R.styleable.RttCheckableButton_android_checked, false)); + contentDescriptionChecked = + typedArray.getText(R.styleable.RttCheckableButton_contentDescriptionChecked); + contentDescriptionUnchecked = + typedArray.getText(R.styleable.RttCheckableButton_contentDescriptionUnchecked); + typedArray.recycle(); + + updateContentDescription(); + setClickable(true); + setFocusable(true); + } + + @Override + public void setChecked(boolean checked) { + performSetChecked(checked); + } + + /** + * Called when the state of the button should be updated, this should not be the result of user + * interaction. + * + * @param checked {@code true} if the button should be in the checked state, {@code false} + * otherwise. + */ + private void performSetChecked(boolean checked) { + if (isChecked() == checked) { + return; + } + isChecked = checked; + CharSequence contentDescription = updateContentDescription(); + announceForAccessibility(contentDescription); + refreshDrawableState(); + } + + private CharSequence updateContentDescription() { + CharSequence contentDescription = + isChecked ? contentDescriptionChecked : contentDescriptionUnchecked; + setContentDescription(contentDescription); + return contentDescription; + } + + /** + * Called when the user interacts with a button. This should not result in the button updating + * state, rather the request should be propagated to the associated listener. + * + * @param checked {@code true} if the button should be in the checked state, {@code false} + * otherwise. + */ + private void userRequestedSetChecked(boolean checked) { + if (isChecked() == checked) { + return; + } + if (broadcasting) { + return; + } + broadcasting = true; + if (onCheckedChangeListener != null) { + onCheckedChangeListener.onCheckedChanged(this, checked); + } + broadcasting = false; + } + + @Override + public boolean isChecked() { + return isChecked; + } + + @Override + public void toggle() { + userRequestedSetChecked(!isChecked()); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + if (isChecked()) { + mergeDrawableStates(drawableState, CHECKED_STATE_SET); + } + return drawableState; + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + invalidate(); + } + + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + this.onCheckedChangeListener = listener; + } + + @Override + public boolean performClick() { + if (!isCheckable()) { + return super.performClick(); + } + + toggle(); + final boolean handled = super.performClick(); + if (!handled) { + // View only makes a sound effect if the onClickListener was + // called, so we'll need to make one here instead. + playSoundEffect(SoundEffectConstants.CLICK); + } + return handled; + } + + private boolean isCheckable() { + return onCheckedChangeListener != null; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState savedState = (SavedState) state; + super.onRestoreInstanceState(savedState.getSuperState()); + performSetChecked(savedState.isChecked); + requestLayout(); + } + + @Override + public Parcelable onSaveInstanceState() { + return new SavedState(isChecked(), super.onSaveInstanceState()); + } + + private static class SavedState extends BaseSavedState { + + public final boolean isChecked; + + private SavedState(boolean isChecked, Parcelable superState) { + super(superState); + this.isChecked = isChecked; + } + + protected SavedState(Parcel in) { + super(in); + isChecked = in.readByte() != 0; + } + + public static final Creator CREATOR = + new Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte((byte) (isChecked ? 1 : 0)); + } + } +} diff --git a/java/com/android/incallui/rtt/impl/RttOverflowMenu.java b/java/com/android/incallui/rtt/impl/RttOverflowMenu.java new file mode 100644 index 000000000..e0916bedf --- /dev/null +++ b/java/com/android/incallui/rtt/impl/RttOverflowMenu.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.incallui.rtt.impl; + +import android.content.Context; +import android.telecom.CallAudioState; +import android.view.View; +import android.widget.PopupWindow; +import com.android.incallui.incall.protocol.InCallButtonUiDelegate; +import com.android.incallui.rtt.impl.RttCheckableButton.OnCheckedChangeListener; +import com.android.incallui.speakerbuttonlogic.SpeakerButtonInfo; +import com.android.incallui.speakerbuttonlogic.SpeakerButtonInfo.IconSize; + +/** Overflow menu for RTT call. */ +public class RttOverflowMenu extends PopupWindow implements OnCheckedChangeListener { + + private final RttCheckableButton muteButton; + private final RttCheckableButton speakerButton; + private final RttCheckableButton dialpadButton; + private final RttCheckableButton addCallButton; + private final InCallButtonUiDelegate inCallButtonUiDelegate; + + RttOverflowMenu(Context context, InCallButtonUiDelegate inCallButtonUiDelegate) { + super(context); + this.inCallButtonUiDelegate = inCallButtonUiDelegate; + View view = View.inflate(context, R.layout.overflow_menu, null); + setContentView(view); + setOnDismissListener(this::dismiss); + setFocusable(true); + setWidth(context.getResources().getDimensionPixelSize(R.dimen.rtt_overflow_menu_width)); + muteButton = view.findViewById(R.id.menu_mute); + muteButton.setOnCheckedChangeListener(this); + speakerButton = view.findViewById(R.id.menu_speaker); + speakerButton.setOnCheckedChangeListener(this); + dialpadButton = view.findViewById(R.id.menu_keypad); + dialpadButton.setOnCheckedChangeListener(this); + addCallButton = view.findViewById(R.id.menu_add_call); + addCallButton.setOnCheckedChangeListener(this); + } + + @Override + public void onCheckedChanged(RttCheckableButton button, boolean isChecked) { + if (button == muteButton) { + inCallButtonUiDelegate.muteClicked(isChecked, true); + } else if (button == speakerButton) { + inCallButtonUiDelegate.toggleSpeakerphone(); + } else if (button == dialpadButton) { + inCallButtonUiDelegate.showDialpadClicked(isChecked); + } else if (button == addCallButton) { + inCallButtonUiDelegate.addCallClicked(); + } + } + + void setMuteButtonChecked(boolean isChecked) { + muteButton.setChecked(isChecked); + } + + void setAudioState(CallAudioState audioState) { + SpeakerButtonInfo info = new SpeakerButtonInfo(audioState, IconSize.SIZE_24_DP); + if (info.checkable) { + speakerButton.setChecked(info.isChecked); + } + } +} diff --git a/java/com/android/incallui/rtt/impl/res/color/rtt_checkable_button_color.xml b/java/com/android/incallui/rtt/impl/res/color/rtt_checkable_button_color.xml new file mode 100644 index 000000000..cb3a4522f --- /dev/null +++ b/java/com/android/incallui/rtt/impl/res/color/rtt_checkable_button_color.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/drawable/overflow_menu_background.xml b/java/com/android/incallui/rtt/impl/res/drawable/overflow_menu_background.xml new file mode 100644 index 000000000..614298679 --- /dev/null +++ b/java/com/android/incallui/rtt/impl/res/drawable/overflow_menu_background.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/layout/overflow_menu.xml b/java/com/android/incallui/rtt/impl/res/layout/overflow_menu.xml new file mode 100644 index 000000000..a29fad5f1 --- /dev/null +++ b/java/com/android/incallui/rtt/impl/res/layout/overflow_menu.xml @@ -0,0 +1,49 @@ + + + + + + + + \ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/values/attrs.xml b/java/com/android/incallui/rtt/impl/res/values/attrs.xml new file mode 100644 index 000000000..2e7d899bb --- /dev/null +++ b/java/com/android/incallui/rtt/impl/res/values/attrs.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/java/com/android/incallui/rtt/impl/res/values/colors.xml b/java/com/android/incallui/rtt/impl/res/values/colors.xml index c25ad21f2..e1702ccdf 100644 --- a/java/com/android/incallui/rtt/impl/res/values/colors.xml +++ b/java/com/android/incallui/rtt/impl/res/values/colors.xml @@ -17,4 +17,6 @@ #03165C #FAFAFA + #757575 + #2A56C6 \ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/values/dimens.xml b/java/com/android/incallui/rtt/impl/res/values/dimens.xml index a3f230c08..4c3fe02d2 100644 --- a/java/com/android/incallui/rtt/impl/res/values/dimens.xml +++ b/java/com/android/incallui/rtt/impl/res/values/dimens.xml @@ -17,4 +17,5 @@ 16dp 2dp + 180dp \ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/values/styles.xml b/java/com/android/incallui/rtt/impl/res/values/styles.xml index 55207a3cc..515e0dfcf 100644 --- a/java/com/android/incallui/rtt/impl/res/values/styles.xml +++ b/java/com/android/incallui/rtt/impl/res/values/styles.xml @@ -15,9 +15,24 @@ ~ limitations under the License --> + + + \ No newline at end of file -- cgit v1.2.3