Add animation when switching output device

Bug: 155822415
Test: manual test
Change-Id: Ia3370222427b77099d987d59d5d5fd08c11557d7
This commit is contained in:
timhypeng
2020-09-24 20:44:13 +08:00
parent b0424a353b
commit a77769555c
6 changed files with 110 additions and 36 deletions

View File

@@ -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"

View File

@@ -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>

View File

@@ -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();
}
}
}
}

View File

@@ -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();
}
});
}
});
}
}
}

View File

@@ -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

View File

@@ -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();