diff options
Diffstat (limited to 'InCallUI')
7 files changed, 603 insertions, 31 deletions
diff --git a/InCallUI/res/layout/qti_video_call_picture_mode_menu.xml b/InCallUI/res/layout/qti_video_call_picture_mode_menu.xml new file mode 100644 index 000000000..bcb15d5f4 --- /dev/null +++ b/InCallUI/res/layout/qti_video_call_picture_mode_menu.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (c) 2016, The Linux Foundation. All rights reserved. + ~ + ~ Redistribution and use in source and binary forms, with or without + ~ modification, are permitted provided that the following conditions are + ~ met: + ~ Redistributions of source code must retain the above copyright + ~ notice, this list of conditions and the following disclaimer. + ~ Redistributions in binary form must reproduce the above + ~ copyright notice, this list of conditions and the following + ~ disclaimer in the documentation and/or other materials provided + ~ with the distribution. + ~ Neither the name of The Linux Foundation nor the names of its + ~ contributors may be used to endorse or promote products derived + ~ from this software without specific prior written permission. + ~ + ~ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + ~ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + ~ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ~ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + ~ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + ~ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + ~ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + ~ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + ~ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + ~ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + ~ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ~ + --> + +<!-- Video Call Picture mode menu for InCall UI --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="100dp" + android:layout_height="wrap_content" + android:theme="@style/Theme.InCallScreen" + android:orientation="vertical"> + + <CheckBox android:id="@+id/preview_video" + android:text="@string/video_call_picture_mode_preview_video" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginLeft="10dp" + /> + + <CheckBox android:id="@+id/incoming_video" + android:text="@string/video_call_picture_mode_incoming_video" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/preview_video" + android:layout_marginTop="10dp" + android:layout_marginLeft="10dp" + /> +</LinearLayout> diff --git a/InCallUI/res/values/qtistrings.xml b/InCallUI/res/values/qtistrings.xml index ea8a952b5..eba800ab7 100644 --- a/InCallUI/res/values/qtistrings.xml +++ b/InCallUI/res/values/qtistrings.xml @@ -180,4 +180,11 @@ <string name="video_call_downgrade_without_lte_toast">Move out of LTE coverage area downgrade the call.</string> <string name="video_call">Video Calling</string> <string name="video_call_cannot_upgrade">cannot accept video calls at this time</string> + + <!-- Pop up menu options for picture mode --> + <string name="video_call_picture_mode_menu_title">Choose Picture Mode</string> + <string name="video_call_picture_mode_preview_video">Camera Preview</string> + <string name="video_call_picture_mode_incoming_video">Incoming Video</string> + <string name="video_call_picture_mode_cancel_option">Cancel</string> + <string name="video_call_picture_mode_save_option">Save</string> </resources> diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index 69446a95c..1c4890b48 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -1688,6 +1688,11 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr } } + @Override + public void showVbButton(boolean show) { + mVbButton.setVisibility(show ? View.VISIBLE : View.GONE); + } + private void showVbNotify() { Toast vbnotify; int resId = R.string.volume_boost_notify_unavailable; diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index af5a7d9a3..315b57f34 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -1147,6 +1147,11 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> } ui.setCallCardVisible(!isFullscreenMode); ui.setSecondaryInfoVisible(!isFullscreenMode); + final int callState = (mPrimary != null) ? mPrimary.getState() : Call.State.INVALID; + ui.setEndCallButtonEnabled(!isFullscreenMode && + shouldShowEndCallButton(mPrimary, callState), + callState != Call.State.INCOMING /* animate */); + ui.showVbButton(!isFullscreenMode); maybeShowManageConferenceCallButton(); } @@ -1285,5 +1290,6 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> void animateForNewOutgoingCall(); void sendAccessibilityAnnouncement(); void showNoteSentToast(); + void showVbButton(boolean show); } } diff --git a/InCallUI/src/com/android/incallui/PictureModeHelper.java b/InCallUI/src/com/android/incallui/PictureModeHelper.java new file mode 100644 index 000000000..d73ef3b7e --- /dev/null +++ b/InCallUI/src/com/android/incallui/PictureModeHelper.java @@ -0,0 +1,335 @@ +/** + * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.incallui; + +import android.content.Context; +import android.content.res.Resources; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.CheckBox; +import android.widget.ListView; + +import com.android.incallui.InCallPresenter.InCallDetailsListener; +import com.android.incallui.InCallPresenter.InCallStateListener; + +import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.List; + +import com.google.common.base.Preconditions; + +/** + * This class displays the picture mode alert dialog and registers listener who wish to listen to + * user selection for the preview video and the incoming video. + */ +public class PictureModeHelper extends AlertDialog implements InCallDetailsListener, + InCallStateListener, CallList.Listener { + + private AlertDialog mAlertDialog; + + /** + * Indicates whether we should display camera preview video view + */ + private static boolean mShowPreviewVideoView = true; + + /** + * Indicates whether we should display incoming video view + */ + private static boolean mShowIncomingVideoView = true; + + public PictureModeHelper(Context context) { + super(context); + } + + public void setUp(VideoCallPresenter videoCallPresenter) { + InCallPresenter.getInstance().addDetailsListener(this); + InCallPresenter.getInstance().addListener(this); + CallList.getInstance().addListener(this); + addListener(videoCallPresenter); + } + + public void tearDown(VideoCallPresenter videoCallPresenter) { + InCallPresenter.getInstance().removeDetailsListener(this); + InCallPresenter.getInstance().removeListener(this); + CallList.getInstance().removeListener(this); + removeListener(videoCallPresenter); + mAlertDialog = null; + } + + /** + * Displays the alert dialog + */ + public void show() { + if (mAlertDialog != null) { + mAlertDialog.show(); + } + } + + /** + * Listener interface to implement if any class is interested in listening to preview + * video or incoming video selection changed + */ + public interface Listener { + public void onPreviewVideoSelectionChanged(); + public void onIncomingVideoSelectionChanged(); + } + + private final List<Listener> mListeners = new CopyOnWriteArrayList<>(); + + /** + * This method adds a new Listener. Users interested in listening to preview video selection + * and incoming video selection changes must register to this class + * @param Listener listener - the listener to be registered + */ + public void addListener(Listener listener) { + Preconditions.checkNotNull(listener); + mListeners.add(listener); + } + + /** + * This method unregisters the listener listening to preview video selection and incoming + * video selection + * @param Listener listener - the listener to be un-registered + */ + public void removeListener(Listener listener) { + Preconditions.checkNotNull(listener); + mListeners.remove(listener); + } + + /** + * Creates and displays the picture mode alert dialog to enable the user to switch + * between picture modes - Picture in picture, Incoming mode or Camera preview mode + * Once users makes their choice, they can save or cancel. Saving will apply the + * new picture mode to the video call by notifying video call presenter of the change. + * Cancel will dismiss the alert dialog without making any changes. Alert dialog is + * cancelable so pressing home/back key will dismiss the dialog. + * @param Context context - The application context. + */ + public void create(Context context) { + final ArrayList<CharSequence> items = new ArrayList<CharSequence>(); + final Resources res = context.getResources(); + + final InCallActivity inCallActivity = InCallPresenter.getInstance().getActivity(); + if (inCallActivity == null) { + return; + } + + final View checkboxView = inCallActivity.getLayoutInflater(). + inflate(R.layout.qti_video_call_picture_mode_menu, null); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.video_call_picture_mode_menu_title); + builder.setView(checkboxView); + builder.setCancelable(true); + + CheckBox previewVideo = (CheckBox) checkboxView.findViewById(R.id.preview_video); + CheckBox incomingVideo = (CheckBox) checkboxView.findViewById(R.id.incoming_video); + + if (previewVideo == null || incomingVideo == null) { + return; + } + + // Intialize the checkboxes with the proper checked values + previewVideo.setChecked(mShowPreviewVideoView); + incomingVideo.setChecked(mShowIncomingVideoView); + + // Ensure that at least one of the check boxes is enabled. Disable the other checkbox + // if checkbox is un-checked and vice versa. Say for e.g: if preview video was unchecked, + // disble incoming video and make it unclickable + enable(previewVideo, mShowIncomingVideoView); + enable(incomingVideo, mShowPreviewVideoView); + + previewVideo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enable(incomingVideo, ((CheckBox) view).isChecked()); + } + }); + + incomingVideo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enable(previewVideo, ((CheckBox) view).isChecked()); + } + }); + + builder.setPositiveButton(res.getText(R.string.video_call_picture_mode_save_option), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int item) { + mShowPreviewVideoView = previewVideo.isChecked(); + mShowIncomingVideoView = incomingVideo.isChecked(); + notifyOnSelectionChanged(); + } + }); + + builder.setNegativeButton(res.getText(R.string.video_call_picture_mode_cancel_option), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int item) { + } + }); + + mAlertDialog = builder.create(); + setDismissListener(); + } + + private void setDismissListener() { + mAlertDialog.setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + mAlertDialog = null; + } + }); + } + + /** + * This method enables or disables the checkbox passed in based on whether the flag enable + * is set to true or false. Also toggle the checkbox being clickable. + * @param CheckBox checkBox - the check Box to enable/disable + * @param boolean enable - Flag to enable/disable checkbox (true/false) + */ + private void enable(CheckBox checkBox, boolean enable) { + checkBox.setEnabled(enable); + checkBox.setClickable(enable); + } + + /** + * Determines if we can show the preview video view + */ + public boolean canShowPreviewVideoView() { + return mShowPreviewVideoView; + } + + /** + * Determines if we can show the incoming video view + */ + public boolean canShowIncomingVideoView() { + return mShowIncomingVideoView; + } + + /** + * Determines whether we are in Picture in Picture mode + */ + public boolean isPipMode() { + return canShowPreviewVideoView() && canShowIncomingVideoView(); + } + + /** + * Notifies all registered classes of preview video or incoming video selection changed + */ + public void notifyOnSelectionChanged() { + Preconditions.checkNotNull(mListeners); + for (Listener listener : mListeners) { + listener.onPreviewVideoSelectionChanged(); + listener.onIncomingVideoSelectionChanged(); + } + } + + /** + * Listens to call details changed and dismisses the dialog if call has been downgraded to + * voice + * @param Call call - The call for which details changed + * @param android.telecom.Call.Details details - The changed details + */ + @Override + public void onDetailsChanged(Call call, android.telecom.Call.Details details) { + if (call == null) { + return; + } + if (!VideoUtils.isVideoCall(call) && mAlertDialog != null) { + mAlertDialog.dismiss(); + } + } + + /** + * Handles call state changes + * + * @param InCallPresenter.InCallState oldState - The old call state + * @param InCallPresenter.InCallState newState - The new call state + * @param CallList callList - The call list. + */ + @Override + public void onStateChange(InCallPresenter.InCallState oldState, + InCallPresenter.InCallState newState, CallList callList) { + Log.d(this, "onStateChange oldState" + oldState + " newState=" + newState); + + if (newState == InCallPresenter.InCallState.NO_CALLS) { + // Set both display preview video and incoming video to true as default display mode is + // to show picture in picture. + mShowPreviewVideoView = true; + mShowIncomingVideoView = true; + } + } + + /** + * Overrides onIncomingCall method of {@interface CallList.Listener} + * @param Call call - The incoming call + */ + @Override + public void onIncomingCall(Call call) { + if (mAlertDialog != null) { + mAlertDialog.dismiss(); + } + } + + /** + * Overrides onCallListChange method of {@interface CallList.Listener} + * Added for completeness + */ + @Override + public void onCallListChange(CallList list) { + // no-op + } + + /** + * Overrides onUpgradeToVideo method of {@interface CallList.Listener} + * @param Call call - The call to be upgraded + */ + @Override + public void onUpgradeToVideo(Call call) { + if (mAlertDialog != null) { + mAlertDialog.dismiss(); + } + } + + /** + * Overrides onDisconnect method of {@interface CallList.Listener} + * @param Call call - The call to be disconnected + */ + @Override + public void onDisconnect(Call call) { + // no-op + } +} diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java index 63a031044..cea9c4d90 100644 --- a/InCallUI/src/com/android/incallui/VideoCallFragment.java +++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java @@ -21,6 +21,7 @@ import android.graphics.Point; import android.graphics.SurfaceTexture; import android.os.Bundle; import android.view.Display; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.Surface; import android.view.TextureView; @@ -115,7 +116,7 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, * changes. */ private static class VideoCallSurface implements TextureView.SurfaceTextureListener, - View.OnClickListener, View.OnAttachStateChangeListener { + View.OnClickListener, View.OnAttachStateChangeListener, View.OnLongClickListener { private int mSurfaceId; private VideoCallPresenter mPresenter; private TextureView mTextureView; @@ -174,6 +175,7 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, mTextureView = view; mTextureView.setSurfaceTextureListener(this); mTextureView.setOnClickListener(this); + mTextureView.setOnLongClickListener(this); final boolean areSameSurfaces = Objects.equal(mSavedSurfaceTexture, mTextureView.getSurfaceTexture()); @@ -412,6 +414,18 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, } /** + * Handles a user long pressing on the surface, which is the trigger to show the + * picture mode pop up alert dialog + * + * @param View The view receiving the long press. + */ + @Override + public boolean onLongClick(View v) { + Log.d(this, "onLongClick:"); + return mPresenter.onLongClick(); + } + + /** * Returns the dimensions of the surface. * * @return The dimensions of the surface. @@ -690,9 +704,15 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, preview.setLayoutParams(params); if (mPreviewVideoContainer != null) { - ViewGroup.LayoutParams containerParams = mPreviewVideoContainer.getLayoutParams(); + FrameLayout.LayoutParams containerParams = (FrameLayout.LayoutParams) + mPreviewVideoContainer.getLayoutParams(); containerParams.width = width; containerParams.height = height; + if (getPresenter().isCameraPreviewMode()) { + containerParams.gravity = Gravity.CENTER; + } else { + containerParams.gravity = Gravity.BOTTOM | Gravity.RIGHT; + } mPreviewVideoContainer.setLayoutParams(containerParams); } diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java index c38e4dd27..e08f4d516 100644 --- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java +++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; +import android.os.SystemProperties; import android.provider.ContactsContract; import android.content.pm.ActivityInfo; import android.telecom.Connection; @@ -72,7 +73,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi IncomingCallListener, InCallOrientationListener, InCallStateListener, InCallDetailsListener, SurfaceChangeListener, VideoEventListener, InCallPresenter.InCallEventListener, InCallUiStateNotifierListener, - CallList.CallUpdateListener { + CallList.CallUpdateListener, PictureModeHelper.Listener { public static final String TAG = "VideoCallPresenter"; public static final boolean DEBUG = false; @@ -216,12 +217,36 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi private static boolean mIsIncomingVideoAvailable = false; /** + * Property when set will disable PIP mode. + * Default value is 0 (disable). To enable, set to 1 (enable) + */ + private static final String PROP_DISABLE_VIDEOCALL_PIP_MODE = + "persist.disable.pip.mode"; + + /** + * Property set to specify the camera preview size when the picture mode is selected as + * camera preview mode only. Format is widthxheight (e.g 320x240) + */ + private static final String PROP_CAMERA_PREVIEW_SIZE = + "persist.camera.preview.size"; + + private static final String CAMERA_PREVIEW_SIZE_DELIM = "x"; + + /** + * Cache the aspect ratio of the preview window. + */ + private float mPreviewAspectRatio = 1.0f; + + private PictureModeHelper mPictureModeHelper; + + /** * Initializes the presenter. * * @param context The current context. */ public void init(Context context) { mContext = context; + mPictureModeHelper = new PictureModeHelper(mContext); mMinimumVideoDimension = mContext.getResources().getDimension( R.dimen.video_preview_small_dimension); mHandler = new Handler(Looper.getMainLooper()); @@ -255,6 +280,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi // To get updates of video call details changes InCallPresenter.getInstance().addDetailsListener(this); InCallPresenter.getInstance().addInCallEventListener(this); + mPictureModeHelper.setUp(this); // Register for surface and video events from {@link InCallVideoCallListener}s. InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this); @@ -295,6 +321,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi if(mPrimaryCall != null) { CallList.getInstance().removeCallUpdateListener(mPrimaryCall.getId(), this); } + mPictureModeHelper.tearDown(this); } /** @@ -330,9 +357,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi // whether PLAYER_START event has been received or not. Since we // start with showing incoming video by default for surface creation, // we need to make sure we hide it once surface is available. - boolean isConf = (mPrimaryCall != null ? mPrimaryCall.isConferenceCall() : - false); - showVideoUi(mCurrentVideoState, mCurrentCallState, isConf); + showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall()); } } @@ -414,10 +439,16 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi switch (surfaceId) { case VideoCallFragment.SURFACE_DISPLAY: boolean isFullscreen = InCallPresenter.getInstance().toggleFullscreenMode(); - Log.d(this, "toggleFullScreen = " + isFullscreen); + Log.d(this, "toggleFullScreen = " + isFullscreen + "surfaceId =" + surfaceId); break; case VideoCallFragment.SURFACE_PREVIEW: - InCallZoomController.getInstance().onPreviewSurfaceClicked(mVideoCall); + if (mPictureModeHelper.canShowPreviewVideoView() && + mPictureModeHelper.canShowIncomingVideoView()) { + InCallZoomController.getInstance().onPreviewSurfaceClicked(mVideoCall); + } else { + isFullscreen = InCallPresenter.getInstance().toggleFullscreenMode(); + Log.d(this, "toggleFullScreen = " + isFullscreen + "surfaceId =" + surfaceId); + } break; default: break; @@ -454,7 +485,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi if (isVideoMode()) { exitVideoMode(); } - cleanupSurfaces(); } @@ -827,9 +857,11 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi final boolean isDisplaySurfaceCreated = ui.isDisplayVideoSurfaceCreated(); final boolean isVideoReceptionEnabled = VideoProfile.isReceptionEnabled(videoState); - boolean showIncomingVideo = showIncomingVideo(videoState, callState) || + boolean showIncomingVideo = (showIncomingVideo(videoState, callState) && + mPictureModeHelper.canShowIncomingVideoView()) || (!isDisplaySurfaceCreated && isVideoReceptionEnabled); - boolean showOutgoingVideo = showOutgoingVideo(videoState); + boolean showOutgoingVideo = showOutgoingVideo(videoState) && + mPictureModeHelper.canShowPreviewVideoView(); Log.v(this, "showVideoUi : showIncoming = " + showIncomingVideo + " showOutgoing = " + showOutgoingVideo); @@ -838,7 +870,13 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi boolean hidePreview = shallHidePreview(isConf, videoState); Log.v(this, "showVideoUi, hidePreview = " + hidePreview); - ui.showOutgoingVideoView(!hidePreview); + if (hidePreview) { + ui.showOutgoingVideoView(!hidePreview); + } + + if (showOutgoingVideo) { + setPreviewSize(mDeviceOrientation, mPreviewAspectRatio); + } if (isVideoReceptionEnabled) { loadProfilePhotoAsync(); @@ -1034,9 +1072,11 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi aspectRatio = (float) width / (float) height; } + mPreviewAspectRatio = aspectRatio; + // Resize the textureview housing the preview video and rotate it appropriately based on // the device orientation - setPreviewSize(mDeviceOrientation, aspectRatio); + setPreviewSize(mDeviceOrientation, mPreviewAspectRatio); } /** @@ -1054,9 +1094,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi case Connection.VideoProvider.SESSION_EVENT_RX_RESUME: mIsIncomingVideoAvailable = event == Connection.VideoProvider.SESSION_EVENT_RX_RESUME; - boolean isConf = (mPrimaryCall != null ? mPrimaryCall.isConferenceCall() : - false); - showVideoUi(mCurrentVideoState, mCurrentCallState, isConf); + showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall()); sb.append(mIsIncomingVideoAvailable ? "rx_resume" : "rx_pause"); break; case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE: @@ -1110,6 +1148,9 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi changePreviewDimensions(previewDimensions.x, previewDimensions.y); ui.setPreviewRotation(mDeviceOrientation); + // Notify picture mode changed so that if camera preview is showing in non PIP + // mode, we can correctly resize the camera preview by swapping width and height. + showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall()); } /** @@ -1130,24 +1171,38 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi return; } - final int adjustedDimension = (aspectRatio > 1.0) ? - (int) (mMinimumVideoDimension * aspectRatio): - (int) (mMinimumVideoDimension / aspectRatio); + float height = 0.0f; + float width = 0.0f; + final boolean isPipMode = mPictureModeHelper.isPipMode(); - int height; - int width; - - // For landscape resolution, make sure width is larger than height. - if (orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_90 || - orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_270) { - width = (int) adjustedDimension; - height = (int) mMinimumVideoDimension; + if (isPipMode) { + width = mMinimumVideoDimension; + height = mMinimumVideoDimension; } else { - // Portrait or reverse portrait orientation. - width = (int) mMinimumVideoDimension; - height = (int) adjustedDimension; + Point size = getPreviewVideoSize(); + // Swap width and height if landscape + final boolean isLayoutLandscape = mContext.getResources().getBoolean( + R.bool.is_layout_landscape); + width = isLayoutLandscape ? size.y : size.x; + height = isLayoutLandscape ? size.x : size.y; + } + + final boolean hasNoPreviewSizeInProp = ((SystemProperties.get( + PROP_CAMERA_PREVIEW_SIZE, "")).isEmpty()); + + // Do not apply aspect ratio if camera preview is set in the adb property - + // "persist.camera.preview.size". Aspect ratio is applied to full screen size for + // camera preview and for Pip mode + if (hasNoPreviewSizeInProp || isPipMode) { + if (orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_90 || + orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_270) { + width = (aspectRatio > 1.0) ? width * aspectRatio : width / aspectRatio; + } else { + // Portrait or reverse portrait orientation. + height = (aspectRatio > 1.0) ? height * aspectRatio : height / aspectRatio; + } } - ui.setPreviewSize(width, height); + ui.setPreviewSize((int) width, (int) height); } /** @@ -1331,6 +1386,83 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi } /** + * The function is called to create and display picture mode alert dialog when user long + * presses on the video call screen + */ + public boolean onLongClick() { + // Don't show the alert if either the adb property "persist.disable.pip.mode" is not set + // or if we are supposed to hide preview for conference calls + if ((SystemProperties.getInt(PROP_DISABLE_VIDEOCALL_PIP_MODE, 0) == 0) || + shallHidePreview(isConfCall(), mCurrentVideoState)) { + return false; + } + mPictureModeHelper.create(mContext); + mPictureModeHelper.show(); + return true; + } + + /** + * Gets the preview video size either from the property - "persist.camera.preview.size" if it + * is set or return the full screen size + */ + private Point getPreviewVideoSize() { + VideoCallUi ui = getUi(); + if (ui == null) { + Log.e(this, "getPreviewVideoSize, VideoCallUi is null returning"); + return null; + } + + Point previewSize = getPreviewVideoSizeFromProp(); + + if (previewSize == null) { + previewSize = ui.getScreenSize(); + } + + return previewSize; + } + + /** + * Gets the preview video size from the property - "persist.camera.preview.size" + * @return Point point - Size of the preview (width and height) + */ + private static Point getPreviewVideoSizeFromProp() { + final String cameraPreviewSize = SystemProperties.get( + PROP_CAMERA_PREVIEW_SIZE, ""); + if (!cameraPreviewSize.isEmpty()) { + final String[] sizeDimensions = cameraPreviewSize.split(CAMERA_PREVIEW_SIZE_DELIM); + final int width = Integer.parseInt(sizeDimensions[0]); + final int height = Integer.parseInt(sizeDimensions[1]); + return new Point(width, height); + } + return null; + } + + /** + * Gets called when preview video selection changes + * @param boolean previewVideoSelection - New value for preview video selection + */ + @Override + public void onPreviewVideoSelectionChanged() { + VideoCallUi ui = getUi(); + if (ui == null) { + Log.e(this, "onPreviewVideoSelectionChanged, VideoCallUi is null returning"); + return; + } + + ui.showOutgoingVideoView(showOutgoingVideo(mCurrentVideoState) && + mPictureModeHelper.canShowPreviewVideoView()); + } + + /** + * Gets called when incoming video selection changes + * @param boolean incomingVideoSelection - New value for incoming video selection + */ + @Override + public void onIncomingVideoSelectionChanged() { + showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall()); + } + + /** * Starts an asynchronous load of the user's profile photo. */ public void loadProfilePhotoAsync() { @@ -1418,6 +1550,15 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi && QtiImsExtUtils.shallHidePreviewInVtConference(mContext); } + private boolean isConfCall() { + return mPrimaryCall != null ? mPrimaryCall.isConferenceCall() : false; + } + + public boolean isCameraPreviewMode() { + return mPictureModeHelper.canShowPreviewVideoView() && + !(mPictureModeHelper.canShowIncomingVideoView()); + } + /** * Defines the VideoCallUI interactions. */ |