Collapse private space container and animate header.

- Just opposite of how it will expand.
- RecyclerView.SmoothScroller is needed to scroll the container.

- Need to separate the lock button because this way I can use animateLayout changes and it itself was its own drawable. Separated into icon and textView in a viewGroup.
- Give the background the 10padding on the left and right so that when in animation, the icon can adjust the padding/margins there.
- Using propertySetter to set animation
- Animates the alpha of the settings alpha

- updated test to account for the nested child views the test needs to inspect

bug: 299294792
test: manual:
Expand + Collapse Video: https://drive.google.com/file/d/1Og66eqmXv3THn0wO4_x6Tfp2AbwFWUwZ/view?usp=sharing
Flag: ACONFIG com.android.launcher3.Flags.private_space_animation TEAMFOOD

Change-Id: I96f1d172a481522d23b4cee996ddec65961fce78
This commit is contained in:
Brandon Dayauon
2024-02-16 21:23:51 -08:00
parent 772e0127c3
commit 08b06523a5
13 changed files with 346 additions and 77 deletions
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2024 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/materialColorOnPrimaryFixed"
android:pathData="M263.72,864Q234,864 213,842.85Q192,821.7 192,792L192,408Q192,378.3 213.15,357.15Q234.3,336 264,336L288,336L288,240Q288,160.32 344.23,104.16Q400.45,48 480.23,48Q560,48 616,104.16Q672,160.32 672,240L672,336L696,336Q725.7,336 746.85,357.15Q768,378.3 768,408L768,792Q768,821.7 746.84,842.85Q725.68,864 695.96,864L263.72,864ZM264,792L696,792Q696,792 696,792Q696,792 696,792L696,408Q696,408 696,408Q696,408 696,408L264,408Q264,408 264,408Q264,408 264,408L264,792Q264,792 264,792Q264,792 264,792ZM480.21,672Q510,672 531,650.79Q552,629.58 552,599.79Q552,570 530.79,549Q509.58,528 479.79,528Q450,528 429,549.21Q408,570.42 408,600.21Q408,630 429.21,651Q450.42,672 480.21,672ZM360,336L600,336L600,240Q600,190 565,155Q530,120 480,120Q430,120 395,155Q360,190 360,240L360,336ZM264,792Q264,792 264,792Q264,792 264,792L264,408Q264,408 264,408Q264,408 264,408L264,408Q264,408 264,408Q264,408 264,408L264,792Q264,792 264,792Q264,792 264,792L264,792Z"/>
</vector>
@@ -15,13 +15,10 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/ps_button_height"
android:width="@dimen/ps_button_width"
android:height="@dimen/ps_button_height"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M20,0L20,0A20,20 0,0 1,40 20L40,20A20,20 0,0 1,20 40L20,40A20,20 0,0 1,0 20L0,20A20,20 0,0 1,20 0z"
android:fillColor="?attr/materialColorSurfaceBright"/>
<group>
<clip-path
android:pathData="M10,10h20v20h-20z"/>
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2024 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.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:inset="4dp">
<shape android:shape="rectangle">
<corners android:radius="@dimen/ps_lock_corner_radius" />
<solid android:color="?attr/materialColorPrimaryFixedDim" />
<padding
android:left="@dimen/ps_lock_button_background_padding"
android:right="@dimen/ps_lock_button_background_padding" />
</shape>
</inset>
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2024 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.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:inset="4dp">
<shape android:shape="rectangle">
<corners android:radius="@dimen/ps_lock_corner_radius" />
<solid android:color="?attr/materialColorSurfaceBright" />
</shape>
</inset>
+47 -19
View File
@@ -18,6 +18,7 @@
<RelativeLayout
android:id="@+id/ps_header_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/ps_header_height"
android:background="@drawable/bg_ps_header"
@@ -25,27 +26,53 @@
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageButton
android:id="@+id/ps_lock_unlock_button"
<LinearLayout
android:id="@+id/settingsAndLockGroup"
android:layout_width="wrap_content"
android:layout_height="@dimen/ps_header_image_height"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="@android:color/transparent"
android:layout_marginEnd="@dimen/ps_header_layout_margin"
android:contentDescription="@string/ps_container_lock_unlock_button" />
<ImageButton
android:id="@+id/ps_settings_button"
android:layout_width="wrap_content"
android:layout_height="@dimen/ps_header_image_height"
android:layout_toStartOf="@+id/ps_lock_unlock_button"
android:layout_centerVertical="true"
android:background="@android:color/transparent"
android:layout_marginEnd="@dimen/ps_header_settings_icon_margin_end"
android:src="@drawable/bg_ps_settings_button"
android:contentDescription="@string/ps_container_settings" />
android:animateLayoutChanges="true">
<ImageButton
android:id="@+id/ps_settings_button"
android:layout_width="@dimen/ps_header_image_height"
android:layout_height="@dimen/ps_header_image_height"
android:background="@drawable/ps_settings_background"
android:layout_marginEnd="@dimen/ps_header_settings_icon_margin_end"
android:src="@drawable/ic_ps_settings"
android:contentDescription="@string/ps_container_settings" />
<LinearLayout
android:id="@+id/ps_lock_unlock_button"
android:layout_width="wrap_content"
android:layout_height="@dimen/ps_header_image_height"
android:background="@drawable/ps_lock_background"
android:gravity="center_vertical"
android:layout_marginEnd="@dimen/ps_header_layout_margin"
android:contentDescription="@string/ps_container_lock_unlock_button">
<ImageView
android:id="@+id/lock_icon"
android:layout_width="@dimen/ps_lock_icon_size"
android:layout_height="@dimen/ps_lock_icon_size"
android:layout_marginTop="@dimen/ps_lock_icon_margin_top"
android:layout_marginBottom="@dimen/ps_lock_icon_margin_bottom"
android:importantForAccessibility="no"
android:src="@drawable/ic_lock"
app:tint="@color/material_color_primary_fixed_dim"
android:scaleType="center"/>
<TextView
android:id="@+id/lock_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/ps_lock_icon_text_margin_start_expanded"
android:layout_marginEnd="@dimen/ps_lock_icon_text_margin_end_expanded"
android:textColor="@color/material_color_on_primary_fixed"
android:textSize="14sp"
android:text="@string/ps_container_lock_title"
android:visibility="gone"
style="@style/TextHeadline"/>
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/ps_transition_image"
android:layout_width="wrap_content"
@@ -63,6 +90,7 @@
android:layout_height="@dimen/ps_header_text_height"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/settingsAndLockGroup"
android:gravity="center_vertical"
android:layout_marginStart="@dimen/ps_header_layout_margin"
android:text="@string/ps_container_title"
+10 -3
View File
@@ -476,15 +476,22 @@
<dimen name="ps_container_corner_radius">24dp</dimen>
<dimen name="ps_header_height">64dp</dimen>
<dimen name="ps_header_relative_layout_height">48dp</dimen>
<dimen name="ps_header_image_height">36dp</dimen>
<dimen name="ps_header_image_height">48dp</dimen>
<dimen name="ps_header_text_height">24dp</dimen>
<dimen name="ps_header_layout_margin">16dp</dimen>
<dimen name="ps_header_settings_icon_margin_end">8dp</dimen>
<dimen name="ps_header_text_size">16sp</dimen>
<dimen name="ps_button_height">36dp</dimen>
<dimen name="ps_button_width">36dp</dimen>
<dimen name="ps_button_height">40dp</dimen>
<dimen name="ps_button_width">40dp</dimen>
<dimen name="ps_lock_button_width">89dp</dimen>
<dimen name="ps_app_divider_padding">16dp</dimen>
<dimen name="ps_lock_corner_radius">20dp</dimen>
<dimen name="ps_lock_icon_size">20dp</dimen>
<dimen name="ps_lock_icon_margin_top">10dp</dimen>
<dimen name="ps_lock_icon_margin_bottom">10dp</dimen>
<dimen name="ps_lock_icon_text_margin_start_expanded">8dp</dimen>
<dimen name="ps_lock_icon_text_margin_end_expanded">6dp</dimen>
<dimen name="ps_lock_button_background_padding">10dp</dimen>
<!-- WindowManagerProxy -->
<dimen name="max_width_and_height_of_small_display_cutout">136px</dimen>
+1
View File
@@ -465,6 +465,7 @@
<string name="ps_container_settings">Private Space Settings</string>
<!-- Description for Private Space Lock/Unlock button -->
<string name="ps_container_lock_unlock_button">Lock/Unlock Private Space</string>
<string name="ps_container_lock_title">Lock</string>
<!-- Description for Private Space Transition button -->
<string name="ps_container_transition">Private Space Transitioning</string>
<!-- Title for Private Space install app icon -->
+1
View File
@@ -441,5 +441,6 @@
<item name="android:textSize">16sp</item>
<item name="android:textColor">@color/material_color_on_surface</item>
<item name="android:fontFamily">google-sans-text-medium</item>
<item name="android:ellipsize">end</item>
</style>
</resources>
@@ -294,6 +294,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
mIconLoadRequest.cancel();
mIconLoadRequest = null;
}
// Reset any shifty arrangements in case animation is disrupted.
setPivotY(0);
setAlpha(1);
setScaleY(1);
setTranslationY(0);
setVisibility(VISIBLE);
}
private void cancelDotScaleAnim() {
@@ -201,7 +201,6 @@ public abstract class FastScrollRecyclerView extends RecyclerView {
if (mScrollbar != null) {
mScrollbar.reattachThumbToScroll();
}
// Emphasized interpolators with 500ms duration
smoothScrollBy(0, getAvailableScrollHeight(), Interpolators.EMPHASIZED, duration);
}
}
@@ -258,6 +258,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.itemView.setVisibility(View.VISIBLE);
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON: {
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
@@ -16,7 +16,12 @@
package com.android.launcher3.allapps;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
import static com.android.launcher3.allapps.PrivateProfileManager.STATE_DISABLED;
import static com.android.launcher3.allapps.PrivateProfileManager.STATE_ENABLED;
import static com.android.launcher3.allapps.PrivateProfileManager.STATE_TRANSITION;
@@ -24,21 +29,37 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
import android.view.View;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ValueAnimator;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import com.android.app.animation.Interpolators;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
import com.android.launcher3.anim.AnimatedPropertySetter;
import com.android.launcher3.anim.PropertySetter;
import java.util.List;
/**
* Controller which returns views to be added to Private Space Header based upon
* {@link UserProfileState}
*/
public class PrivateSpaceHeaderViewController {
private static final int ANIMATION_DURATION = 2000;
private static final int EXPAND_SCROLL_DURATION = 2000;
private static final int EXPAND_COLLAPSE_DURATION = 800;
private static final int SETTINGS_OPACITY_DURATION = 160;
private final ActivityAllAppsContainerView mAllApps;
private final PrivateProfileManager mPrivateProfileManager;
@@ -50,10 +71,17 @@ public class PrivateSpaceHeaderViewController {
/** Add Private Space Header view elements based upon {@link UserProfileState} */
public void addPrivateSpaceHeaderViewElements(RelativeLayout parent) {
// Set the transition duration for the settings and lock button to animate.
ViewGroup settingsAndLockGroup = parent.findViewById(R.id.settingsAndLockGroup);
LayoutTransition settingsAndLockTransition = settingsAndLockGroup.getLayoutTransition();
settingsAndLockTransition.enableTransitionType(LayoutTransition.CHANGING);
settingsAndLockTransition.setDuration(EXPAND_COLLAPSE_DURATION);
//Add quietMode image and action for lock/unlock button
ImageButton quietModeButton = parent.findViewById(R.id.ps_lock_unlock_button);
assert quietModeButton != null;
addQuietModeButton(quietModeButton);
ViewGroup lockButton =
parent.findViewById(R.id.ps_lock_unlock_button);
assert lockButton != null;
addLockButton(parent, lockButton);
//Trigger lock/unlock action from header.
addHeaderOnClickListener(parent);
@@ -69,74 +97,181 @@ public class PrivateSpaceHeaderViewController {
addTransitionImage(transitionView);
}
private void addQuietModeButton(ImageButton quietModeButton) {
/**
* Adds the quietModeButton and attach onClickListener for the header to animate different
* states when clicked.
*/
private void addLockButton(ViewGroup psHeader, ViewGroup lockButton) {
TextView lockText = lockButton.findViewById(R.id.lock_text);
switch (mPrivateProfileManager.getCurrentState()) {
case STATE_ENABLED -> {
quietModeButton.setVisibility(View.VISIBLE);
quietModeButton.setImageResource(R.drawable.bg_ps_lock_button);
quietModeButton.setOnClickListener(view -> lockAction());
lockText.setVisibility(VISIBLE);
lockButton.setVisibility(VISIBLE);
lockButton.setOnClickListener(view -> lockAction(psHeader));
}
case STATE_DISABLED -> {
quietModeButton.setVisibility(View.VISIBLE);
quietModeButton.setImageResource(R.drawable.bg_ps_unlock_button);
quietModeButton.setOnClickListener(view -> unLockAction());
lockText.setVisibility(GONE);
lockButton.setVisibility(VISIBLE);
lockButton.setOnClickListener(view -> unlockAction(psHeader));
}
default -> quietModeButton.setVisibility(View.GONE);
default -> lockButton.setVisibility(GONE);
}
}
private void addHeaderOnClickListener(RelativeLayout header) {
if (mPrivateProfileManager.getCurrentState() == STATE_DISABLED) {
header.setOnClickListener(view -> unLockAction());
header.setOnClickListener(view -> unlockAction(header));
} else {
header.setOnClickListener(null);
}
}
private void unLockAction() {
private void unlockAction(ViewGroup psHeader) {
mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
mPrivateProfileManager.unlockPrivateProfile((this::onPrivateProfileUnlocked));
mPrivateProfileManager.unlockPrivateProfile((() -> onPrivateProfileUnlocked(psHeader)));
}
private void lockAction() {
private void lockAction(ViewGroup psHeader) {
mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_LOCK_TAP);
mPrivateProfileManager.lockPrivateProfile();
if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()) {
updatePrivateStateAnimator(false, psHeader);
} else {
mPrivateProfileManager.lockPrivateProfile();
}
}
private void addPrivateSpaceSettingsButton(ImageButton settingsButton) {
if (mPrivateProfileManager.getCurrentState() == STATE_ENABLED
&& mPrivateProfileManager.isPrivateSpaceSettingsAvailable()) {
settingsButton.setVisibility(View.VISIBLE);
settingsButton.setVisibility(VISIBLE);
settingsButton.setAlpha(1f);
settingsButton.setOnClickListener(
view -> {
mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
mPrivateProfileManager.openPrivateSpaceSettings();
});
} else {
settingsButton.setVisibility(View.GONE);
settingsButton.setVisibility(GONE);
}
}
private void addTransitionImage(ImageView transitionImage) {
if (mPrivateProfileManager.getCurrentState() == STATE_TRANSITION) {
transitionImage.setVisibility(View.VISIBLE);
transitionImage.setVisibility(VISIBLE);
} else {
transitionImage.setVisibility(View.GONE);
transitionImage.setVisibility(GONE);
}
}
private void onPrivateProfileUnlocked() {
private void onPrivateProfileUnlocked(ViewGroup header) {
// If we are on main adapter view, we apply the PS Container expansion animation and
// then scroll down to load the entire container, making animation visible.
ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder =
(ActivityAllAppsContainerView<?>.AdapterHolder) mAllApps.mAH.get(MAIN);
if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
&& mAllApps.getActiveRecyclerView() == mainAdapterHolder.mRecyclerView) {
mAllApps.getActiveRecyclerView().scrollToBottomWithMotion(ANIMATION_DURATION);
// Animate the text and settings icon.
updatePrivateStateAnimator(true, header);
mAllApps.getActiveRecyclerView().scrollToBottomWithMotion(EXPAND_SCROLL_DURATION);
}
}
/** Finds the private space header to scroll to and set the private space icons to GONE. */
private void collapse() {
AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
for (int i = allAppsRecyclerView.getChildCount() - 1; i > 0; i--) {
int adapterPosition = allAppsRecyclerView.getChildAdapterPosition(
allAppsRecyclerView.getChildAt(i));
List<BaseAllAppsAdapter.AdapterItem> allAppsAdapters = allAppsRecyclerView.getApps()
.getAdapterItems();
if (adapterPosition < 0 || adapterPosition >= allAppsAdapters.size()) {
continue;
}
// Scroll to the private space header.
if (allAppsAdapters.get(adapterPosition).viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
// Note: SmoothScroller is meant to be used once.
RecyclerView.SmoothScroller smoothScroller =
new LinearSmoothScroller(mAllApps.getContext()) {
@Override protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_END;
}
};
smoothScroller.setTargetPosition(adapterPosition);
RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
if (layoutManager != null) {
layoutManager.startSmoothScroll(smoothScroller);
}
break;
}
// Make the private space apps gone to "collapse".
if (allAppsAdapters.get(adapterPosition).decorationInfo != null) {
allAppsRecyclerView.getChildAt(i).setVisibility(GONE);
}
}
}
PrivateProfileManager getPrivateProfileManager() {
return mPrivateProfileManager;
}
/**
* Scrolls up to the private space header and animates the collapsing of the text.
*/
private ValueAnimator animateCollapseAnimation(ViewGroup lockButton) {
float from = 1;
float to = 0;
ValueAnimator collapseAnim = ValueAnimator.ofFloat(from, to);
collapseAnim.setDuration(EXPAND_COLLAPSE_DURATION);
collapseAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// scroll up
collapse();
// Animate the collapsing of the text.
lockButton.findViewById(R.id.lock_text).setVisibility(GONE);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mPrivateProfileManager.lockPrivateProfile();
}
});
return collapseAnim;
}
/**
* Using PropertySetter{@link PropertySetter}, we can update the view's attributes within an
* animation. At the moment, collapsing, setting alpha changes, and animating the text is done
* here.
*/
private void updatePrivateStateAnimator(boolean expand, ViewGroup psHeader) {
PropertySetter setter = new AnimatedPropertySetter();
ViewGroup lockButton = psHeader.findViewById(R.id.ps_lock_unlock_button);
ImageButton settingsButton = psHeader.findViewById(R.id.ps_settings_button);
updateSettingsGearAlpha(settingsButton, expand, setter);
AnimatorSet animatorSet = setter.buildAnim();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// Animate the collapsing of the text at the same time while updating lock button.
lockButton.findViewById(R.id.lock_text).setVisibility(expand ? VISIBLE : GONE);
}
});
// Play the collapsing together of the stateAnimator to avoid being unable to scroll to the
// header. Otherwise the smooth scrolling will scroll higher when played with the state
// animator.
if (!expand) {
animatorSet.playTogether(animateCollapseAnimation(lockButton));
}
animatorSet.setDuration(EXPAND_COLLAPSE_DURATION);
animatorSet.start();
}
/** Change the settings gear alpha when expanded or collapsed. */
private void updateSettingsGearAlpha(ImageButton settingsButton, boolean expand,
PropertySetter setter) {
float toAlpha = expand ? 1 : 0;
setter.setFloat(settingsButton, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
.setDuration(SETTINGS_OPACITY_DURATION).setStartDelay(0);
}
}
@@ -24,6 +24,7 @@ import static com.android.launcher3.allapps.UserProfileManager.STATE_TRANSITION;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -33,8 +34,11 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -59,7 +63,6 @@ public class PrivateSpaceHeaderViewControllerTest {
private static final int PS_TRANSITION_IMAGE_COUNT = 1;
private Context mContext;
private LayoutInflater mLayoutInflater;
private PrivateSpaceHeaderViewController mPsHeaderViewController;
private RelativeLayout mPsHeaderLayout;
@Mock
@@ -71,16 +74,15 @@ public class PrivateSpaceHeaderViewControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = new ActivityContextWrapper(getApplicationContext());
mLayoutInflater = LayoutInflater.from(getApplicationContext());
mPsHeaderViewController = new PrivateSpaceHeaderViewController(mAllApps,
mPrivateProfileManager);
mPsHeaderLayout = (RelativeLayout) mLayoutInflater.inflate(R.layout.private_space_header,
null);
mPsHeaderLayout = (RelativeLayout) LayoutInflater.from(mContext).inflate(
R.layout.private_space_header, null);
}
@Test
public void privateProfileDisabled_psHeaderContainsLockedView() throws Exception {
Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.bg_ps_unlock_button));
Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
@@ -93,11 +95,15 @@ public class PrivateSpaceHeaderViewControllerTest {
if (view.getId() == R.id.ps_container_header) {
totalContainerHeaderView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
} else if (view.getId() == R.id.ps_lock_unlock_button
&& view instanceof ImageView imageView) {
} else if (view.getId() == R.id.settingsAndLockGroup) {
ImageView lockIcon = view.findViewById(R.id.lock_icon);
assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(unlockButton));
assertEquals(View.VISIBLE, lockIcon.getVisibility());
// Verify textView shouldn't be showing when disabled.
TextView lockText = view.findViewById(R.id.lock_text);
assertEquals(View.GONE, lockText.getVisibility());
totalLockUnlockButtonView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
getBitmap(imageView.getDrawable()).sameAs(unlockButton);
} else {
assertEquals(View.GONE, view.getVisibility());
}
@@ -108,8 +114,8 @@ public class PrivateSpaceHeaderViewControllerTest {
@Test
public void privateProfileEnabled_psHeaderContainsUnlockedView() throws Exception {
Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_lock_button));
Bitmap settingsImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_settings_button));
Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
Bitmap settingsImage = getBitmap(mContext.getDrawable(R.drawable.ic_ps_settings));
when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true);
@@ -124,16 +130,20 @@ public class PrivateSpaceHeaderViewControllerTest {
if (view.getId() == R.id.ps_container_header) {
totalContainerHeaderView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
} else if (view.getId() == R.id.ps_lock_unlock_button
&& view instanceof ImageView imageView) {
totalLockUnlockButtonView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
getBitmap(imageView.getDrawable()).sameAs(lockImage);
} else if (view.getId() == R.id.ps_settings_button
&& view instanceof ImageView imageView) {
} else if (view.getId() == R.id.settingsAndLockGroup) {
// Look for settings button.
ImageButton settingsButton = view.findViewById(R.id.ps_settings_button);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
totalSettingsImageView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
getBitmap(imageView.getDrawable()).sameAs(settingsImage);
assertTrue(getBitmap(settingsButton.getDrawable()).sameAs(settingsImage));
// Look for lock_icon and lock_text.
ImageView lockIcon = view.findViewById(R.id.lock_icon);
assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(lockImage));
assertEquals(View.VISIBLE, lockIcon.getVisibility());
TextView lockText = view.findViewById(R.id.lock_text);
assertEquals(View.VISIBLE, lockText.getVisibility());
totalLockUnlockButtonView += 1;
} else {
assertEquals(View.GONE, view.getVisibility());
}
@@ -146,7 +156,7 @@ public class PrivateSpaceHeaderViewControllerTest {
@Test
public void privateProfileEnabledAndNoSettingsIntent_psHeaderContainsUnlockedView()
throws Exception {
Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_lock_button));
Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false);
@@ -161,11 +171,18 @@ public class PrivateSpaceHeaderViewControllerTest {
if (view.getId() == R.id.ps_container_header) {
totalContainerHeaderView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
} else if (view.getId() == R.id.ps_lock_unlock_button
&& view instanceof ImageView imageView) {
} else if (view.getId() == R.id.settingsAndLockGroup) {
// Ensure there is no settings button.
ImageButton settingsImage = view.findViewById(R.id.ps_settings_button);
assertEquals(View.GONE, settingsImage.getVisibility());
// Check lock icon and lock text is there.
ImageView lockIcon = view.findViewById(R.id.lock_icon);
assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(lockImage));
assertEquals(View.VISIBLE, lockIcon.getVisibility());
TextView lockText = view.findViewById(R.id.lock_text);
assertEquals(View.VISIBLE, lockText.getVisibility());
totalLockUnlockButtonView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
getBitmap(imageView.getDrawable()).sameAs(lockImage);
} else {
assertEquals(View.GONE, view.getVisibility());
}
@@ -194,7 +211,10 @@ public class PrivateSpaceHeaderViewControllerTest {
&& view instanceof ImageView imageView) {
totalLockUnlockButtonView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
getBitmap(imageView.getDrawable()).sameAs(transitionImage);
assertTrue(getBitmap(imageView.getDrawable()).sameAs(transitionImage));
} else if (view.getId() == R.id.settingsAndLockGroup) {
LinearLayout lockUnlockButton = view.findViewById(R.id.ps_lock_unlock_button);
assertEquals(View.GONE, lockUnlockButton.getVisibility());
} else {
assertEquals(View.GONE, view.getVisibility());
}