diff options
| author | 2020-09-24 20:44:13 +0800 | |
|---|---|---|
| committer | 2020-09-29 15:06:33 +0800 | |
| commit | a77769555cd237a195ecd4a00c07454948d35711 (patch) | |
| tree | ff7ffc96ca51bc2935ec0043dd83f696399e60cd | |
| parent | b0424a353bc21259eb529faabab8d933e04ffb5d (diff) | |
Add animation when switching output device
Bug: 155822415
Test: manual test
Change-Id: Ia3370222427b77099d987d59d5d5fd08c11557d7
6 files changed, 110 insertions, 36 deletions
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml index 92d0858a1a31..ac8b7b5812bd 100644 --- a/packages/SystemUI/res/layout/media_output_list_item.xml +++ b/packages/SystemUI/res/layout/media_output_list_item.xml @@ -75,18 +75,6 @@ android:textSize="12sp" android:fontFamily="roboto-regular" android:visibility="gone"/> - <ProgressBar - android:id="@+id/volume_indeterminate_progress" - style="@*android:style/Widget.Material.ProgressBar.Horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginEnd="15dp" - android:layout_marginBottom="1dp" - android:layout_alignParentBottom="true" - android:indeterminate="true" - android:indeterminateOnly="true" - android:visibility="gone"/> <SeekBar android:id="@+id/volume_seekbar" android:layout_width="match_parent" @@ -94,6 +82,17 @@ android:layout_alignParentBottom="true"/> </RelativeLayout> + <ProgressBar + android:id="@+id/volume_indeterminate_progress" + style="@*android:style/Widget.Material.ProgressBar.Horizontal" + android:layout_width="258dp" + android:layout_height="18dp" + android:layout_marginStart="68dp" + android:layout_marginTop="40dp" + android:indeterminate="true" + android:indeterminateOnly="true" + android:visibility="gone"/> + <View android:layout_width="1dp" android:layout_height="36dp" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 2e56df3e1fb8..8777d5077b87 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1380,4 +1380,5 @@ <dimen name="media_output_dialog_header_back_icon_size">36dp</dimen> <dimen name="media_output_dialog_header_icon_padding">16dp</dimen> <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen> + <dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 9b6a9ea80ebe..d1630ebe8dc8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -44,6 +44,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { private static final String TAG = "MediaOutputAdapter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private ViewGroup mConnectedItem; + public MediaOutputAdapter(MediaOutputController controller) { super(controller); } @@ -79,18 +81,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { return mController.getMediaDevices().size(); } - void onItemClick(MediaDevice device) { - mController.connectDevice(device); - device.setState(MediaDeviceState.STATE_CONNECTING); - notifyDataSetChanged(); - } - - void onItemClick(int customizedItem) { - if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) { - mController.launchBluetoothPairing(); - } - } - @Override CharSequence getItemTitle(MediaDevice device) { if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE @@ -117,6 +107,10 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) { super.onBind(device, topMargin, bottomMargin); + final boolean currentlyConnected = isCurrentlyConnected(device); + if (currentlyConnected) { + mConnectedItem = mFrameLayout; + } if (mController.isTransferring()) { if (device.getState() == MediaDeviceState.STATE_CONNECTING && !mController.hasAdjustVolumeUserRestriction()) { @@ -133,16 +127,16 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { false /* showSeekBar*/, false /* showProgressBar */, true /* showSubtitle */); mSubTitleText.setText(R.string.media_output_dialog_connect_failed); - mFrameLayout.setOnClickListener(v -> onItemClick(device)); + mFrameLayout.setOnClickListener(v -> onItemClick(v, device)); } else if (!mController.hasAdjustVolumeUserRestriction() - && isCurrentConnected(device)) { + && currentlyConnected) { setTwoLineLayout(device, null /* title */, true /* bFocused */, true /* showSeekBar*/, false /* showProgressBar */, false /* showSubtitle */); initSeekbar(device); } else { setSingleLineLayout(getItemTitle(device), false /* bFocused */); - mFrameLayout.setOnClickListener(v -> onItemClick(device)); + mFrameLayout.setOnClickListener(v -> onItemClick(v, device)); } } } @@ -160,5 +154,24 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW)); } } + + private void onItemClick(View view, MediaDevice device) { + if (mController.isTransferring()) { + return; + } + + playSwitchingAnim(mConnectedItem, view); + mController.connectDevice(device); + device.setState(MediaDeviceState.STATE_CONNECTING); + if (!isAnimating()) { + notifyDataSetChanged(); + } + } + + private void onItemClick(int customizedItem) { + if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) { + mController.launchBluetoothPairing(); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index 01dc6c4b71da..2d3e77db1ea3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -16,6 +16,8 @@ package com.android.systemui.media.dialog; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.graphics.Typeface; import android.text.TextUtils; @@ -33,6 +35,7 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.android.settingslib.media.MediaDevice; +import com.android.systemui.Interpolators; import com.android.systemui.R; /** @@ -50,6 +53,7 @@ public abstract class MediaOutputBaseAdapter extends private boolean mIsDragging; private int mMargin; + private boolean mIsAnimating; Context mContext; View mHolderView; @@ -75,7 +79,7 @@ public abstract class MediaOutputBaseAdapter extends return device.getName(); } - boolean isCurrentConnected(MediaDevice device) { + boolean isCurrentlyConnected(MediaDevice device) { return TextUtils.equals(device.getId(), mController.getCurrentConnectedMediaDevice().getId()); } @@ -84,10 +88,17 @@ public abstract class MediaOutputBaseAdapter extends return mIsDragging; } + boolean isAnimating() { + return mIsAnimating; + } + /** * ViewHolder for binding device view. */ abstract class MediaDeviceBaseViewHolder extends RecyclerView.ViewHolder { + + private static final int ANIM_DURATION = 200; + final FrameLayout mFrameLayout; final TextView mTitleText; final TextView mTwoLineTitleText; @@ -123,17 +134,16 @@ public abstract class MediaOutputBaseAdapter extends private void setMargin(boolean topMargin, boolean bottomMargin) { ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout .getLayoutParams(); - if (topMargin) { - params.topMargin = mMargin; - } - if (bottomMargin) { - params.bottomMargin = mMargin; - } + params.topMargin = topMargin ? mMargin : 0; + params.bottomMargin = bottomMargin ? mMargin : 0; mFrameLayout.setLayoutParams(params); } + void setSingleLineLayout(CharSequence title, boolean bFocused) { - mTitleText.setVisibility(View.VISIBLE); mTwoLineLayout.setVisibility(View.GONE); + mProgressBar.setVisibility(View.GONE); + mTitleText.setVisibility(View.VISIBLE); + mTitleText.setTranslationY(0); mTitleText.setText(title); if (bFocused) { mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL)); @@ -146,9 +156,11 @@ public abstract class MediaOutputBaseAdapter extends boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) { mTitleText.setVisibility(View.GONE); mTwoLineLayout.setVisibility(View.VISIBLE); + mSeekBar.setAlpha(1); mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE); mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE); mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE); + mTwoLineTitleText.setTranslationY(0); if (device == null) { mTwoLineTitleText.setText(title); } else { @@ -189,5 +201,53 @@ public abstract class MediaOutputBaseAdapter extends } }); } + + void playSwitchingAnim(@NonNull View from, @NonNull View to) { + final float delta = (float) (mContext.getResources().getDimensionPixelSize( + R.dimen.media_output_dialog_title_anim_y_delta)); + final SeekBar fromSeekBar = from.requireViewById(R.id.volume_seekbar); + final TextView toTitleText = to.requireViewById(R.id.title); + if (fromSeekBar.getVisibility() != View.VISIBLE || toTitleText.getVisibility() + != View.VISIBLE) { + return; + } + mIsAnimating = true; + // Animation for title text + toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL)); + toTitleText.animate() + .setDuration(ANIM_DURATION) + .translationY(-delta) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + to.requireViewById(R.id.volume_indeterminate_progress).setVisibility( + View.VISIBLE); + } + }); + // Animation for seek bar + fromSeekBar.animate() + .alpha(0) + .setDuration(ANIM_DURATION) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + final TextView fromTitleText = from.requireViewById( + R.id.two_line_title); + fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL)); + fromTitleText.animate() + .setDuration(ANIM_DURATION) + .translationY(delta) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIsAnimating = false; + notifyDataSetChanged(); + } + }); + } + }); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index ebca8a735ad5..3b8299913e5b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -170,7 +170,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mHeaderSubtitle.setText(subTitle); mHeaderTitle.setGravity(Gravity.NO_GRAVITY); } - if (!mAdapter.isDragging()) { + if (!mAdapter.isDragging() && !mAdapter.isAnimating()) { mAdapter.notifyDataSetChanged(); } // Show when remote media session is available diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 0e376bd356a2..2d460aa0c9c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -192,6 +192,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { public void onItemClick_clickDevice_verifyConnectDevice() { assertThat(mMediaDevice2.getState()).isEqualTo( LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); mViewHolder.mFrameLayout.performClick(); |