Merge "Prevent clipping clock during shrink, bold transition" into qt-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f47630e739
@@ -48,16 +48,16 @@
|
|||||||
android:id="@+id/default_clock_view_bold"
|
android:id="@+id/default_clock_view_bold"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="bottom|center_horizontal"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:letterSpacing="0.03"
|
android:letterSpacing="0.03"
|
||||||
android:textColor="?attr/wallpaperTextColor"
|
android:textColor="?attr/wallpaperTextColor"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
style="@style/widget_big_bold"
|
style="@style/widget_title_bold"
|
||||||
android:format12Hour="@string/keyguard_widget_12_hours_format"
|
android:format12Hour="@string/keyguard_widget_12_hours_format"
|
||||||
android:format24Hour="@string/keyguard_widget_24_hours_format"
|
android:format24Hour="@string/keyguard_widget_24_hours_format"
|
||||||
android:elegantTextHeight="false"
|
android:elegantTextHeight="false"
|
||||||
android:visibility="gone"
|
android:visibility="invisible"
|
||||||
/>
|
/>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
<include layout="@layout/keyguard_status_area"
|
<include layout="@layout/keyguard_status_area"
|
||||||
|
|||||||
@@ -66,16 +66,16 @@
|
|||||||
<item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
|
<item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
|
||||||
<item name="android:ellipsize">none</item>
|
<item name="android:ellipsize">none</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="widget_big_bold">
|
<style name="widget_title_bold">
|
||||||
<item name="android:textStyle">bold</item>
|
<item name="android:textStyle">bold</item>
|
||||||
<item name="android:textSize">@dimen/widget_big_font_size</item>
|
<item name="android:textSize">@dimen/widget_title_font_size</item>
|
||||||
<item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
|
<item name="android:paddingBottom">@dimen/widget_vertical_padding_clock</item>
|
||||||
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
|
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
|
||||||
<item name="android:ellipsize">none</item>
|
<item name="android:ellipsize">none</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="widget_small_bold">
|
<style name="widget_small_bold">
|
||||||
<item name="android:textStyle">bold</item>
|
<item name="android:textStyle">bold</item>
|
||||||
<item name="android:textSize">@dimen/widget_title_font_size</item>
|
<item name="android:textSize">@dimen/widget_small_font_size</item>
|
||||||
<item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
|
<item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
|
||||||
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
|
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
|
||||||
<item name="android:ellipsize">none</item>
|
<item name="android:ellipsize">none</item>
|
||||||
|
|||||||
@@ -3,22 +3,20 @@ package com.android.keyguard;
|
|||||||
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
|
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.app.WallpaperManager;
|
import android.app.WallpaperManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Paint.Style;
|
import android.graphics.Paint.Style;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.transition.ChangeBounds;
|
|
||||||
import android.transition.Transition;
|
import android.transition.Transition;
|
||||||
import android.transition.TransitionListenerAdapter;
|
import android.transition.TransitionListenerAdapter;
|
||||||
import android.transition.TransitionManager;
|
import android.transition.TransitionManager;
|
||||||
|
import android.transition.TransitionSet;
|
||||||
import android.transition.TransitionValues;
|
import android.transition.TransitionValues;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.MathUtils;
|
import android.util.MathUtils;
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
@@ -51,6 +49,11 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
|
|
||||||
private static final String TAG = "KeyguardClockSwitch";
|
private static final String TAG = "KeyguardClockSwitch";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation fraction when text is transitioned to/from bold.
|
||||||
|
*/
|
||||||
|
private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller used to track StatusBar state to know when to show the big_clock_container.
|
* Controller used to track StatusBar state to know when to show the big_clock_container.
|
||||||
*/
|
*/
|
||||||
@@ -71,10 +74,8 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
*/
|
*/
|
||||||
private final Transition mTransition;
|
private final Transition mTransition;
|
||||||
|
|
||||||
/**
|
private final ClockVisibilityTransition mClockTransition;
|
||||||
* Listener for layout transitions.
|
private final ClockVisibilityTransition mBoldClockTransition;
|
||||||
*/
|
|
||||||
private final Transition.TransitionListener mTransitionListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional/alternative clock injected via plugin.
|
* Optional/alternative clock injected via plugin.
|
||||||
@@ -156,8 +157,19 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
mStatusBarState = mStatusBarStateController.getState();
|
mStatusBarState = mStatusBarStateController.getState();
|
||||||
mSysuiColorExtractor = colorExtractor;
|
mSysuiColorExtractor = colorExtractor;
|
||||||
mClockManager = clockManager;
|
mClockManager = clockManager;
|
||||||
mTransition = new ClockBoundsTransition();
|
|
||||||
mTransitionListener = new ClockBoundsTransitionListener();
|
mClockTransition = new ClockVisibilityTransition().setCutoff(
|
||||||
|
1 - TO_BOLD_TRANSITION_FRACTION);
|
||||||
|
mClockTransition.addTarget(R.id.default_clock_view);
|
||||||
|
mBoldClockTransition = new ClockVisibilityTransition().setCutoff(
|
||||||
|
TO_BOLD_TRANSITION_FRACTION);
|
||||||
|
mBoldClockTransition.addTarget(R.id.default_clock_view_bold);
|
||||||
|
mTransition = new TransitionSet()
|
||||||
|
.setOrdering(TransitionSet.ORDERING_TOGETHER)
|
||||||
|
.addTransition(mClockTransition)
|
||||||
|
.addTransition(mBoldClockTransition)
|
||||||
|
.setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2)
|
||||||
|
.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,7 +194,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
mClockManager.addOnClockChangedListener(mClockChangedListener);
|
mClockManager.addOnClockChangedListener(mClockChangedListener);
|
||||||
mStatusBarStateController.addCallback(mStateListener);
|
mStatusBarStateController.addCallback(mStateListener);
|
||||||
mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener);
|
mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener);
|
||||||
mTransition.addListener(mTransitionListener);
|
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +203,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
mClockManager.removeOnClockChangedListener(mClockChangedListener);
|
mClockManager.removeOnClockChangedListener(mClockChangedListener);
|
||||||
mStatusBarStateController.removeCallback(mStateListener);
|
mStatusBarStateController.removeCallback(mStateListener);
|
||||||
mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener);
|
mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener);
|
||||||
mTransition.removeListener(mTransitionListener);
|
|
||||||
setClockPlugin(null);
|
setClockPlugin(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +297,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
|
|
||||||
public void setTextSize(int unit, float size) {
|
public void setTextSize(int unit, float size) {
|
||||||
mClockView.setTextSize(unit, size);
|
mClockView.setTextSize(unit, size);
|
||||||
mClockViewBold.setTextSize(unit, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFormat12Hour(CharSequence format) {
|
public void setFormat12Hour(CharSequence format) {
|
||||||
@@ -390,23 +399,46 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
* Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in
|
* Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in
|
||||||
* these cases.
|
* these cases.
|
||||||
*/
|
*/
|
||||||
public void setKeyguardShowingHeader(boolean hasHeader) {
|
void setKeyguardShowingHeader(boolean hasHeader) {
|
||||||
if (mShowingHeader == hasHeader || hasCustomClock()) {
|
if (mShowingHeader == hasHeader || hasCustomClock()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mShowingHeader = hasHeader;
|
mShowingHeader = hasHeader;
|
||||||
|
|
||||||
|
float smallFontSize = mContext.getResources().getDimensionPixelSize(
|
||||||
|
R.dimen.widget_small_font_size);
|
||||||
|
float bigFontSize = mContext.getResources().getDimensionPixelSize(
|
||||||
|
R.dimen.widget_big_font_size);
|
||||||
|
mClockTransition.setScale(smallFontSize / bigFontSize);
|
||||||
|
mBoldClockTransition.setScale(bigFontSize / smallFontSize);
|
||||||
|
|
||||||
TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
|
TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
|
||||||
int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader
|
mClockView.setVisibility(hasHeader ? View.INVISIBLE : View.VISIBLE);
|
||||||
? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size);
|
mClockViewBold.setVisibility(hasHeader ? View.VISIBLE : View.INVISIBLE);
|
||||||
int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader
|
int paddingBottom = mContext.getResources().getDimensionPixelSize(hasHeader
|
||||||
? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding);
|
? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding);
|
||||||
mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
|
|
||||||
mClockViewBold.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
|
|
||||||
mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
|
mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
|
||||||
mClockView.getPaddingRight(), paddingBottom);
|
mClockView.getPaddingRight(), paddingBottom);
|
||||||
mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(),
|
mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(),
|
||||||
mClockViewBold.getPaddingRight(), paddingBottom);
|
mClockViewBold.getPaddingRight(), paddingBottom);
|
||||||
|
|
||||||
|
if (hasHeader) {
|
||||||
|
// After the transition, make the default clock GONE so that it doesn't make the
|
||||||
|
// KeyguardStatusView appear taller in KeyguardClockPositionAlgorithm and elsewhere.
|
||||||
|
mTransition.addListener(new TransitionListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onTransitionEnd(Transition transition) {
|
||||||
|
super.onTransitionEnd(transition);
|
||||||
|
// Check that header is actually showing. I saw issues where this event was
|
||||||
|
// fired after the big clock transitioned back to visible, which causes the time
|
||||||
|
// to completely disappear.
|
||||||
|
if (mShowingHeader) {
|
||||||
|
mClockView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
transition.removeListener(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||||
@@ -434,91 +466,114 @@ public class KeyguardClockSwitch extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special layout transition that scales the clock view as its bounds change, to make it look
|
* {@link Visibility} transformation that scales the view while it is disappearing/appearing and
|
||||||
* like the text is shrinking.
|
* transitions suddenly at a cutoff fraction during the animation.
|
||||||
*/
|
*/
|
||||||
private class ClockBoundsTransition extends ChangeBounds {
|
private class ClockVisibilityTransition extends android.transition.Visibility {
|
||||||
|
|
||||||
|
private static final String PROPNAME_VISIBILITY = "systemui:keyguard:visibility";
|
||||||
|
|
||||||
|
private float mCutoff;
|
||||||
|
private float mScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animation fraction when text is transitioned to/from bold.
|
* Constructs a transition that switches between visible/invisible at a cutoff and scales in
|
||||||
|
* size while appearing/disappearing.
|
||||||
*/
|
*/
|
||||||
private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f;
|
ClockVisibilityTransition() {
|
||||||
|
setCutoff(1f);
|
||||||
|
setScale(1f);
|
||||||
|
}
|
||||||
|
|
||||||
ClockBoundsTransition() {
|
/**
|
||||||
setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2);
|
* Sets the transition point between visible/invisible.
|
||||||
setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
|
*
|
||||||
|
* @param cutoff The fraction in [0, 1] when the view switches between visible/invisible.
|
||||||
|
* @return This transition object
|
||||||
|
*/
|
||||||
|
public ClockVisibilityTransition setCutoff(float cutoff) {
|
||||||
|
mCutoff = cutoff;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scale factor applied while appearing/disappearing.
|
||||||
|
*
|
||||||
|
* @param scale Scale factor applied while appearing/disappearing. When factor is less than
|
||||||
|
* one, the view will shrink while disappearing. When it is greater than one,
|
||||||
|
* the view will expand while disappearing.
|
||||||
|
* @return This transition object
|
||||||
|
*/
|
||||||
|
public ClockVisibilityTransition setScale(float scale) {
|
||||||
|
mScale = scale;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
|
public void captureStartValues(TransitionValues transitionValues) {
|
||||||
|
super.captureStartValues(transitionValues);
|
||||||
|
captureVisibility(transitionValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void captureEndValues(TransitionValues transitionValues) {
|
||||||
|
super.captureStartValues(transitionValues);
|
||||||
|
captureVisibility(transitionValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void captureVisibility(TransitionValues transitionValues) {
|
||||||
|
transitionValues.values.put(PROPNAME_VISIBILITY,
|
||||||
|
transitionValues.view.getVisibility());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
|
||||||
TransitionValues endValues) {
|
TransitionValues endValues) {
|
||||||
Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
|
final float cutoff = mCutoff;
|
||||||
if (animator == null || startValues.view != mClockView) {
|
final int startVisibility = View.INVISIBLE;
|
||||||
return animator;
|
final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
|
||||||
}
|
final float startScale = mScale;
|
||||||
|
final float endScale = 1f;
|
||||||
|
return createAnimator(view, cutoff, startVisibility, endVisibility, startScale,
|
||||||
|
endScale);
|
||||||
|
}
|
||||||
|
|
||||||
ValueAnimator boundsAnimator = null;
|
@Override
|
||||||
if (animator instanceof AnimatorSet) {
|
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
|
||||||
Animator first = ((AnimatorSet) animator).getChildAnimations().get(0);
|
TransitionValues endValues) {
|
||||||
if (first instanceof ValueAnimator) {
|
final float cutoff = 1f - mCutoff;
|
||||||
boundsAnimator = (ValueAnimator) first;
|
final int startVisibility = View.VISIBLE;
|
||||||
|
final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
|
||||||
|
final float startScale = 1f;
|
||||||
|
final float endScale = mScale;
|
||||||
|
return createAnimator(view, cutoff, startVisibility, endVisibility, startScale,
|
||||||
|
endScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Animator createAnimator(View view, float cutoff, int startVisibility,
|
||||||
|
int endVisibility, float startScale, float endScale) {
|
||||||
|
view.setPivotY(view.getHeight() - view.getPaddingBottom());
|
||||||
|
view.setVisibility(startVisibility);
|
||||||
|
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
|
||||||
|
animator.addUpdateListener(animation -> {
|
||||||
|
final float fraction = animation.getAnimatedFraction();
|
||||||
|
if (fraction > cutoff) {
|
||||||
|
view.setVisibility(endVisibility);
|
||||||
}
|
}
|
||||||
} else if (animator instanceof ValueAnimator) {
|
final float scale = MathUtils.lerp(startScale, endScale, fraction);
|
||||||
boundsAnimator = (ValueAnimator) animator;
|
view.setScaleX(scale);
|
||||||
}
|
view.setScaleY(scale);
|
||||||
|
});
|
||||||
if (boundsAnimator != null) {
|
addListener(new TransitionListenerAdapter() {
|
||||||
float bigFontSize = mContext.getResources()
|
@Override
|
||||||
.getDimensionPixelSize(R.dimen.widget_big_font_size);
|
public void onTransitionEnd(Transition transition) {
|
||||||
float smallFontSize = mContext.getResources()
|
view.setVisibility(endVisibility);
|
||||||
.getDimensionPixelSize(R.dimen.widget_small_font_size);
|
view.setScaleX(1f);
|
||||||
float startScale = mShowingHeader
|
view.setScaleY(1f);
|
||||||
? bigFontSize / smallFontSize : smallFontSize / bigFontSize;
|
transition.removeListener(this);
|
||||||
final int normalViewVisibility = mShowingHeader ? View.INVISIBLE : View.VISIBLE;
|
}
|
||||||
final int boldViewVisibility = mShowingHeader ? View.VISIBLE : View.INVISIBLE;
|
});
|
||||||
final float boldTransitionFraction = mShowingHeader ? TO_BOLD_TRANSITION_FRACTION :
|
|
||||||
1f - TO_BOLD_TRANSITION_FRACTION;
|
|
||||||
boundsAnimator.addUpdateListener(animation -> {
|
|
||||||
final float fraction = animation.getAnimatedFraction();
|
|
||||||
if (fraction > boldTransitionFraction) {
|
|
||||||
mClockView.setVisibility(normalViewVisibility);
|
|
||||||
mClockViewBold.setVisibility(boldViewVisibility);
|
|
||||||
}
|
|
||||||
float scale = MathUtils.lerp(startScale, 1f /* stop */,
|
|
||||||
animation.getAnimatedFraction());
|
|
||||||
mClockView.setPivotX(mClockView.getWidth() / 2f);
|
|
||||||
mClockViewBold.setPivotX(mClockViewBold.getWidth() / 2f);
|
|
||||||
mClockView.setPivotY(0);
|
|
||||||
mClockViewBold.setPivotY(0);
|
|
||||||
mClockView.setScaleX(scale);
|
|
||||||
mClockViewBold.setScaleX(scale);
|
|
||||||
mClockView.setScaleY(scale);
|
|
||||||
mClockViewBold.setScaleY(scale);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return animator;
|
return animator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Transition listener for layout transition that scales the clock view.
|
|
||||||
*/
|
|
||||||
private class ClockBoundsTransitionListener extends TransitionListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTransitionEnd(Transition transition) {
|
|
||||||
mClockView.setVisibility(mShowingHeader ? View.INVISIBLE : View.VISIBLE);
|
|
||||||
mClockViewBold.setVisibility(mShowingHeader ? View.VISIBLE : View.INVISIBLE);
|
|
||||||
mClockView.setScaleX(1f);
|
|
||||||
mClockViewBold.setScaleX(1f);
|
|
||||||
mClockView.setScaleY(1f);
|
|
||||||
mClockViewBold.setScaleY(1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTransitionCancel(Transition transition) {
|
|
||||||
onTransitionEnd(transition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user