Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 604e481f authored by wangqi's avatar wangqi Committed by Copybara-Service
Browse files

Implement overflow menu for RTT call.

Bug: 67596257
Test: manual
PiperOrigin-RevId: 187529383
Change-Id: I6ef6593a79ef0c4fb407284eede966a1eaabcd1e
parent 0cf82d3d
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
<!--
  ~ 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
  -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0"
    android:tint="?attr/colorControlNormal">
  <path
      android:fillColor="@android:color/white"
      android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM21,6h-3V3h-2v3h-3v2h3v3h2V8h3z"/>
</vector>
 No newline at end of file
+25 −0
Original line number Diff line number Diff line
<!--
  ~ 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
  -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0"
    android:tint="?attr/colorControlNormal">
  <path
      android:fillColor="@android:color/white"
      android:pathData="M12,19c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM6,1c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM6,7c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM6,13c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,13c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,13c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,7c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,7c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,1c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>
 No newline at end of file
+30 −3
Original line number Diff line number Diff line
@@ -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() {}
}
+222 −0
Original line number Diff line number Diff line
/*
 * 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<SavedState> CREATOR =
        new Creator<SavedState>() {
          @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));
    }
  }
}
+78 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
  }
}
Loading