diff options
author | Cassie(Yitong) Wang <cassieyw@google.com> | 2019-06-26 02:02:50 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-06-26 02:02:50 -0700 |
commit | c515c355b191da148734cec41ae97c8509ab6bf3 (patch) | |
tree | 4fa14194e1a509bf18fe6362c9649dc6d500019a | |
parent | 7c76e58fe1724554322ba7f43fb6a35598bc6683 (diff) | |
parent | 9128a19af948485ae75b4ca6d77529fc4f0a90da (diff) | |
download | Dialer-c515c355b191da148734cec41ae97c8509ab6bf3.tar.gz |
Show onhold call info and swap call button on ongoing call page
am: 9128a19af9
Change-Id: I813fc60b076f659d026138f3aa3b0ab42f1416f9
-rw-r--r-- | res/drawable/ic_swap_calls.xml | 27 | ||||
-rw-r--r-- | res/layout/ongoing_call_fragment.xml | 12 | ||||
-rw-r--r-- | res/layout/onhold_user_profile.xml | 80 | ||||
-rw-r--r-- | res/values-night/colors.xml | 3 | ||||
-rw-r--r-- | res/values/colors.xml | 3 | ||||
-rw-r--r-- | res/values/dimens.xml | 7 | ||||
-rw-r--r-- | res/values/strings.xml | 4 | ||||
-rw-r--r-- | src/com/android/car/dialer/ui/activecall/InCallViewModel.java | 42 | ||||
-rw-r--r-- | src/com/android/car/dialer/ui/activecall/OnHoldCallUserProfileFragment.java | 98 | ||||
-rw-r--r-- | src/com/android/car/dialer/ui/activecall/OngoingCallFragment.java | 13 |
10 files changed, 277 insertions, 12 deletions
diff --git a/res/drawable/ic_swap_calls.xml b/res/drawable/ic_swap_calls.xml new file mode 100644 index 00000000..098ca973 --- /dev/null +++ b/res/drawable/ic_swap_calls.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/primary_icon_size" + android:height="@dimen/primary_icon_size" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + + <path + android:pathData="M0 0h24v24H0z" /> + <path + android:fillColor="#fffafafa" + android:pathData="M18 4l-4 4h3v7c0 1.1-.9 2-2 2s-2-.9-2-2V8c0-2.21-1.79-4-4-4S5 5.79 5 8v7H2l4 4 4-4H7V8c0-1.1.9-2 2-2s2 .9 2 2v7c0 2.21 1.79 4 4 4s4-1.79 4-4V8h3l-4-4z"/> +</vector> diff --git a/res/layout/ongoing_call_fragment.xml b/res/layout/ongoing_call_fragment.xml index cf3d9c09..007531b1 100644 --- a/res/layout/ongoing_call_fragment.xml +++ b/res/layout/ongoing_call_fragment.xml @@ -56,6 +56,18 @@ limitations under the License. app:layout_constraintStart_toStartOf="parent"/> <fragment + android:id="@+id/onhold_user_profile" + android:name="com.android.car.dialer.ui.activecall.OnHoldCallUserProfileFragment" + android:layout_width="match_parent" + android:layout_height="@dimen/onhold_user_info_height" + android:layout_marginTop="@dimen/onhold_profile_margin_y" + android:layout_marginStart="@dimen/onhold_profile_margin_x" + android:layout_marginEnd="@dimen/onhold_profile_margin_x" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + <fragment android:id="@+id/ongoing_call_control_bar" android:name="com.android.car.dialer.ui.activecall.OnGoingCallControllerBarFragment" android:layout_width="match_parent" diff --git a/res/layout/onhold_user_profile.xml b/res/layout/onhold_user_profile.xml new file mode 100644 index 00000000..806226bb --- /dev/null +++ b/res/layout/onhold_user_profile.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<androidx.cardview.widget.CardView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:cardBackgroundColor="@color/onhold_call_background" + app:cardCornerRadius="@dimen/onhold_profile_corner_radius"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <androidx.constraintlayout.widget.Guideline + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/guideline" + android:orientation="vertical" + app:layout_constraintGuide_begin="@dimen/onhold_profile_guideline"/> + + <ImageView + android:id="@+id/icon" + android:layout_width="@dimen/avatar_icon_size" + android:layout_height="@dimen/avatar_icon_size" + android:scaleType="centerCrop" + android:layout_marginStart="@dimen/onhold_profile_avatar_margin" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent"/> + + <TextView + android:id="@+id/title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:singleLine="true" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@+id/text" + app:layout_constraintStart_toStartOf="@id/guideline" + app:layout_constraintEnd_toStartOf="@+id/swap_calls_button"/> + + <TextView + android:id="@id/text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/onhold_call_label" + android:textAppearance="?android:attr/textAppearanceSmall" + android:singleLine="true" + app:layout_constraintTop_toBottomOf="@id/title" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="@id/guideline" + app:layout_constraintEnd_toStartOf="@+id/swap_calls_button"/> + + <ImageView + android:id="@+id/swap_calls_button" + android:layout_width="0dp" + android:layout_height="match_parent" + android:src="@drawable/ic_swap_calls" + android:scaleType="center" + android:tint="@color/secondary_icon_color" + android:background="?android:attr/selectableItemBackground" + android:paddingLeft="@dimen/swap_call_button_margin" + android:paddingRight="@dimen/swap_call_button_margin" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + </androidx.constraintlayout.widget.ConstraintLayout> +</androidx.cardview.widget.CardView> diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml index c5f70536..abd5d8ee 100644 --- a/res/values-night/colors.xml +++ b/res/values-night/colors.xml @@ -16,6 +16,9 @@ <resources> <color name="car_key2">@color/car_key2_light</color> + <!-- InCall page --> + <color name="onhold_call_background">@*android:color/car_grey_900</color> + <!-- Components --> <color name="divider_color">@color/divider_color_dark</color> <color name="secondary_icon_color">@color/secondary_icon_color_dark</color> diff --git a/res/values/colors.xml b/res/values/colors.xml index 74d067d7..2b99d101 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -17,10 +17,11 @@ <!-- InCall page --> <color name="phone_call">@*android:color/car_green_700</color> <color name="phone_end_call">@*android:color/car_red_500a</color> + <color name="onhold_call_background">@*android:color/car_grey_868</color> <!-- Dialpad page --> <color name="call_button_outline">@*android:color/car_green_500</color> - + <!--Contact details--> <color name="contact_details_icon_tint">@color/primary_icon_color</color> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 222e11b3..cd8bff5d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -65,6 +65,13 @@ <dimen name="in_call_state_margin_top">@*android:dimen/car_padding_2</dimen> <dimen name="in_call_margin_between_avatar_and_text">48dp</dimen> <dimen name="in_call_user_profile_margin">@*android:dimen/car_margin</dimen> + <dimen name="onhold_user_info_height">@dimen/list_item_height</dimen> + <dimen name="onhold_profile_margin_x">@dimen/list_item_padding</dimen> + <dimen name="onhold_profile_margin_y">@*android:dimen/car_padding_3</dimen> + <dimen name="onhold_profile_corner_radius">8dp</dimen> + <dimen name="onhold_profile_avatar_margin">@*android:dimen/car_keyline_1</dimen> + <dimen name="onhold_profile_guideline">@dimen/list_item_guideline</dimen> + <dimen name="swap_call_button_margin">@*android:dimen/car_keyline_1</dimen> <!-- Ringing call dimensions --> <dimen name="ringing_call_button_touch_target_size">@dimen/touch_target_size</dimen> diff --git a/res/values/strings.xml b/res/values/strings.xml index 554c5f9a..f98c6db4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -139,6 +139,10 @@ <!-- Title for missed call notification [CHAR LIMIT=40]--> <string name="notification_missed_call">Missed call</string> + <!-- Onhold User Profile Info --> + <!-- Text to show the call is onhold [CHAR LIMIT=40]--> + <string name="onhold_call_label">On Hold</string> + <!-- Dialer Setting --> <!-- Title of the settings page [CHAR LIMIT=30]--> <string name="setting_title">Settings</string> diff --git a/src/com/android/car/dialer/ui/activecall/InCallViewModel.java b/src/com/android/car/dialer/ui/activecall/InCallViewModel.java index 83c66839..f5f8704e 100644 --- a/src/com/android/car/dialer/ui/activecall/InCallViewModel.java +++ b/src/com/android/car/dialer/ui/activecall/InCallViewModel.java @@ -64,6 +64,8 @@ public class InCallViewModel extends AndroidViewModel implements private final LiveData<CallDetail> mCallDetailLiveData; private final LiveData<Integer> mCallStateLiveData; private final LiveData<Call> mPrimaryCallLiveData; + private final LiveData<Call> mSecondaryCallLiveData; + private final LiveData<CallDetail> mSecondaryCallDetailLiveData; private final LiveData<Integer> mAudioRouteLiveData; private LiveData<Long> mCallConnectTimeLiveData; private LiveData<Pair<Integer, Long>> mCallStateAndConnectTimeLiveData; @@ -88,12 +90,11 @@ public class InCallViewModel extends AndroidViewModel implements }; // Reuse the same instance so the callback won't be registered more than once. - private final Call.Callback mIncomingCallStateChangedCallback = new Call.Callback() { + private final Call.Callback mCallStateChangedCallback = new Call.Callback() { @Override public void onStateChanged(Call call, int state) { // Sets value to trigger the live data for incoming call and active call list to update. mCallListLiveData.setValue(mCallListLiveData.getValue()); - call.unregisterCallback(this); } }; @@ -105,14 +106,8 @@ public class InCallViewModel extends AndroidViewModel implements mCallComparator = new CallComparator(); mIncomingCallLiveData = Transformations.map(mCallListLiveData, - callList -> { - Call incomingCall = firstMatch(callList, - call -> call != null && call.getState() == Call.STATE_RINGING); - if (incomingCall != null) { - incomingCall.registerCallback(mIncomingCallStateChangedCallback); - } - return incomingCall; - }); + callList -> firstMatch(callList, + call -> call != null && call.getState() == Call.STATE_RINGING)); mOngoingCallListLiveData = Transformations.map(mCallListLiveData, callList -> { @@ -137,6 +132,12 @@ public class InCallViewModel extends AndroidViewModel implements mCallStateAndConnectTimeLiveData = LiveDataFunctions.pair(mCallStateLiveData, mCallConnectTimeLiveData); + mSecondaryCallLiveData = Transformations.map(mOngoingCallListLiveData, + callList -> (callList != null && callList.size() > 1) ? callList.get(1) : null); + + mSecondaryCallDetailLiveData = Transformations.switchMap(mSecondaryCallLiveData, + input -> input != null ? new CallDetailLiveData(input) : null); + mAudioRouteLiveData = new AudioRouteLiveData(mContext); Intent intent = new Intent(mContext, InCallServiceImpl.class); @@ -177,12 +178,31 @@ public class InCallViewModel extends AndroidViewModel implements /** * Returns the live data which monitor the primary call. + * A primary call in the first call in the ongoing call list, + * which is sorted based on {@link CallComparator}. */ public LiveData<Call> getPrimaryCall() { return mPrimaryCallLiveData; } /** + * Returns the live data which monitor the secondary call. + * A secondary call in the second call in the ongoing call list, + * which is sorted based on {@link CallComparator}. + * The value will be null if there is no second call in the call list. + */ + public LiveData<Call> getSecondaryCall() { + return mSecondaryCallLiveData; + } + + /** + * Returns the live data which monitors the secondary call details. + */ + public LiveData<CallDetail> getSecondaryCallDetail() { + return mSecondaryCallDetailLiveData; + } + + /** * Returns current audio route. */ public LiveData<Integer> getAudioRoute() { @@ -192,6 +212,7 @@ public class InCallViewModel extends AndroidViewModel implements @Override public boolean onTelecomCallAdded(Call telecomCall) { L.i(TAG, "onTelecomCallAdded %s %s", telecomCall, this); + telecomCall.registerCallback(mCallStateChangedCallback); updateCallList(); return false; } @@ -199,6 +220,7 @@ public class InCallViewModel extends AndroidViewModel implements @Override public boolean onTelecomCallRemoved(Call telecomCall) { L.i(TAG, "onTelecomCallRemoved %s %s", telecomCall, this); + telecomCall.unregisterCallback(mCallStateChangedCallback); updateCallList(); return false; } diff --git a/src/com/android/car/dialer/ui/activecall/OnHoldCallUserProfileFragment.java b/src/com/android/car/dialer/ui/activecall/OnHoldCallUserProfileFragment.java new file mode 100644 index 00000000..7740392a --- /dev/null +++ b/src/com/android/car/dialer/ui/activecall/OnHoldCallUserProfileFragment.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 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.car.dialer.ui.activecall; + +import android.net.Uri; +import android.os.Bundle; +import android.telecom.Call; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProviders; + +import com.android.car.dialer.R; +import com.android.car.dialer.ui.view.ContactAvatarOutputlineProvider; +import com.android.car.telephony.common.CallDetail; +import com.android.car.telephony.common.TelecomUtils; + +/** + * A fragment that displays information about onhold call. + */ +public class OnHoldCallUserProfileFragment extends Fragment { + + private TextView mTitle; + private ImageView mAvatarView; + private ImageView mSwapCallsButton; + private LiveData<Call> mPrimaryCallLiveData; + private LiveData<Call> mSecondaryCallLiveData; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View fragmentView = inflater.inflate(R.layout.onhold_user_profile, container, false); + + mTitle = fragmentView.findViewById(R.id.title); + mAvatarView = fragmentView.findViewById(R.id.icon); + mAvatarView.setOutlineProvider(ContactAvatarOutputlineProvider.get()); + + mSwapCallsButton = fragmentView.findViewById(R.id.swap_calls_button); + mSwapCallsButton.setOnClickListener(v -> swapCalls()); + + InCallViewModel inCallViewModel = ViewModelProviders.of(getActivity()).get( + InCallViewModel.class); + inCallViewModel.getSecondaryCallDetail().observe(this, this::updateProfile); + mPrimaryCallLiveData = inCallViewModel.getPrimaryCall(); + mSecondaryCallLiveData = inCallViewModel.getSecondaryCall(); + + return fragmentView; + } + + private void updateProfile(@Nullable CallDetail callDetail) { + if (callDetail == null) { + return; + } + + String number = callDetail.getNumber(); + Pair<String, Uri> displayNameAndAvatarUri = TelecomUtils.getDisplayNameAndAvatarUri( + getContext(), number); + + mTitle.setText(displayNameAndAvatarUri.first); + TelecomUtils.setContactBitmapAsync(getContext(), mAvatarView, + displayNameAndAvatarUri.second, displayNameAndAvatarUri.first); + } + + private void swapCalls() { + // Unholds onhold call + if (mSecondaryCallLiveData.getValue() != null) { + mSecondaryCallLiveData.getValue().unhold(); + } + + // hold primary call + if (mPrimaryCallLiveData.getValue().getState() != Call.STATE_HOLDING) { + mPrimaryCallLiveData.getValue().hold(); + } + } +} diff --git a/src/com/android/car/dialer/ui/activecall/OngoingCallFragment.java b/src/com/android/car/dialer/ui/activecall/OngoingCallFragment.java index c82f04fe..c7421521 100644 --- a/src/com/android/car/dialer/ui/activecall/OngoingCallFragment.java +++ b/src/com/android/car/dialer/ui/activecall/OngoingCallFragment.java @@ -17,6 +17,7 @@ package com.android.car.dialer.ui.activecall; import android.os.Bundle; +import android.telecom.Call; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -37,11 +38,11 @@ import com.google.common.annotations.VisibleForTesting; */ public class OngoingCallFragment extends InCallFragment { private Fragment mDialpadFragment; + private Fragment mOnholdCallFragment; private View mUserProfileContainerView; private BackgroundImageView mBackgroundImage; private MutableLiveData<Boolean> mDialpadState; - @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -49,6 +50,7 @@ public class OngoingCallFragment extends InCallFragment { mUserProfileContainerView = fragmentView.findViewById(R.id.user_profile_container); mBackgroundImage = fragmentView.findViewById(R.id.background_image); + mOnholdCallFragment = getChildFragmentManager().findFragmentById(R.id.onhold_user_profile); mDialpadFragment = getChildFragmentManager().findFragmentById(R.id.incall_dialpad_fragment); InCallViewModel inCallViewModel = ViewModelProviders.of(getActivity()).get( @@ -56,6 +58,7 @@ public class OngoingCallFragment extends InCallFragment { inCallViewModel.getPrimaryCallDetail().observe(this, this::bindUserProfileView); inCallViewModel.getCallStateAndConnectTime().observe(this, this::updateCallDescription); + inCallViewModel.getSecondaryCall().observe(this, this::maybeShowOnholdCallFragment); OngoingCallStateViewModel ongoingCallStateViewModel = ViewModelProviders.of( getActivity()).get(OngoingCallStateViewModel.class); @@ -88,4 +91,12 @@ public class OngoingCallFragment extends InCallFragment { mUserProfileContainerView.setVisibility(View.VISIBLE); mBackgroundImage.setDimmed(false); } + + private void maybeShowOnholdCallFragment(@Nullable Call secondaryCall) { + if (secondaryCall == null || secondaryCall.getState() != Call.STATE_HOLDING) { + getChildFragmentManager().beginTransaction().hide(mOnholdCallFragment).commit(); + } else { + getChildFragmentManager().beginTransaction().show(mOnholdCallFragment).commit(); + } + } } |