summaryrefslogtreecommitdiff
path: root/java/com/android/incallui
diff options
context:
space:
mode:
authorEric Erfanian <erfanian@google.com>2017-03-20 08:50:25 -0700
committerEric Erfanian <erfanian@google.com>2017-03-20 09:03:10 -0700
commit06b6b56e9eaa91ebf757ea641e38a9c885fa40bd (patch)
tree6a0f9f0809cc02004f4be6ef9d20cbced397c3d1 /java/com/android/incallui
parent9a090c89e7a0c43a668bd6e2ac5e5e680f323aed (diff)
Update AOSP Dialer source from internal google3 repository at
cl/150622237 Test: make, treehugger, on device testing. This CL updates the AOSP Dialer source with all the changes that have gone into the private google3 repository. This includes all the changes from cl/150392808 (3/16/2017) to cl/150622237 (3/20/2017). This goal of these drops is to keep the AOSP source in sync with the internal google3 repository. Currently these sync are done by hand with very minor modifications to the internal source code. See the Android.mk file for list of modifications. Our current goal is to do frequent drops (daily if possible) and eventually switched to an automated process. Change-Id: Id53e0e580a4ef73760a8afb7bb8c265ee27ad535
Diffstat (limited to 'java/com/android/incallui')
-rw-r--r--java/com/android/incallui/InCallActivity.java6
-rw-r--r--java/com/android/incallui/InCallPresenter.java2
-rw-r--r--java/com/android/incallui/answer/bindings/AnswerBindings.java8
-rw-r--r--java/com/android/incallui/answer/impl/AnswerFragment.java19
-rw-r--r--java/com/android/incallui/answer/impl/FixedAspectSurfaceView.java86
-rw-r--r--java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java268
-rw-r--r--java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml20
-rw-r--r--java/com/android/incallui/answer/impl/res/values-land/dimens.xml20
-rw-r--r--java/com/android/incallui/answer/impl/res/values/attrs.xml26
-rw-r--r--java/com/android/incallui/answer/impl/res/values/dimens.xml3
-rw-r--r--java/com/android/incallui/calllocation/CallLocationComponent.java20
-rw-r--r--java/com/android/incallui/calllocation/impl/HttpFetcher.java2
-rw-r--r--java/com/android/incallui/calllocation/stub/StubCallLocationModule.java6
-rw-r--r--java/com/android/incallui/incall/impl/AndroidManifest.xml4
-rw-r--r--java/com/android/incallui/incall/impl/InCallFragment.java29
-rw-r--r--java/com/android/incallui/incall/impl/InCallPaginator.java210
-rw-r--r--java/com/android/incallui/incall/impl/LockableViewPager.java46
-rw-r--r--java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml20
-rw-r--r--java/com/android/incallui/incall/impl/res/values/dimens.xml5
-rw-r--r--java/com/android/incallui/incall/impl/res/values/styles.xml3
-rw-r--r--java/com/android/incallui/maps/MapsComponent.java21
-rw-r--r--java/com/android/incallui/maps/stub/StubMapsModule.java2
-rw-r--r--java/com/android/incallui/res/values-uz/strings.xml2
-rw-r--r--java/com/android/incallui/videotech/VideoTech.java6
-rw-r--r--java/com/android/incallui/videotech/empty/EmptyVideoTech.java5
-rw-r--r--java/com/android/incallui/videotech/ims/ImsVideoTech.java7
-rw-r--r--java/com/android/incallui/videotech/rcs/RcsVideoShare.java5
27 files changed, 777 insertions, 74 deletions
diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java
index 7c4394872..395829b80 100644
--- a/java/com/android/incallui/InCallActivity.java
+++ b/java/com/android/incallui/InCallActivity.java
@@ -625,7 +625,11 @@ public class InCallActivity extends TransactionSafeFragmentActivity
// Show a new answer screen.
AnswerScreen answerScreen =
- AnswerBindings.createAnswerScreen(call.getId(), call.isVideoCall(), isVideoUpgradeRequest);
+ AnswerBindings.createAnswerScreen(
+ call.getId(),
+ call.isVideoCall(),
+ isVideoUpgradeRequest,
+ call.getVideoTech().isSelfManagedCamera());
transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
index 0f3982ce4..ea7bc9cc3 100644
--- a/java/com/android/incallui/InCallPresenter.java
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -758,7 +758,7 @@ public class InCallPresenter implements CallList.Listener {
}
if (!call.getLogState().isIncoming && !mCallList.hasLiveCall()) {
- PostCall.onCallDisconnected(mContext, call.getNumber(), call.getConnectTimeMillis());
+ PostCall.onCallDisconnected(mContext, call.getNumber(), call.getTimeAddedMs());
}
}
diff --git a/java/com/android/incallui/answer/bindings/AnswerBindings.java b/java/com/android/incallui/answer/bindings/AnswerBindings.java
index 442e207a0..8be283990 100644
--- a/java/com/android/incallui/answer/bindings/AnswerBindings.java
+++ b/java/com/android/incallui/answer/bindings/AnswerBindings.java
@@ -23,7 +23,11 @@ import com.android.incallui.answer.protocol.AnswerScreen;
public class AnswerBindings {
public static AnswerScreen createAnswerScreen(
- String callId, boolean isVideoCall, boolean isVideoUpgradeRequest) {
- return AnswerFragment.newInstance(callId, isVideoCall, isVideoUpgradeRequest);
+ String callId,
+ boolean isVideoCall,
+ boolean isVideoUpgradeRequest,
+ boolean isSelfManagedCamera) {
+ return AnswerFragment.newInstance(
+ callId, isVideoCall, isVideoUpgradeRequest, isSelfManagedCamera);
}
}
diff --git a/java/com/android/incallui/answer/impl/AnswerFragment.java b/java/com/android/incallui/answer/impl/AnswerFragment.java
index 6874daea3..a6174686c 100644
--- a/java/com/android/incallui/answer/impl/AnswerFragment.java
+++ b/java/com/android/incallui/answer/impl/AnswerFragment.java
@@ -106,6 +106,9 @@ public class AnswerFragment extends Fragment
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static final String ARG_IS_VIDEO_UPGRADE_REQUEST = "is_video_upgrade_request";
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String ARG_IS_SELF_MANAGED_CAMERA = "is_self_managed_camera";
+
private static final String STATE_HAS_ANIMATED_ENTRY = "hasAnimated";
private static final int HINT_SECONDARY_SHOW_DURATION_MILLIS = 5000;
@@ -288,11 +291,15 @@ public class AnswerFragment extends Fragment
}
public static AnswerFragment newInstance(
- String callId, boolean isVideoCall, boolean isVideoUpgradeRequest) {
+ String callId,
+ boolean isVideoCall,
+ boolean isVideoUpgradeRequest,
+ boolean isSelfManagedCamera) {
Bundle bundle = new Bundle();
bundle.putString(ARG_CALL_ID, Assert.isNotNull(callId));
bundle.putBoolean(ARG_IS_VIDEO_CALL, isVideoCall);
bundle.putBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST, isVideoUpgradeRequest);
+ bundle.putBoolean(ARG_IS_SELF_MANAGED_CAMERA, isSelfManagedCamera);
AnswerFragment instance = new AnswerFragment();
instance.setArguments(bundle);
@@ -620,7 +627,11 @@ public class AnswerFragment extends Fragment
view.setSystemUiVisibility(flags);
if (isVideoCall() || isVideoUpgradeRequest()) {
if (VideoUtils.hasCameraPermissionAndAllowedByUser(getContext())) {
- answerVideoCallScreen = new AnswerVideoCallScreen(getCallId(), this, view);
+ if (isSelfManagedCamera()) {
+ answerVideoCallScreen = new SelfManagedAnswerVideoCallScreen(getCallId(), this, view);
+ } else {
+ answerVideoCallScreen = new AnswerVideoCallScreen(getCallId(), this, view);
+ }
} else {
view.findViewById(R.id.videocall_video_off).setVisibility(View.VISIBLE);
}
@@ -718,6 +729,10 @@ public class AnswerFragment extends Fragment
return getArguments().getBoolean(ARG_IS_VIDEO_CALL);
}
+ public boolean isSelfManagedCamera() {
+ return getArguments().getBoolean(ARG_IS_SELF_MANAGED_CAMERA);
+ }
+
@Override
public void onAnswerProgressUpdate(@FloatRange(from = -1f, to = 1f) float answerProgress) {
// Don't fade the window background for call waiting or video upgrades. Fading the background
diff --git a/java/com/android/incallui/answer/impl/FixedAspectSurfaceView.java b/java/com/android/incallui/answer/impl/FixedAspectSurfaceView.java
new file mode 100644
index 000000000..ad7d94d95
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/FixedAspectSurfaceView.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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.answer.impl;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.SurfaceView;
+import android.view.View;
+import com.android.dialer.common.Assert;
+
+/**
+ * A SurfaceView that maintains its aspect ratio to be a desired target value.
+ *
+ * <p>The FixedAspectSurfaceView will not be able to maintain the requested aspect ratio if both the
+ * width and the height are exactly determined by the layout. To avoid this, ensure that either the
+ * height or the width is adjustable by the view; for example, by setting the layout parameters to
+ * be WRAP_CONTENT for the dimension that is best adjusted to maintain the aspect ratio.
+ */
+public class FixedAspectSurfaceView extends SurfaceView {
+
+ /** Desired width/height ratio */
+ private float mAspectRatio;
+
+ private final boolean scaleWidth;
+ private final boolean scaleHeight;
+
+ public FixedAspectSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Get initial aspect ratio from custom attributes
+ TypedArray a =
+ context.getTheme().obtainStyledAttributes(attrs, R.styleable.FixedAspectSurfaceView, 0, 0);
+ scaleHeight = a.getBoolean(R.styleable.FixedAspectSurfaceView_scaleHeight, false);
+ scaleWidth = a.getBoolean(R.styleable.FixedAspectSurfaceView_scaleWidth, false);
+ Assert.checkArgument(scaleHeight != scaleWidth, "Must either scale width or height");
+ setAspectRatio(a.getFloat(R.styleable.FixedAspectSurfaceView_aspectRatio, 1.f));
+ a.recycle();
+ }
+
+ /**
+ * Set the desired aspect ratio for this view.
+ *
+ * @param aspect the desired width/height ratio in the current UI orientation. Must be a positive
+ * value.
+ */
+ public void setAspectRatio(float aspect) {
+ Assert.checkArgument(aspect >= 0, "Aspect ratio must be positive");
+ mAspectRatio = aspect;
+ requestLayout();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ // Do the scaling
+ if (scaleWidth) {
+ width = (int) (height * mAspectRatio);
+ } else if (scaleHeight) {
+ height = (int) (width / mAspectRatio);
+ }
+
+ // Override width/height if needed for EXACTLY and AT_MOST specs
+ width = View.resolveSizeAndState(width, widthMeasureSpec, 0);
+ height = View.resolveSizeAndState(height, heightMeasureSpec, 0);
+
+ // Finally set the calculated dimensions
+ setMeasuredDimension(width, height);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java b/java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java
new file mode 100644
index 000000000..522d77235
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2017 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.answer.impl;
+
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.StateCallback;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.Size;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.video.protocol.VideoCallScreen;
+import java.util.Arrays;
+
+/**
+ * Shows the local preview for the incoming video call or video upgrade request. This class is used
+ * for RCS Video Share where we need to open the camera preview ourselves. For IMS Video the camera
+ * is managed by the modem, see {@link AnswerVideoCallScreen}.
+ */
+public class SelfManagedAnswerVideoCallScreen extends StateCallback implements VideoCallScreen {
+
+ private static final int MAX_WIDTH = 1920;
+ private static final float ASPECT_TOLERANCE = 0.1f;
+ private static final float TARGET_ASPECT = 16.f / 9.f;
+
+ @NonNull private final String callId;
+ @NonNull private final Fragment fragment;
+ @NonNull private final FixedAspectSurfaceView surfaceView;
+ private final Context context;
+
+ private String cameraId;
+ private CameraDevice camera;
+ private CaptureRequest.Builder captureRequestBuilder;
+
+ public SelfManagedAnswerVideoCallScreen(
+ @NonNull String callId, @NonNull Fragment fragment, @NonNull View view) {
+ this.callId = Assert.isNotNull(callId);
+ this.fragment = Assert.isNotNull(fragment);
+ this.context = Assert.isNotNull(fragment.getContext());
+
+ surfaceView =
+ Assert.isNotNull(
+ (FixedAspectSurfaceView) view.findViewById(R.id.incoming_preview_surface_view));
+ surfaceView.setVisibility(View.VISIBLE);
+ view.findViewById(R.id.incoming_preview_texture_view_overlay).setVisibility(View.VISIBLE);
+ view.setBackgroundColor(0xff000000);
+ }
+
+ @Override
+ public void onVideoScreenStart() {
+ openCamera();
+ }
+
+ @Override
+ public void onVideoScreenStop() {
+ closeCamera();
+ }
+
+ @Override
+ public void showVideoViews(
+ boolean shouldShowPreview, boolean shouldShowRemote, boolean isRemotelyHeld) {}
+
+ @Override
+ public void onLocalVideoDimensionsChanged() {}
+
+ @Override
+ public void onLocalVideoOrientationChanged() {}
+
+ @Override
+ public void onRemoteVideoDimensionsChanged() {}
+
+ @Override
+ public void updateFullscreenAndGreenScreenMode(
+ boolean shouldShowFullscreen, boolean shouldShowGreenScreen) {}
+
+ @Override
+ public Fragment getVideoCallScreenFragment() {
+ return fragment;
+ }
+
+ @Override
+ public String getCallId() {
+ return callId;
+ }
+
+ /**
+ * Opens the first front facing camera on the device into a {@link SurfaceView} while preserving
+ * aspect ratio.
+ */
+ private void openCamera() {
+ CameraManager manager = context.getSystemService(CameraManager.class);
+
+ StreamConfigurationMap configMap = getFrontFacingCameraSizes(manager);
+ if (configMap == null) {
+ return;
+ }
+
+ Size previewSize = getOptimalSize(configMap.getOutputSizes(SurfaceHolder.class));
+ LogUtil.i("SelfManagedAnswerVideoCallScreen.openCamera", "Optimal size: " + previewSize);
+ float outputAspect = (float) previewSize.getWidth() / previewSize.getHeight();
+ surfaceView.setAspectRatio(outputAspect);
+ surfaceView.getHolder().setFixedSize(previewSize.getWidth(), previewSize.getHeight());
+
+ try {
+ manager.openCamera(cameraId, this, null);
+ } catch (CameraAccessException e) {
+ LogUtil.e("SelfManagedAnswerVideoCallScreen.openCamera", "failed to open camera", e);
+ }
+ }
+
+ @Nullable
+ private StreamConfigurationMap getFrontFacingCameraSizes(CameraManager manager) {
+ String[] cameraIds;
+ try {
+ cameraIds = manager.getCameraIdList();
+ } catch (CameraAccessException e) {
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.getFrontFacingCameraSizes",
+ "failed to get camera ids",
+ e);
+ return null;
+ }
+
+ for (String cameraId : cameraIds) {
+ CameraCharacteristics characteristics;
+ try {
+ characteristics = manager.getCameraCharacteristics(cameraId);
+ } catch (CameraAccessException e) {
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.getFrontFacingCameraSizes",
+ "failed to get camera characteristics",
+ e);
+ continue;
+ }
+
+ if (characteristics.get(CameraCharacteristics.LENS_FACING)
+ != CameraCharacteristics.LENS_FACING_FRONT) {
+ continue;
+ }
+
+ StreamConfigurationMap configMap =
+ characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ if (configMap == null) {
+ continue;
+ }
+
+ this.cameraId = cameraId;
+ return configMap;
+ }
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.getFrontFacingCameraSizes", "No valid configurations.");
+ return null;
+ }
+
+ /**
+ * Given an array of {@link Size}s, tries to find the largest Size such that the aspect ratio of
+ * the returned size is within {@code ASPECT_TOLERANCE} of {@code TARGET_ASPECT}. This is useful
+ * because it provides us with an adequate size/camera resolution that will experience the least
+ * stretching from our fullscreen UI that doesn't match any of the camera sizes.
+ */
+ private static Size getOptimalSize(Size[] outputSizes) {
+ Size bestCandidateSize = outputSizes[0];
+ float bestCandidateAspect =
+ (float) bestCandidateSize.getWidth() / bestCandidateSize.getHeight();
+
+ for (Size candidateSize : outputSizes) {
+ if (candidateSize.getWidth() < MAX_WIDTH) {
+ float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight();
+ boolean isGoodCandidateAspect =
+ Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
+ boolean isGoodOutputAspect =
+ Math.abs(bestCandidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
+
+ if ((isGoodCandidateAspect && !isGoodOutputAspect)
+ || candidateSize.getWidth() > bestCandidateSize.getWidth()) {
+ bestCandidateSize = candidateSize;
+ bestCandidateAspect = candidateAspect;
+ }
+ }
+ }
+ return bestCandidateSize;
+ }
+
+ @Override
+ public void onOpened(CameraDevice camera) {
+ LogUtil.i("SelfManagedAnswerVideoCallScreen.opOpened", "camera opened.");
+ this.camera = camera;
+ Surface surface = surfaceView.getHolder().getSurface();
+ try {
+ captureRequestBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ captureRequestBuilder.addTarget(surface);
+ camera.createCaptureSession(Arrays.asList(surface), new CaptureSessionCallback(), null);
+ } catch (CameraAccessException e) {
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.createCameraPreview", "failed to create preview", e);
+ }
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ closeCamera();
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ closeCamera();
+ }
+
+ private void closeCamera() {
+ if (camera != null) {
+ camera.close();
+ camera = null;
+ }
+ }
+
+ private class CaptureSessionCallback extends CameraCaptureSession.StateCallback {
+
+ @Override
+ public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
+ LogUtil.i(
+ "SelfManagedAnswerVideoCallScreen.onConfigured", "camera capture session configured.");
+ // The camera is already closed.
+ if (camera == null) {
+ return;
+ }
+
+ // When the session is ready, we start displaying the preview.
+ captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
+ try {
+ cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
+ } catch (CameraAccessException e) {
+ LogUtil.e("CaptureSessionCallback.onConfigured", "failed to configure", e);
+ }
+ }
+
+ @Override
+ public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
+ LogUtil.e("CaptureSessionCallback.onConfigureFailed", "failed to configure");
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml b/java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml
index aa153dd4b..042e7b82f 100644
--- a/java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml
+++ b/java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml
@@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
<com.android.incallui.answer.impl.AffordanceHolderLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -27,11 +26,20 @@
android:keepScreenOn="true">
<TextureView
- android:id="@+id/incoming_preview_texture_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- android:visibility="gone"/>
+ android:id="@+id/incoming_preview_texture_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ android:visibility="gone"/>
+
+ <com.android.incallui.answer.impl.FixedAspectSurfaceView
+ android:id="@+id/incoming_preview_surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ android:visibility="gone"
+ app:scaleWidth="@bool/scale_width"
+ app:scaleHeight="@bool/scale_height"/>
<View
android:id="@+id/incoming_preview_texture_view_overlay"
diff --git a/java/com/android/incallui/answer/impl/res/values-land/dimens.xml b/java/com/android/incallui/answer/impl/res/values-land/dimens.xml
new file mode 100644
index 000000000..5e2a88ae9
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values-land/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+<resources>
+ <bool name="scale_width">false</bool>
+ <bool name="scale_height">true</bool>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/res/values/attrs.xml b/java/com/android/incallui/answer/impl/res/values/attrs.xml
new file mode 100644
index 000000000..1086e1ca5
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values/attrs.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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.
+-->
+<resources>
+ <declare-styleable name="FixedAspectSurfaceView">
+ <attr name="aspectRatio" format="float" />
+ <attr name="scaleWidth" format="boolean"/>
+ <attr name="scaleHeight" format="boolean"/>
+ </declare-styleable>
+
+ <item name="match_parent" type="dimen">-1</item>
+ <item name="wrap_content" type="dimen">-2</item>
+</resources> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/res/values/dimens.xml b/java/com/android/incallui/answer/impl/res/values/dimens.xml
index 8329707a6..50aec0328 100644
--- a/java/com/android/incallui/answer/impl/res/values/dimens.xml
+++ b/java/com/android/incallui/answer/impl/res/values/dimens.xml
@@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
<resources>
<dimen name="answer_contact_name_text_size">24sp</dimen>
<dimen name="answer_contact_name_min_size">24sp</dimen>
@@ -22,5 +21,7 @@
<dimen name="answer_avatar_size">0dp</dimen>
<dimen name="answer_importance_margin_bottom">0dp</dimen>
<bool name="answer_important_call_allowed">false</bool>
+ <bool name="scale_width">true</bool>
+ <bool name="scale_height">false</bool>
<integer name="answer_animate_entry_millis">1000</integer>
</resources>
diff --git a/java/com/android/incallui/calllocation/CallLocationComponent.java b/java/com/android/incallui/calllocation/CallLocationComponent.java
index 6b1faf299..46ca669db 100644
--- a/java/com/android/incallui/calllocation/CallLocationComponent.java
+++ b/java/com/android/incallui/calllocation/CallLocationComponent.java
@@ -17,26 +17,18 @@
package com.android.incallui.calllocation;
import android.content.Context;
+import com.android.dialer.inject.HasRootComponent;
import dagger.Subcomponent;
-import com.android.incallui.calllocation.stub.StubCallLocationModule;
/** Subcomponent that can be used to access the call location implementation. */
-public class CallLocationComponent {
- private static CallLocationComponent instance;
- private CallLocation callLocation;
+@Subcomponent
+public abstract class CallLocationComponent {
- public CallLocation getCallLocation(){
- if (callLocation == null) {
- callLocation = new StubCallLocationModule.StubCallLocation();
- }
- return callLocation;
- }
+ public abstract CallLocation getCallLocation();
public static CallLocationComponent get(Context context) {
- if (instance == null) {
- instance = new CallLocationComponent();
- }
- return instance;
+ return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
+ .callLocationComponent();
}
/** Used to refer to the root application component. */
diff --git a/java/com/android/incallui/calllocation/impl/HttpFetcher.java b/java/com/android/incallui/calllocation/impl/HttpFetcher.java
index c4aaa2257..7bfbaa6ef 100644
--- a/java/com/android/incallui/calllocation/impl/HttpFetcher.java
+++ b/java/com/android/incallui/calllocation/impl/HttpFetcher.java
@@ -223,6 +223,8 @@ public class HttpFetcher {
/**
* Lookup up url re-write rules from gServices and apply to the given url.
*
+ * <p>https://wiki.corp.google.com/twiki/bin/view/Main/AndroidGservices#URL_Rewriting_Rules
+ *
* @return The new url.
*/
private static URL reWriteUrl(Context context, String url) {
diff --git a/java/com/android/incallui/calllocation/stub/StubCallLocationModule.java b/java/com/android/incallui/calllocation/stub/StubCallLocationModule.java
index fc198c724..20460699a 100644
--- a/java/com/android/incallui/calllocation/stub/StubCallLocationModule.java
+++ b/java/com/android/incallui/calllocation/stub/StubCallLocationModule.java
@@ -32,9 +32,9 @@ public abstract class StubCallLocationModule {
@Binds
public abstract CallLocation bindCallLocation(StubCallLocation callLocation);
- static public class StubCallLocation implements CallLocation {
+ static class StubCallLocation implements CallLocation {
@Inject
- public StubCallLocation() {}
+ StubCallLocation() {}
@Override
public boolean canGetLocation(@NonNull Context context) {
@@ -44,7 +44,7 @@ public abstract class StubCallLocationModule {
@Override
@NonNull
public Fragment getLocationFragment(@NonNull Context context) {
- return null;
+ throw Assert.createUnsupportedOperationFailException();
}
@Override
diff --git a/java/com/android/incallui/incall/impl/AndroidManifest.xml b/java/com/android/incallui/incall/impl/AndroidManifest.xml
index a0e3110d8..3d646506d 100644
--- a/java/com/android/incallui/incall/impl/AndroidManifest.xml
+++ b/java/com/android/incallui/incall/impl/AndroidManifest.xml
@@ -1,3 +1 @@
-<manifest
- package="com.android.incall.incall.impl">
-</manifest>
+<manifest package="com.android.incallui.incall.impl"/>
diff --git a/java/com/android/incallui/incall/impl/InCallFragment.java b/java/com/android/incallui/incall/impl/InCallFragment.java
index b6ae4902a..02e9b4add 100644
--- a/java/com/android/incallui/incall/impl/InCallFragment.java
+++ b/java/com/android/incallui/incall/impl/InCallFragment.java
@@ -25,11 +25,9 @@ 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;
@@ -74,8 +72,8 @@ public class InCallFragment extends Fragment
private List<ButtonController> buttonControllers = new ArrayList<>();
private View endCallButton;
- private TabLayout tabLayout;
- private ViewPager pager;
+ private InCallPaginator paginator;
+ private LockableViewPager pager;
private InCallPagerAdapter adapter;
private ContactGridManager contactGridManager;
private InCallScreenDelegate inCallScreenDelegate;
@@ -134,8 +132,8 @@ public class InCallFragment extends Fragment
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);
+ paginator = (InCallPaginator) view.findViewById(R.id.incall_paginator);
+ pager = (LockableViewPager) view.findViewById(R.id.incall_pager);
endCallButton = view.findViewById(R.id.incall_end_call);
endCallButton.setOnClickListener(this);
@@ -248,8 +246,8 @@ public class InCallFragment extends Fragment
}
if (adapter.getCount() > 1) {
- tabLayout.setVisibility(pager.getVisibility());
- tabLayout.setupWithViewPager(pager, true);
+ paginator.setVisibility(View.VISIBLE);
+ paginator.setupWithViewPager(pager);
if (!stateRestored) {
new Handler()
.postDelayed(
@@ -263,9 +261,9 @@ public class InCallFragment extends Fragment
}
},
2000);
+ } else {
+ paginator.setVisibility(View.GONE);
}
- } else {
- tabLayout.setVisibility(View.GONE);
}
}
@@ -428,8 +426,15 @@ public class InCallFragment extends Fragment
int visibility = numVisibleButtons == 0 ? View.GONE : View.VISIBLE;
pager.setVisibility(visibility);
- if (adapter != null && adapter.getCount() > 1) {
- tabLayout.setVisibility(visibility);
+ if (adapter != null
+ && adapter.getCount() > 1
+ && getResources().getInteger(R.integer.incall_num_rows) > 1) {
+ paginator.setVisibility(View.VISIBLE);
+ pager.setSwipingLocked(false);
+ } else {
+ paginator.setVisibility(View.GONE);
+ pager.setSwipingLocked(true);
+ pager.setCurrentItem(adapter.getButtonGridPosition());
}
}
diff --git a/java/com/android/incallui/incall/impl/InCallPaginator.java b/java/com/android/incallui/incall/impl/InCallPaginator.java
new file mode 100644
index 000000000..8ebbd76a2
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/InCallPaginator.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2017 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.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.util.AttributeSet;
+import android.view.View;
+import com.android.dialer.common.Assert;
+
+/**
+ * This is the view class for incall paginator visible when a user has EC data attached to their
+ * call. It contains animation methods when the swipe gesture is performed.
+ */
+public class InCallPaginator extends View implements OnPageChangeListener {
+
+ private int dotRadius;
+ private int dotsSeparation;
+
+ private Paint activeDotPaintPortrait;
+ private Paint inactiveDotPaintPortrait;
+
+ private Path inactiveDotPath;
+ private ValueAnimator transitionAnimator;
+ private boolean useModeSwitchTransition;
+
+ private float progress;
+ private boolean toFirstPage;
+ private boolean pageChanged;
+
+ public InCallPaginator(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public InCallPaginator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ private void init(Context context) {
+ dotRadius = getResources().getDimensionPixelSize(R.dimen.paginator_dot_radius);
+ dotsSeparation = getResources().getDimensionPixelSize(R.dimen.paginator_dots_separation);
+
+ int activeDotColor = context.getColor(R.color.paginator_dot);
+ int inactiveDotColor = context.getColor(R.color.paginator_path);
+ activeDotPaintPortrait = new Paint(Paint.ANTI_ALIAS_FLAG);
+ activeDotPaintPortrait.setColor(activeDotColor);
+ inactiveDotPaintPortrait = new Paint(Paint.ANTI_ALIAS_FLAG);
+ inactiveDotPaintPortrait.setColor(inactiveDotColor);
+
+ inactiveDotPath = new Path();
+ transitionAnimator = ValueAnimator.ofFloat(0f, 1f);
+ transitionAnimator.setInterpolator(null);
+ transitionAnimator.setCurrentFraction(0f);
+ transitionAnimator.addUpdateListener(animation -> invalidate());
+ }
+
+ @VisibleForTesting
+ public void setProgress(float progress, boolean toFirstPage) {
+ this.progress = progress;
+ this.toFirstPage = toFirstPage;
+
+ // Ensure the dot transition keeps up with the swipe progress.
+ if (transitionAnimator.isStarted() && progress > transitionAnimator.getAnimatedFraction()) {
+ transitionAnimator.setCurrentFraction(progress);
+ }
+
+ invalidate();
+ }
+
+ private void startTransition() {
+ if (transitionAnimator.getAnimatedFraction() < 1f) {
+ transitionAnimator.setCurrentFraction(progress);
+ useModeSwitchTransition = false;
+ transitionAnimator.cancel();
+ transitionAnimator.start();
+ }
+ }
+
+ private void endTransition(boolean snapBack) {
+ if (transitionAnimator.getAnimatedFraction() > 0f) {
+ useModeSwitchTransition = !snapBack;
+ transitionAnimator.cancel();
+ transitionAnimator.reverse();
+ }
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int centerX = getWidth() / 2;
+ int centerY = getHeight() / 2;
+
+ float transitionFraction = (float) transitionAnimator.getAnimatedValue();
+
+ // Draw the inactive "dots".
+ inactiveDotPath.reset();
+ if (useModeSwitchTransition) {
+ float trackWidth = 2 * dotRadius + transitionFraction * (2 * dotRadius + dotsSeparation);
+ float indicatorRadius = dotRadius * (1f - 2f * Math.min(transitionFraction, 0.5f));
+ float indicatorOffset = dotRadius + dotsSeparation / 2;
+ if (toFirstPage) {
+ float trackLeft = centerX - indicatorOffset - dotRadius;
+ inactiveDotPath.addRoundRect(
+ trackLeft,
+ centerY - dotRadius,
+ trackLeft + trackWidth,
+ centerY + dotRadius,
+ dotRadius,
+ dotRadius,
+ Path.Direction.CW);
+ inactiveDotPath.addCircle(
+ centerX + indicatorOffset, centerY, indicatorRadius, Path.Direction.CW);
+ } else {
+ float trackRight = centerX + indicatorOffset + dotRadius;
+ inactiveDotPath.addRoundRect(
+ trackRight - trackWidth,
+ centerY - dotRadius,
+ trackRight,
+ centerY + dotRadius,
+ dotRadius,
+ dotRadius,
+ Path.Direction.CW);
+ inactiveDotPath.addCircle(
+ centerX - indicatorOffset, centerY, indicatorRadius, Path.Direction.CW);
+ }
+ } else {
+ float centerOffset = dotsSeparation / 2f;
+ float innerOffset = centerOffset - transitionFraction * (dotRadius + centerOffset);
+ float outerOffset = 2f * dotRadius + centerOffset;
+ inactiveDotPath.addRoundRect(
+ centerX - outerOffset,
+ centerY - dotRadius,
+ centerX - innerOffset,
+ centerY + dotRadius,
+ dotRadius,
+ dotRadius,
+ Path.Direction.CW);
+ inactiveDotPath.addRoundRect(
+ centerX + innerOffset,
+ centerY - dotRadius,
+ centerX + outerOffset,
+ centerY + dotRadius,
+ dotRadius,
+ dotRadius,
+ Path.Direction.CW);
+ }
+ Paint inactivePaint = inactiveDotPaintPortrait;
+ canvas.drawPath(inactiveDotPath, inactivePaint);
+
+ // Draw the white active dot.
+ float activeDotOffset =
+ (toFirstPage ? 1f - 2f * progress : 2f * progress - 1f) * (dotRadius + dotsSeparation / 2);
+ Paint activePaint = activeDotPaintPortrait;
+ canvas.drawCircle(centerX + activeDotOffset, centerY, dotRadius, activePaint);
+ }
+
+ public void setupWithViewPager(ViewPager pager) {
+ Assert.checkArgument(pager.getAdapter().getCount() == 2, "Invalid page count.");
+ pager.addOnPageChangeListener(this);
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ setProgress(positionOffset, position != 0);
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ pageChanged = true;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ switch (state) {
+ case ViewPager.SCROLL_STATE_IDLE:
+ endTransition(!pageChanged);
+ pageChanged = false;
+ break;
+ case ViewPager.SCROLL_STATE_DRAGGING:
+ startTransition();
+ break;
+ case ViewPager.SCROLL_STATE_SETTLING:
+ default:
+ break;
+ }
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/LockableViewPager.java b/java/com/android/incallui/incall/impl/LockableViewPager.java
new file mode 100644
index 000000000..5b8b12609
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/LockableViewPager.java
@@ -0,0 +1,46 @@
+/*
+ * 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.content.Context;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+/** {@link ViewPager} useful for disabled swiping between pages. */
+public class LockableViewPager extends ViewPager {
+
+ private boolean swipingLocked;
+
+ public LockableViewPager(Context context, AttributeSet attributeSet) {
+ super(context, attributeSet);
+ }
+
+ public void setSwipingLocked(boolean swipingLocked) {
+ this.swipingLocked = swipingLocked;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
+ return !swipingLocked && super.onInterceptTouchEvent(motionEvent);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent motionEvent) {
+ return !swipingLocked && super.onTouchEvent(motionEvent);
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml b/java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml
index 9b950462c..e4bc942bb 100644
--- a/java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml
+++ b/java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml
@@ -60,23 +60,21 @@
android:layout_height="match_parent"/>
</LinearLayout>
- <android.support.v4.view.ViewPager
+ <com.android.incallui.incall.impl.LockableViewPager
android:id="@+id/incall_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_above="@+id/incall_tab_dots"
+ android:layout_above="@+id/incall_paginator"
android:layout_below="@+id/incall_contact_grid"
android:layout_centerHorizontal="true"/>
- <android.support.design.widget.TabLayout
- android:id="@+id/incall_tab_dots"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_above="@+id/incall_end_call"
- android:visibility="gone"
- app:tabBackground="@drawable/tab_selector"
- app:tabGravity="center"
- app:tabIndicatorHeight="0dp"/>
+ <com.android.incallui.incall.impl.InCallPaginator
+ android:id="@+id/incall_paginator"
+ android:layout_height="@dimen/paginator_height"
+ android:layout_width="@dimen/paginator_width"
+ android:layout_above="@+id/incall_end_call"
+ android:layout_centerHorizontal="true"
+ android:visibility="gone"/>
<FrameLayout
android:id="@+id/incall_dialpad_container"
diff --git a/java/com/android/incallui/incall/impl/res/values/dimens.xml b/java/com/android/incallui/incall/impl/res/values/dimens.xml
index 249788785..72602e3fc 100644
--- a/java/com/android/incallui/incall/impl/res/values/dimens.xml
+++ b/java/com/android/incallui/incall/impl/res/values/dimens.xml
@@ -14,4 +14,9 @@
<bool name="incall_dialpad_allowed">false</bool>
<integer name="incall_num_rows">0</integer>
+
+ <dimen name="paginator_dot_radius">5dp</dimen>
+ <dimen name="paginator_dots_separation">8dp</dimen>
+ <dimen name="paginator_height">38dp</dimen>
+ <dimen name="paginator_width">72dp</dimen>
</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values/styles.xml b/java/com/android/incallui/incall/impl/res/values/styles.xml
index 2392574a3..a8cf2ddf1 100644
--- a/java/com/android/incallui/incall/impl/res/values/styles.xml
+++ b/java/com/android/incallui/incall/impl/res/values/styles.xml
@@ -17,6 +17,9 @@
<resources>
+ <color name="paginator_dot">#FFF</color>
+ <color name="paginator_path">#66FFFFFF</color>
+
<style name="DialpadContainer">
<item name="android:layout_alignParentTop">true</item>
</style>
diff --git a/java/com/android/incallui/maps/MapsComponent.java b/java/com/android/incallui/maps/MapsComponent.java
index 1ca17b781..796abaa92 100644
--- a/java/com/android/incallui/maps/MapsComponent.java
+++ b/java/com/android/incallui/maps/MapsComponent.java
@@ -19,29 +19,18 @@ package com.android.incallui.maps;
import android.content.Context;
import com.android.dialer.inject.HasRootComponent;
import dagger.Subcomponent;
-import com.android.incallui.maps.stub.StubMapsModule;
/** Subcomponent that can be used to access the maps implementation. */
-public class MapsComponent {
+@Subcomponent
+public abstract class MapsComponent {
- private static MapsComponent instance;
- private Maps maps;
-
- public Maps getMaps() {
- if (maps == null) {
- maps = new StubMapsModule.StubMaps();
- }
- return maps;
- }
+ public abstract Maps getMaps();
public static MapsComponent get(Context context) {
- if (instance == null) {
- instance = new MapsComponent();
- }
- return instance;
+ return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
+ .mapsComponent();
}
-
/** Used to refer to the root application component. */
public interface HasComponent {
MapsComponent mapsComponent();
diff --git a/java/com/android/incallui/maps/stub/StubMapsModule.java b/java/com/android/incallui/maps/stub/StubMapsModule.java
index 72678143c..3a193b15d 100644
--- a/java/com/android/incallui/maps/stub/StubMapsModule.java
+++ b/java/com/android/incallui/maps/stub/StubMapsModule.java
@@ -34,7 +34,7 @@ public abstract class StubMapsModule {
@Singleton
public abstract Maps bindMaps(StubMaps maps);
- static public final class StubMaps implements Maps {
+ static final class StubMaps implements Maps {
@Inject
public StubMaps() {}
diff --git a/java/com/android/incallui/res/values-uz/strings.xml b/java/com/android/incallui/res/values-uz/strings.xml
index fcf221f38..585f983ac 100644
--- a/java/com/android/incallui/res/values-uz/strings.xml
+++ b/java/com/android/incallui/res/values-uz/strings.xml
@@ -30,7 +30,7 @@
<string name="caller_manage_header" msgid="7358710345135355578">"Konferensiya qo‘ng‘irog‘i <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
<string name="voicemail_settings_number_label" msgid="8935904934161608885">"Ovozli pochta raqami"</string>
<string name="notification_dialing" msgid="9072177265772083826">"Raqam terilmoqda"</string>
- <string name="notification_missedCallTicker" msgid="238492086972857643">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> qo‘ng‘irog‘i javobsiz qoldirildi"</string>
+ <string name="notification_missedCallTicker" msgid="238492086972857643">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> chaqiruvi javobsiz qoldi"</string>
<string name="notification_ongoing_call" msgid="8712641937577776125">"Joriy qo‘ng‘iroq"</string>
<string name="notification_ongoing_work_call" msgid="3189581218078981547">"Chiquvchi ishchi qo‘ng‘irog‘i"</string>
<string name="notification_ongoing_call_wifi" msgid="297183051021070949">"Chiquvchi Wi-Fi qo‘ng‘irog‘i"</string>
diff --git a/java/com/android/incallui/videotech/VideoTech.java b/java/com/android/incallui/videotech/VideoTech.java
index fb2641793..bd957b699 100644
--- a/java/com/android/incallui/videotech/VideoTech.java
+++ b/java/com/android/incallui/videotech/VideoTech.java
@@ -27,6 +27,12 @@ public interface VideoTech {
boolean isTransmittingOrReceiving();
+ /**
+ * Determines if the answer video UI should open the camera directly instead of letting the video
+ * tech manage the camera.
+ */
+ boolean isSelfManagedCamera();
+
void onCallStateChanged(int newState);
@SessionModificationState
diff --git a/java/com/android/incallui/videotech/empty/EmptyVideoTech.java b/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
index bc8db4c07..c76043540 100644
--- a/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
+++ b/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
@@ -32,6 +32,11 @@ public class EmptyVideoTech implements VideoTech {
}
@Override
+ public boolean isSelfManagedCamera() {
+ return false;
+ }
+
+ @Override
public void onCallStateChanged(int newState) {}
@Override
diff --git a/java/com/android/incallui/videotech/ims/ImsVideoTech.java b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
index 890e5c80c..a37500c3b 100644
--- a/java/com/android/incallui/videotech/ims/ImsVideoTech.java
+++ b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
@@ -58,6 +58,13 @@ public class ImsVideoTech implements VideoTech {
}
@Override
+ public boolean isSelfManagedCamera() {
+ // Return false to indicate that the answer UI shouldn't open the camera itself.
+ // For IMS Video the modem is responsible for opening the camera.
+ return false;
+ }
+
+ @Override
public void onCallStateChanged(int newState) {
if (!isAvailable()) {
return;
diff --git a/java/com/android/incallui/videotech/rcs/RcsVideoShare.java b/java/com/android/incallui/videotech/rcs/RcsVideoShare.java
index 2cb43036f..1e951408c 100644
--- a/java/com/android/incallui/videotech/rcs/RcsVideoShare.java
+++ b/java/com/android/incallui/videotech/rcs/RcsVideoShare.java
@@ -65,6 +65,11 @@ public class RcsVideoShare implements VideoTech, CapabilitiesListener, VideoShar
}
@Override
+ public boolean isSelfManagedCamera() {
+ return true;
+ }
+
+ @Override
public void onCallStateChanged(int newState) {
if (newState == Call.STATE_DISCONNECTING) {
enrichedCallManager.unregisterVideoShareListener(this);