diff options
Diffstat (limited to 'java/com/android/incallui/video')
7 files changed, 1322 insertions, 4 deletions
diff --git a/java/com/android/incallui/video/bindings/VideoBindings.java b/java/com/android/incallui/video/bindings/VideoBindings.java index a80a6c702..08db593fd 100644 --- a/java/com/android/incallui/video/bindings/VideoBindings.java +++ b/java/com/android/incallui/video/bindings/VideoBindings.java @@ -16,13 +16,17 @@ package com.android.incallui.video.bindings; +import com.android.incallui.video.impl.SurfaceViewVideoCallFragment; import com.android.incallui.video.impl.VideoCallFragment; import com.android.incallui.video.protocol.VideoCallScreen; /** Bindings for video module. */ public class VideoBindings { - public static VideoCallScreen createVideoCallScreen(String callId) { + public static VideoCallScreen createVideoCallScreen(String callId, boolean usesSurfaceView) { + if (usesSurfaceView) { + return SurfaceViewVideoCallFragment.newInstance(callId); + } return VideoCallFragment.newInstance(callId); } } diff --git a/java/com/android/incallui/video/impl/SurfaceViewVideoCallFragment.java b/java/com/android/incallui/video/impl/SurfaceViewVideoCallFragment.java new file mode 100644 index 000000000..c969fb05a --- /dev/null +++ b/java/com/android/incallui/video/impl/SurfaceViewVideoCallFragment.java @@ -0,0 +1,1081 @@ +/* + * 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.video.impl; + +import android.Manifest.permission; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.drawable.Animatable; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.animation.FastOutLinearInInterpolator; +import android.support.v4.view.animation.LinearOutSlowInInterpolator; +import android.telecom.CallAudioState; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.Surface; +import android.view.SurfaceView; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnSystemUiVisibilityChangeListener; +import android.view.ViewGroup; +import android.view.ViewGroup.MarginLayoutParams; +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityEvent; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import com.android.dialer.common.Assert; +import com.android.dialer.common.FragmentUtils; +import com.android.dialer.common.LogUtil; +import com.android.dialer.compat.ActivityCompat; +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.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 com.android.incallui.video.impl.CheckableImageButton.OnCheckedChangeListener; +import com.android.incallui.video.protocol.VideoCallScreen; +import com.android.incallui.video.protocol.VideoCallScreenDelegate; +import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory; +import com.android.incallui.videotech.utils.VideoUtils; + +/** + * Contains UI elements for a video call. + * + * <p>This version is used by RCS Video Share since Dreamchip requires a SurfaceView instead of the + * TextureView, which is present in {@link VideoCallFragment} and used by IMS. + */ +public class SurfaceViewVideoCallFragment extends Fragment + implements InCallScreen, + InCallButtonUi, + VideoCallScreen, + OnClickListener, + OnCheckedChangeListener, + AudioRouteSelectorPresenter, + OnSystemUiVisibilityChangeListener { + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + static final String ARG_CALL_ID = "call_id"; + + private static final int CAMERA_PERMISSION_REQUEST_CODE = 1; + private static final String CAMERA_PERMISSION_DIALOG_FRAMENT_TAG = + "CameraPermissionDialogFragment"; + private static final long CAMERA_PERMISSION_DIALOG_DELAY_IN_MILLIS = 2000L; + private static final long VIDEO_OFF_VIEW_FADE_OUT_DELAY_IN_MILLIS = 2000L; + + private InCallScreenDelegate inCallScreenDelegate; + private VideoCallScreenDelegate videoCallScreenDelegate; + private InCallButtonUiDelegate inCallButtonUiDelegate; + private View endCallButton; + private CheckableImageButton speakerButton; + private SpeakerButtonController speakerButtonController; + private CheckableImageButton muteButton; + private CheckableImageButton cameraOffButton; + private ImageButton swapCameraButton; + private View switchOnHoldButton; + private View onHoldContainer; + private SwitchOnHoldCallController switchOnHoldCallController; + private TextView remoteVideoOff; + private View mutePreviewOverlay; + private View previewOffOverlay; + private ImageView previewOffBlurredImageView; + private View controls; + private View controlsContainer; + private SurfaceView previewSurfaceView; + private SurfaceView remoteSurfaceView; + private View greenScreenBackgroundView; + private View fullscreenBackgroundView; + private boolean shouldShowRemote; + private boolean shouldShowPreview; + private boolean isInFullscreenMode; + private boolean isInGreenScreenMode; + private boolean hasInitializedScreenModes; + private boolean isRemotelyHeld; + private ContactGridManager contactGridManager; + private SecondaryInfo savedSecondaryInfo; + private final Runnable cameraPermissionDialogRunnable = + new Runnable() { + @Override + public void run() { + if (videoCallScreenDelegate.shouldShowCameraPermissionDialog()) { + LogUtil.i("VideoCallFragment.cameraPermissionDialogRunnable", "showing dialog"); + checkCameraPermission(); + } + } + }; + + public static SurfaceViewVideoCallFragment newInstance(String callId) { + Bundle bundle = new Bundle(); + bundle.putString(ARG_CALL_ID, Assert.isNotNull(callId)); + + SurfaceViewVideoCallFragment instance = new SurfaceViewVideoCallFragment(); + instance.setArguments(bundle); + return instance; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LogUtil.i("VideoCallFragment.onCreate", null); + + inCallButtonUiDelegate = + FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class) + .newInCallButtonUiDelegate(); + if (savedInstanceState != null) { + inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState); + } + } + + @Override + public void onRequestPermissionsResult( + int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + LogUtil.i("VideoCallFragment.onRequestPermissionsResult", "Camera permission granted."); + videoCallScreenDelegate.onCameraPermissionGranted(); + } else { + LogUtil.i("VideoCallFragment.onRequestPermissionsResult", "Camera permission denied."); + } + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Nullable + @Override + public View onCreateView( + LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) { + LogUtil.i("VideoCallFragment.onCreateView", null); + + View view = + layoutInflater.inflate( + isLandscape() + ? R.layout.frag_videocall_land_surfaceview + : R.layout.frag_videocall_surfaceview, + viewGroup, + false); + contactGridManager = + new ContactGridManager(view, null /* no avatar */, 0, false /* showAnonymousAvatar */); + + controls = view.findViewById(R.id.videocall_video_controls); + controls.setVisibility( + ActivityCompat.isInMultiWindowMode(getActivity()) ? View.GONE : View.VISIBLE); + controlsContainer = view.findViewById(R.id.videocall_video_controls_container); + speakerButton = (CheckableImageButton) view.findViewById(R.id.videocall_speaker_button); + muteButton = (CheckableImageButton) view.findViewById(R.id.videocall_mute_button); + muteButton.setOnCheckedChangeListener(this); + mutePreviewOverlay = view.findViewById(R.id.videocall_video_preview_mute_overlay); + cameraOffButton = (CheckableImageButton) view.findViewById(R.id.videocall_mute_video); + cameraOffButton.setOnCheckedChangeListener(this); + previewOffOverlay = view.findViewById(R.id.videocall_video_preview_off_overlay); + previewOffBlurredImageView = + (ImageView) view.findViewById(R.id.videocall_preview_off_blurred_image_view); + swapCameraButton = (ImageButton) view.findViewById(R.id.videocall_switch_video); + swapCameraButton.setOnClickListener(this); + view.findViewById(R.id.videocall_switch_controls) + .setVisibility( + ActivityCompat.isInMultiWindowMode(getActivity()) ? View.GONE : View.VISIBLE); + switchOnHoldButton = view.findViewById(R.id.videocall_switch_on_hold); + onHoldContainer = view.findViewById(R.id.videocall_on_hold_banner); + remoteVideoOff = (TextView) view.findViewById(R.id.videocall_remote_video_off); + remoteVideoOff.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); + endCallButton = view.findViewById(R.id.videocall_end_call); + endCallButton.setOnClickListener(this); + previewSurfaceView = (SurfaceView) view.findViewById(R.id.videocall_video_preview); + previewSurfaceView.setClipToOutline(true); + previewOffOverlay.setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + checkCameraPermission(); + } + }); + remoteSurfaceView = (SurfaceView) view.findViewById(R.id.videocall_video_remote); + greenScreenBackgroundView = view.findViewById(R.id.videocall_green_screen_background); + fullscreenBackgroundView = view.findViewById(R.id.videocall_fullscreen_background); + + // We need the texture view size to be able to scale the remote video. At this point the view + // layout won't be complete so add a layout listener. + ViewTreeObserver observer = remoteSurfaceView.getViewTreeObserver(); + observer.addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + LogUtil.i("VideoCallFragment.onGlobalLayout", null); + updateVideoOffViews(); + // Remove the listener so we don't continually re-layout. + ViewTreeObserver observer = remoteSurfaceView.getViewTreeObserver(); + if (observer.isAlive()) { + observer.removeOnGlobalLayoutListener(this); + } + } + }); + + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle bundle) { + super.onViewCreated(view, bundle); + LogUtil.i("VideoCallFragment.onViewCreated", null); + + inCallScreenDelegate = + FragmentUtils.getParentUnsafe(this, InCallScreenDelegateFactory.class) + .newInCallScreenDelegate(); + videoCallScreenDelegate = + FragmentUtils.getParentUnsafe(this, VideoCallScreenDelegateFactory.class) + .newVideoCallScreenDelegate(this); + + speakerButtonController = + new SpeakerButtonController(speakerButton, inCallButtonUiDelegate, videoCallScreenDelegate); + switchOnHoldCallController = + new SwitchOnHoldCallController( + switchOnHoldButton, onHoldContainer, inCallScreenDelegate, videoCallScreenDelegate); + + videoCallScreenDelegate.initVideoCallScreenDelegate(getContext(), this); + + inCallScreenDelegate.onInCallScreenDelegateInit(this); + inCallScreenDelegate.onInCallScreenReady(); + inCallButtonUiDelegate.onInCallButtonUiReady(this); + + view.setOnSystemUiVisibilityChangeListener(this); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + inCallButtonUiDelegate.onSaveInstanceState(outState); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + LogUtil.i("VideoCallFragment.onDestroyView", null); + inCallButtonUiDelegate.onInCallButtonUiUnready(); + inCallScreenDelegate.onInCallScreenUnready(); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (savedSecondaryInfo != null) { + setSecondary(savedSecondaryInfo); + } + } + + @Override + public void onStart() { + super.onStart(); + LogUtil.i("VideoCallFragment.onStart", null); + onVideoScreenStart(); + } + + @Override + public void onVideoScreenStart() { + inCallButtonUiDelegate.refreshMuteState(); + videoCallScreenDelegate.onVideoCallScreenUiReady(); + getView().postDelayed(cameraPermissionDialogRunnable, CAMERA_PERMISSION_DIALOG_DELAY_IN_MILLIS); + } + + @Override + public void onResume() { + super.onResume(); + LogUtil.i("VideoCallFragment.onResume", null); + inCallScreenDelegate.onInCallScreenResumed(); + } + + @Override + public void onPause() { + super.onPause(); + LogUtil.i("VideoCallFragment.onPause", null); + } + + @Override + public void onStop() { + super.onStop(); + LogUtil.i("VideoCallFragment.onStop", null); + onVideoScreenStop(); + } + + @Override + public void onVideoScreenStop() { + getView().removeCallbacks(cameraPermissionDialogRunnable); + videoCallScreenDelegate.onVideoCallScreenUiUnready(); + } + + private void exitFullscreenMode() { + LogUtil.i("VideoCallFragment.exitFullscreenMode", null); + + if (!getView().isAttachedToWindow()) { + LogUtil.i("VideoCallFragment.exitFullscreenMode", "not attached"); + return; + } + + showSystemUI(); + + LinearOutSlowInInterpolator linearOutSlowInInterpolator = new LinearOutSlowInInterpolator(); + + // Animate the controls to the shown state. + controls + .animate() + .translationX(0) + .translationY(0) + .setInterpolator(linearOutSlowInInterpolator) + .alpha(1) + .start(); + + // Animate onHold to the shown state. + switchOnHoldButton + .animate() + .translationX(0) + .translationY(0) + .setInterpolator(linearOutSlowInInterpolator) + .alpha(1) + .withStartAction( + new Runnable() { + @Override + public void run() { + switchOnHoldCallController.setOnScreen(); + } + }); + + View contactGridView = contactGridManager.getContainerView(); + // Animate contact grid to the shown state. + contactGridView + .animate() + .translationX(0) + .translationY(0) + .setInterpolator(linearOutSlowInInterpolator) + .alpha(1) + .withStartAction( + new Runnable() { + @Override + public void run() { + contactGridManager.show(); + } + }); + + endCallButton + .animate() + .translationX(0) + .translationY(0) + .setInterpolator(linearOutSlowInInterpolator) + .alpha(1) + .withStartAction( + new Runnable() { + @Override + public void run() { + endCallButton.setVisibility(View.VISIBLE); + } + }) + .start(); + + // Animate all the preview controls up to make room for the navigation bar. + // In green screen mode we don't need this because the preview takes up the whole screen and has + // a fixed position. + if (!isInGreenScreenMode) { + Point previewOffsetStartShown = getPreviewOffsetStartShown(); + for (View view : getAllPreviewRelatedViews()) { + // Animate up with the preview offset above the navigation bar. + view.animate() + .translationX(previewOffsetStartShown.x) + .translationY(previewOffsetStartShown.y) + .setInterpolator(new AccelerateDecelerateInterpolator()) + .start(); + } + } + + updateOverlayBackground(); + } + + private void showSystemUI() { + View view = getView(); + if (view != null) { + // Code is more expressive with all flags present, even though some may be combined + //noinspection PointlessBitwiseExpression + view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + } + } + + /** Set view flags to hide the system UI. System UI will return on any touch event */ + private void hideSystemUI() { + View view = getView(); + if (view != null) { + view.setSystemUiVisibility( + View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + } + } + + private Point getControlsOffsetEndHidden(View controls) { + if (isLandscape()) { + return new Point(0, getOffsetBottom(controls)); + } else { + return new Point(getOffsetStart(controls), 0); + } + } + + private Point getSwitchOnHoldOffsetEndHidden(View swapCallButton) { + if (isLandscape()) { + return new Point(0, getOffsetTop(swapCallButton)); + } else { + return new Point(getOffsetEnd(swapCallButton), 0); + } + } + + private Point getContactGridOffsetEndHidden(View view) { + return new Point(0, getOffsetTop(view)); + } + + private Point getEndCallOffsetEndHidden(View endCallButton) { + if (isLandscape()) { + return new Point(getOffsetEnd(endCallButton), 0); + } else { + return new Point(0, ((MarginLayoutParams) endCallButton.getLayoutParams()).bottomMargin); + } + } + + private Point getPreviewOffsetStartShown() { + // No insets in multiwindow mode, and rootWindowInsets will get the display's insets. + if (ActivityCompat.isInMultiWindowMode(getActivity())) { + return new Point(); + } + if (isLandscape()) { + int stableInsetEnd = + getView().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? getView().getRootWindowInsets().getStableInsetLeft() + : -getView().getRootWindowInsets().getStableInsetRight(); + return new Point(stableInsetEnd, 0); + } else { + return new Point(0, -getView().getRootWindowInsets().getStableInsetBottom()); + } + } + + private View[] getAllPreviewRelatedViews() { + return new View[] { + previewSurfaceView, previewOffOverlay, previewOffBlurredImageView, mutePreviewOverlay, + }; + } + + private int getOffsetTop(View view) { + return -(view.getHeight() + ((MarginLayoutParams) view.getLayoutParams()).topMargin); + } + + private int getOffsetBottom(View view) { + return view.getHeight() + ((MarginLayoutParams) view.getLayoutParams()).bottomMargin; + } + + private int getOffsetStart(View view) { + int offset = view.getWidth() + ((MarginLayoutParams) view.getLayoutParams()).getMarginStart(); + if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + offset = -offset; + } + return -offset; + } + + private int getOffsetEnd(View view) { + int offset = view.getWidth() + ((MarginLayoutParams) view.getLayoutParams()).getMarginEnd(); + if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + offset = -offset; + } + return offset; + } + + private void enterFullscreenMode() { + LogUtil.i("VideoCallFragment.enterFullscreenMode", null); + + hideSystemUI(); + + Interpolator fastOutLinearInInterpolator = new FastOutLinearInInterpolator(); + + // Animate controls to the hidden state. + Point offset = getControlsOffsetEndHidden(controls); + controls + .animate() + .translationX(offset.x) + .translationY(offset.y) + .setInterpolator(fastOutLinearInInterpolator) + .alpha(0) + .start(); + + // Animate onHold to the hidden state. + offset = getSwitchOnHoldOffsetEndHidden(switchOnHoldButton); + switchOnHoldButton + .animate() + .translationX(offset.x) + .translationY(offset.y) + .setInterpolator(fastOutLinearInInterpolator) + .alpha(0); + + View contactGridView = contactGridManager.getContainerView(); + // Animate contact grid to the hidden state. + offset = getContactGridOffsetEndHidden(contactGridView); + contactGridView + .animate() + .translationX(offset.x) + .translationY(offset.y) + .setInterpolator(fastOutLinearInInterpolator) + .alpha(0); + + offset = getEndCallOffsetEndHidden(endCallButton); + // Use a fast out interpolator to quickly fade out the button. This is important because the + // button can't draw under the navigation bar which means that it'll look weird if it just + // abruptly disappears when it reaches the edge of the naivgation bar. + endCallButton + .animate() + .translationX(offset.x) + .translationY(offset.y) + .setInterpolator(fastOutLinearInInterpolator) + .alpha(0) + .withEndAction( + new Runnable() { + @Override + public void run() { + endCallButton.setVisibility(View.INVISIBLE); + } + }) + .setInterpolator(new FastOutLinearInInterpolator()) + .start(); + + // Animate all the preview controls down now that the navigation bar is hidden. + // In green screen mode we don't need this because the preview takes up the whole screen and has + // a fixed position. + if (!isInGreenScreenMode) { + for (View view : getAllPreviewRelatedViews()) { + // Animate down with the navigation bar hidden. + view.animate() + .translationX(0) + .translationY(0) + .setInterpolator(new AccelerateDecelerateInterpolator()) + .start(); + } + } + updateOverlayBackground(); + } + + @Override + public void onClick(View v) { + if (v == endCallButton) { + LogUtil.i("VideoCallFragment.onClick", "end call button clicked"); + inCallButtonUiDelegate.onEndCallClicked(); + videoCallScreenDelegate.resetAutoFullscreenTimer(); + } else if (v == swapCameraButton) { + if (swapCameraButton.getDrawable() instanceof Animatable) { + ((Animatable) swapCameraButton.getDrawable()).start(); + } + inCallButtonUiDelegate.toggleCameraClicked(); + videoCallScreenDelegate.resetAutoFullscreenTimer(); + } + } + + @Override + public void onCheckedChanged(CheckableImageButton button, boolean isChecked) { + if (button == cameraOffButton) { + if (!isChecked && !VideoUtils.hasCameraPermissionAndAllowedByUser(getContext())) { + LogUtil.i("VideoCallFragment.onCheckedChanged", "show camera permission dialog"); + checkCameraPermission(); + } else { + inCallButtonUiDelegate.pauseVideoClicked(isChecked); + videoCallScreenDelegate.resetAutoFullscreenTimer(); + } + } else if (button == muteButton) { + inCallButtonUiDelegate.muteClicked(isChecked, true /* clickedByUser */); + videoCallScreenDelegate.resetAutoFullscreenTimer(); + } + } + + @Override + public void showVideoViews( + boolean shouldShowPreview, boolean shouldShowRemote, boolean isRemotelyHeld) { + LogUtil.i( + "VideoCallFragment.showVideoViews", + "showPreview: %b, shouldShowRemote: %b", + shouldShowPreview, + shouldShowRemote); + this.shouldShowPreview = shouldShowPreview; + this.shouldShowRemote = shouldShowRemote; + this.isRemotelyHeld = isRemotelyHeld; + + videoCallScreenDelegate.setSurfaceViews(previewSurfaceView, remoteSurfaceView); + updateVideoOffViews(); + } + + /** + * This method scales the video feed inside the texture view, it doesn't change the texture view's + * size. In the old UI we would change the view size to match the aspect ratio of the video. In + * the new UI the view is always square (with the circular clip) so we have to do additional work + * to make sure the non-square video doesn't look squished. + */ + @Override + public void onLocalVideoDimensionsChanged() { + LogUtil.i("VideoCallFragment.onLocalVideoDimensionsChanged", null); + } + + @Override + public void onLocalVideoOrientationChanged() { + LogUtil.i("VideoCallFragment.onLocalVideoOrientationChanged", null); + } + + /** Called when the remote video's dimensions change. */ + @Override + public void onRemoteVideoDimensionsChanged() { + LogUtil.i("VideoCallFragment.onRemoteVideoDimensionsChanged", null); + } + + @Override + public void updateFullscreenAndGreenScreenMode( + boolean shouldShowFullscreen, boolean shouldShowGreenScreen) { + LogUtil.i( + "VideoCallFragment.updateFullscreenAndGreenScreenMode", + "shouldShowFullscreen: %b, shouldShowGreenScreen: %b", + shouldShowFullscreen, + shouldShowGreenScreen); + + if (getActivity() == null) { + LogUtil.i("VideoCallFragment.updateFullscreenAndGreenScreenMode", "not attached to activity"); + return; + } + + // Check if anything is actually going to change. The first time this function is called we + // force a change by checking the hasInitializedScreenModes flag. We also force both fullscreen + // and green screen modes to update even if only one has changed. That's because they both + // depend on each other. + if (hasInitializedScreenModes + && shouldShowGreenScreen == isInGreenScreenMode + && shouldShowFullscreen == isInFullscreenMode) { + LogUtil.i( + "VideoCallFragment.updateFullscreenAndGreenScreenMode", "no change to screen modes"); + return; + } + hasInitializedScreenModes = true; + isInGreenScreenMode = shouldShowGreenScreen; + isInFullscreenMode = shouldShowFullscreen; + + if (getView().isAttachedToWindow() && !ActivityCompat.isInMultiWindowMode(getActivity())) { + controlsContainer.onApplyWindowInsets(getView().getRootWindowInsets()); + } + if (shouldShowGreenScreen) { + enterGreenScreenMode(); + } else { + exitGreenScreenMode(); + } + if (shouldShowFullscreen) { + enterFullscreenMode(); + } else { + exitFullscreenMode(); + } + updateVideoOffViews(); + + OnHoldFragment onHoldFragment = + ((OnHoldFragment) + getChildFragmentManager().findFragmentById(R.id.videocall_on_hold_banner)); + if (onHoldFragment != null) { + onHoldFragment.setPadTopInset(!isInFullscreenMode); + } + } + + @Override + public Fragment getVideoCallScreenFragment() { + return this; + } + + @Override + @NonNull + public String getCallId() { + return Assert.isNotNull(getArguments().getString(ARG_CALL_ID)); + } + + @Override + public void showButton(@InCallButtonIds int buttonId, boolean show) { + LogUtil.v( + "VideoCallFragment.showButton", + "buttonId: %s, show: %b", + InCallButtonIdsExtension.toString(buttonId), + show); + if (buttonId == InCallButtonIds.BUTTON_AUDIO) { + speakerButtonController.setEnabled(show); + } else if (buttonId == InCallButtonIds.BUTTON_MUTE) { + muteButton.setEnabled(show); + } else if (buttonId == InCallButtonIds.BUTTON_PAUSE_VIDEO) { + cameraOffButton.setEnabled(show); + } else if (buttonId == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY) { + switchOnHoldCallController.setVisible(show); + } else if (buttonId == InCallButtonIds.BUTTON_SWITCH_CAMERA) { + swapCameraButton.setEnabled(show); + } + } + + @Override + public void enableButton(@InCallButtonIds int buttonId, boolean enable) { + LogUtil.v( + "VideoCallFragment.setEnabled", + "buttonId: %s, enable: %b", + InCallButtonIdsExtension.toString(buttonId), + enable); + if (buttonId == InCallButtonIds.BUTTON_AUDIO) { + speakerButtonController.setEnabled(enable); + } else if (buttonId == InCallButtonIds.BUTTON_MUTE) { + muteButton.setEnabled(enable); + } else if (buttonId == InCallButtonIds.BUTTON_PAUSE_VIDEO) { + cameraOffButton.setEnabled(enable); + } else if (buttonId == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY) { + switchOnHoldCallController.setEnabled(enable); + } + } + + @Override + public void setEnabled(boolean enabled) { + LogUtil.v("VideoCallFragment.setEnabled", "enabled: " + enabled); + speakerButtonController.setEnabled(enabled); + muteButton.setEnabled(enabled); + cameraOffButton.setEnabled(enabled); + switchOnHoldCallController.setEnabled(enabled); + } + + @Override + public void setHold(boolean value) { + LogUtil.i("VideoCallFragment.setHold", "value: " + value); + } + + @Override + public void setCameraSwitched(boolean isBackFacingCamera) { + LogUtil.i("VideoCallFragment.setCameraSwitched", "isBackFacingCamera: " + isBackFacingCamera); + } + + @Override + public void setVideoPaused(boolean isPaused) { + LogUtil.i("VideoCallFragment.setVideoPaused", "isPaused: " + isPaused); + cameraOffButton.setChecked(isPaused); + } + + @Override + public void setAudioState(CallAudioState audioState) { + LogUtil.i("VideoCallFragment.setAudioState", "audioState: " + audioState); + speakerButtonController.setAudioState(audioState); + muteButton.setChecked(audioState.isMuted()); + updateMutePreviewOverlayVisibility(); + } + + @Override + public void updateButtonStates() { + LogUtil.i("VideoCallFragment.updateButtonState", null); + speakerButtonController.updateButtonState(); + switchOnHoldCallController.updateButtonState(); + } + + @Override + public void updateInCallButtonUiColors() {} + + @Override + public Fragment getInCallButtonUiFragment() { + return this; + } + + @Override + public void showAudioRouteSelector() { + LogUtil.i("VideoCallFragment.showAudioRouteSelector", null); + AudioRouteSelectorDialogFragment.newInstance(inCallButtonUiDelegate.getCurrentAudioState()) + .show(getChildFragmentManager(), null); + } + + @Override + public void onAudioRouteSelected(int audioRoute) { + LogUtil.i("VideoCallFragment.onAudioRouteSelected", "audioRoute: " + audioRoute); + inCallButtonUiDelegate.setAudioRoute(audioRoute); + } + + @Override + public void setPrimary(@NonNull PrimaryInfo primaryInfo) { + LogUtil.i("VideoCallFragment.setPrimary", primaryInfo.toString()); + contactGridManager.setPrimary(primaryInfo); + } + + @Override + public void setSecondary(@NonNull SecondaryInfo secondaryInfo) { + LogUtil.i("VideoCallFragment.setSecondary", secondaryInfo.toString()); + if (!isAdded()) { + savedSecondaryInfo = secondaryInfo; + return; + } + savedSecondaryInfo = null; + switchOnHoldCallController.setSecondaryInfo(secondaryInfo); + updateButtonStates(); + FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); + Fragment oldBanner = getChildFragmentManager().findFragmentById(R.id.videocall_on_hold_banner); + if (secondaryInfo.shouldShow) { + OnHoldFragment onHoldFragment = OnHoldFragment.newInstance(secondaryInfo); + onHoldFragment.setPadTopInset(!isInFullscreenMode); + transaction.replace(R.id.videocall_on_hold_banner, onHoldFragment); + } 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("VideoCallFragment.setCallState", primaryCallState.toString()); + contactGridManager.setCallState(primaryCallState); + } + + @Override + public void setEndCallButtonEnabled(boolean enabled, boolean animate) { + LogUtil.i("VideoCallFragment.setEndCallButtonEnabled", "enabled: " + enabled); + } + + @Override + public void showManageConferenceCallButton(boolean visible) { + LogUtil.i("VideoCallFragment.showManageConferenceCallButton", "visible: " + visible); + } + + @Override + public boolean isManageConferenceVisible() { + LogUtil.i("VideoCallFragment.isManageConferenceVisible", null); + return false; + } + + @Override + public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + contactGridManager.dispatchPopulateAccessibilityEvent(event); + } + + @Override + public void showNoteSentToast() { + LogUtil.i("VideoCallFragment.showNoteSentToast", null); + } + + @Override + public void updateInCallScreenColors() { + LogUtil.i("VideoCallFragment.updateColors", null); + } + + @Override + public void onInCallScreenDialpadVisibilityChange(boolean isShowing) { + LogUtil.i("VideoCallFragment.onInCallScreenDialpadVisibilityChange", null); + } + + @Override + public int getAnswerAndDialpadContainerResourceId() { + return 0; + } + + @Override + public Fragment getInCallScreenFragment() { + return this; + } + + @Override + public boolean isShowingLocationUi() { + return false; + } + + @Override + public void showLocationUi(Fragment locationUi) { + LogUtil.e("VideoCallFragment.showLocationUi", "Emergency video calling not supported"); + // Do nothing + } + + private boolean isLandscape() { + // Choose orientation based on display orientation, not window orientation + int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation(); + return rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270; + } + + private void enterGreenScreenMode() { + LogUtil.i("VideoCallFragment.enterGreenScreenMode", null); + RelativeLayout.LayoutParams params = + new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); + params.addRule(RelativeLayout.ALIGN_PARENT_START); + params.addRule(RelativeLayout.ALIGN_PARENT_TOP); + previewSurfaceView.setLayoutParams(params); + previewSurfaceView.setOutlineProvider(null); + updateOverlayBackground(); + contactGridManager.setIsMiddleRowVisible(true); + updateMutePreviewOverlayVisibility(); + + previewOffBlurredImageView.setLayoutParams(params); + previewOffBlurredImageView.setOutlineProvider(null); + previewOffBlurredImageView.setClipToOutline(false); + } + + private void exitGreenScreenMode() { + LogUtil.i("VideoCallFragment.exitGreenScreenMode", null); + Resources resources = getResources(); + RelativeLayout.LayoutParams params = + new RelativeLayout.LayoutParams( + (int) resources.getDimension(R.dimen.videocall_preview_width), + (int) resources.getDimension(R.dimen.videocall_preview_height)); + params.setMargins( + 0, 0, 0, (int) resources.getDimension(R.dimen.videocall_preview_margin_bottom)); + if (isLandscape()) { + params.addRule(RelativeLayout.ALIGN_PARENT_END); + params.setMarginEnd((int) resources.getDimension(R.dimen.videocall_preview_margin_end)); + } else { + params.addRule(RelativeLayout.ALIGN_PARENT_START); + params.setMarginStart((int) resources.getDimension(R.dimen.videocall_preview_margin_start)); + } + params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + previewSurfaceView.setLayoutParams(params); + updateOverlayBackground(); + contactGridManager.setIsMiddleRowVisible(false); + updateMutePreviewOverlayVisibility(); + + previewOffBlurredImageView.setLayoutParams(params); + previewOffBlurredImageView.setClipToOutline(true); + } + + private void updateVideoOffViews() { + // Always hide the preview off and remote off views in green screen mode. + boolean previewEnabled = isInGreenScreenMode || shouldShowPreview; + previewOffOverlay.setVisibility(previewEnabled ? View.GONE : View.VISIBLE); + + boolean remoteEnabled = isInGreenScreenMode || shouldShowRemote; + boolean isResumed = remoteEnabled && !isRemotelyHeld; + if (isResumed) { + boolean wasRemoteVideoOff = + TextUtils.equals( + remoteVideoOff.getText(), + remoteVideoOff.getResources().getString(R.string.videocall_remote_video_off)); + // The text needs to be updated and hidden after enough delay in order to be announced by + // talkback. + remoteVideoOff.setText( + wasRemoteVideoOff + ? R.string.videocall_remote_video_on + : R.string.videocall_remotely_resumed); + remoteVideoOff.postDelayed( + new Runnable() { + @Override + public void run() { + remoteVideoOff.setVisibility(View.GONE); + } + }, + VIDEO_OFF_VIEW_FADE_OUT_DELAY_IN_MILLIS); + } else { + remoteVideoOff.setText( + isRemotelyHeld ? R.string.videocall_remotely_held : R.string.videocall_remote_video_off); + remoteVideoOff.setVisibility(View.VISIBLE); + } + } + + private void updateOverlayBackground() { + if (isInGreenScreenMode) { + // We want to darken the preview view to make text and buttons readable. The fullscreen + // background is below the preview view so use the green screen background instead. + animateSetVisibility(greenScreenBackgroundView, View.VISIBLE); + animateSetVisibility(fullscreenBackgroundView, View.GONE); + } else if (!isInFullscreenMode) { + // We want to darken the remote view to make text and buttons readable. The green screen + // background is above the preview view so it would darken the preview too. Use the fullscreen + // background instead. + animateSetVisibility(greenScreenBackgroundView, View.GONE); + animateSetVisibility(fullscreenBackgroundView, View.VISIBLE); + } else { + animateSetVisibility(greenScreenBackgroundView, View.GONE); + animateSetVisibility(fullscreenBackgroundView, View.GONE); + } + } + + private void updateMutePreviewOverlayVisibility() { + // Normally the mute overlay shows on the bottom right of the preview bubble. In green screen + // mode the preview is fullscreen so there's no where to anchor it. + mutePreviewOverlay.setVisibility( + muteButton.isChecked() && !isInGreenScreenMode ? View.VISIBLE : View.GONE); + } + + private static void animateSetVisibility(final View view, final int visibility) { + if (view.getVisibility() == visibility) { + return; + } + + int startAlpha; + int endAlpha; + if (visibility == View.GONE) { + startAlpha = 1; + endAlpha = 0; + } else if (visibility == View.VISIBLE) { + startAlpha = 0; + endAlpha = 1; + } else { + Assert.fail(); + return; + } + + view.setAlpha(startAlpha); + view.setVisibility(View.VISIBLE); + view.animate() + .alpha(endAlpha) + .withEndAction( + new Runnable() { + @Override + public void run() { + view.setVisibility(visibility); + } + }) + .start(); + } + + @Override + public void onSystemUiVisibilityChange(int visibility) { + boolean navBarVisible = (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + videoCallScreenDelegate.onSystemUiVisibilityChange(navBarVisible); + } + + protected void onCameraPermissionGranted() { + videoCallScreenDelegate.onCameraPermissionGranted(); + } + + private void checkCameraPermission() { + // Checks if user has consent of camera permission and the permission is granted. + // If camera permission is revoked, shows system permission dialog. + // If camera permission is granted but user doesn't have consent of camera permission + // (which means it's first time making video call), shows custom dialog instead. This + // will only be shown to user once. + if (!VideoUtils.hasCameraPermissionAndAllowedByUser(getContext())) { + videoCallScreenDelegate.onCameraPermissionDialogShown(); + if (!VideoUtils.hasCameraPermission(getContext())) { + requestPermissions(new String[] {permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); + } else { + CameraPermissionDialogFragment.newInstance() + .show(getChildFragmentManager(), CAMERA_PERMISSION_DIALOG_FRAMENT_TAG); + } + } + } +} diff --git a/java/com/android/incallui/video/impl/VideoCallFragment.java b/java/com/android/incallui/video/impl/VideoCallFragment.java index 05d0baa85..e2850ccdf 100644 --- a/java/com/android/incallui/video/impl/VideoCallFragment.java +++ b/java/com/android/incallui/video/impl/VideoCallFragment.java @@ -62,7 +62,6 @@ import com.android.dialer.common.LogUtil; import com.android.dialer.compat.ActivityCompat; import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment; import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter; -import com.android.incallui.call.VideoUtils; import com.android.incallui.contactgrid.ContactGridManager; import com.android.incallui.hold.OnHoldFragment; import com.android.incallui.incall.protocol.InCallButtonIds; @@ -82,6 +81,7 @@ import com.android.incallui.video.protocol.VideoCallScreenDelegate; import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory; import com.android.incallui.videosurface.bindings.VideoSurfaceBindings; import com.android.incallui.videosurface.protocol.VideoSurfaceTexture; +import com.android.incallui.videotech.utils.VideoUtils; /** Contains UI elements for a video call. */ public class VideoCallFragment extends Fragment @@ -281,7 +281,7 @@ public class VideoCallFragment extends Fragment .newInCallScreenDelegate(); videoCallScreenDelegate = FragmentUtils.getParentUnsafe(this, VideoCallScreenDelegateFactory.class) - .newVideoCallScreenDelegate(); + .newVideoCallScreenDelegate(this); speakerButtonController = new SpeakerButtonController(speakerButton, inCallButtonUiDelegate, videoCallScreenDelegate); diff --git a/java/com/android/incallui/video/impl/res/layout/frag_videocall_land_surfaceview.xml b/java/com/android/incallui/video/impl/res/layout/frag_videocall_land_surfaceview.xml new file mode 100644 index 000000000..45a9c3392 --- /dev/null +++ b/java/com/android/incallui/video/impl/res/layout/frag_videocall_land_surfaceview.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black"> + + <SurfaceView + android:id="@+id/videocall_video_remote" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:importantForAccessibility="no"/> + + <ImageView + android:id="@+id/videocall_remote_off_blurred_image_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:scaleType="fitCenter"/> + + <TextView + android:gravity="center" + android:id="@+id/videocall_remote_video_off" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:accessibilityTraversalBefore="@+id/videocall_speaker_button" + android:drawablePadding="8dp" + android:drawableTop="@drawable/quantum_ic_videocam_off_white_36" + android:padding="64dp" + android:text="@string/videocall_remote_video_off" + android:textAppearance="@style/Dialer.Incall.TextAppearance" + android:visibility="gone" + tools:visibility="visible"/> + + <View + android:id="@+id/videocall_fullscreen_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentBottom="true" + android:layout_alignParentStart="true" + android:background="@color/videocall_overlay_background_color"/> + + <SurfaceView + android:id="@+id/videocall_video_preview" + android:layout_width="@dimen/videocall_preview_width" + android:layout_height="@dimen/videocall_preview_height" + android:layout_marginEnd="@dimen/videocall_preview_margin_end" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:importantForAccessibility="no"/> + + <ImageView + android:id="@+id/videocall_preview_off_blurred_image_view" + android:layout_width="@dimen/videocall_preview_width" + android:layout_height="@dimen/videocall_preview_height" + android:layout_marginEnd="@dimen/videocall_preview_margin_end" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:scaleType="center"/> + + <View + android:id="@+id/videocall_green_screen_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentBottom="true" + android:layout_alignParentStart="true" + android:background="@color/videocall_overlay_background_color"/> + + <ImageView + android:id="@+id/videocall_video_preview_off_overlay" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/videocall_video_preview" + android:layout_alignLeft="@+id/videocall_video_preview" + android:layout_alignRight="@+id/videocall_video_preview" + android:layout_alignTop="@+id/videocall_video_preview" + android:scaleType="center" + android:src="@drawable/quantum_ic_videocam_off_white_36" + android:visibility="gone" + android:importantForAccessibility="no" + tools:visibility="visible"/> + + <ImageView + android:id="@+id/videocall_video_preview_mute_overlay" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_alignBottom="@+id/videocall_video_preview" + android:layout_alignRight="@+id/videocall_video_preview" + android:background="@drawable/videocall_background_circle_white" + android:contentDescription="@string/incall_content_description_muted" + android:scaleType="center" + android:src="@drawable/quantum_ic_mic_off_black_24" + android:visibility="gone" + tools:visibility="visible"/> + + <include + layout="@layout/videocall_controls_land" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + + <FrameLayout + android:id="@+id/videocall_on_hold_banner" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true"/> + +</RelativeLayout> diff --git a/java/com/android/incallui/video/impl/res/layout/frag_videocall_surfaceview.xml b/java/com/android/incallui/video/impl/res/layout/frag_videocall_surfaceview.xml new file mode 100644 index 000000000..1b7a9301a --- /dev/null +++ b/java/com/android/incallui/video/impl/res/layout/frag_videocall_surfaceview.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + android:orientation="vertical"> + + <SurfaceView + android:id="@+id/videocall_video_remote" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:importantForAccessibility="no"/> + + <ImageView + android:id="@+id/videocall_remote_off_blurred_image_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:scaleType="fitCenter"/> + + <TextView + android:gravity="center" + android:id="@+id/videocall_remote_video_off" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:accessibilityTraversalBefore="@+id/videocall_speaker_button" + android:drawablePadding="8dp" + android:drawableTop="@drawable/quantum_ic_videocam_off_white_36" + android:drawableTint="@color/videocall_camera_off_tint" + android:padding="64dp" + android:text="@string/videocall_remote_video_off" + android:textAppearance="@style/Dialer.Incall.TextAppearance" + android:visibility="gone" + tools:visibility="visible"/> + + <View + android:id="@+id/videocall_fullscreen_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentBottom="true" + android:layout_alignParentStart="true" + android:background="@color/videocall_overlay_background_color" + tools:visibility="gone"/> + + <SurfaceView + android:id="@+id/videocall_video_preview" + android:layout_width="@dimen/videocall_preview_width" + android:layout_height="@dimen/videocall_preview_height" + android:layout_marginBottom="@dimen/videocall_preview_margin_bottom" + android:layout_marginStart="@dimen/videocall_preview_margin_start" + android:layout_alignParentBottom="true" + android:layout_alignParentStart="true" + android:importantForAccessibility="no"/> + + <ImageView + android:id="@+id/videocall_preview_off_blurred_image_view" + android:layout_width="@dimen/videocall_preview_width" + android:layout_height="@dimen/videocall_preview_height" + android:layout_marginBottom="@dimen/videocall_preview_margin_bottom" + android:layout_marginStart="@dimen/videocall_preview_margin_start" + android:layout_alignParentBottom="true" + android:layout_alignParentStart="true" + android:scaleType="center"/> + + <View + android:id="@+id/videocall_green_screen_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentBottom="true" + android:layout_alignParentStart="true" + android:background="@color/videocall_overlay_background_color" + tools:visibility="gone"/> + + <ImageView + android:id="@+id/videocall_video_preview_off_overlay" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/videocall_video_preview" + android:layout_alignLeft="@+id/videocall_video_preview" + android:layout_alignRight="@+id/videocall_video_preview" + android:layout_alignTop="@+id/videocall_video_preview" + android:scaleType="center" + android:src="@drawable/quantum_ic_videocam_off_white_24" + android:tint="@color/videocall_camera_off_tint" + android:tintMode="src_in" + android:visibility="gone" + android:importantForAccessibility="no" + tools:visibility="visible"/> + + <ImageView + android:id="@+id/videocall_video_preview_mute_overlay" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_alignBottom="@+id/videocall_video_preview" + android:layout_alignRight="@+id/videocall_video_preview" + android:background="@drawable/videocall_background_circle_white" + android:contentDescription="@string/incall_content_description_muted" + android:scaleType="center" + android:src="@drawable/quantum_ic_mic_off_black_24" + android:visibility="gone" + tools:visibility="visible"/> + + <include + layout="@layout/videocall_controls" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + + <FrameLayout + android:id="@+id/videocall_on_hold_banner" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true"/> + +</RelativeLayout> diff --git a/java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java b/java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java index bbd86ee6a..e7e69df2f 100644 --- a/java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java +++ b/java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java @@ -17,6 +17,7 @@ package com.android.incallui.video.protocol; import android.content.Context; +import android.view.SurfaceView; import com.android.incallui.videosurface.protocol.VideoSurfaceTexture; /** Callbacks from the module out to the container. */ @@ -44,5 +45,7 @@ public interface VideoCallScreenDelegate { VideoSurfaceTexture getRemoteVideoSurfaceTexture(); + void setSurfaceViews(SurfaceView preview, SurfaceView remote); + int getDeviceOrientation(); } diff --git a/java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java b/java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java index 285857a23..fb7c78b81 100644 --- a/java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java +++ b/java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java @@ -19,5 +19,5 @@ package com.android.incallui.video.protocol; /** Callbacks from the module out to the container. */ public interface VideoCallScreenDelegateFactory { - VideoCallScreenDelegate newVideoCallScreenDelegate(); + VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen); } |