Add work profile footer in work tab

Screenshot(work mode is on): https://hsv.googleplex.com/6279751685636096
Screenshot (work mode is off): https://hsv.googleplex.com/6279751685636096

TODO:
It is just the UX bit, once the platform change is in, we should then
consume the new API to
1. Turn on / off work mode
2. Get the organization name and set the textview accordingly.

BUG=69905244

Change-Id: Id94ae57625d6b629dac89c1c28f8d65f395fff6c
This commit is contained in:
Tony Mak
2017-11-24 16:40:03 +08:00
parent 0c2d9b0f3e
commit 6ffe4e0eee
10 changed files with 170 additions and 16 deletions
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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">
<path
android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S13.1,15 12,15zM14,6h-4V4h4V6z"
android:fillColor="?android:attr/textColorHint"/>
</vector>
+72
View File
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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="wrap_content"
android:orientation="vertical"
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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Switch
android:id="@+id/work_mode_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:theme="@style/WorkModeSwitchTheme"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/work_mode_toggle"
android:layout_alignParentLeft="true"
android:ellipsize="end"
android:fontFamily="roboto-regular"
android:lines="1"
android:text="@string/work_profile_toggle_label"
android:textColor="?android:attr/textColorTertiary"
android:textSize="16sp"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_corp"/>
<TextView
android:id="@+id/managed_by_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:ellipsize="end"
android:lines="1"
android:paddingLeft="12dp"
android:textColor="?android:attr/textColorHint"
android:textSize="13sp"/>
</LinearLayout>
</LinearLayout>
+2
View File
@@ -40,4 +40,6 @@
<color name="all_apps_bg_hand_fill">#E5E5E5</color>
<color name="all_apps_bg_hand_fill_dark">#9AA0A6</color>
<color name="work_profile_color">#FF6D00</color>
</resources>
+2
View File
@@ -94,6 +94,8 @@
<dimen name="all_apps_tabs_indicator_height">2dp</dimen>
<dimen name="all_apps_header_top_padding">36dp</dimen>
<dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">3dp</dimen>
+3
View File
@@ -324,4 +324,7 @@
<!-- Label of tab to indicate work apps -->
<string name="all_apps_work_tab">Work</string>
<!-- Label of the work mode toggle -->
<string name="work_profile_toggle_label">Work profile</string>
</resources>
+4
View File
@@ -168,4 +168,8 @@
<item name="android:colorControlHighlight">#DFE1E5</item>
<item name="android:colorForeground">@color/all_apps_bg_hand_fill_dark</item>
</style>
<style name="WorkModeSwitchTheme" parent="@style/LauncherTheme">
<item name="android:colorAccent">@color/work_profile_color</item>
</style>
</resources>
@@ -120,8 +120,8 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
addView(mTouchFeedbackView, size, size);
mAH = new AdapterHolder[2];
mAH[AdapterHolder.MAIN] = new AdapterHolder();
mAH[AdapterHolder.WORK] = new AdapterHolder();
mAH[AdapterHolder.MAIN] = new AdapterHolder(false /* isWork */);
mAH[AdapterHolder.WORK] = new AdapterHolder(true /* isWork */);
}
@Override
@@ -627,8 +627,8 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
AllAppsRecyclerView recyclerView;
boolean verticalFadingEdge;
AdapterHolder() {
appsList = new AlphabeticalAppsList(mLauncher, mComponentToAppMap);
AdapterHolder(boolean isWork) {
appsList = new AlphabeticalAppsList(mLauncher, mComponentToAppMap, isWork);
adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
AllAppsContainerView.this, true);
appsList.setAdapter(adapter);
@@ -18,6 +18,7 @@ package com.android.launcher3.allapps;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.UserHandle;
import android.support.animation.DynamicAnimation;
import android.support.animation.SpringAnimation;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
@@ -32,6 +33,7 @@ import android.view.View.OnFocusChangeListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Switch;
import android.widget.TextView;
import com.android.launcher3.AppInfo;
@@ -41,6 +43,7 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.discovery.AppDiscoveryItemView;
@@ -68,14 +71,15 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
// but differ in enough attributes to require different view types
// A divider that separates the apps list and the search market button
public static final int VIEW_TYPE_SEARCH_MARKET_DIVIDER = 1 << 5;
public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 5;
// The divider that separates prediction icons from the app list
public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6;
public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 7;
public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 8;
public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 9;
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_MARKET_DIVIDER
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER
| VIEW_TYPE_PREDICTION_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON
| VIEW_TYPE_PREDICTION_ICON;
@@ -182,8 +186,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
return 1;
} else {
// Section breaks span the full width
return mAppsPerRow;
// Section breaks span the full width
return mAppsPerRow;
}
}
}
@@ -323,9 +327,13 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
R.layout.all_apps_discovery_loading_divider, parent, false);
return new ViewHolder(loadingDividerView);
case VIEW_TYPE_PREDICTION_DIVIDER:
case VIEW_TYPE_SEARCH_MARKET_DIVIDER:
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);
// TODO: Implement the work mode toggle logic here.
return new ViewHolder(footer);
default:
throw new RuntimeException("Unexpected view type");
}
@@ -367,9 +375,17 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
holder.itemView.findViewById(R.id.loadingProgressBar).setVisibility(visLoading);
holder.itemView.findViewById(R.id.loadedDivider).setVisibility(visLoaded);
break;
case VIEW_TYPE_SEARCH_MARKET_DIVIDER:
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
break;
case VIEW_TYPE_WORK_TAB_FOOTER:
Switch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
workModeToggle.setChecked(!isAnyProfileQuietModeEnabled());
TextView textView = holder.itemView.findViewById(R.id.managed_by_label);
// TODO: Configure the textview properly.
textView.setText("Managed by your company");
break;
}
if (mBindViewCallback != null) {
mBindViewCallback.onBindView(holder);
@@ -537,4 +553,15 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
return factor;
}
}
private boolean isAnyProfileQuietModeEnabled() {
UserManagerCompat userManager = UserManagerCompat.getInstance(mLauncher);
List<UserHandle> userProfiles = userManager.getUserProfiles();
for (UserHandle userProfile : userProfiles) {
if (userManager.isQuietModeEnabled(userProfile)) {
return true;
}
}
return false;
}
}
@@ -143,7 +143,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, mNumAppsPerRow);
@@ -167,7 +167,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER,
AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER);
AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER);
putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET);
putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
@@ -140,9 +140,9 @@ public class AlphabeticalAppsList {
return item;
}
public static AdapterItem asMarketDivider(int pos) {
public static AdapterItem asAllAppsDivider(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER;
item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
item.position = pos;
return item;
}
@@ -160,6 +160,13 @@ public class AlphabeticalAppsList {
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 Launcher mLauncher;
@@ -179,6 +186,8 @@ public class AlphabeticalAppsList {
// The set of predicted apps resolved from the component names and the current set of apps
private final List<AppInfo> mPredictedApps = new ArrayList<>();
private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>();
// Is it the work profile app list.
private final boolean mIsWork;
// The of ordered component names as a result of a search query
private ArrayList<ComponentKey> mSearchResults;
@@ -191,11 +200,16 @@ public class AlphabeticalAppsList {
private int mNumAppRowsInAdapter;
private ItemInfoMatcher mItemFilter;
public AlphabeticalAppsList(Context context, HashMap<ComponentKey, AppInfo> componentToAppMap) {
public AlphabeticalAppsList(
Context context,
HashMap<ComponentKey,
AppInfo> componentToAppMap,
boolean isWork) {
mComponentToAppMap = componentToAppMap;
mLauncher = Launcher.getLauncher(context);
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppInfoComparator(context);
mIsWork = isWork;
}
public void updateItemFilter(ItemInfoMatcher itemFilter) {
@@ -545,7 +559,7 @@ public class AlphabeticalAppsList {
if (hasNoFilteredResults()) {
mAdapterItems.add(AdapterItem.asEmptySearch(position++));
} else {
mAdapterItems.add(AdapterItem.asMarketDivider(position++));
mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
}
mAdapterItems.add(AdapterItem.asMarketSearch(position++));
}
@@ -604,6 +618,12 @@ public class AlphabeticalAppsList {
break;
}
}
// Add the work profile footer if required.
if (mIsWork) {
mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
mAdapterItems.add(AdapterItem.asWorkTabFooter(position++));
}
}
public boolean isAppDiscoveryRunning() {