diff options
| author | 2015-11-18 16:48:00 +0000 | |
|---|---|---|
| committer | 2015-12-15 16:45:02 +0000 | |
| commit | cc52ce4bc4a7cfb28119d1949f44d1b1287936e7 (patch) | |
| tree | 7acbc3c75846bda36c659a171e192cdef1a5c11c | |
| parent | e5f4a9c1128be91e9d0c2c8109ae633e744deb37 (diff) | |
Mask work widgets if work profile is in quiet mode.
If a work profile is in quiet mode, mask widgets belonging to that profile
with a generic RemoteView.
Bug: 22541941
Change-Id: I3baa179630af497afa02b32f236d2bdaa4a75904
| -rw-r--r-- | core/res/res/drawable/ic_corp_badge_off.xml | 56 | ||||
| -rw-r--r-- | core/res/res/drawable/work_widget_mask_view_background.xml | 20 | ||||
| -rw-r--r-- | core/res/res/layout/work_widget_mask_view.xml | 34 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 5 | ||||
| -rw-r--r-- | services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java | 110 |
5 files changed, 221 insertions, 4 deletions
diff --git a/core/res/res/drawable/ic_corp_badge_off.xml b/core/res/res/drawable/ic_corp_badge_off.xml new file mode 100644 index 000000000000..6799bf716d97 --- /dev/null +++ b/core/res/res/drawable/ic_corp_badge_off.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2015 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="20dp" + android:height="20dp" + android:viewportWidth="20" + android:viewportHeight="20"> + + <path + android:fillColor="#607D8B" + android:pathData="M10,0 C15.5228,0,20,4.47715,20,10 C20,15.5228,15.5228,20,10,20 +C4.47715,20,0,15.5228,0,10 C0,4.47715,4.47715,0,10,0 Z" /> + <path + android:pathData="M1.91667,1.91667 L18.0833,1.91667 L18.0833,18.0833 L1.91667,18.0833 +L1.91667,1.91667 Z" /> + <path + android:fillColor="#ffffff" + android:pathData="M11.9167,11.9167 L11.4167,11.9167 L11.4167,12.8333 L8.5,12.8333 L8.5,11.9167 +L4.16667,11.9167 L4.16667,14.3333 C4.16667,14.8333,4.58333,15.25,5.08333,15.25 +L14.75,15.25 C14.9167,15.25,15,15.25,15.1667,15.1667 L11.9167,11.9167 Z" /> + <path + android:fillColor="#ffffff" + android:pathData="M15.8333,13.75 L15.8333,11.9167 L14,11.9167 +C14.6667,12.6667,15.3333,13.3333,15.8333,13.75 Z" /> + <path + android:fillColor="#ffffff" + android:pathData="M6.16667,6.16667 L4.66667,6.16667 C4.16667,6.16667,3.75,6.58333,3.75,7.08333 +L3.75,10 C3.75,10.5,4.16667,10.9167,4.66667,10.9167 L8.5,10.9167 L8.5,10 L10,10 +L6.16667,6.16667 Z" /> + <path + android:fillColor="#ffffff" + android:pathData="M8.08333,6 L8.08333,5.16667 L11.9167,5.16667 L11.9167,6.08333 L8.16667,6.08333 +C9.66667,7.58333,11.4167,9.33333,12.9167,10.8333 L15.25,10.8333 +C15.75,10.8333,16.1667,10.4167,16.1667,9.91667 L16.1667,7.08333 +C16.1667,6.58333,15.75,6.16667,15.25,6.16667 L12.8333,6.16667 L12.8333,5.25 +L11.9167,4.33333 L8.08333,4.33333 L7.16667,5.16667 +C7.41667,5.41667,7.75,5.75,8.08333,6 Z" /> + <path + android:fillColor="#ffffff" + android:pathData="M15.6824,15.676 L14.6807,16.6777 L3.24921,5.24624 L4.25093,4.24452 +L15.6824,15.676 Z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/work_widget_mask_view_background.xml b/core/res/res/drawable/work_widget_mask_view_background.xml new file mode 100644 index 000000000000..17f0dbc2b91c --- /dev/null +++ b/core/res/res/drawable/work_widget_mask_view_background.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2015 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. +--> + <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> + <padding android:left="5dp" android:right="5dp" android:top="5dp" android:bottom="5dp"/> + <stroke android:width="1dp" android:color="#CCCCCC" /> + </shape> diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml new file mode 100644 index 000000000000..ce86ddc61876 --- /dev/null +++ b/core/res/res/layout/work_widget_mask_view.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2015 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. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#F3374248" > + + <ImageView android:id="@+id/work_widget_app_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|right" + android:layout_marginBottom="4dp" + android:layout_marginRight="4dp" + android:src="@drawable/ic_corp_badge_off" /> +</FrameLayout> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 36ba30611719..832984ea1813 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1243,6 +1243,7 @@ <java-symbol type="drawable" name="cling_arrow_up" /> <java-symbol type="drawable" name="cling_bg" /> <java-symbol type="drawable" name="ic_corp_badge" /> + <java-symbol type="drawable" name="ic_corp_badge_off" /> <java-symbol type="drawable" name="ic_corp_icon_badge" /> <java-symbol type="drawable" name="ic_corp_icon" /> <java-symbol type="drawable" name="ic_corp_statusbar_icon" /> @@ -2384,4 +2385,8 @@ <java-symbol type="dimen" name="notification_content_margin_top" /> <java-symbol type="string" name="importance_from_topic" /> <java-symbol type="string" name="importance_from_person" /> + + <java-symbol type="layout" name="work_widget_mask_view" /> + <java-symbol type="id" name="work_widget_app_icon" /> + <java-symbol type="drawable" name="work_widget_mask_view_background" /> </resources> diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 25fef1870dda..df18d3ea9205 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -37,6 +37,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -44,7 +45,10 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Point; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -79,6 +83,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsAdapterConnection; import com.android.internal.widget.IRemoteViewsFactory; +import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.WidgetBackupProvider; @@ -149,6 +154,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } else if (Intent.ACTION_USER_STOPPED.equals(action)) { onUserStopped(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); + } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { + refreshProfileWidgetsMaskedState(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + UserHandle.USER_NULL)); + } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) { + UserHandle profile = (UserHandle)intent.getParcelableExtra(Intent.EXTRA_USER); + if (profile != null) { + refreshWidgetMaskedState(profile.getIdentifier()); + } } else { onPackageBroadcastReceived(intent, intent.getIntExtra( Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); @@ -208,6 +221,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mCallbackHandler = new CallbackHandler(mContext.getMainLooper()); mBackupRestoreController = new BackupRestoreController(); mSecurityPolicy = new SecurityPolicy(); + computeMaximumWidgetBitmapMemory(); registerBroadcastReceiver(); registerOnCrossProfileProvidersChangedListener(); @@ -251,8 +265,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku IntentFilter userFilter = new IntentFilter(); userFilter.addAction(Intent.ACTION_USER_STARTED); userFilter.addAction(Intent.ACTION_USER_STOPPED); + userFilter.addAction(Intent.ACTION_USER_SWITCHED); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); + + IntentFilter offModeFilter = new IntentFilter(); + offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED); + mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, + offModeFilter, null, null); } private void registerOnCrossProfileProvidersChangedListener() { @@ -395,6 +415,60 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } + /** + * Refresh the masked state for all profiles under the given user. + */ + private void refreshProfileWidgetsMaskedState(int userId) { + if (userId == UserHandle.USER_NULL) { + return; + } + List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId); + if (profiles != null) { + for (int i = 0; i < profiles.size(); i++) { + UserInfo user = profiles.get(i); + refreshWidgetMaskedState(user.id); + } + } + } + + /** + * Mask/unmask widgets in the given profile, depending on the quiet state of the profile. + */ + private void refreshWidgetMaskedState(int profileId) { + final long identity = Binder.clearCallingIdentity(); + try { + UserInfo user = mUserManager.getUserInfo(profileId); + if (!user.isManagedProfile()) { + return; + } + boolean shouldMask = user.isQuietModeEnabled(); + final int iconSize = (int) mContext.getResources().getDimension( + android.R.dimen.app_icon_size); + synchronized (mLock) { + final int N = mProviders.size(); + for (int i = 0; i < N; i++) { + Provider provider = mProviders.get(i); + int providerUserId = provider.getUserId(); + if (providerUserId == profileId) { + final int widgetCount = provider.widgets.size(); + for (int j = 0; j < widgetCount; j++) { + Widget widget = provider.widgets.get(j); + if (shouldMask) { + widget.replaceWithMaskedViewsLocked(mContext, iconSize); + } else { + widget.clearMaskedViewsLocked(); + } + scheduleNotifyUpdateAppWidgetLocked(widget, + widget.getEffectiveViewsLocked()); + } + } + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private void resolveHostUidLocked(String pkg, int uid) { final int N = mHosts.size(); for (int i = 0; i < N; i++) { @@ -516,7 +590,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku for (int i = 0; i < N; i++) { Widget widget = instances.get(i); updatedIds[i] = widget.appWidgetId; - updatedViews.add(cloneIfLocalBinder(widget.views)); + updatedViews.add(cloneIfLocalBinder(widget.getEffectiveViewsLocked())); } return updatedIds; @@ -1128,7 +1202,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku Binder.getCallingUid(), callingPackage); if (widget != null) { - return cloneIfLocalBinder(widget.views); + return cloneIfLocalBinder(widget.getEffectiveViewsLocked()); } return null; @@ -1554,8 +1628,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // For a full update we replace the RemoteViews completely. widget.views = views; } - - scheduleNotifyUpdateAppWidgetLocked(widget, views); + scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked()); } } @@ -3536,6 +3609,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku int restoredId; // tracking & remapping any restored state Provider provider; RemoteViews views; + RemoteViews maskedViews; Bundle options; Host host; @@ -3543,6 +3617,34 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku public String toString() { return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}'; } + + public void replaceWithMaskedViewsLocked(Context context, int iconSize) { + if (maskedViews != null) { + return; + } + maskedViews = new RemoteViews(context.getPackageName(), R.layout.work_widget_mask_view); + try { + Drawable icon = context.getPackageManager().getApplicationIcon( + provider.info.provider.getPackageName()); + final int width = iconSize; + final int height = iconSize; + Bitmap iconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(iconBitmap); + icon.setBounds(0, 0, width, height); + icon.draw(canvas); + maskedViews.setImageViewBitmap(R.id.work_widget_app_icon, iconBitmap); + } catch (NameNotFoundException e) { + Slog.e(TAG, "Fail to get application icon", e); + } + } + + public void clearMaskedViewsLocked() { + maskedViews = null; + } + + public RemoteViews getEffectiveViewsLocked() { + return maskedViews != null ? maskedViews : views; + } } /** |