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