summaryrefslogtreecommitdiff
path: root/java/com/android/incallui/incall/impl/InCallFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/incallui/incall/impl/InCallFragment.java')
-rw-r--r--java/com/android/incallui/incall/impl/InCallFragment.java501
1 files changed, 501 insertions, 0 deletions
diff --git a/java/com/android/incallui/incall/impl/InCallFragment.java b/java/com/android/incallui/incall/impl/InCallFragment.java
new file mode 100644
index 000000000..ef8a1edd8
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/InCallFragment.java
@@ -0,0 +1,501 @@
+/*
+ * 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.incall.impl;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TabLayout;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewPager;
+import android.telecom.CallAudioState;
+import android.telephony.TelephonyManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.multimedia.MultimediaData;
+import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment;
+import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter;
+import com.android.incallui.contactgrid.ContactGridManager;
+import com.android.incallui.hold.OnHoldFragment;
+import com.android.incallui.incall.impl.ButtonController.SpeakerButtonController;
+import com.android.incallui.incall.impl.InCallButtonGridFragment.OnButtonGridCreatedListener;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import com.android.incallui.incall.protocol.InCallButtonIdsExtension;
+import com.android.incallui.incall.protocol.InCallButtonUi;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
+import com.android.incallui.incall.protocol.InCallScreen;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+import com.android.incallui.incall.protocol.PrimaryInfo;
+import com.android.incallui.incall.protocol.SecondaryInfo;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Fragment that shows UI for an ongoing voice call. */
+public class InCallFragment extends Fragment
+ implements InCallScreen,
+ InCallButtonUi,
+ OnClickListener,
+ AudioRouteSelectorPresenter,
+ OnButtonGridCreatedListener {
+
+ private List<ButtonController> buttonControllers = new ArrayList<>();
+ private View endCallButton;
+ private TabLayout tabLayout;
+ private ViewPager pager;
+ private InCallPagerAdapter adapter;
+ private ContactGridManager contactGridManager;
+ private InCallScreenDelegate inCallScreenDelegate;
+ private InCallButtonUiDelegate inCallButtonUiDelegate;
+ private InCallButtonGridFragment inCallButtonGridFragment;
+ @Nullable private ButtonChooser buttonChooser;
+ private SecondaryInfo savedSecondaryInfo;
+ private int voiceNetworkType;
+ private int phoneType;
+ private boolean stateRestored;
+
+ private static boolean isSupportedButton(@InCallButtonIds int id) {
+ return id == InCallButtonIds.BUTTON_AUDIO
+ || id == InCallButtonIds.BUTTON_MUTE
+ || id == InCallButtonIds.BUTTON_DIALPAD
+ || id == InCallButtonIds.BUTTON_HOLD
+ || id == InCallButtonIds.BUTTON_SWAP
+ || id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO
+ || id == InCallButtonIds.BUTTON_ADD_CALL
+ || id == InCallButtonIds.BUTTON_MERGE
+ || id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (savedSecondaryInfo != null) {
+ setSecondary(savedSecondaryInfo);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ inCallButtonUiDelegate =
+ FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class)
+ .newInCallButtonUiDelegate();
+ if (savedInstanceState != null) {
+ inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState);
+ stateRestored = true;
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater layoutInflater,
+ @Nullable ViewGroup viewGroup,
+ @Nullable Bundle bundle) {
+ LogUtil.i("InCallFragment.onCreateView", null);
+ final View view = layoutInflater.inflate(R.layout.frag_incall_voice, viewGroup, false);
+ contactGridManager =
+ new ContactGridManager(
+ view,
+ (ImageView) view.findViewById(R.id.contactgrid_avatar),
+ getResources().getDimensionPixelSize(R.dimen.incall_avatar_size),
+ true /* showAnonymousAvatar */);
+
+ tabLayout = (TabLayout) view.findViewById(R.id.incall_tab_dots);
+ pager = (ViewPager) view.findViewById(R.id.incall_pager);
+
+ endCallButton = view.findViewById(R.id.incall_end_call);
+ endCallButton.setOnClickListener(this);
+
+ if (ContextCompat.checkSelfPermission(getContext(), permission.READ_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ } else {
+
+ voiceNetworkType =
+ VERSION.SDK_INT >= VERSION_CODES.N
+ ? getContext().getSystemService(TelephonyManager.class).getVoiceNetworkType()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ phoneType = getContext().getSystemService(TelephonyManager.class).getPhoneType();
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ inCallButtonUiDelegate.refreshMuteState();
+ inCallScreenDelegate.onInCallScreenResumed();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) {
+ LogUtil.i("InCallFragment.onViewCreated", null);
+ super.onViewCreated(view, bundle);
+ inCallScreenDelegate =
+ FragmentUtils.getParent(this, InCallScreenDelegateFactory.class).newInCallScreenDelegate();
+ Assert.isNotNull(inCallScreenDelegate);
+
+ buttonControllers.add(new ButtonController.MuteButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.SpeakerButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.DialpadButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.HoldButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.AddCallButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.SwapButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.MergeButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(
+ new ButtonController.UpgradeToVideoButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(
+ new ButtonController.ManageConferenceButtonController(inCallScreenDelegate));
+ buttonControllers.add(
+ new ButtonController.SwitchToSecondaryButtonController(inCallScreenDelegate));
+
+ inCallScreenDelegate.onInCallScreenDelegateInit(this);
+ inCallScreenDelegate.onInCallScreenReady();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ inCallScreenDelegate.onInCallScreenUnready();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ inCallButtonUiDelegate.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == endCallButton) {
+ LogUtil.i("InCallFragment.onClick", "end call button clicked");
+ inCallScreenDelegate.onEndCallClicked();
+ } else {
+ LogUtil.e("InCallFragment.onClick", "unknown view: " + view);
+ Assert.fail();
+ }
+ }
+
+ @Override
+ public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
+ LogUtil.i("InCallFragment.setPrimary", primaryInfo.toString());
+ if (adapter == null) {
+ initAdapter(primaryInfo.multimediaData);
+ }
+ contactGridManager.setPrimary(primaryInfo);
+
+ if (primaryInfo.shouldShowLocation) {
+ // Hide the avatar to make room for location
+ contactGridManager.setAvatarHidden(true);
+
+ // Need to widen the contact grid to fit location information
+ View contactGridView = getView().findViewById(R.id.incall_contact_grid);
+ ViewGroup.LayoutParams params = contactGridView.getLayoutParams();
+ if (params instanceof ViewGroup.MarginLayoutParams) {
+ ((ViewGroup.MarginLayoutParams) params).setMarginStart(0);
+ ((ViewGroup.MarginLayoutParams) params).setMarginEnd(0);
+ }
+ contactGridView.setLayoutParams(params);
+
+ // Need to let the dialpad move up a little further when location info is being shown
+ View dialpadView = getView().findViewById(R.id.incall_dialpad_container);
+ params = dialpadView.getLayoutParams();
+ if (params instanceof RelativeLayout.LayoutParams) {
+ ((RelativeLayout.LayoutParams) params).removeRule(RelativeLayout.BELOW);
+ }
+ dialpadView.setLayoutParams(params);
+ }
+ }
+
+ private void initAdapter(MultimediaData multimediaData) {
+ adapter = new InCallPagerAdapter(getChildFragmentManager(), multimediaData);
+ pager.setAdapter(adapter);
+
+ if (adapter.getCount() > 1) {
+ tabLayout.setVisibility(pager.getVisibility());
+ tabLayout.setupWithViewPager(pager, true);
+ if (!stateRestored) {
+ new Handler()
+ .postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ // In order to prevent user confusion and educate the user on our UI, we animate
+ // the view pager to the button grid after 2 seconds show them when the UI is
+ // that they are more familiar with.
+ pager.setCurrentItem(adapter.getButtonGridPosition());
+ }
+ },
+ 2000);
+ }
+ } else {
+ tabLayout.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {
+ LogUtil.i("InCallFragment.setSecondary", secondaryInfo.toString());
+ getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY)
+ .setEnabled(secondaryInfo.shouldShow);
+ getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY)
+ .setAllowed(secondaryInfo.shouldShow);
+ updateButtonStates();
+
+ if (!isAdded()) {
+ savedSecondaryInfo = secondaryInfo;
+ return;
+ }
+ savedSecondaryInfo = null;
+ FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
+ Fragment oldBanner = getChildFragmentManager().findFragmentById(R.id.incall_on_hold_banner);
+ if (secondaryInfo.shouldShow) {
+ transaction.replace(R.id.incall_on_hold_banner, OnHoldFragment.newInstance(secondaryInfo));
+ } else {
+ if (oldBanner != null) {
+ transaction.remove(oldBanner);
+ }
+ }
+ transaction.setCustomAnimations(R.anim.abc_slide_in_top, R.anim.abc_slide_out_top);
+ transaction.commitAllowingStateLoss();
+ }
+
+ @Override
+ public void setCallState(@NonNull PrimaryCallState primaryCallState) {
+ LogUtil.i("InCallFragment.setCallState", primaryCallState.toString());
+ contactGridManager.setCallState(primaryCallState);
+ buttonChooser =
+ ButtonChooserFactory.newButtonChooser(voiceNetworkType, primaryCallState.isWifi, phoneType);
+ updateButtonStates();
+ }
+
+ @Override
+ public void setEndCallButtonEnabled(boolean enabled, boolean animate) {
+ if (endCallButton != null) {
+ endCallButton.setEnabled(enabled);
+ }
+ }
+
+ @Override
+ public void showManageConferenceCallButton(boolean visible) {
+ getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setAllowed(visible);
+ getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setEnabled(visible);
+ updateButtonStates();
+ }
+
+ @Override
+ public boolean isManageConferenceVisible() {
+ return getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).isAllowed();
+ }
+
+ @Override
+ public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ contactGridManager.dispatchPopulateAccessibilityEvent(event);
+ }
+
+ @Override
+ public void showNoteSentToast() {
+ LogUtil.i("InCallFragment.showNoteSentToast", null);
+ Toast.makeText(getContext(), R.string.incall_note_sent, Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void updateInCallScreenColors() {}
+
+ @Override
+ public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {
+ LogUtil.i("InCallFragment.onInCallScreenDialpadVisibilityChange", "isShowing: " + isShowing);
+ // Take note that the dialpad button isShowing
+ getButtonController(InCallButtonIds.BUTTON_DIALPAD).setChecked(isShowing);
+
+ // This check is needed because there is a race condition where we attempt to update
+ // ButtonGridFragment before it is ready, so we check whether it is ready first and once it is
+ // ready, #onButtonGridCreated will mark the dialpad button as isShowing.
+ if (inCallButtonGridFragment != null) {
+ // Update the Android Button's state to isShowing.
+ inCallButtonGridFragment.onInCallScreenDialpadVisibilityChange(isShowing);
+ }
+ }
+
+ @Override
+ public int getAnswerAndDialpadContainerResourceId() {
+ return R.id.incall_dialpad_container;
+ }
+
+ @Override
+ public Fragment getInCallScreenFragment() {
+ return this;
+ }
+
+ @Override
+ public void showButton(@InCallButtonIds int buttonId, boolean show) {
+ LogUtil.v(
+ "InCallFragment.showButton",
+ "buttionId: %s, show: %b",
+ InCallButtonIdsExtension.toString(buttonId),
+ show);
+ if (isSupportedButton(buttonId)) {
+ getButtonController(buttonId).setAllowed(show);
+ }
+ }
+
+ @Override
+ public void enableButton(@InCallButtonIds int buttonId, boolean enable) {
+ LogUtil.v(
+ "InCallFragment.enableButton",
+ "buttonId: %s, enable: %b",
+ InCallButtonIdsExtension.toString(buttonId),
+ enable);
+ if (isSupportedButton(buttonId)) {
+ getButtonController(buttonId).setEnabled(enable);
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ LogUtil.v("InCallFragment.setEnabled", "enabled: " + enabled);
+ for (ButtonController buttonController : buttonControllers) {
+ buttonController.setEnabled(enabled);
+ }
+ }
+
+ @Override
+ public void setHold(boolean value) {
+ getButtonController(InCallButtonIds.BUTTON_HOLD).setChecked(value);
+ }
+
+ @Override
+ public void setCameraSwitched(boolean isBackFacingCamera) {}
+
+ @Override
+ public void setVideoPaused(boolean isPaused) {}
+
+ @Override
+ public void setAudioState(CallAudioState audioState) {
+ LogUtil.i("InCallFragment.setAudioState", "audioState: " + audioState);
+ ((SpeakerButtonController) getButtonController(InCallButtonIds.BUTTON_AUDIO))
+ .setAudioState(audioState);
+ getButtonController(InCallButtonIds.BUTTON_MUTE).setChecked(audioState.isMuted());
+ }
+
+ @Override
+ public void updateButtonStates() {
+ // When the incall screen is ready, this method is called from #setSecondary, even though the
+ // incall button ui is not ready yet. This method is called again once the incall button ui is
+ // ready though, so this operation is safe and will be executed asap.
+ if (inCallButtonGridFragment == null) {
+ return;
+ }
+ int numVisibleButtons =
+ inCallButtonGridFragment.updateButtonStates(
+ buttonControllers, buttonChooser, voiceNetworkType, phoneType);
+
+ int visibility = numVisibleButtons == 0 ? View.GONE : View.VISIBLE;
+ pager.setVisibility(visibility);
+ if (adapter != null && adapter.getCount() > 1) {
+ tabLayout.setVisibility(visibility);
+ }
+ }
+
+ @Override
+ public void updateInCallButtonUiColors() {}
+
+ @Override
+ public Fragment getInCallButtonUiFragment() {
+ return this;
+ }
+
+ @Override
+ public void showAudioRouteSelector() {
+ AudioRouteSelectorDialogFragment.newInstance(inCallButtonUiDelegate.getCurrentAudioState())
+ .show(getChildFragmentManager(), null);
+ }
+
+ @Override
+ public void onAudioRouteSelected(int audioRoute) {
+ inCallButtonUiDelegate.setAudioRoute(audioRoute);
+ }
+
+ @NonNull
+ @Override
+ public ButtonController getButtonController(@InCallButtonIds int id) {
+ for (ButtonController buttonController : buttonControllers) {
+ if (buttonController.getInCallButtonId() == id) {
+ return buttonController;
+ }
+ }
+ Assert.fail();
+ return null;
+ }
+
+ @Override
+ public void onButtonGridCreated(InCallButtonGridFragment inCallButtonGridFragment) {
+ LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiReady");
+ this.inCallButtonGridFragment = inCallButtonGridFragment;
+ inCallButtonUiDelegate.onInCallButtonUiReady(this);
+ updateButtonStates();
+ }
+
+ @Override
+ public void onButtonGridDestroyed() {
+ LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiUnready");
+ inCallButtonUiDelegate.onInCallButtonUiUnready();
+ this.inCallButtonGridFragment = null;
+ }
+
+ @Override
+ public boolean isShowingLocationUi() {
+ Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder);
+ return fragment != null && fragment.isVisible();
+ }
+
+ @Override
+ public void showLocationUi(@Nullable Fragment locationUi) {
+ boolean isShowing = isShowingLocationUi();
+ if (!isShowing && locationUi != null) {
+ // Show the location fragment.
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(R.id.incall_location_holder, locationUi)
+ .commitAllowingStateLoss();
+ } else if (isShowing && locationUi == null) {
+ // Hide the location fragment
+ Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder);
+ getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
+ }
+ }
+}