Merge changes from topic "tv-mic-indicator-refresh"
* changes: Make AudioRecordingDisclosureBar animations interruptable Update AudioRecordingDisclosureBar animations Clean up unused Views in AudioRecordingDisclosureBar
This commit is contained in:
committed by
Android (Google) Code Review
commit
fa9e5cd355
@@ -1,26 +0,0 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners
|
||||
android:bottomLeftRadius="8dp"
|
||||
android:topLeftRadius="8dp" />
|
||||
<solid android:color="@color/tv_audio_recording_indicator_background" />
|
||||
|
||||
</shape>
|
||||
@@ -1,26 +0,0 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners
|
||||
android:bottomRightRadius="8dp"
|
||||
android:topRightRadius="8dp" />
|
||||
<solid android:color="@color/tv_audio_recording_indicator_background" />
|
||||
|
||||
</shape>
|
||||
@@ -22,80 +22,17 @@
|
||||
android:padding="12dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/tv_rect_shadow_rounded">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/icon_texts_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/icon_container_bg"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/tv_rect_dark_left_rounded"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/icon_mic"
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/tv_rect_shadow_rounded">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="13dp"
|
||||
android:layout_height="13dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/tv_ic_mic_white"/>
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/texts_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="47dp"
|
||||
android:background="@color/tv_audio_recording_indicator_background"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:text="@string/mic_active"
|
||||
android:textColor="@android:color/white"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textSize="10dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="14dp"
|
||||
android:singleLine="true"
|
||||
android:text="SomeApplication accessed your microphone"
|
||||
android:textColor="@android:color/white"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textSize="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:layout_width="13dp"
|
||||
android:layout_height="13dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/tv_ic_mic_white"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/bg_end"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="47dp"
|
||||
android:background="@drawable/tv_rect_dark_right_rounded"
|
||||
android:visibility="visible"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
<color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
|
||||
<color name="recents_tv_text_shadow_color">#7F000000</color>
|
||||
|
||||
<!-- Background color for audio recording indicator (G800) -->
|
||||
<color name="tv_audio_recording_indicator_background">#FF3C4043</color>
|
||||
<color name="tv_audio_recording_indicator_icon_background">#CC000000</color>
|
||||
<color name="tv_audio_recording_indicator_stroke">#33FFFFFF</color>
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.UiThread;
|
||||
@@ -36,7 +35,6 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.tv.TvStatusBar;
|
||||
@@ -83,19 +81,14 @@ public class AudioRecordingDisclosureBar implements
|
||||
private static final int STATE_SHOWN = 2;
|
||||
private static final int STATE_DISAPPEARING = 3;
|
||||
|
||||
private static final int ANIMATION_DURATION = 600;
|
||||
private static final int ANIMATION_DURATION_MS = 200;
|
||||
|
||||
private final Context mContext;
|
||||
private boolean mIsEnabled;
|
||||
|
||||
private View mIndicatorView;
|
||||
private View mIconTextsContainer;
|
||||
private View mIconContainerBg;
|
||||
private View mIcon;
|
||||
private View mBgEnd;
|
||||
private View mTextsContainers;
|
||||
private TextView mTextView;
|
||||
private boolean mIsLtr;
|
||||
private boolean mViewAndWindowAdded;
|
||||
private ObjectAnimator mAnimator;
|
||||
|
||||
@State private int mState = STATE_STOPPED;
|
||||
|
||||
@@ -190,7 +183,7 @@ public class AudioRecordingDisclosureBar implements
|
||||
}
|
||||
|
||||
if (active) {
|
||||
showIfNotShown();
|
||||
showIfNeeded();
|
||||
} else {
|
||||
hideIndicatorIfNeeded();
|
||||
}
|
||||
@@ -198,153 +191,132 @@ public class AudioRecordingDisclosureBar implements
|
||||
|
||||
@UiThread
|
||||
private void hideIndicatorIfNeeded() {
|
||||
// If not STATE_APPEARING, will check whether the indicator should be hidden when the
|
||||
// indicator comes to the STATE_SHOWN.
|
||||
// If STATE_DISAPPEARING or STATE_SHOWN - nothing else for us to do here.
|
||||
if (mState != STATE_SHOWN) return;
|
||||
// If STOPPED, NOT_SHOWN or DISAPPEARING - nothing else for us to do here.
|
||||
if (mState != STATE_SHOWN && mState != STATE_APPEARING) return;
|
||||
|
||||
// If is in the STATE_SHOWN and there are no active recorders - hide.
|
||||
if (!hasActiveRecorders()) {
|
||||
hide();
|
||||
if (hasActiveRecorders()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mViewAndWindowAdded) {
|
||||
mState = STATE_DISAPPEARING;
|
||||
animateDisappearance();
|
||||
} else {
|
||||
// Appearing animation has not started yet, as we were still waiting for the View to be
|
||||
// laid out.
|
||||
mState = STATE_NOT_SHOWN;
|
||||
removeIndicatorView();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void showIfNotShown() {
|
||||
if (mState != STATE_NOT_SHOWN) return;
|
||||
private void showIfNeeded() {
|
||||
// If STOPPED, SHOWN or APPEARING - nothing else for us to do here.
|
||||
if (mState != STATE_NOT_SHOWN && mState != STATE_DISAPPEARING) return;
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Showing indicator");
|
||||
|
||||
mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
|
||||
== View.LAYOUT_DIRECTION_LTR;
|
||||
final int prevState = mState;
|
||||
mState = STATE_APPEARING;
|
||||
|
||||
if (prevState == STATE_DISAPPEARING) {
|
||||
animateAppearance();
|
||||
return;
|
||||
}
|
||||
|
||||
// Inflate the indicator view
|
||||
mIndicatorView = LayoutInflater.from(mContext).inflate(
|
||||
R.layout.tv_audio_recording_indicator,
|
||||
null);
|
||||
mIconTextsContainer = mIndicatorView.findViewById(R.id.icon_texts_container);
|
||||
mIconContainerBg = mIconTextsContainer.findViewById(R.id.icon_container_bg);
|
||||
mIcon = mIconTextsContainer.findViewById(R.id.icon_mic);
|
||||
mTextsContainers = mIconTextsContainer.findViewById(R.id.texts_container);
|
||||
mTextView = mTextsContainers.findViewById(R.id.text);
|
||||
mBgEnd = mIndicatorView.findViewById(R.id.bg_end);
|
||||
R.layout.tv_audio_recording_indicator, null);
|
||||
|
||||
mTextsContainers.setVisibility(View.GONE);
|
||||
mIconContainerBg.setVisibility(View.GONE);
|
||||
mTextView.setVisibility(View.GONE);
|
||||
mBgEnd.setVisibility(View.GONE);
|
||||
mTextsContainers = null;
|
||||
mIconContainerBg = null;
|
||||
mTextView = null;
|
||||
mBgEnd = null;
|
||||
|
||||
// Initially change the visibility to INVISIBLE, wait until and receives the size and
|
||||
// then animate it moving from "off" the screen correctly
|
||||
mIndicatorView.setVisibility(View.INVISIBLE);
|
||||
// 1. Set alpha to 0.
|
||||
// 2. Wait until the window is shown and the view is laid out.
|
||||
// 3. Start a "fade in" (alpha) animation.
|
||||
mIndicatorView.setAlpha(0f);
|
||||
mIndicatorView
|
||||
.getViewTreeObserver()
|
||||
.addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (mState == STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
// State could have changed to NOT_SHOWN (if all the recorders are
|
||||
// already gone) to STOPPED (if the indicator was disabled)
|
||||
if (mState != STATE_APPEARING) return;
|
||||
|
||||
mViewAndWindowAdded = true;
|
||||
// Remove the observer
|
||||
mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
|
||||
this);
|
||||
|
||||
// Now that the width of the indicator has been assigned, we can
|
||||
// move it in from off the screen.
|
||||
final int initialOffset =
|
||||
(mIsLtr ? 1 : -1) * mIndicatorView.getWidth();
|
||||
final AnimatorSet set = new AnimatorSet();
|
||||
set.setDuration(ANIMATION_DURATION);
|
||||
set.playTogether(
|
||||
ObjectAnimator.ofFloat(mIndicatorView,
|
||||
View.TRANSLATION_X, initialOffset, 0),
|
||||
ObjectAnimator.ofFloat(mIndicatorView, View.ALPHA, 0f,
|
||||
1f));
|
||||
set.addListener(
|
||||
new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation,
|
||||
boolean isReverse) {
|
||||
if (mState == STATE_STOPPED) return;
|
||||
|
||||
// Indicator is INVISIBLE at the moment, change it.
|
||||
mIndicatorView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
onAppeared();
|
||||
}
|
||||
});
|
||||
set.start();
|
||||
animateAppearance();
|
||||
}
|
||||
});
|
||||
|
||||
final boolean isLtr = mContext.getResources().getConfiguration().getLayoutDirection()
|
||||
== View.LAYOUT_DIRECTION_LTR;
|
||||
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
|
||||
WRAP_CONTENT,
|
||||
WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
layoutParams.gravity = Gravity.TOP | (mIsLtr ? Gravity.RIGHT : Gravity.LEFT);
|
||||
layoutParams.gravity = Gravity.TOP | (isLtr ? Gravity.RIGHT : Gravity.LEFT);
|
||||
layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
|
||||
layoutParams.packageName = mContext.getPackageName();
|
||||
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
|
||||
Context.WINDOW_SERVICE);
|
||||
windowManager.addView(mIndicatorView, layoutParams);
|
||||
|
||||
mState = STATE_APPEARING;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void hide() {
|
||||
if (DEBUG) Log.d(TAG, "Hide indicator");
|
||||
|
||||
final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth()
|
||||
- (int) mIconTextsContainer.getTranslationX());
|
||||
final AnimatorSet set = new AnimatorSet();
|
||||
set.playTogether(
|
||||
ObjectAnimator.ofFloat(mIndicatorView, View.TRANSLATION_X, targetOffset),
|
||||
ObjectAnimator.ofFloat(mIcon, View.ALPHA, 0f));
|
||||
set.setDuration(ANIMATION_DURATION);
|
||||
set.addListener(
|
||||
new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
onHidden();
|
||||
private void animateAppearance() {
|
||||
animateAlphaTo(1f);
|
||||
}
|
||||
|
||||
private void animateDisappearance() {
|
||||
animateAlphaTo(0f);
|
||||
}
|
||||
|
||||
private void animateAlphaTo(final float endValue) {
|
||||
if (mAnimator == null) {
|
||||
if (DEBUG) Log.d(TAG, "set up animator");
|
||||
|
||||
mAnimator = new ObjectAnimator();
|
||||
mAnimator.setTarget(mIndicatorView);
|
||||
mAnimator.setProperty(View.ALPHA);
|
||||
mAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation, boolean isReverse) {
|
||||
if (DEBUG) Log.d(TAG, "onAnimationStart");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
if (DEBUG) Log.d(TAG, "onAnimationCancel");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (DEBUG) Log.d(TAG, "onAnimationEnd");
|
||||
|
||||
if (mState == STATE_APPEARING) {
|
||||
mState = STATE_SHOWN;
|
||||
} else if (mState == STATE_DISAPPEARING) {
|
||||
removeIndicatorView();
|
||||
mState = STATE_NOT_SHOWN;
|
||||
}
|
||||
});
|
||||
set.start();
|
||||
|
||||
mState = STATE_DISAPPEARING;
|
||||
}
|
||||
|
||||
|
||||
@UiThread
|
||||
private void onAppeared() {
|
||||
if (mState == STATE_STOPPED) return;
|
||||
|
||||
mState = STATE_SHOWN;
|
||||
|
||||
hideIndicatorIfNeeded();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void onHidden() {
|
||||
if (mState == STATE_STOPPED) return;
|
||||
|
||||
removeIndicatorView();
|
||||
mState = STATE_NOT_SHOWN;
|
||||
|
||||
if (hasActiveRecorders()) {
|
||||
// Got new recorders, show again.
|
||||
showIfNotShown();
|
||||
}
|
||||
});
|
||||
} else if (mAnimator.isRunning()) {
|
||||
if (DEBUG) Log.d(TAG, "cancel running animation");
|
||||
mAnimator.cancel();
|
||||
}
|
||||
|
||||
final float currentValue = mIndicatorView.getAlpha();
|
||||
if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
|
||||
|
||||
mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
|
||||
mAnimator.setFloatValues(endValue);
|
||||
mAnimator.start();
|
||||
}
|
||||
|
||||
private boolean hasActiveRecorders() {
|
||||
@@ -363,12 +335,9 @@ public class AudioRecordingDisclosureBar implements
|
||||
windowManager.removeView(mIndicatorView);
|
||||
|
||||
mIndicatorView = null;
|
||||
mIconTextsContainer = null;
|
||||
mIconContainerBg = null;
|
||||
mIcon = null;
|
||||
mTextsContainers = null;
|
||||
mTextView = null;
|
||||
mBgEnd = null;
|
||||
mAnimator = null;
|
||||
|
||||
mViewAndWindowAdded = false;
|
||||
}
|
||||
|
||||
private static List<String> splitByComma(String string) {
|
||||
|
||||
Reference in New Issue
Block a user