diff --git a/packages/SystemUI/res/drawable/ic_mic_blocked.xml b/packages/SystemUI/res/drawable/ic_mic_blocked.xml
new file mode 100644
index 0000000000000..0ce7a581a7e5d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_mic_blocked.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 86817f7bd1c46..93d2f751e7542 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e12342c9d42bb..ac2e342b3c34a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -823,6 +823,8 @@
Location Off
Block Camera
+
+ Mute Microphone
Media device
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 9f229b13f383f..902de23a9e2a4 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -52,7 +52,7 @@
+ android:title="@string/quick_settings_camera_label" />
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 69ddd2513a951..9b3775e72f9a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -40,6 +40,7 @@ import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.InternetTile;
import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.MicrophoneToggleTile;
import com.android.systemui.qs.tiles.NfcTile;
import com.android.systemui.qs.tiles.NightDisplayTile;
import com.android.systemui.qs.tiles.ReduceBrightColorsTile;
@@ -85,6 +86,7 @@ public class QSFactoryImpl implements QSFactory {
private final Provider mScreenRecordTileProvider;
private final Provider mReduceBrightColorsTileProvider;
private final Provider mCameraToggleTileProvider;
+ private final Provider mMicrophoneToggleTileProvider;
private final Lazy mQsHostLazy;
private final Provider mCustomTileBuilderProvider;
@@ -118,7 +120,8 @@ public class QSFactoryImpl implements QSFactory {
Provider uiModeNightTileProvider,
Provider screenRecordTileProvider,
Provider reduceBrightColorsTileProvider,
- Provider cameraToggleTileProvider) {
+ Provider cameraToggleTileProvider,
+ Provider microphoneToggleTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -147,6 +150,7 @@ public class QSFactoryImpl implements QSFactory {
mScreenRecordTileProvider = screenRecordTileProvider;
mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider;
mCameraToggleTileProvider = cameraToggleTileProvider;
+ mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -204,6 +208,8 @@ public class QSFactoryImpl implements QSFactory {
return mReduceBrightColorsTileProvider.get();
case "cameratoggle":
return mCameraToggleTileProvider.get();
+ case "mictoggle":
+ return mMicrophoneToggleTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 093163070dd50..98740a20a7c0c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -16,33 +16,31 @@
package com.android.systemui.qs.tiles;
+import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import android.content.Intent;
+import android.annotation.StringRes;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
-import android.service.quicksettings.Tile;
-import android.widget.Switch;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.CameraToggleController;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import javax.inject.Inject;
-public class CameraToggleTile extends QSTileImpl implements
- CameraToggleController.Callback {
-
- private CameraToggleController mCameraToggleController;
+public class CameraToggleTile extends SensorPrivacyToggleTile {
@Inject
protected CameraToggleTile(QSHost host,
@@ -52,11 +50,9 @@ public class CameraToggleTile extends QSTileImpl implements
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- CameraToggleController cameraToggleController) {
+ IndividualSensorPrivacyController sensorPrivacyController) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
- mCameraToggleController = cameraToggleController;
- mCameraToggleController.observe(getLifecycle(), this);
+ activityStarter, qsLogger, sensorPrivacyController);
}
@Override
@@ -68,46 +64,17 @@ public class CameraToggleTile extends QSTileImpl implements
}
@Override
- public BooleanState newTileState() {
- return new BooleanState();
+ public @DrawableRes int getIconRes() {
+ return R.drawable.ic_camera_blocked;
}
@Override
- protected void handleClick() {
- mCameraToggleController.setCameraBlocked(!mCameraToggleController.isCameraBlocked());
- }
-
- @Override
- protected void handleUpdateState(BooleanState state, Object arg) {
- boolean isBlocked = arg == null ? mCameraToggleController.setCameraBlocked()
- : (boolean) arg;
-
- state.icon = ResourceIcon.get(R.drawable.ic_camera_blocked);
- state.state = isBlocked ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
- state.value = isBlocked;
- state.label = getTileLabel();
- state.handlesLongClick = false;
- state.contentDescription = state.label;
- state.expandedAccessibilityClassName = Switch.class.getName();
- }
-
- @Override
- public int getMetricsCategory() {
- return 0;
- }
-
- @Override
- public Intent getLongClickIntent() {
- return null;
- }
-
- @Override
- public CharSequence getTileLabel() {
+ public @NonNull CharSequence getTileLabel() {
return mContext.getString(R.string.quick_settings_camera_label);
}
@Override
- public void onCameraBlockedChanged(boolean enable) {
- refreshState(enable);
+ public int getSensorId() {
+ return CAMERA;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
new file mode 100644
index 0000000000000..8cc0d7b4e8b8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+
+import static com.android.systemui.DejankUtils.whitelistIpcs;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+
+import javax.inject.Inject;
+
+public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
+
+ @Inject
+ protected MicrophoneToggleTile(QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ IndividualSensorPrivacyController sensorPrivacyController) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger, sensorPrivacyController);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ "mic_toggle_enabled",
+ false));
+ }
+
+ @Override
+ public @DrawableRes int getIconRes() {
+ return R.drawable.ic_mic_blocked;
+ }
+
+ @Override
+ public @NonNull CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_mic_label);
+ }
+
+ @Override
+ public int getSensorId() {
+ return MICROPHONE;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
new file mode 100644
index 0000000000000..12205d6483a8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.os.Handler;
+import android.os.Looper;
+import android.service.quicksettings.Tile;
+import android.widget.Switch;
+
+import androidx.annotation.DrawableRes;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+
+/**
+ * Superclass to toggle individual sensor privacy via quick settings tiles
+ */
+public abstract class SensorPrivacyToggleTile extends QSTileImpl implements
+ IndividualSensorPrivacyController.Callback {
+
+ private IndividualSensorPrivacyController mSensorPrivacyController;
+
+ /**
+ * @return Id of the sensor that will be toggled
+ */
+ public abstract @IndividualSensor int getSensorId();
+
+ /**
+ * @return icon for the QS tile
+ */
+ public abstract @DrawableRes int getIconRes();
+
+ protected SensorPrivacyToggleTile(QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ IndividualSensorPrivacyController sensorPrivacyController) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
+ mSensorPrivacyController = sensorPrivacyController;
+ mSensorPrivacyController.observe(getLifecycle(), this);
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ protected void handleClick() {
+ mSensorPrivacyController.setSensorBlocked(getSensorId(),
+ !mSensorPrivacyController.isSensorBlocked(getSensorId()));
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ boolean isBlocked = arg == null ? mSensorPrivacyController.isSensorBlocked(getSensorId())
+ : (boolean) arg;
+
+ state.icon = ResourceIcon.get(getIconRes());
+ state.state = isBlocked ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.value = isBlocked;
+ state.label = getTileLabel();
+ state.handlesLongClick = false;
+ state.contentDescription = state.label;
+ state.expandedAccessibilityClassName = Switch.class.getName();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ public void onSensorBlockedChanged(int sensor, boolean blocked) {
+ if (sensor == getSensorId()) {
+ refreshState(blocked);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index b9de21c1fb827..22e6b072f6a47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -16,14 +16,16 @@
package com.android.systemui.statusbar.policy;
-public interface CameraToggleController extends
- CallbackController {
+import android.hardware.SensorPrivacyManager.IndividualSensor;
- boolean isCameraBlocked();
+public interface IndividualSensorPrivacyController extends
+ CallbackController {
- void setCameraBlocked(boolean blocked);
+ boolean isSensorBlocked(@IndividualSensor int sensor);
+
+ void setSensorBlocked(@IndividualSensor int sensor, boolean blocked);
interface Callback {
- void onCameraBlockedChanged(boolean blocked);
+ void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
similarity index 55%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 3fb1ac56226e9..a7eb5944200f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -17,10 +17,13 @@
package com.android.systemui.statusbar.policy;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.IndividualSensor;
import android.util.ArraySet;
+import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
@@ -28,30 +31,36 @@ import java.util.Set;
import javax.inject.Inject;
-public class CameraToggleControllerImpl implements CameraToggleController {
+public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController {
+
+ private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE};
private final @NonNull Context mContext;
private final @NonNull SensorPrivacyManager mSensorPrivacyManager;
- private boolean mState;
- private Set mCallbacks = new ArraySet<>();
+ private final SparseBooleanArray mState = new SparseBooleanArray();
+ private final Set mCallbacks = new ArraySet<>();
@Inject
- public CameraToggleControllerImpl(@NonNull Context context) {
+ public IndividualSensorPrivacyControllerImpl(@NonNull Context context) {
mContext = context;
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
- mSensorPrivacyManager.addSensorPrivacyListener(CAMERA, this::onCameraPrivacyChanged);
- mState = mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(CAMERA);
+ for (int sensor : SENSORS) {
+ mSensorPrivacyManager.addSensorPrivacyListener(sensor,
+ (enabled) -> onSensorPrivacyChanged(sensor, enabled));
+
+ mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor));
+ }
}
@Override
- public boolean isCameraBlocked() {
- return mState;
+ public boolean isSensorBlocked(@IndividualSensor int sensor) {
+ return mState.get(sensor, false);
}
@Override
- public void setCameraBlocked(boolean blocked) {
- mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(CAMERA, blocked);
+ public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) {
+ mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked);
}
@Override
@@ -64,10 +73,11 @@ public class CameraToggleControllerImpl implements CameraToggleController {
mCallbacks.remove(listener);
}
- private void onCameraPrivacyChanged(boolean state) {
- mState = state;
+ private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) {
+ mState.put(sensor, blocked);
+
for (Callback callback : mCallbacks) {
- callback.onCameraBlockedChanged(mState);
+ callback.onSensorBlockedChanged(sensor, blocked);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index a6278d689e0fe..366ae5491cd49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -24,8 +24,6 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.AccessPointControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
-import com.android.systemui.statusbar.policy.CameraToggleController;
-import com.android.systemui.statusbar.policy.CameraToggleControllerImpl;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.ExtensionController;
@@ -34,6 +32,8 @@ import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
import com.android.systemui.statusbar.policy.LocationController;
@@ -122,7 +122,8 @@ public interface StatusBarPolicyModule {
/** */
@Binds
- CameraToggleController provideCameraToggleController(CameraToggleControllerImpl controllerImpl);
+ IndividualSensorPrivacyController provideIndividualSensorPrivacyController(
+ IndividualSensorPrivacyControllerImpl controllerImpl);
/** */
@SysUISingleton