diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
new file mode 100644
index 0000000000000..1a356769d334d
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e2ba615e84e6f..2be89c1dff634 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1286,6 +1286,9 @@
Network may be monitored
+
+ This device is managed by your parent
+
Your organization owns this device and may monitor network traffic
@@ -1359,6 +1362,9 @@
View Policies
+
+ View controls
+
This device belongs to %1$s.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.
@@ -1428,6 +1434,10 @@
Your work profile is managed by %1$s.\n\nYour admin is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your admin.\n\nYou\'re also connected to a VPN, which can monitor your network activity.
+
+ This device is managed by your parent. Your parent can see and manage information such as the apps you use, your location, and your screen time.
+
+
VPN
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index c90182b15da62..270fcbffbd712 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -16,11 +16,13 @@
package com.android.systemui.qs;
import android.app.AlertDialog;
+import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyEventLogger;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -39,6 +41,8 @@ import android.view.Window;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
@@ -156,15 +160,16 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
mSecurityController.getWorkProfileOrganizationName();
final boolean isProfileOwnerOfOrganizationOwnedDevice =
mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
+ final boolean isParentalControlsEnabled = mSecurityController.isParentalControlsEnabled();
// Update visibility of footer
mIsVisible = (isDeviceManaged && !isDemoDevice) || hasCACerts || hasCACertsInWorkProfile
|| vpnName != null || vpnNameWorkProfile != null
- || isProfileOwnerOfOrganizationOwnedDevice;
+ || isProfileOwnerOfOrganizationOwnedDevice || isParentalControlsEnabled;
// Update the string
mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
vpnNameWorkProfile, organizationName, workProfileOrganizationName,
- isProfileOwnerOfOrganizationOwnedDevice);
+ isProfileOwnerOfOrganizationOwnedDevice, isParentalControlsEnabled);
// Update the icon
int footerIconId = R.drawable.ic_info_outline;
if (vpnName != null || vpnNameWorkProfile != null) {
@@ -185,7 +190,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
CharSequence workProfileOrganizationName,
- boolean isProfileOwnerOfOrganizationOwnedDevice) {
+ boolean isProfileOwnerOfOrganizationOwnedDevice, boolean isParentalControlsEnabled) {
+ if (isParentalControlsEnabled) {
+ return mContext.getString(R.string.quick_settings_disclosure_parental_controls);
+ }
if (isDeviceManaged || DEBUG_FORCE_VISIBLE) {
if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
if (organizationName == null) {
@@ -268,6 +276,27 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
}
private void createDialog() {
+ mDialog = new SystemUIDialog(mContext);
+ mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
+ mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);
+
+ mDialog.setView(createDialogView());
+
+ mDialog.show();
+ mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ @VisibleForTesting
+ View createDialogView() {
+ if (mSecurityController.isParentalControlsEnabled()) {
+ return createParentalControlsDialogView();
+ }
+ return createOrganizationDialogView();
+ }
+
+ private View createOrganizationDialogView() {
final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
boolean isProfileOwnerOfOrganizationOwnedDevice =
mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
@@ -282,13 +311,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
final String vpnName = mSecurityController.getPrimaryVpnName();
final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
- mDialog = new SystemUIDialog(mContext);
- mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+
View dialogView = LayoutInflater.from(
new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_Dialog))
.inflate(R.layout.quick_settings_footer_dialog, null, false);
- mDialog.setView(dialogView);
- mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
// device management section
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
@@ -353,9 +379,26 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
vpnMessage != null,
dialogView);
- mDialog.show();
- mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
+ return dialogView;
+ }
+
+ private View createParentalControlsDialogView() {
+ View dialogView = LayoutInflater.from(
+ new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_Dialog))
+ .inflate(R.layout.quick_settings_footer_dialog_parental_controls, null, false);
+
+ DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo();
+ Drawable icon = mSecurityController.getIcon(info);
+ if (icon != null) {
+ ImageView imageView = (ImageView) dialogView.findViewById(R.id.parental_controls_icon);
+ imageView.setImageDrawable(icon);
+ }
+
+ TextView parentalControlsTitle =
+ (TextView) dialogView.findViewById(R.id.parental_controls_title);
+ parentalControlsTitle.setText(mSecurityController.getLabel(info));
+
+ return dialogView;
}
protected void configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts,
@@ -394,6 +437,13 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return mContext.getString(R.string.ok);
}
+ private String getNegativeButton() {
+ if (mSecurityController.isParentalControlsEnabled()) {
+ return mContext.getString(R.string.monitoring_button_view_controls);
+ }
+ return null;
+ }
+
protected CharSequence getManagementMessage(boolean isDeviceManaged,
CharSequence organizationName, boolean isProfileOwnerOfOrganizationOwnedDevice,
CharSequence workProfileOrganizationName) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 79d264ca45776..e8331a176134f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.policy;
+import android.app.admin.DeviceAdminInfo;
+import android.graphics.drawable.Drawable;
+
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
@@ -40,6 +43,15 @@ public interface SecurityController extends CallbackController {
Pair idWithCert = null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index fd1866b9ebc30..c82aee48ab913 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.qs;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -66,6 +67,7 @@ public class QSSecurityFooterTest extends SysuiTestCase {
private final String DEVICE_OWNER_PACKAGE = "TestDPC";
private final String VPN_PACKAGE = "TestVPN";
private final String VPN_PACKAGE_2 = "TestVPN 2";
+ private static final String PARENTAL_CONTROLS_LABEL = "Parental Control App";
private ViewGroup mRootView;
private TextView mFooterText;
@@ -525,6 +527,27 @@ public class QSSecurityFooterTest extends SysuiTestCase {
verify(mockHost, never()).collapsePanels();
}
+ @Test
+ public void testParentalControls() {
+ when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
+ mFooter.refreshState();
+
+ TestableLooper.get(this).processAllMessages();
+
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testParentalControlsDialog() {
+ when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
+ when(mSecurityController.getLabel(any())).thenReturn(PARENTAL_CONTROLS_LABEL);
+
+ View view = mFooter.createDialogView();
+ TextView textView = (TextView) view.findViewById(R.id.parental_controls_title);
+ assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText());
+ }
+
private CharSequence addLink(CharSequence description) {
final SpannableStringBuilder message = new SpannableStringBuilder();
message.append(description);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index e8911a2f5d4e0..c0722a459929e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -14,6 +14,8 @@
package com.android.systemui.utils.leaks;
+import android.app.admin.DeviceAdminInfo;
+import android.graphics.drawable.Drawable;
import android.testing.LeakCheck;
import com.android.systemui.statusbar.policy.SecurityController;
@@ -109,4 +111,24 @@ public class FakeSecurityController extends BaseLeakChecker