Merge "Change Icon for app icon dismiss action" into ub-launcher3-master
diff --git a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
index 97eef66..22f1f23 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
@@ -30,6 +30,8 @@
private static final String TAG = "GraphicsUtils";
+ public static Runnable sOnNewBitmapRunnable = () -> { };
+
/**
* Set the alpha component of {@code color} to be {@code alpha}. Unlike the support lib version,
* it bounds the alpha in valid range instead of throwing an exception to allow for safer
@@ -77,5 +79,7 @@
/**
* Utility method to track new bitmap creation
*/
- public static void noteNewBitmapCreated() { }
+ public static void noteNewBitmapCreated() {
+ sOnNewBitmapRunnable.run();
+ }
}
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index a7cd167..d94c665 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -34,10 +34,10 @@
android:orientation="vertical">
<TextView
+ style="@style/TextHeadline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
- android:fontFamily="google-sans"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
android:paddingRight="@dimen/bottom_sheet_edu_padding"
android:text="@string/hotseat_migrate_title"
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 06b9f1f..632b9b5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -317,8 +317,8 @@
&& itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
return;
}
- if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
- HotseatPredictionController.fillInHybridHotseatRank(itemInfo, target);
+ if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) {
+ HotseatPredictionController.encodeHotseatLayoutIntoPredictionRank(itemInfo, target);
return;
}
@@ -327,7 +327,7 @@
IntStream.range(0, predictedApps.size())
.filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
.findFirst()
- .ifPresent((rank) -> target.predictedRank = rank);
+ .ifPresent((rank) -> target.predictedRank = 0 - rank);
}
public static class PredictionState {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index c2b55ab..109439f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -68,6 +68,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.OptionalInt;
import java.util.stream.IntStream;
/**
@@ -548,12 +549,11 @@
* Fill in predicted_rank field based on app prediction.
* Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
*/
- public static void fillInHybridHotseatRank(
+ public static void encodeHotseatLayoutIntoPredictionRank(
@NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
QuickstepLauncher launcher = QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
if (launcher == null || launcher.getHotseatPredictionController() == null
- || itemInfo.getTargetComponent() == null
- || itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ || itemInfo.getTargetComponent() == null) {
return;
}
HotseatPredictionController controller = launcher.getHotseatPredictionController();
@@ -561,11 +561,12 @@
final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
final List<ComponentKeyMapper> predictedApps = controller.mComponentKeyMappers;
- IntStream.range(0, predictedApps.size())
+ OptionalInt rank = IntStream.range(0, predictedApps.size())
.filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
- .findFirst()
- .ifPresent((rank) -> target.predictedRank =
- Integer.parseInt(controller.mPredictedSpotsCount + "0" + rank));
+ .findFirst();
+
+ target.predictedRank = 10000 + (controller.mPredictedSpotsCount * 100)
+ + (rank.isPresent() ? rank.getAsInt() + 1 : 0);
}
private static boolean isPredictedIcon(View view) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index bd89626..b2e1798 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -47,6 +47,7 @@
private static final float RING_EFFECT_RATIO = 0.11f;
+ boolean mIsDrawingDot = false;
private final DeviceProfile mDeviceProfile;
private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private boolean mIsPinned = false;
@@ -81,6 +82,16 @@
}
@Override
+ protected void drawDotIfNecessary(Canvas canvas) {
+ mIsDrawingDot = true;
+ int count = canvas.save();
+ canvas.translate(-getWidth() * RING_EFFECT_RATIO, -getHeight() * RING_EFFECT_RATIO);
+ super.drawDotIfNecessary(canvas);
+ canvas.restoreToCount(count);
+ mIsDrawingDot = false;
+ }
+
+ @Override
public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
super.applyFromWorkspaceItem(info);
int color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
@@ -112,7 +123,7 @@
@Override
public void getIconBounds(Rect outBounds) {
super.getIconBounds(outBounds);
- if (!mIsPinned) {
+ if (!mIsPinned && !mIsDrawingDot) {
int predictionInset = (int) (getIconSize() * RING_EFFECT_RATIO);
outBounds.inset(predictionInset, predictionInset);
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index c99df10..d782f8d 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -17,6 +17,8 @@
package com.android.quickstep;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -40,6 +42,7 @@
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
@@ -268,7 +271,8 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- @Ignore // b/143285809
+ // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
startTestActivity(3);
diff --git a/res/drawable/ic_corp.xml b/res/drawable/ic_corp.xml
index b59113d..76dccd3 100644
--- a/res/drawable/ic_corp.xml
+++ b/res/drawable/ic_corp.xml
@@ -19,9 +19,5 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/textColorHint" >
- <path
- android:pathData="M20 6h-4V4c0-1.11-0.89-2-2-2h-4c-1.11 0-2 0.89-2 2v2H4c-1.11 0-1.99 0.89 -1.99
-2L2 19c0 1.11 0.89 2 2 2h16c1.11 0 2-0.89 2-2V8c0-1.11-0.89-2-2-2zM10
-4h4v2h-4V4zm10 15H4V8h16v11z"
- android:fillColor="@android:color/white"/>
+ <path android:fillColor="@android:color/white" android:pathData="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-8 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm2-9h-4V4h4v2z"/>
</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_corp_off.xml b/res/drawable/ic_corp_off.xml
new file mode 100644
index 0000000..62a9787
--- /dev/null
+++ b/res/drawable/ic_corp_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorHint" >
+ <path
+ android:pathData="M22 7.95c.05-1.11-.84-2-1.95-1.95H16V3.95c0-1.11-.84-2-1.95-1.95h-4C8.94 1.95 8 2.84 8 3.95v.32l14 14V7.95zM14 6h-4V4h4v2zm7.54 14.28l-7.56-7.56v.01l-1.7-1.7h.01L7.21 5.95 3.25 1.99 1.99 3.27 4.69 6h-.64c-1.11 0-1.99.86-1.99 1.97l-.01 11.02c0 1.11.89 2.01 2 2.01h15.64l2.05 2.02L23 21.75l-1.46-1.47z"
+ android:fillColor="@android:color/white"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
new file mode 100644
index 0000000..5607e78
--- /dev/null
+++ b/res/layout/work_apps_paused.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:orientation="vertical"
+ android:background="?attr/allAppsScrimColor"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:contentDescription="@string/work_apps_paused_title"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:tint="?attr/folderTextColor"
+ android:src="@drawable/ic_corp_off" />
+
+ <TextView
+ style="@style/TextHeadline"
+ android:textColor="?attr/folderTextColor"
+ android:id="@+id/work_apps_paused_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:text="@string/work_apps_paused_title"
+ android:textAlignment="center"
+ android:textSize="24sp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?attr/folderTextColor"
+ android:text="@string/work_apps_paused_body"
+ android:textAlignment="center"
+ android:textSize="16sp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
index a8e3d20..f7a529d 100644
--- a/res/layout/work_profile_edu.xml
+++ b/res/layout/work_profile_edu.xml
@@ -35,12 +35,12 @@
android:paddingRight="@dimen/bottom_sheet_edu_padding">
<TextView
+ style="@style/TextHeadline"
android:id="@+id/content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:layout_marginBottom="48dp"
- android:fontFamily="google-sans"
android:text="@string/work_profile_edu_personal_apps"
android:textAlignment="center"
android:textColor="@android:color/white"
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index 379e9d0..db95416 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -17,63 +17,31 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:id="@+id/work_toggle_container"
android:focusable="true"
- android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_bottom_padding"
- android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
- android:paddingRight="@dimen/dynamic_grid_cell_padding_x"
- android:paddingTop="@dimen/all_apps_work_profile_tab_footer_top_padding">
-
- <ImageView
- android:id="@+id/work_footer_divider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:focusable="false"
- android:importantForAccessibility="no"
- android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
- android:paddingTop="@dimen/all_apps_divider_margin_vertical"
- android:scaleType="fitXY"
- android:src="@drawable/all_apps_divider"/>
-
- <com.android.launcher3.allapps.WorkModeSwitch
- android:id="@+id/work_mode_toggle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/work_footer_divider"/>
+ android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_padding_vertical"
+ android:orientation="horizontal"
+ android:paddingLeft="@dimen/all_apps_work_profile_tab_footer_padding_horizontal"
+ android:background="?attr/allAppsScrimColor"
+ android:paddingRight="@dimen/all_apps_work_profile_tab_footer_padding_horizontal"
+ android:paddingTop="@dimen/all_apps_work_profile_tab_footer_padding_vertical">
<TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
+ android:id="@+id/work_mode_label"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:drawableStart="@drawable/ic_corp"
+ android:drawablePadding="3dp"
android:layout_height="wrap_content"
- android:layout_alignBaseline="@id/work_mode_toggle"
- android:layout_alignParentStart="true"
- android:ellipsize="end"
- android:lines="1"
- android:text="@string/work_profile_toggle_label"
- android:textColor="?android:attr/textColorTertiary"
- android:textSize="16sp"/>
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_below="@android:id/title"
- android:layout_marginTop="8dp"
- android:src="@drawable/ic_corp"/>
-
- <TextView
- android:id="@+id/managed_by_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_marginTop="8dp"
- android:layout_toEndOf="@android:id/icon"
android:ellipsize="end"
android:gravity="center_vertical"
android:lines="1"
android:minHeight="24dp"
- android:paddingStart="12dp"
- android:textColor="?android:attr/textColorHint"
- android:textSize="13sp"/>
+ android:paddingEnd="12dp"
+ android:textSize="16sp"/>
+ <com.android.launcher3.allapps.WorkModeSwitch
+ android:id="@+id/work_mode_toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
</com.android.launcher3.views.WorkFooterContainer>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0293573..edae7f4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -85,6 +85,9 @@
<dimen name="all_apps_tabs_side_padding">12dp</dimen>
<dimen name="all_apps_divider_height">1dp</dimen>
+ <dimen name="all_apps_work_profile_tab_footer_padding_vertical">20dp</dimen>
+ <dimen name="all_apps_work_profile_tab_footer_padding_horizontal">24dp</dimen>
+
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">3dp</dimen>
<dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3f279f4..bfa92f7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -338,9 +338,12 @@
<!-- This string is in the work profile tab when a user has All Apps open on their phone. It describes the label of a toggle, "Work profile," as being managed by the user's employer.
"Organization" is used to represent a variety of businesses, non-profits, and educational institutions).-->
- <string name="work_mode_on_label">Managed by your organization</string>
+ <string name="work_mode_on_label">Work apps: On</string>
<!-- This string appears under a the label of a toggle in the work profile tab on a user's phone. It describes the status of the toggle, "Work profile," when it's turned off. "Work profile" means a separate profile on a user's phone that's speficially for their work apps and is managed by their company.-->
- <string name="work_mode_off_label">Notifications and apps are off</string>
+ <string name="work_mode_off_label">Work apps: Paused</string>
+
+ <string name="work_apps_paused_title">Work apps are paused</string>
+ <string name="work_apps_paused_body">You won\'t get any work notifications, and your IT admin can\'t see your location</string>
<!-- Failed action error message: e.g. Failed: Pause -->
<string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 80c791c..35ae49c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -55,6 +55,8 @@
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">#00000000</item>
<item name="android:navigationBarColor">#00000000</item>
+
+
</style>
<style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 95ee687..5f6ecb5 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -19,6 +19,7 @@
import android.content.Context;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapCreationCheck;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -39,5 +40,9 @@
FeatureFlags.initialize(context);
SessionCommitReceiver.applyDefaultUserPrefs(context);
IconShape.init(context);
+
+ if (BitmapCreationCheck.ENABLED) {
+ BitmapCreationCheck.startTracking(context);
+ }
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 6f15c9b..56bd1b6 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.allapps;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
@@ -51,6 +54,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -59,6 +63,7 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
+import com.android.launcher3.views.WorkFooterContainer;
/**
* The all apps view container.
@@ -83,7 +88,10 @@
protected SearchUiManager mSearchUiManager;
private View mSearchContainer;
private AllAppsPagedView mViewPager;
+
private FloatingHeaderView mHeader;
+ private WorkFooterContainer mWorkFooterContainer;
+
private SpannableStringBuilder mSearchQueryBuilder = null;
@@ -173,6 +181,15 @@
}
}
rebindAdapters(hasWorkApps);
+ if (hasWorkApps) {
+ resetWorkProfile();
+ }
+ }
+
+ private void resetWorkProfile() {
+ mWorkFooterContainer.refresh();
+ mAH[AdapterHolder.WORK].setupOverlay();
+ mAH[AdapterHolder.WORK].applyPadding();
}
/**
@@ -311,6 +328,7 @@
mAH[i].padding.bottom = insets.bottom;
mAH[i].padding.left = mAH[i].padding.right = leftRightPadding;
mAH[i].applyPadding();
+ mAH[i].setupOverlay();
}
ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
@@ -368,12 +386,17 @@
mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.WORK].recyclerView);
if (mUsingTabs) {
+ setupWorkToggle();
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
onTabChanged(mViewPager.getNextPage());
} else {
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
+ if (mWorkFooterContainer != null) {
+ ((ViewGroup) mWorkFooterContainer.getParent()).removeView(mWorkFooterContainer);
+ mWorkFooterContainer = null;
+ }
}
setupHeader();
@@ -381,6 +404,16 @@
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
}
+ private void setupWorkToggle() {
+ mWorkFooterContainer = (WorkFooterContainer) mLauncher.getLayoutInflater().inflate(
+ R.layout.work_tab_footer, findViewById(R.id.work_toggle_container));
+ mWorkFooterContainer.setLayoutParams(
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ this.addView(mWorkFooterContainer);
+ mWorkFooterContainer.post(() -> mAH[AdapterHolder.WORK].applyPadding());
+ }
+
private void replaceRVContainer(boolean showTabs) {
for (int i = 0; i < mAH.length; i++) {
if (mAH[i].recyclerView != null) {
@@ -417,6 +450,9 @@
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
}
+ if (mWorkFooterContainer != null) {
+ mWorkFooterContainer.setWorkTabVisible(pos == AdapterHolder.WORK);
+ }
}
// Used by tests only
@@ -562,6 +598,7 @@
public static final int MAIN = 0;
public static final int WORK = 1;
+ private final boolean mIsWork;
public final AllAppsGridAdapter adapter;
final LinearLayoutManager layoutManager;
final AlphabeticalAppsList appsList;
@@ -569,7 +606,10 @@
AllAppsRecyclerView recyclerView;
boolean verticalFadingEdge;
+ boolean mWorkDisabled;
+
AdapterHolder(boolean isWork) {
+ mIsWork = isWork;
appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
adapter = new AllAppsGridAdapter(mLauncher, appsList);
appsList.setAdapter(adapter);
@@ -591,11 +631,36 @@
adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
applyVerticalFadingEdgeEnabled(verticalFadingEdge);
applyPadding();
+ setupOverlay();
+ }
+
+ void setupOverlay() {
+ if (!mIsWork || recyclerView == null) return;
+ boolean workDisabled = UserCache.INSTANCE.get(mLauncher).isAnyProfileQuietModeEnabled();
+ recyclerView.getOverlay().clear();
+ if (workDisabled) {
+ View pausedOverlay = mLauncher.getLayoutInflater().inflate(
+ R.layout.work_apps_paused, null);
+ recyclerView.post(() -> {
+ int width = recyclerView.getWidth();
+ int height = recyclerView.getHeight();
+ pausedOverlay.measure(makeMeasureSpec(width, EXACTLY),
+ makeMeasureSpec(height, EXACTLY));
+ pausedOverlay.layout(0, 0, width, height);
+ applyPadding();
+ });
+ recyclerView.getOverlay().add(pausedOverlay);
+ }
+ mWorkDisabled = workDisabled;
}
void applyPadding() {
if (recyclerView != null) {
- recyclerView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ int bottomOffset =
+ mWorkFooterContainer != null && mIsWork ? mWorkFooterContainer.getHeight()
+ : 0;
+ recyclerView.setPadding(padding.left, padding.top, padding.right,
+ padding.bottom + bottomOffset);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 442b77b..1f861bc 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -43,7 +43,6 @@
import com.android.launcher3.R;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -67,7 +66,6 @@
// A divider that separates the apps list and the search market button
public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
- public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 5;
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
@@ -289,9 +287,6 @@
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
- case VIEW_TYPE_WORK_TAB_FOOTER:
- View footer = mLayoutInflater.inflate(R.layout.work_tab_footer, parent, false);
- return new ViewHolder(footer);
default:
throw new RuntimeException("Unexpected view type");
}
@@ -323,15 +318,6 @@
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
break;
- case VIEW_TYPE_WORK_TAB_FOOTER:
- WorkModeSwitch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
- workModeToggle.refresh();
- TextView managedByLabel = holder.itemView.findViewById(R.id.managed_by_label);
- boolean anyProfileQuietModeEnabled = UserCache.INSTANCE.get(
- managedByLabel.getContext()).isAnyProfileQuietModeEnabled();
- managedByLabel.setText(anyProfileQuietModeEnabled
- ? R.string.work_mode_off_label : R.string.work_mode_on_label);
- break;
}
if (mBindViewCallback != null) {
mBindViewCallback.onBindView(holder);
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index a33fe4d..b501c82 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -15,14 +15,11 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
import android.content.Context;
-import android.content.pm.PackageManager;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
@@ -117,13 +114,6 @@
item.position = pos;
return item;
}
-
- public static AdapterItem asWorkTabFooter(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_WORK_TAB_FOOTER;
- item.position = pos;
- return item;
- }
}
private final BaseDraggingActivity mLauncher;
@@ -390,18 +380,6 @@
break;
}
}
-
- // Add the work profile footer if required.
- if (shouldShowWorkFooter()) {
- mAdapterItems.add(AdapterItem.asWorkTabFooter(position++));
- }
- }
-
- private boolean shouldShowWorkFooter() {
- return mIsWork && Utilities.ATLEAST_P &&
- (hasShortcutsPermission(mLauncher)
- || mLauncher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
- == PackageManager.PERMISSION_GRANTED);
}
private List<AppInfo> getFiltersAppInfos() {
diff --git a/src/com/android/launcher3/graphics/BitmapCreationCheck.java b/src/com/android/launcher3/graphics/BitmapCreationCheck.java
new file mode 100644
index 0000000..e63e542
--- /dev/null
+++ b/src/com/android/launcher3/graphics/BitmapCreationCheck.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+package com.android.launcher3.graphics;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.GraphicsUtils;
+
+/**
+ * Utility class to check bitmap creation during draw pass.
+ */
+public class BitmapCreationCheck {
+
+ private static final String TAG = "BitmapCreationCheck";
+
+ public static final boolean ENABLED = false;
+
+ /**
+ * Starts tracking bitmap creations during {@link View#draw(Canvas)} calls
+ */
+ public static void startTracking(Context context) {
+ MyTracker tracker = new MyTracker();
+ ((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks(tracker);
+ GraphicsUtils.sOnNewBitmapRunnable = tracker::onBitmapCreated;
+ }
+
+ @TargetApi(VERSION_CODES.Q)
+ private static class MyTracker
+ implements ActivityLifecycleCallbacks, OnAttachStateChangeListener {
+
+ private final ThreadLocal<Boolean> mCurrentThreadDrawing =
+ ThreadLocal.withInitial(() -> false);
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) {
+ activity.getWindow().getDecorView().addOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) { }
+
+ @Override
+ public void onActivityResumed(Activity activity) { }
+
+ @Override
+ public void onActivityPaused(Activity activity) { }
+
+ @Override
+ public void onActivityStopped(Activity activity) { }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) { }
+
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ view.getViewTreeObserver().addOnDrawListener(new MyViewDrawListener(view.getHandler()));
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) { }
+
+ private class MyViewDrawListener implements OnDrawListener, Runnable {
+
+ private final Handler mHandler;
+
+ MyViewDrawListener(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void onDraw() {
+ mCurrentThreadDrawing.set(true);
+ Utilities.postAsyncCallback(mHandler, this);
+ }
+
+ @Override
+ public void run() {
+ mCurrentThreadDrawing.set(false);
+ }
+ }
+
+ private void onBitmapCreated() {
+ if (mCurrentThreadDrawing.get()) {
+ Log.e(TAG, "Bitmap created during draw pass", new Exception());
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index f352b46..b004edf 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -44,7 +44,7 @@
public class LoggerUtils {
private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
private static final String UNKNOWN = "UNKNOWN";
- private static final int DEFAULT_PREDICTED_RANK = -100;
+ private static final int DEFAULT_PREDICTED_RANK = 10000;
public static String getFieldName(int value, Class c) {
SparseArray<String> cache;
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index c3186f6..b6c81ae 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -52,8 +52,6 @@
private static final int WORK_EDU_PERSONAL_APPS = 1;
private static final int WORK_EDU_WORK_APPS = 2;
- private static LauncherStateManager.StateListener sStateListener;
-
private Rect mInsets = new Rect();
private View mViewWrapper;
private Button mProceedButton;
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
index fb17b4f..f8add9a 100644
--- a/src/com/android/launcher3/views/WorkFooterContainer.java
+++ b/src/com/android/launcher3/views/WorkFooterContainer.java
@@ -15,32 +15,61 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.WorkModeSwitch;
+import com.android.launcher3.pm.UserCache;
/**
* Container to show work footer in all-apps.
*/
-public class WorkFooterContainer extends RelativeLayout {
+public class WorkFooterContainer extends LinearLayout implements Insettable {
+ private Rect mInsets = new Rect();
+
+ private WorkModeSwitch mWorkModeSwitch;
+ private TextView mWorkModeLabel;
+
+ protected final ObjectAnimator mOpenCloseAnimator;
public WorkFooterContainer(Context context) {
- super(context);
+ this(context, null, 0);
}
public WorkFooterContainer(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public WorkFooterContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
updateTranslation();
+ this.setVisibility(shouldShowWorkFooter() ? VISIBLE : GONE);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mWorkModeSwitch = findViewById(R.id.work_mode_toggle);
+ mWorkModeLabel = findViewById(R.id.work_mode_label);
}
@Override
@@ -56,4 +85,44 @@
setTranslationY(Math.max(0, availableBot - getBottom()));
}
}
+
+ @Override
+ public void setInsets(Rect insets) {
+ int bottomInset = insets.bottom - mInsets.bottom;
+ mInsets.set(insets);
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
+ getPaddingBottom() + bottomInset);
+ }
+
+ /**
+ * Animates in/out work profile toggle panel based on the tab user is on
+ */
+ public void setWorkTabVisible(boolean workTabVisible) {
+ if (!shouldShowWorkFooter()) return;
+
+ mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(ALPHA, workTabVisible ? 1 : 0));
+ mOpenCloseAnimator.start();
+ }
+
+ /**
+ * Refreshes views based on current work profile enabled status
+ */
+ public void refresh() {
+ if (!shouldShowWorkFooter()) return;
+ boolean anyProfileQuietModeEnabled = UserCache.INSTANCE.get(
+ getContext()).isAnyProfileQuietModeEnabled();
+
+ mWorkModeLabel.setText(anyProfileQuietModeEnabled
+ ? R.string.work_mode_off_label : R.string.work_mode_on_label);
+ mWorkModeLabel.setCompoundDrawablesWithIntrinsicBounds(
+ anyProfileQuietModeEnabled ? R.drawable.ic_corp_off : R.drawable.ic_corp, 0, 0, 0);
+ mWorkModeSwitch.refresh();
+ }
+
+ private boolean shouldShowWorkFooter() {
+ Launcher launcher = Launcher.getLauncher(getContext());
+ return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
+ || launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ == PackageManager.PERMISSION_GRANTED);
+ }
}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
index dcc7b7c..d6dfdd9 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
@@ -32,12 +32,62 @@
return Pattern.compile(regex).matcher(string).find();
}
+ static class LogcatMatch {
+ String logcatPattern;
+ int bug;
+
+ LogcatMatch(String logcatPattern, int bug) {
+ this.logcatPattern = logcatPattern;
+ this.bug = bug;
+ }
+ }
+
+ static class ExceptionMatch {
+ String exceptionPattern;
+ LogcatMatch[] logcatMatches;
+
+ ExceptionMatch(String exceptionPattern, LogcatMatch[] logcatMatches) {
+ this.exceptionPattern = exceptionPattern;
+ this.logcatMatches = logcatMatches;
+ }
+ }
+
+ private static final ExceptionMatch[] EXCEPTION_MATCHES = {
+ new ExceptionMatch(
+ "java.lang.AssertionError: http://go/tapl : Tests are broken by a "
+ + "non-Launcher system error: Phone is locked",
+ new LogcatMatch[]{
+ new LogcatMatch(
+ "BroadcastQueue: Can't deliver broadcast to com.android"
+ + ".systemui.*Crashing it",
+ 147845913),
+ new LogcatMatch(
+ "Attempt to invoke virtual method 'boolean android\\"
+ + ".graphics\\.Bitmap\\.isRecycled\\(\\)' on a null "
+ + "object reference",
+ 148424291),
+ new LogcatMatch(
+ "java\\.lang\\.IllegalArgumentException\\: Ranking map "
+ + "doesn't contain key",
+ 148570537),
+ }),
+ new ExceptionMatch("Launcher didn't initialize",
+ new LogcatMatch[]{
+ new LogcatMatch(
+ "ActivityManager: Reason: executing service com.google"
+ + ".android.apps.nexuslauncher/com.android.launcher3"
+ + ".notification.NotificationListener",
+ 148238677),
+ }),
+ };
+
static int getBugForFailure(CharSequence exception) {
if ("com.google.android.setupwizard".equals(
UiDevice.getInstance(getInstrumentation()).getLauncherPackageName())) {
return 145935261;
}
+
final String logSinceBoot;
try {
final String systemBootTime =
@@ -51,39 +101,14 @@
return 0;
}
- if (matches(
- "java.lang.AssertionError: http://go/tapl : Tests are broken by a non-Launcher "
- + "system error: Phone is locked",
- exception)) {
- if (matches(
- "BroadcastQueue: Can't deliver broadcast to com.android.systemui.*Crashing it",
- logSinceBoot)) {
- return 147845913;
- }
- if (matches(
- "Attempt to invoke virtual method 'boolean android\\.graphics\\.Bitmap\\"
- + ".isRecycled\\(\\)' on a null object reference",
- logSinceBoot)) {
- return 148424291;
- }
- if (matches(
- "java\\.lang\\.IllegalArgumentException\\: Ranking map doesn't contain key",
- logSinceBoot)) {
- return 148570537;
- }
- } else if (matches("java.lang.AssertionError: Launcher build match not found", exception)) {
- if (matches(
- "TestStabilityRule: Launcher package: com.google.android.setupwizard",
- logSinceBoot)) {
- return 145935261;
- }
- } else if (matches("Launcher didn't initialize", exception)) {
- if (matches(
- "ActivityManager: Reason: executing service com.google.android.apps"
- + ".nexuslauncher/com.android.launcher3.notification"
- + ".NotificationListener",
- logSinceBoot)) {
- return 148238677;
+ for (ExceptionMatch exceptionMatch : EXCEPTION_MATCHES) {
+ if (matches(exceptionMatch.exceptionPattern, exception)) {
+ for (LogcatMatch logcatMatch : exceptionMatch.logcatMatches) {
+ if (matches(logcatMatch.logcatPattern, logSinceBoot)) {
+ return logcatMatch.bug;
+ }
+ }
+ break;
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 59d295f..9f29a1a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -25,6 +25,7 @@
import android.view.MotionEvent;
import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.testing.TestProtocol;
@@ -179,9 +180,12 @@
endX = startX;
endY = 0;
}
+ final boolean launcherIsVisible =
+ mLauncher.hasLauncherObject(By.textStartsWith(""));
+ final boolean isZeroButton = mLauncher.getNavigationModel()
+ == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
- mLauncher.getNavigationModel()
- == LauncherInstrumentation.NavigationModel.ZERO_BUTTON
+ launcherIsVisible && isZeroButton
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
: LauncherInstrumentation.GestureScope.OUTSIDE
);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5555eab..cf5b24e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -797,7 +797,7 @@
return mDevice.hasObject(getLauncherObjectSelector(resId));
}
- private boolean hasLauncherObject(BySelector selector) {
+ boolean hasLauncherObject(BySelector selector) {
return mDevice.hasObject(makeLauncherSelector(selector));
}