Add shrinking animation to the tv privacy chip.
Updated to match ux design: icons shrinking to a dot design after a few seconds. Bug: 186718078 Test: manual + atest CameraMicindicatorsPermissionTest + atest MicIndicatorTest Change-Id: If032c235cb5ef124fb48523796ce09f5784e50e0
This commit is contained in:
41
packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
Normal file
41
packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together"
|
||||
android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
|
||||
android:duration="@integer/privacy_chip_animation_millis">
|
||||
<objectAnimator
|
||||
android:propertyName="height"
|
||||
android:valueTo="@dimen/privacy_chip_dot_size"
|
||||
android:valueType="floatType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="marginEnd"
|
||||
android:valueTo="@dimen/privacy_chip_dot_margin_horizontal"
|
||||
android:valueType="floatType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="radius"
|
||||
android:valueTo="@dimen/privacy_chip_dot_radius"
|
||||
android:valueType="floatType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="dotAlpha"
|
||||
android:valueTo="255"
|
||||
android:valueType="intType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="bgAlpha"
|
||||
android:valueTo="255"
|
||||
android:valueType="intType"/>
|
||||
</set>
|
||||
41
packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
Normal file
41
packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together"
|
||||
android:interpolator="@interpolator/tv_privacy_chip_expand_interpolator"
|
||||
android:duration="@integer/privacy_chip_animation_millis">
|
||||
<objectAnimator
|
||||
android:propertyName="height"
|
||||
android:valueTo="@dimen/privacy_chip_height"
|
||||
android:valueType="floatType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="marginEnd"
|
||||
android:valueTo="0"
|
||||
android:valueType="floatType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="radius"
|
||||
android:valueTo="@dimen/privacy_chip_radius"
|
||||
android:valueType="floatType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="dotAlpha"
|
||||
android:valueTo="255"
|
||||
android:valueType="intType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="bgAlpha"
|
||||
android:valueTo="0"
|
||||
android:valueType="intType"/>
|
||||
</set>
|
||||
25
packages/SystemUI/res/anim/tv_privacy_chip_fade_in.xml
Normal file
25
packages/SystemUI/res/anim/tv_privacy_chip_fade_in.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together"
|
||||
android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
|
||||
android:duration="@integer/privacy_chip_animation_millis">
|
||||
<objectAnimator
|
||||
android:propertyName="dotAlpha"
|
||||
android:valueTo="255"
|
||||
android:valueType="intType"/>
|
||||
</set>
|
||||
29
packages/SystemUI/res/anim/tv_privacy_chip_fade_out.xml
Normal file
29
packages/SystemUI/res/anim/tv_privacy_chip_fade_out.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together"
|
||||
android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
|
||||
android:duration="@integer/privacy_chip_animation_millis">
|
||||
<objectAnimator
|
||||
android:propertyName="dotAlpha"
|
||||
android:valueTo="0"
|
||||
android:valueType="intType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="bgAlpha"
|
||||
android:valueTo="0"
|
||||
android:valueType="intType"/>
|
||||
</set>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~ Copyright (C) 2021 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.
|
||||
@@ -15,11 +15,8 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners android:radius="20dp"/>
|
||||
<solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
|
||||
<stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
|
||||
|
||||
</shape>
|
||||
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:controlX1="0.4"
|
||||
android:controlY1="1.00"
|
||||
android:controlX2="0.12"
|
||||
android:controlY2="1.00"/>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
|
||||
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:controlX1="0.12"
|
||||
android:controlY1="1.00"
|
||||
android:controlX2="0.4"
|
||||
android:controlY2="1.00"/>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~ Copyright (C) 2021 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.
|
||||
@@ -20,16 +20,25 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:layout_gravity="center">
|
||||
android:gravity="center"
|
||||
android:animateLayoutChanges="false"
|
||||
android:padding="@dimen/privacy_chip_margin">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chip_drawable"
|
||||
android:layout_width="51dp"
|
||||
android:layout_height="@dimen/privacy_chip_height"
|
||||
android:minWidth="@dimen/privacy_chip_dot_bg_width"
|
||||
android:minHeight="@dimen/privacy_chip_dot_bg_height"
|
||||
android:layout_gravity="top|end" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/icons_container"
|
||||
android:background="@drawable/tv_rect_shadow_rounded"
|
||||
android:padding="@dimen/privacy_chip_icon_padding"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"/>
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:animateLayoutChanges="true"
|
||||
android:paddingHorizontal="@dimen/privacy_chip_padding_horizontal" />
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<fade android:fadingMode="fade_in" />
|
||||
<changeBounds/>
|
||||
</transitionSet>
|
||||
20
packages/SystemUI/res/transition/tv_privacy_chip_expand.xml
Normal file
20
packages/SystemUI/res/transition/tv_privacy_chip_expand.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<changeBounds/>
|
||||
<fade android:fadingMode="fade_out" />
|
||||
</transitionSet>
|
||||
@@ -34,4 +34,7 @@
|
||||
<color name="bottom_sheet_button_text_color_focused">#DB202124</color>
|
||||
<color name="bottom_sheet_button_text_color_unfocused">#B5E8EAED</color>
|
||||
|
||||
<color name="privacy_circle">#5BB974</color> <!-- g400 -->
|
||||
<color name="privacy_icon_tint">#30302A</color>
|
||||
<color name="privacy_chip_dot_bg_tint">#66000000</color>
|
||||
</resources>
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
<!-- Opacity at which the background for the shutdown UI will be drawn. -->
|
||||
<item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item>
|
||||
|
||||
<dimen name="privacy_chip_icon_margin">3dp</dimen>
|
||||
<dimen name="privacy_chip_icon_padding">8dp</dimen>
|
||||
<dimen name="privacy_chip_icon_size">13dp</dimen>
|
||||
|
||||
<dimen name="bottom_sheet_padding_horizontal">32dp</dimen>
|
||||
<dimen name="bottom_sheet_padding_vertical">24dp</dimen>
|
||||
|
||||
@@ -44,4 +40,20 @@
|
||||
<dimen name="bottom_sheet_margin">24dp</dimen>
|
||||
<dimen name="bottom_sheet_background_blur_radius">120dp</dimen>
|
||||
|
||||
<dimen name="privacy_chip_margin">12dp</dimen>
|
||||
<dimen name="privacy_chip_icon_margin_in_between">9dp</dimen>
|
||||
<dimen name="privacy_chip_padding_horizontal">9dp</dimen>
|
||||
<dimen name="privacy_chip_icon_size">12dp</dimen>
|
||||
<dimen name="privacy_chip_height">24dp</dimen>
|
||||
<dimen name="privacy_chip_min_width">30dp</dimen>
|
||||
<dimen name="privacy_chip_radius">12dp</dimen>
|
||||
|
||||
<dimen name="privacy_chip_dot_size">8dp</dimen>
|
||||
<dimen name="privacy_chip_dot_radius">4dp</dimen>
|
||||
<dimen name="privacy_chip_dot_margin_horizontal">8dp</dimen>
|
||||
|
||||
<dimen name="privacy_chip_dot_bg_width">24dp</dimen>
|
||||
<dimen name="privacy_chip_dot_bg_height">18dp</dimen>
|
||||
<dimen name="privacy_chip_dot_bg_radius">9dp</dimen>
|
||||
|
||||
</resources>
|
||||
@@ -20,4 +20,6 @@
|
||||
Value 81 corresponds to BOTTOM|CENTER_HORIZONTAL.
|
||||
Value 21 corresponds to RIGHT|CENTER_VERTICAL. -->
|
||||
<integer name="volume_dialog_gravity">21</integer>
|
||||
|
||||
<integer name="privacy_chip_animation_millis">300</integer>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.systemui.privacy.television;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Drawable that can go from being the background of the privacy icons to a small dot.
|
||||
* The icons are not included.
|
||||
*/
|
||||
public class PrivacyChipDrawable extends Drawable {
|
||||
|
||||
private static final String TAG = PrivacyChipDrawable.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private float mWidth;
|
||||
private float mHeight;
|
||||
private float mMarginEnd;
|
||||
private float mRadius;
|
||||
private int mDotAlpha;
|
||||
private int mBgAlpha;
|
||||
|
||||
private float mTargetWidth;
|
||||
private final int mMinWidth;
|
||||
private final int mIconWidth;
|
||||
private final int mIconPadding;
|
||||
private final int mBgWidth;
|
||||
private final int mBgHeight;
|
||||
private final int mBgRadius;
|
||||
private final int mDotSize;
|
||||
|
||||
private final AnimatorSet mFadeIn;
|
||||
private final AnimatorSet mFadeOut;
|
||||
private final AnimatorSet mCollapse;
|
||||
private final AnimatorSet mExpand;
|
||||
private Animator mWidthAnimator;
|
||||
|
||||
private final Paint mChipPaint;
|
||||
private final Paint mBgPaint;
|
||||
|
||||
private boolean mIsRtl;
|
||||
|
||||
private boolean mIsExpanded = true;
|
||||
|
||||
private PrivacyChipDrawableListener mListener;
|
||||
|
||||
interface PrivacyChipDrawableListener {
|
||||
void onFadeOutFinished();
|
||||
}
|
||||
|
||||
public PrivacyChipDrawable(Context context) {
|
||||
mChipPaint = new Paint();
|
||||
mChipPaint.setStyle(Paint.Style.FILL);
|
||||
mChipPaint.setColor(context.getColor(R.color.privacy_circle));
|
||||
mChipPaint.setAlpha(mDotAlpha);
|
||||
mChipPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
mBgPaint = new Paint();
|
||||
mBgPaint.setStyle(Paint.Style.FILL);
|
||||
mBgPaint.setColor(context.getColor(R.color.privacy_chip_dot_bg_tint));
|
||||
mBgPaint.setAlpha(mBgAlpha);
|
||||
mBgPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
mBgWidth = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_width);
|
||||
mBgHeight = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_dot_bg_height);
|
||||
mBgRadius = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_dot_bg_radius);
|
||||
|
||||
mMinWidth = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_min_width);
|
||||
mIconWidth = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
|
||||
mIconPadding = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_icon_margin_in_between);
|
||||
mDotSize = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_dot_size);
|
||||
|
||||
mWidth = mMinWidth;
|
||||
mHeight = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_height);
|
||||
mRadius = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_radius);
|
||||
|
||||
mExpand = (AnimatorSet) AnimatorInflater.loadAnimator(context,
|
||||
R.anim.tv_privacy_chip_expand);
|
||||
mExpand.setTarget(this);
|
||||
|
||||
mCollapse = (AnimatorSet) AnimatorInflater.loadAnimator(context,
|
||||
R.anim.tv_privacy_chip_collapse);
|
||||
mCollapse.setTarget(this);
|
||||
|
||||
mFadeIn = (AnimatorSet) AnimatorInflater.loadAnimator(context,
|
||||
R.anim.tv_privacy_chip_fade_in);
|
||||
mFadeIn.setTarget(this);
|
||||
|
||||
mFadeOut = (AnimatorSet) AnimatorInflater.loadAnimator(context,
|
||||
R.anim.tv_privacy_chip_fade_out);
|
||||
mFadeOut.setTarget(this);
|
||||
mFadeOut.addListener(new Animator.AnimatorListener() {
|
||||
private boolean mCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mCancelled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mCancelled && mListener != null) {
|
||||
if (DEBUG) Log.d(TAG, "Fade-out complete");
|
||||
mListener.onFadeOutFinished();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mCancelled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
// no-op
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass null to remove listener.
|
||||
*/
|
||||
public void setListener(@Nullable PrivacyChipDrawableListener listener) {
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call once the view that is showing the drawable is visible to start fading the chip in.
|
||||
*/
|
||||
public void startInitialFadeIn() {
|
||||
if (DEBUG) Log.d(TAG, "initial fade-in");
|
||||
mFadeIn.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
Rect bounds = getBounds();
|
||||
|
||||
int centerVertical = (bounds.bottom - bounds.top) / 2;
|
||||
// Dot background
|
||||
RectF bgBounds = new RectF(
|
||||
mIsRtl ? bounds.left : bounds.right - mBgWidth,
|
||||
centerVertical - mBgHeight / 2f,
|
||||
mIsRtl ? bounds.left + mBgWidth : bounds.right,
|
||||
centerVertical + mBgHeight / 2f);
|
||||
if (DEBUG) Log.v(TAG, "bg: " + bgBounds.toShortString());
|
||||
canvas.drawRoundRect(bgBounds, mBgRadius, mBgRadius, mBgPaint);
|
||||
|
||||
// Icon background / dot
|
||||
RectF greenBounds = new RectF(
|
||||
mIsRtl ? bounds.left + mMarginEnd : bounds.right - mWidth - mMarginEnd,
|
||||
centerVertical - mHeight / 2,
|
||||
mIsRtl ? bounds.left + mWidth + mMarginEnd : bounds.right - mMarginEnd,
|
||||
centerVertical + mHeight / 2);
|
||||
if (DEBUG) Log.v(TAG, "green: " + greenBounds.toShortString());
|
||||
canvas.drawRoundRect(greenBounds, mRadius, mRadius, mChipPaint);
|
||||
}
|
||||
|
||||
private void animateToNewTargetWidth(float width) {
|
||||
if (DEBUG) Log.d(TAG, "new target width: " + width);
|
||||
if (width != mTargetWidth) {
|
||||
mTargetWidth = width;
|
||||
Animator newWidthAnimator = ObjectAnimator.ofFloat(this, "width", mTargetWidth);
|
||||
newWidthAnimator.start();
|
||||
if (mWidthAnimator != null) {
|
||||
mWidthAnimator.cancel();
|
||||
}
|
||||
mWidthAnimator = newWidthAnimator;
|
||||
}
|
||||
}
|
||||
|
||||
private void expand() {
|
||||
if (DEBUG) Log.d(TAG, "expanding");
|
||||
if (mIsExpanded) {
|
||||
return;
|
||||
}
|
||||
mIsExpanded = true;
|
||||
|
||||
mExpand.start();
|
||||
mCollapse.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the animation to a dot.
|
||||
*/
|
||||
public void collapse() {
|
||||
if (DEBUG) Log.d(TAG, "collapsing");
|
||||
if (!mIsExpanded) {
|
||||
return;
|
||||
}
|
||||
mIsExpanded = false;
|
||||
|
||||
animateToNewTargetWidth(mDotSize);
|
||||
mCollapse.start();
|
||||
mExpand.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades out the view if 0 icons are to be shown, expands the chip if it has been collapsed and
|
||||
* makes the width of the chip adjust to the amount of icons to be shown.
|
||||
* Should not be called when only the order of the icons was changed as the chip will expand
|
||||
* again without there being any real update.
|
||||
*
|
||||
* @param iconCount Can be 0 to fade out the chip.
|
||||
*/
|
||||
public void updateIcons(int iconCount) {
|
||||
if (DEBUG) Log.d(TAG, "updating icons: " + iconCount);
|
||||
|
||||
// calculate chip size and use it for end value of animation that is specified in code,
|
||||
// not xml
|
||||
if (iconCount == 0) {
|
||||
// fade out if there are no icons
|
||||
mFadeOut.start();
|
||||
|
||||
mWidthAnimator.cancel();
|
||||
mFadeIn.cancel();
|
||||
mExpand.cancel();
|
||||
mCollapse.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
mFadeOut.cancel();
|
||||
expand();
|
||||
animateToNewTargetWidth(mMinWidth + (iconCount - 1) * (mIconWidth + mIconPadding));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
setDotAlpha(alpha);
|
||||
setBgAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha() {
|
||||
return mDotAlpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set alpha value the green part of the chip.
|
||||
*/
|
||||
@Keep
|
||||
public void setDotAlpha(int alpha) {
|
||||
if (DEBUG) Log.v(TAG, "dot alpha updated to: " + alpha);
|
||||
mDotAlpha = alpha;
|
||||
mChipPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Keep
|
||||
public int getDotAlpha() {
|
||||
return mDotAlpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set alpha value of the background of the chip.
|
||||
*/
|
||||
@Keep
|
||||
public void setBgAlpha(int alpha) {
|
||||
if (DEBUG) Log.v(TAG, "bg alpha updated to: " + alpha);
|
||||
mBgAlpha = alpha;
|
||||
mBgPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Keep
|
||||
public int getBgAlpha() {
|
||||
return mBgAlpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* The radius of the green part of the chip, not the background.
|
||||
*/
|
||||
@Keep
|
||||
public void setRadius(float radius) {
|
||||
mRadius = radius;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The radius of the green part of the chip, not the background.
|
||||
*/
|
||||
@Keep
|
||||
public float getRadius() {
|
||||
return mRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Height of the green part of the chip, not including the background.
|
||||
*/
|
||||
@Keep
|
||||
public void setHeight(float height) {
|
||||
mHeight = height;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Height of the green part of the chip, not including the background.
|
||||
*/
|
||||
@Keep
|
||||
public float getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Width of the green part of the chip, not including the background.
|
||||
*/
|
||||
@Keep
|
||||
public void setWidth(float width) {
|
||||
mWidth = width;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Width of the green part of the chip, not including the background.
|
||||
*/
|
||||
@Keep
|
||||
public float getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Margin at the end of the green part of the chip, so that it will be placed in the middle of
|
||||
* the rounded rectangle in the background.
|
||||
*/
|
||||
@Keep
|
||||
public void setMarginEnd(float marginEnd) {
|
||||
mMarginEnd = marginEnd;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Margin at the end of the green part of the chip, so that it will be placed in the
|
||||
* middle of the rounded rectangle in the background.
|
||||
*/
|
||||
@Keep
|
||||
public float getMarginEnd() {
|
||||
return mMarginEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the layout direction.
|
||||
*/
|
||||
public void setRtl(boolean isRtl) {
|
||||
mIsRtl = isRtl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,9 +25,10 @@ import android.annotation.IntDef;
|
||||
import android.annotation.UiThread;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -38,15 +39,20 @@ import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.privacy.PrivacyChipBuilder;
|
||||
import com.android.systemui.privacy.PrivacyItem;
|
||||
import com.android.systemui.privacy.PrivacyItemController;
|
||||
import com.android.systemui.privacy.PrivacyType;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -56,9 +62,10 @@ import javax.inject.Inject;
|
||||
* recording audio, accessing the camera or accessing the location.
|
||||
*/
|
||||
@SysUISingleton
|
||||
public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback {
|
||||
public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback,
|
||||
PrivacyChipDrawable.PrivacyChipDrawableListener {
|
||||
private static final String TAG = "TvOngoingPrivacyChip";
|
||||
static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
// This title is used in CameraMicIndicatorsPermissionTest and
|
||||
// RecognitionServiceMicIndicatorTest.
|
||||
@@ -68,7 +75,8 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
@IntDef(prefix = {"STATE_"}, value = {
|
||||
STATE_NOT_SHOWN,
|
||||
STATE_APPEARING,
|
||||
STATE_SHOWN,
|
||||
STATE_EXPANDED,
|
||||
STATE_COLLAPSED,
|
||||
STATE_DISAPPEARING
|
||||
})
|
||||
public @interface State {
|
||||
@@ -76,46 +84,58 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
|
||||
private static final int STATE_NOT_SHOWN = 0;
|
||||
private static final int STATE_APPEARING = 1;
|
||||
private static final int STATE_SHOWN = 2;
|
||||
private static final int STATE_DISAPPEARING = 3;
|
||||
private static final int STATE_EXPANDED = 2;
|
||||
private static final int STATE_COLLAPSED = 3;
|
||||
private static final int STATE_DISAPPEARING = 4;
|
||||
|
||||
private static final int ANIMATION_DURATION_MS = 200;
|
||||
private static final int EXPANDED_DURATION_MS = 4000;
|
||||
public final int mAnimationDurationMs;
|
||||
|
||||
private final Context mContext;
|
||||
private final PrivacyItemController mPrivacyItemController;
|
||||
|
||||
private View mIndicatorView;
|
||||
private ViewGroup mIndicatorView;
|
||||
private boolean mViewAndWindowAdded;
|
||||
private ObjectAnimator mAnimator;
|
||||
|
||||
private boolean mMicCameraIndicatorFlagEnabled;
|
||||
private boolean mLocationIndicatorEnabled;
|
||||
private List<PrivacyItem> mPrivacyItems;
|
||||
private boolean mAllIndicatorsEnabled;
|
||||
|
||||
@NonNull
|
||||
private List<PrivacyItem> mPrivacyItems = Collections.emptyList();
|
||||
|
||||
private LinearLayout mIconsContainer;
|
||||
private final int mIconSize;
|
||||
private final int mIconMarginStart;
|
||||
|
||||
private PrivacyChipDrawable mChipDrawable;
|
||||
|
||||
private final Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
|
||||
private final Runnable mCollapseRunnable = this::collapseChip;
|
||||
|
||||
@State
|
||||
private int mState = STATE_NOT_SHOWN;
|
||||
|
||||
@Inject
|
||||
public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController) {
|
||||
super(context);
|
||||
Log.d(TAG, "Privacy chip running without id");
|
||||
if (DEBUG) Log.d(TAG, "Privacy chip running");
|
||||
mContext = context;
|
||||
mPrivacyItemController = privacyItemController;
|
||||
|
||||
Resources res = mContext.getResources();
|
||||
mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin));
|
||||
mIconMarginStart = Math.round(
|
||||
res.getDimension(R.dimen.privacy_chip_icon_margin_in_between));
|
||||
mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
|
||||
|
||||
mAnimationDurationMs = res.getInteger(R.integer.privacy_chip_animation_millis);
|
||||
|
||||
mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable();
|
||||
mLocationIndicatorEnabled = privacyItemController.getLocationAvailable();
|
||||
mAllIndicatorsEnabled = privacyItemController.getAllIndicatorsAvailable();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled);
|
||||
Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled);
|
||||
Log.d(TAG, "allIndicators: " + mAllIndicatorsEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,69 +145,145 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
|
||||
public void onPrivacyItemsChanged(@NonNull List<PrivacyItem> privacyItems) {
|
||||
if (DEBUG) Log.d(TAG, "PrivacyItemsChanged");
|
||||
mPrivacyItems = privacyItems;
|
||||
updateUI();
|
||||
|
||||
List<PrivacyItem> updatedPrivacyItems = new ArrayList<>(privacyItems);
|
||||
// Never show the location indicator on tv.
|
||||
if (updatedPrivacyItems.removeIf(
|
||||
privacyItem -> privacyItem.getPrivacyType() == PrivacyType.TYPE_LOCATION)) {
|
||||
if (DEBUG) Log.v(TAG, "Removed the location item");
|
||||
}
|
||||
|
||||
if (isChipDisabled()) {
|
||||
fadeOutIndicator();
|
||||
mPrivacyItems = updatedPrivacyItems;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do they have the same elements? (order doesn't matter)
|
||||
if (updatedPrivacyItems.size() == mPrivacyItems.size()
|
||||
&& mPrivacyItems.containsAll(updatedPrivacyItems)) {
|
||||
if (DEBUG) Log.d(TAG, "List wasn't updated");
|
||||
return;
|
||||
}
|
||||
|
||||
mPrivacyItems = updatedPrivacyItems;
|
||||
updateChip();
|
||||
}
|
||||
|
||||
private void updateChip() {
|
||||
if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
|
||||
|
||||
if (mPrivacyItems.isEmpty()) {
|
||||
if (DEBUG) Log.d(TAG, "removing indicator (state: " + stateToString(mState) + ")");
|
||||
fadeOutIndicator();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Current state: " + stateToString(mState));
|
||||
switch (mState) {
|
||||
case STATE_NOT_SHOWN:
|
||||
createAndShowIndicator();
|
||||
break;
|
||||
case STATE_APPEARING:
|
||||
case STATE_EXPANDED:
|
||||
updateIcons();
|
||||
collapseLater();
|
||||
break;
|
||||
case STATE_COLLAPSED:
|
||||
case STATE_DISAPPEARING:
|
||||
mState = STATE_EXPANDED;
|
||||
updateIcons();
|
||||
animateIconAppearance();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse the chip EXPANDED_DURATION_MS from now.
|
||||
*/
|
||||
private void collapseLater() {
|
||||
mUiThreadHandler.removeCallbacks(mCollapseRunnable);
|
||||
if (DEBUG) Log.d(TAG, "chip will collapse in " + EXPANDED_DURATION_MS + "ms");
|
||||
mUiThreadHandler.postDelayed(mCollapseRunnable, EXPANDED_DURATION_MS);
|
||||
}
|
||||
|
||||
private void collapseChip() {
|
||||
if (DEBUG) Log.d(TAG, "collapseChip");
|
||||
|
||||
if (mState != STATE_EXPANDED) {
|
||||
return;
|
||||
}
|
||||
mState = STATE_COLLAPSED;
|
||||
|
||||
if (mChipDrawable != null) {
|
||||
mChipDrawable.collapse();
|
||||
}
|
||||
animateIconDisappearance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlagMicCameraChanged(boolean flag) {
|
||||
if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag);
|
||||
mMicCameraIndicatorFlagEnabled = flag;
|
||||
updateChipOnFlagChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlagLocationChanged(boolean flag) {
|
||||
if (DEBUG) Log.d(TAG, "location indicators enabled: " + flag);
|
||||
mLocationIndicatorEnabled = flag;
|
||||
public void onFlagAllChanged(boolean flag) {
|
||||
if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag);
|
||||
mAllIndicatorsEnabled = flag;
|
||||
updateChipOnFlagChanged();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
|
||||
private boolean isChipDisabled() {
|
||||
return !(mMicCameraIndicatorFlagEnabled || mAllIndicatorsEnabled);
|
||||
}
|
||||
|
||||
if ((mMicCameraIndicatorFlagEnabled || mLocationIndicatorEnabled)
|
||||
&& !mPrivacyItems.isEmpty()) {
|
||||
if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) {
|
||||
showIndicator();
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "only updating icons");
|
||||
PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems);
|
||||
setIcons(builder.generateIcons(), mIconsContainer);
|
||||
mIconsContainer.requestLayout();
|
||||
}
|
||||
private void updateChipOnFlagChanged() {
|
||||
if (isChipDisabled()) {
|
||||
fadeOutIndicator();
|
||||
} else {
|
||||
hideIndicatorIfNeeded();
|
||||
updateChip();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void hideIndicatorIfNeeded() {
|
||||
private void fadeOutIndicator() {
|
||||
if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) return;
|
||||
|
||||
mUiThreadHandler.removeCallbacks(mCollapseRunnable);
|
||||
|
||||
if (mViewAndWindowAdded) {
|
||||
mState = STATE_DISAPPEARING;
|
||||
animateDisappearance();
|
||||
animateIconDisappearance();
|
||||
} else {
|
||||
// Appearing animation has not started yet, as we were still waiting for the View to be
|
||||
// laid out.
|
||||
mState = STATE_NOT_SHOWN;
|
||||
removeIndicatorView();
|
||||
}
|
||||
if (mChipDrawable != null) {
|
||||
mChipDrawable.updateIcons(0);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void showIndicator() {
|
||||
private void createAndShowIndicator() {
|
||||
mState = STATE_APPEARING;
|
||||
|
||||
if (mIndicatorView != null || mViewAndWindowAdded) {
|
||||
removeIndicatorView();
|
||||
}
|
||||
|
||||
// Inflate the indicator view
|
||||
mIndicatorView = LayoutInflater.from(mContext).inflate(
|
||||
mIndicatorView = (ViewGroup) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.tv_ongoing_privacy_chip, null);
|
||||
|
||||
// 1. Set alpha to 0.
|
||||
// 1. Set icon 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(
|
||||
@@ -196,20 +292,35 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
public void onGlobalLayout() {
|
||||
// State could have changed to NOT_SHOWN (if all the recorders are
|
||||
// already gone)
|
||||
if (mState != STATE_APPEARING) return;
|
||||
if (mState != STATE_APPEARING) {
|
||||
return;
|
||||
}
|
||||
|
||||
mViewAndWindowAdded = true;
|
||||
// Remove the observer
|
||||
mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
|
||||
this);
|
||||
|
||||
animateAppearance();
|
||||
animateIconAppearance();
|
||||
mChipDrawable.startInitialFadeIn();
|
||||
}
|
||||
});
|
||||
|
||||
final boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
|
||||
== View.LAYOUT_DIRECTION_RTL;
|
||||
if (DEBUG) Log.d(TAG, "is RTL: " + isRtl);
|
||||
|
||||
mChipDrawable = new PrivacyChipDrawable(mContext);
|
||||
mChipDrawable.setListener(this);
|
||||
mChipDrawable.setRtl(isRtl);
|
||||
ImageView chipBackground = mIndicatorView.findViewById(R.id.chip_drawable);
|
||||
if (chipBackground != null) {
|
||||
chipBackground.setImageDrawable(mChipDrawable);
|
||||
}
|
||||
|
||||
mIconsContainer = mIndicatorView.findViewById(R.id.icons_container);
|
||||
PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems);
|
||||
setIcons(builder.generateIcons(), mIconsContainer);
|
||||
mIconsContainer.setAlpha(0f);
|
||||
updateIcons();
|
||||
|
||||
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
|
||||
WRAP_CONTENT,
|
||||
@@ -217,19 +328,19 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
layoutParams.gravity = Gravity.TOP | Gravity.END;
|
||||
layoutParams.gravity = Gravity.TOP | (isRtl ? Gravity.LEFT : Gravity.RIGHT);
|
||||
layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
|
||||
layoutParams.packageName = mContext.getPackageName();
|
||||
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
|
||||
windowManager.addView(mIndicatorView, layoutParams);
|
||||
|
||||
}
|
||||
|
||||
private void setIcons(List<Drawable> icons, ViewGroup iconsContainer) {
|
||||
iconsContainer.removeAllViews();
|
||||
private void updateIcons() {
|
||||
List<Drawable> icons = new PrivacyChipBuilder(mContext, mPrivacyItems).generateIcons();
|
||||
mIconsContainer.removeAllViews();
|
||||
for (int i = 0; i < icons.size(); i++) {
|
||||
Drawable icon = icons.get(i);
|
||||
icon.mutate().setTint(Color.WHITE);
|
||||
icon.mutate().setTint(mContext.getColor(R.color.privacy_icon_tint));
|
||||
ImageView imageView = new ImageView(mContext);
|
||||
imageView.setImageDrawable(icon);
|
||||
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
@@ -241,22 +352,25 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
imageView.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
if (mChipDrawable != null) {
|
||||
mChipDrawable.updateIcons(icons.size());
|
||||
}
|
||||
}
|
||||
|
||||
private void animateAppearance() {
|
||||
animateAlphaTo(1f);
|
||||
private void animateIconAppearance() {
|
||||
animateIconAlphaTo(1f);
|
||||
}
|
||||
|
||||
private void animateDisappearance() {
|
||||
animateAlphaTo(0f);
|
||||
private void animateIconDisappearance() {
|
||||
animateIconAlphaTo(0f);
|
||||
}
|
||||
|
||||
private void animateAlphaTo(final float endValue) {
|
||||
private void animateIconAlphaTo(float endValue) {
|
||||
if (mAnimator == null) {
|
||||
if (DEBUG) Log.d(TAG, "set up animator");
|
||||
|
||||
mAnimator = new ObjectAnimator();
|
||||
mAnimator.setTarget(mIndicatorView);
|
||||
mAnimator.setTarget(mIconsContainer);
|
||||
mAnimator.setProperty(View.ALPHA);
|
||||
mAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
boolean mCancelled;
|
||||
@@ -280,7 +394,7 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
// and then onAnimationEnd(...). We, however, only want to proceed here if the
|
||||
// animation ended "naturally".
|
||||
if (!mCancelled) {
|
||||
onAnimationFinished();
|
||||
onIconAnimationFinished();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -289,19 +403,37 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
mAnimator.cancel();
|
||||
}
|
||||
|
||||
final float currentValue = mIndicatorView.getAlpha();
|
||||
final float currentValue = mIconsContainer.getAlpha();
|
||||
if (currentValue == endValue) {
|
||||
if (DEBUG) Log.d(TAG, "alpha not changing");
|
||||
return;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
|
||||
|
||||
mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
|
||||
mAnimator.setDuration(mAnimationDurationMs);
|
||||
mAnimator.setFloatValues(endValue);
|
||||
mAnimator.start();
|
||||
}
|
||||
|
||||
private void onAnimationFinished() {
|
||||
if (DEBUG) Log.d(TAG, "onAnimationFinished");
|
||||
@Override
|
||||
public void onFadeOutFinished() {
|
||||
if (DEBUG) Log.d(TAG, "drawable fade-out finished");
|
||||
|
||||
if (mState == STATE_DISAPPEARING) {
|
||||
removeIndicatorView();
|
||||
mState = STATE_NOT_SHOWN;
|
||||
}
|
||||
}
|
||||
|
||||
private void onIconAnimationFinished() {
|
||||
if (DEBUG) Log.d(TAG, "onAnimationFinished (icon fade)");
|
||||
|
||||
if (mState == STATE_APPEARING || mState == STATE_EXPANDED) {
|
||||
collapseLater();
|
||||
}
|
||||
|
||||
if (mState == STATE_APPEARING) {
|
||||
mState = STATE_SHOWN;
|
||||
mState = STATE_EXPANDED;
|
||||
} else if (mState == STATE_DISAPPEARING) {
|
||||
removeIndicatorView();
|
||||
mState = STATE_NOT_SHOWN;
|
||||
@@ -312,14 +444,39 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
|
||||
if (DEBUG) Log.d(TAG, "removeIndicatorView");
|
||||
|
||||
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
|
||||
if (windowManager != null) {
|
||||
if (windowManager != null && mIndicatorView != null) {
|
||||
windowManager.removeView(mIndicatorView);
|
||||
}
|
||||
|
||||
mIndicatorView = null;
|
||||
mAnimator = null;
|
||||
|
||||
if (mChipDrawable != null) {
|
||||
mChipDrawable.setListener(null);
|
||||
mChipDrawable = null;
|
||||
}
|
||||
|
||||
mViewAndWindowAdded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in debug logs.
|
||||
*/
|
||||
private String stateToString(@State int state) {
|
||||
switch (state) {
|
||||
case STATE_NOT_SHOWN:
|
||||
return "NOT_SHOWN";
|
||||
case STATE_APPEARING:
|
||||
return "APPEARING";
|
||||
case STATE_EXPANDED:
|
||||
return "EXPANDED";
|
||||
case STATE_COLLAPSED:
|
||||
return "COLLAPSED";
|
||||
case STATE_DISAPPEARING:
|
||||
return "DISAPPEARING";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user