1 /* 2 * Copyright (C) 2020 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 package com.android.launcher3.allapps; 17 18 import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth; 19 20 import android.animation.LayoutTransition; 21 import android.content.Context; 22 import android.graphics.Rect; 23 import android.util.AttributeSet; 24 import android.view.View; 25 import android.view.WindowInsets; 26 import android.widget.ImageView; 27 import android.widget.LinearLayout; 28 import android.widget.TextView; 29 30 import androidx.annotation.NonNull; 31 import androidx.core.graphics.Insets; 32 import androidx.core.view.WindowInsetsCompat; 33 34 import com.android.launcher3.DeviceProfile; 35 import com.android.launcher3.Insettable; 36 import com.android.launcher3.R; 37 import com.android.launcher3.Utilities; 38 import com.android.launcher3.anim.KeyboardInsetAnimationCallback; 39 import com.android.launcher3.config.FeatureFlags; 40 import com.android.launcher3.logging.StatsLogManager; 41 import com.android.launcher3.model.StringCache; 42 import com.android.launcher3.views.ActivityContext; 43 /** 44 * Work profile toggle switch shown at the bottom of AllApps work tab 45 */ 46 public class WorkModeSwitch extends LinearLayout implements Insettable, 47 KeyboardInsetAnimationCallback.KeyboardInsetListener { 48 49 private static final int FLAG_FADE_ONGOING = 1 << 1; 50 private static final int FLAG_TRANSLATION_ONGOING = 1 << 2; 51 private static final int FLAG_PROFILE_TOGGLE_ONGOING = 1 << 3; 52 private static final int SCROLL_THRESHOLD_DP = 10; 53 54 private final Rect mInsets = new Rect(); 55 private final Rect mImeInsets = new Rect(); 56 private int mFlags; 57 private final ActivityContext mActivityContext; 58 59 // Threshold when user scrolls up/down to determine when should button extend/collapse 60 private final int mScrollThreshold; 61 private ImageView mIcon; 62 private TextView mTextView; 63 private final StatsLogManager mStatsLogManager; 64 65 WorkModeSwitch(@onNull Context context)66 public WorkModeSwitch(@NonNull Context context) { 67 this(context, null, 0); 68 } 69 WorkModeSwitch(@onNull Context context, @NonNull AttributeSet attrs)70 public WorkModeSwitch(@NonNull Context context, @NonNull AttributeSet attrs) { 71 this(context, attrs, 0); 72 } 73 WorkModeSwitch(@onNull Context context, @NonNull AttributeSet attrs, int defStyleAttr)74 public WorkModeSwitch(@NonNull Context context, @NonNull AttributeSet attrs, int defStyleAttr) { 75 super(context, attrs, defStyleAttr); 76 mScrollThreshold = Utilities.dpToPx(SCROLL_THRESHOLD_DP); 77 mActivityContext = ActivityContext.lookupContext(getContext()); 78 mStatsLogManager = mActivityContext.getStatsLogManager(); 79 } 80 81 @Override onFinishInflate()82 protected void onFinishInflate() { 83 super.onFinishInflate(); 84 85 mIcon = findViewById(R.id.work_icon); 86 mTextView = findViewById(R.id.pause_text); 87 setSelected(true); 88 if (Utilities.ATLEAST_R) { 89 KeyboardInsetAnimationCallback keyboardInsetAnimationCallback = 90 new KeyboardInsetAnimationCallback(this); 91 setWindowInsetsAnimationCallback(keyboardInsetAnimationCallback); 92 } 93 94 setInsets(mActivityContext.getDeviceProfile().getInsets()); 95 StringCache cache = mActivityContext.getStringCache(); 96 if (cache != null) { 97 mTextView.setText(cache.workProfilePauseButton); 98 } 99 100 mIcon.setColorFilter(mTextView.getCurrentTextColor()); 101 getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); 102 } 103 104 @Override setInsets(Rect insets)105 public void setInsets(Rect insets) { 106 mInsets.set(insets); 107 updateTranslationY(); 108 MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); 109 if (lp != null) { 110 int bottomMargin = getResources().getDimensionPixelSize(R.dimen.work_fab_margin_bottom); 111 DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile(); 112 if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) { 113 bottomMargin += dp.hotseatQsbHeight; 114 } 115 116 if (!dp.isGestureMode && dp.isTaskbarPresent) { 117 bottomMargin += dp.taskbarHeight; 118 } 119 120 lp.bottomMargin = bottomMargin; 121 } 122 } 123 124 @Override onLayout(boolean changed, int left, int top, int right, int bottom)125 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 126 super.onLayout(changed, left, top, right, bottom); 127 View parent = (View) getParent(); 128 int allAppsLeftRightPadding = mActivityContext.getDeviceProfile().allAppsLeftRightPadding; 129 int size = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight() 130 - 2 * allAppsLeftRightPadding; 131 int tabWidth = getTabWidth(getContext(), size); 132 int shift = (size - tabWidth) / 2 + allAppsLeftRightPadding; 133 setTranslationX(Utilities.isRtl(getResources()) ? shift : -shift); 134 } 135 136 @Override isEnabled()137 public boolean isEnabled() { 138 return super.isEnabled() && getVisibility() == VISIBLE && mFlags == 0; 139 } 140 animateVisibility(boolean visible)141 public void animateVisibility(boolean visible) { 142 clearAnimation(); 143 if (visible) { 144 setFlag(FLAG_FADE_ONGOING); 145 setVisibility(VISIBLE); 146 extend(); 147 animate().alpha(1).withEndAction(() -> removeFlag(FLAG_FADE_ONGOING)).start(); 148 } else if (getVisibility() != GONE) { 149 setFlag(FLAG_FADE_ONGOING); 150 animate().alpha(0).withEndAction(() -> { 151 removeFlag(FLAG_FADE_ONGOING); 152 setVisibility(GONE); 153 }).start(); 154 } 155 } 156 157 @Override onApplyWindowInsets(WindowInsets insets)158 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 159 WindowInsetsCompat windowInsetsCompat = 160 WindowInsetsCompat.toWindowInsetsCompat(insets, this); 161 if (windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime())) { 162 setInsets(mImeInsets, windowInsetsCompat.getInsets(WindowInsetsCompat.Type.ime())); 163 } else { 164 mImeInsets.setEmpty(); 165 } 166 updateTranslationY(); 167 return super.onApplyWindowInsets(insets); 168 } 169 updateTranslationY()170 private void updateTranslationY() { 171 setTranslationY(-mImeInsets.bottom); 172 } 173 174 @Override setTranslationY(float translationY)175 public void setTranslationY(float translationY) { 176 // Always translate at least enough for nav bar insets. 177 super.setTranslationY(Math.min(translationY, -mInsets.bottom)); 178 } 179 setInsets(Rect rect, Insets insets)180 private void setInsets(Rect rect, Insets insets) { 181 rect.set(insets.left, insets.top, insets.right, insets.bottom); 182 } 183 184 @Override onTranslationStart()185 public void onTranslationStart() { 186 setFlag(FLAG_TRANSLATION_ONGOING); 187 } 188 189 @Override onTranslationEnd()190 public void onTranslationEnd() { 191 removeFlag(FLAG_TRANSLATION_ONGOING); 192 } 193 setFlag(int flag)194 private void setFlag(int flag) { 195 mFlags |= flag; 196 } 197 removeFlag(int flag)198 private void removeFlag(int flag) { 199 mFlags &= ~flag; 200 } 201 extend()202 public void extend() { 203 mTextView.setVisibility(VISIBLE); 204 } 205 shrink()206 public void shrink(){ 207 mTextView.setVisibility(GONE); 208 } 209 getScrollThreshold()210 public int getScrollThreshold() { 211 return mScrollThreshold; 212 } 213 } 214