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