1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.wm.shell.compatui; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.AnimatorSet; 22 import android.animation.ObjectAnimator; 23 import android.annotation.IdRes; 24 import android.annotation.NonNull; 25 import android.content.Context; 26 import android.util.AttributeSet; 27 import android.view.View; 28 import android.view.animation.Interpolator; 29 import android.view.animation.LinearInterpolator; 30 import android.view.animation.PathInterpolator; 31 import android.widget.ImageButton; 32 import android.widget.LinearLayout; 33 import android.widget.TextView; 34 35 import com.android.wm.shell.R; 36 37 /** 38 * Layout for the user aspect ratio button which opens the app list page in settings 39 * and allows users to change apps aspect ratio. 40 */ 41 public class UserAspectRatioSettingsLayout extends LinearLayout { 42 43 private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); 44 45 private static final Interpolator PATH_INTERPOLATOR = 46 new PathInterpolator(0.2f, 0f, 0f, 1f); 47 48 private static final float ALPHA_FULL_TRANSPARENT = 0f; 49 50 private static final float ALPHA_FULL_OPAQUE = 1f; 51 52 private static final float SCALE_START = 0.8f; 53 54 private static final float SCALE_END = 1f; 55 56 private static final long FADE_ANIMATION_DURATION_MS = 167; 57 58 private static final long SCALE_ANIMATION_DURATION_MS = 300; 59 60 private static final String ALPHA_PROPERTY_NAME = "alpha"; 61 62 private static final String SCALE_X_PROPERTY_NAME = "scaleX"; 63 64 private static final String SCALE_Y_PROPERTY_NAME = "scaleY"; 65 66 private UserAspectRatioSettingsWindowManager mWindowManager; 67 UserAspectRatioSettingsLayout(Context context)68 public UserAspectRatioSettingsLayout(Context context) { 69 this(context, null); 70 } 71 UserAspectRatioSettingsLayout(Context context, AttributeSet attrs)72 public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs) { 73 this(context, attrs, 0); 74 } 75 UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr)76 public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr) { 77 this(context, attrs, defStyleAttr, 0); 78 } 79 UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)80 public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr, 81 int defStyleRes) { 82 super(context, attrs, defStyleAttr, defStyleRes); 83 } 84 inject(@onNull UserAspectRatioSettingsWindowManager windowManager)85 void inject(@NonNull UserAspectRatioSettingsWindowManager windowManager) { 86 mWindowManager = windowManager; 87 } 88 setUserAspectRatioSettingsHintVisibility(boolean show)89 void setUserAspectRatioSettingsHintVisibility(boolean show) { 90 setViewVisibility(R.id.user_aspect_ratio_settings_hint, show); 91 } 92 setUserAspectRatioButtonVisibility(boolean show)93 void setUserAspectRatioButtonVisibility(boolean show) { 94 setViewVisibility(R.id.user_aspect_ratio_settings_button, show); 95 // Hint should never be visible without button. 96 if (!show) { 97 setUserAspectRatioSettingsHintVisibility(/* show= */ false); 98 } 99 } 100 setViewVisibility(@dRes int resId, boolean show)101 private void setViewVisibility(@IdRes int resId, boolean show) { 102 final View view = findViewById(resId); 103 int visibility = show ? View.VISIBLE : View.GONE; 104 if (view.getVisibility() == visibility) { 105 return; 106 } 107 if (show) { 108 showItem(view); 109 } else { 110 hideItem(view); 111 } 112 } 113 114 @Override onLayout(boolean changed, int left, int top, int right, int bottom)115 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 116 super.onLayout(changed, left, top, right, bottom); 117 // Need to relayout after changes like hiding / showing a hint since they affect size. 118 // Doing this directly in setUserAspectRatioButtonVisibility can result in flaky animation. 119 mWindowManager.relayout(); 120 } 121 122 @Override onFinishInflate()123 protected void onFinishInflate() { 124 super.onFinishInflate(); 125 126 final ImageButton userAspectRatioButton = 127 findViewById(R.id.user_aspect_ratio_settings_button); 128 userAspectRatioButton.setOnClickListener( 129 view -> mWindowManager.onUserAspectRatioSettingsButtonClicked()); 130 userAspectRatioButton.setOnLongClickListener(view -> { 131 mWindowManager.onUserAspectRatioSettingsButtonLongClicked(); 132 return true; 133 }); 134 135 final LinearLayout sizeCompatHint = findViewById(R.id.user_aspect_ratio_settings_hint); 136 ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text)) 137 .setText(R.string.user_aspect_ratio_settings_button_hint); 138 sizeCompatHint.setOnClickListener( 139 view -> setUserAspectRatioSettingsHintVisibility(/* show= */ false)); 140 } 141 showItem(@onNull View view)142 private void showItem(@NonNull View view) { 143 final AnimatorSet animatorSet = new AnimatorSet(); 144 final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME, 145 ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE); 146 fadeIn.setDuration(FADE_ANIMATION_DURATION_MS); 147 fadeIn.setInterpolator(LINEAR_INTERPOLATOR); 148 final ObjectAnimator scaleY = 149 ObjectAnimator.ofFloat(view, SCALE_Y_PROPERTY_NAME, SCALE_START, SCALE_END); 150 final ObjectAnimator scaleX = 151 ObjectAnimator.ofFloat(view, SCALE_X_PROPERTY_NAME, SCALE_START, SCALE_END); 152 scaleX.setDuration(SCALE_ANIMATION_DURATION_MS); 153 scaleX.setInterpolator(PATH_INTERPOLATOR); 154 scaleY.setDuration(SCALE_ANIMATION_DURATION_MS); 155 scaleY.setInterpolator(PATH_INTERPOLATOR); 156 animatorSet.addListener(new AnimatorListenerAdapter() { 157 @Override 158 public void onAnimationStart(Animator animation) { 159 view.setVisibility(View.VISIBLE); 160 } 161 }); 162 animatorSet.playTogether(fadeIn, scaleY, scaleX); 163 animatorSet.start(); 164 } 165 hideItem(@onNull View view)166 private void hideItem(@NonNull View view) { 167 final ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME, 168 ALPHA_FULL_OPAQUE, ALPHA_FULL_TRANSPARENT); 169 fadeOut.setDuration(FADE_ANIMATION_DURATION_MS); 170 fadeOut.setInterpolator(LINEAR_INTERPOLATOR); 171 fadeOut.addListener(new AnimatorListenerAdapter() { 172 @Override 173 public void onAnimationEnd(Animator animation) { 174 view.setVisibility(View.GONE); 175 } 176 }); 177 fadeOut.start(); 178 } 179 } 180