Merge changes from topic "fix-mic-disclosure"
* changes: Refresh mic disclosure indicator Add Setting kill switch for mic disclosure
This commit is contained in:
committed by
Android (Google) Code Review
commit
027523362d
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid
|
||||
android:color="@color/tv_audio_recording_indicator_pulse" />
|
||||
|
||||
</shape>
|
||||
@@ -16,9 +16,10 @@
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid
|
||||
android:color="@color/tv_audio_recording_indicator_background" />
|
||||
<corners android:radius="20dp"/>
|
||||
<solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
|
||||
<stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
|
||||
|
||||
</shape>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@android:color/white" />
|
||||
|
||||
</shape>
|
||||
@@ -19,7 +19,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="32dp">
|
||||
android:padding="12dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
@@ -32,45 +32,25 @@
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="47dp">
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/icon_container_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/tv_rect_dark_left_rounded"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/icon_mic"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp">
|
||||
|
||||
<View
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/tv_circle_dark"/>
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/tv_rect_shadow_rounded">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pulsating_circle"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/tv_circle_white_translucent"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/tv_ring_white"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_width="13dp"
|
||||
android:layout_height="13dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/tv_ic_mic_white"/>
|
||||
</FrameLayout>
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
|
||||
<!-- Background color for audio recording indicator (G800) -->
|
||||
<color name="tv_audio_recording_indicator_background">#FF3C4043</color>
|
||||
<color name="tv_audio_recording_indicator_pulse">#4DFFFFFF</color>
|
||||
<color name="tv_audio_recording_indicator_icon_background">#CC000000</color>
|
||||
<color name="tv_audio_recording_indicator_stroke">#33FFFFFF</color>
|
||||
|
||||
<color name="red">#FFCC0000</color>
|
||||
</resources>
|
||||
|
||||
@@ -40,5 +40,9 @@ abstract class AudioActivityObserver {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
abstract void start();
|
||||
|
||||
abstract void stop();
|
||||
|
||||
abstract Set<String> getActivePackages();
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.UiThread;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
@@ -65,11 +65,13 @@ public class AudioRecordingDisclosureBar implements
|
||||
// CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest
|
||||
private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
|
||||
|
||||
private static final String ENABLE_FLAG = "sysui_mic_disclosure_enable";
|
||||
private static final String EXEMPT_PACKAGES_LIST = "sysui_mic_disclosure_exempt";
|
||||
private static final String FORCED_PACKAGES_LIST = "sysui_mic_disclosure_forced";
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = {"STATE_"}, value = {
|
||||
STATE_STOPPED,
|
||||
STATE_NOT_SHOWN,
|
||||
STATE_APPEARING,
|
||||
STATE_SHOWN,
|
||||
@@ -80,6 +82,7 @@ public class AudioRecordingDisclosureBar implements
|
||||
})
|
||||
public @interface State {}
|
||||
|
||||
private static final int STATE_STOPPED = -1;
|
||||
private static final int STATE_NOT_SHOWN = 0;
|
||||
private static final int STATE_APPEARING = 1;
|
||||
private static final int STATE_SHOWN = 2;
|
||||
@@ -90,10 +93,9 @@ public class AudioRecordingDisclosureBar implements
|
||||
|
||||
private static final int ANIMATION_DURATION = 600;
|
||||
private static final int MAXIMIZED_DURATION = 3000;
|
||||
private static final int PULSE_BIT_DURATION = 1000;
|
||||
private static final float PULSE_SCALE = 1.25f;
|
||||
|
||||
private final Context mContext;
|
||||
private boolean mIsEnabledInSettings;
|
||||
|
||||
private View mIndicatorView;
|
||||
private View mIconTextsContainer;
|
||||
@@ -104,13 +106,13 @@ public class AudioRecordingDisclosureBar implements
|
||||
private TextView mTextView;
|
||||
private boolean mIsLtr;
|
||||
|
||||
@State private int mState = STATE_NOT_SHOWN;
|
||||
@State private int mState = STATE_STOPPED;
|
||||
|
||||
/**
|
||||
* Array of the observers that monitor different aspects of the system, such as AppOps and
|
||||
* microphone foreground services
|
||||
*/
|
||||
private final AudioActivityObserver[] mAudioActivityObservers;
|
||||
private AudioActivityObserver[] mAudioActivityObservers;
|
||||
/**
|
||||
* Whether the indicator should expand and show the recording application's label.
|
||||
* If disabled ({@code false}) the "minimized" ({@link #STATE_MINIMIZED}) indicator would appear
|
||||
@@ -144,6 +146,7 @@ public class AudioRecordingDisclosureBar implements
|
||||
public AudioRecordingDisclosureBar(Context context) {
|
||||
mContext = context;
|
||||
|
||||
// Loading configs
|
||||
mRevealRecordingPackages = mContext.getResources().getBoolean(
|
||||
R.bool.audio_recording_disclosure_reveal_packages);
|
||||
mExemptPackages = new ArraySet<>(
|
||||
@@ -152,10 +155,52 @@ public class AudioRecordingDisclosureBar implements
|
||||
mExemptPackages.addAll(Arrays.asList(getGlobalStringArray(EXEMPT_PACKAGES_LIST)));
|
||||
mExemptPackages.removeAll(Arrays.asList(getGlobalStringArray(FORCED_PACKAGES_LIST)));
|
||||
|
||||
mAudioActivityObservers = new AudioActivityObserver[]{
|
||||
new RecordAudioAppOpObserver(mContext, this),
|
||||
new MicrophoneForegroundServicesObserver(mContext, this),
|
||||
};
|
||||
// Check setting, and start if enabled
|
||||
mIsEnabledInSettings = checkIfEnabledInSettings();
|
||||
registerSettingsObserver();
|
||||
if (mIsEnabledInSettings) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void start() {
|
||||
if (mState != STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
mState = STATE_NOT_SHOWN;
|
||||
|
||||
if (mAudioActivityObservers == null) {
|
||||
mAudioActivityObservers = new AudioActivityObserver[]{
|
||||
new RecordAudioAppOpObserver(mContext, this),
|
||||
new MicrophoneForegroundServicesObserver(mContext, this),
|
||||
};
|
||||
}
|
||||
|
||||
for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) {
|
||||
mAudioActivityObservers[i].start();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void stop() {
|
||||
if (mState == STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
mState = STATE_STOPPED;
|
||||
|
||||
for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) {
|
||||
mAudioActivityObservers[i].stop();
|
||||
}
|
||||
|
||||
// Remove the view if shown.
|
||||
if (mState != STATE_NOT_SHOWN) {
|
||||
removeIndicatorView();
|
||||
}
|
||||
|
||||
// Clean up the state.
|
||||
mSessionNotifiedPackages.clear();
|
||||
mPendingNotificationPackages.clear();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -213,7 +258,6 @@ public class AudioRecordingDisclosureBar implements
|
||||
|
||||
@UiThread
|
||||
private void hideIndicatorIfNeeded() {
|
||||
if (DEBUG) Log.d(TAG, "hideIndicatorIfNeeded");
|
||||
// If not MINIMIZED, will check whether the indicator should be hidden when the indicator
|
||||
// comes to the STATE_MINIMIZED eventually.
|
||||
if (mState != STATE_MINIMIZED) return;
|
||||
@@ -222,7 +266,6 @@ public class AudioRecordingDisclosureBar implements
|
||||
for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) {
|
||||
for (String activePackage : mAudioActivityObservers[index].getActivePackages()) {
|
||||
if (mExemptPackages.contains(activePackage)) continue;
|
||||
if (DEBUG) Log.d(TAG, " - there are still ongoing activities");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -235,7 +278,7 @@ public class AudioRecordingDisclosureBar implements
|
||||
@UiThread
|
||||
private void show(String packageName) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Showing indicator for " + packageName);
|
||||
Log.d(TAG, "Showing indicator");
|
||||
}
|
||||
|
||||
mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
|
||||
@@ -286,6 +329,10 @@ public class AudioRecordingDisclosureBar implements
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (mState == STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the observer
|
||||
mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
|
||||
this);
|
||||
@@ -306,13 +353,16 @@ public class AudioRecordingDisclosureBar implements
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation,
|
||||
boolean isReverse) {
|
||||
if (mState == STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Indicator is INVISIBLE at the moment, change it.
|
||||
mIndicatorView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
startPulsatingAnimation();
|
||||
if (mRevealRecordingPackages) {
|
||||
onExpanded();
|
||||
} else {
|
||||
@@ -345,9 +395,6 @@ public class AudioRecordingDisclosureBar implements
|
||||
assertRevealingRecordingPackages();
|
||||
|
||||
final String label = getApplicationLabel(packageName);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Expanding for " + packageName + " (" + label + ")...");
|
||||
}
|
||||
mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
|
||||
|
||||
final AnimatorSet set = new AnimatorSet();
|
||||
@@ -373,7 +420,6 @@ public class AudioRecordingDisclosureBar implements
|
||||
private void minimize() {
|
||||
assertRevealingRecordingPackages();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Minimizing...");
|
||||
final int targetOffset = (mIsLtr ? 1 : -1) * mTextsContainers.getWidth();
|
||||
final AnimatorSet set = new AnimatorSet();
|
||||
set.playTogether(
|
||||
@@ -396,7 +442,9 @@ public class AudioRecordingDisclosureBar implements
|
||||
|
||||
@UiThread
|
||||
private void hide() {
|
||||
if (DEBUG) Log.d(TAG, "Hiding...");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Hide indicator");
|
||||
}
|
||||
final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth()
|
||||
- (int) mIconTextsContainer.getTranslationX());
|
||||
final AnimatorSet set = new AnimatorSet();
|
||||
@@ -418,9 +466,12 @@ public class AudioRecordingDisclosureBar implements
|
||||
|
||||
@UiThread
|
||||
private void onExpanded() {
|
||||
if (mState == STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
assertRevealingRecordingPackages();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Expanded");
|
||||
mState = STATE_SHOWN;
|
||||
|
||||
mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION);
|
||||
@@ -428,7 +479,10 @@ public class AudioRecordingDisclosureBar implements
|
||||
|
||||
@UiThread
|
||||
private void onMinimized() {
|
||||
if (DEBUG) Log.d(TAG, "Minimized");
|
||||
if (mState == STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
mState = STATE_MINIMIZED;
|
||||
|
||||
if (mRevealRecordingPackages) {
|
||||
@@ -443,8 +497,21 @@ public class AudioRecordingDisclosureBar implements
|
||||
|
||||
@UiThread
|
||||
private void onHidden() {
|
||||
if (DEBUG) Log.d(TAG, "Hidden");
|
||||
if (mState == STATE_STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeIndicatorView();
|
||||
mState = STATE_NOT_SHOWN;
|
||||
|
||||
// Check if anybody started recording while we were in STATE_DISAPPEARING
|
||||
if (!mPendingNotificationPackages.isEmpty()) {
|
||||
// There is a new application that started recording, tell the user about it.
|
||||
show(mPendingNotificationPackages.poll());
|
||||
}
|
||||
}
|
||||
|
||||
private void removeIndicatorView() {
|
||||
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
|
||||
Context.WINDOW_SERVICE);
|
||||
windowManager.removeView(mIndicatorView);
|
||||
@@ -456,28 +523,6 @@ public class AudioRecordingDisclosureBar implements
|
||||
mTextsContainers = null;
|
||||
mTextView = null;
|
||||
mBgEnd = null;
|
||||
|
||||
mState = STATE_NOT_SHOWN;
|
||||
|
||||
// Check if anybody started recording while we were in STATE_DISAPPEARING
|
||||
if (!mPendingNotificationPackages.isEmpty()) {
|
||||
// There is a new application that started recording, tell the user about it.
|
||||
show(mPendingNotificationPackages.poll());
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void startPulsatingAnimation() {
|
||||
final View pulsatingView = mIconTextsContainer.findViewById(R.id.pulsating_circle);
|
||||
final ObjectAnimator animator =
|
||||
ObjectAnimator.ofPropertyValuesHolder(
|
||||
pulsatingView,
|
||||
PropertyValuesHolder.ofFloat(View.SCALE_X, PULSE_SCALE),
|
||||
PropertyValuesHolder.ofFloat(View.SCALE_Y, PULSE_SCALE));
|
||||
animator.setDuration(PULSE_BIT_DURATION);
|
||||
animator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
animator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private String[] getGlobalStringArray(String setting) {
|
||||
@@ -504,4 +549,33 @@ public class AudioRecordingDisclosureBar implements
|
||||
DEBUG ? new RuntimeException("Should not be called") : null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkIfEnabledInSettings() {
|
||||
// 0 = disabled, everything else = enabled. Enabled by default.
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
ENABLE_FLAG, 1) != 0;
|
||||
}
|
||||
|
||||
private void registerSettingsObserver() {
|
||||
final ContentObserver contentObserver = new ContentObserver(
|
||||
mContext.getMainThreadHandler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
if (mIsEnabledInSettings == checkIfEnabledInSettings()) {
|
||||
// Nothing changed as we know it - ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
// Things changed: flip the flag.
|
||||
mIsEnabledInSettings = !mIsEnabledInSettings;
|
||||
if (mIsEnabledInSettings) {
|
||||
start();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(ENABLE_FLAG), false, contentObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -41,9 +40,8 @@ import java.util.Set;
|
||||
*/
|
||||
class MicrophoneForegroundServicesObserver extends AudioActivityObserver {
|
||||
private static final String TAG = "MicrophoneForegroundServicesObserver";
|
||||
private static final boolean ENABLED = true;
|
||||
|
||||
private final IActivityManager mActivityManager;
|
||||
private IActivityManager mActivityManager;
|
||||
/**
|
||||
* A dictionary that maps PIDs to the package names. We only keep track of the PIDs that are
|
||||
* "active" (those that are running FGS with FOREGROUND_SERVICE_TYPE_MICROPHONE flag).
|
||||
@@ -60,7 +58,10 @@ class MicrophoneForegroundServicesObserver extends AudioActivityObserver {
|
||||
MicrophoneForegroundServicesObserver(Context context,
|
||||
OnAudioActivityStateChangeListener listener) {
|
||||
super(context, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
void start() {
|
||||
mActivityManager = ActivityManager.getService();
|
||||
try {
|
||||
mActivityManager.registerProcessObserver(mProcessObserver);
|
||||
@@ -69,9 +70,20 @@ class MicrophoneForegroundServicesObserver extends AudioActivityObserver {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void stop() {
|
||||
try {
|
||||
mActivityManager.unregisterProcessObserver(mProcessObserver);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't unregister process observer", e);
|
||||
}
|
||||
mActivityManager = null;
|
||||
mPackageToProcessCount.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<String> getActivePackages() {
|
||||
return ENABLED ? mPackageToProcessCount.keySet() : Collections.emptySet();
|
||||
return mPackageToProcessCount.keySet();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -141,13 +153,12 @@ class MicrophoneForegroundServicesObserver extends AudioActivityObserver {
|
||||
|
||||
@UiThread
|
||||
private void notifyPackageStateChanged(String packageName, boolean active) {
|
||||
if (active) {
|
||||
if (DEBUG) Log.d(TAG, "New microphone fgs detected, package=" + packageName);
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "Microphone fgs is gone, package=" + packageName);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, (active ? "New microphone fgs detected" : "Microphone fgs is gone")
|
||||
+ ", package=" + packageName);
|
||||
}
|
||||
|
||||
if (ENABLED) mListener.onAudioActivityStateChange(active, packageName);
|
||||
mListener.onAudioActivityStateChange(active, packageName);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -42,14 +42,33 @@ class RecordAudioAppOpObserver extends AudioActivityObserver implements
|
||||
|
||||
RecordAudioAppOpObserver(Context context, OnAudioActivityStateChangeListener listener) {
|
||||
super(context, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
void start() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Start");
|
||||
}
|
||||
|
||||
// Register AppOpsManager callback
|
||||
final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(
|
||||
Context.APP_OPS_SERVICE);
|
||||
appOpsManager.startWatchingActive(
|
||||
new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
|
||||
mContext.getMainExecutor(),
|
||||
this);
|
||||
mContext.getSystemService(AppOpsManager.class)
|
||||
.startWatchingActive(
|
||||
new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
|
||||
mContext.getMainExecutor(),
|
||||
this);
|
||||
}
|
||||
|
||||
@Override
|
||||
void stop() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Stop");
|
||||
}
|
||||
|
||||
// Unregister AppOpsManager callback
|
||||
mContext.getSystemService(AppOpsManager.class).stopWatchingActive(this);
|
||||
|
||||
// Clean up state
|
||||
mActiveAudioRecordingPackages.clear();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
||||
Reference in New Issue
Block a user