1f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock/*
2f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * Copyright (C) 2015 The Android Open Source Project
3f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *
4f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * Licensed under the Apache License, Version 2.0 (the "License");
5f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * you may not use this file except in compliance with the License.
6f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * You may obtain a copy of the License at
7f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *
8f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *      http://www.apache.org/licenses/LICENSE-2.0
9f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *
10f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * Unless required by applicable law or agreed to in writing, software
11f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * distributed under the License is distributed on an "AS IS" BASIS,
12f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * See the License for the specific language governing permissions and
14f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock * limitations under the License.
15f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock */
16f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
17f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockpackage com.android.systemui.volume;
18f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
19f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.app.NotificationManager;
20f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.BroadcastReceiver;
21f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.ComponentName;
22f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.Context;
23f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.Intent;
24f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.IntentFilter;
25f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.pm.ApplicationInfo;
26f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.pm.PackageManager;
27f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.content.pm.PackageManager.NameNotFoundException;
28f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.database.ContentObserver;
29f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.media.AudioManager;
30f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.media.AudioSystem;
31f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.media.IVolumeController;
32f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.media.VolumePolicy;
33f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.media.session.MediaController.PlaybackInfo;
34f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.media.session.MediaSession.Token;
35f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.net.Uri;
36f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.os.Handler;
37f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.os.HandlerThread;
38f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.os.Looper;
39f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.os.Message;
40f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.os.RemoteException;
41f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.os.Vibrator;
42f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.provider.Settings;
43f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.service.notification.Condition;
44b2278d65714c0dd0a6f94d1913db1ebc8bfc8b06John Spurlockimport android.service.notification.ZenModeConfig;
45f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.util.Log;
46f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport android.util.SparseArray;
47f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
48f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport com.android.systemui.R;
49cd863ad094f38ab6b2339bb7961a34ae11414412John Spurlockimport com.android.systemui.qs.tiles.DndTile;
50f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
51f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport java.io.FileDescriptor;
52f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport java.io.PrintWriter;
53f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport java.util.HashMap;
54f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport java.util.Map;
55f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockimport java.util.Objects;
56f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
57f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock/**
58f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *  Source of truth for all state / events related to the volume dialog.  No presentation.
59f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *
60f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *  All work done on a dedicated background worker thread & associated worker.
61f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *
62f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock *  Methods ending in "W" must be called on the worker thread.
63f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock */
64f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlockpublic class VolumeDialogController {
65f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private static final String TAG = Util.logTag(VolumeDialogController.class);
66f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
67f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private static final int DYNAMIC_STREAM_START_INDEX = 100;
68f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private static final int VIBRATE_HINT_DURATION = 50;
69f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
70f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private static final int[] STREAMS = {
71f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_ALARM,
72f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_BLUETOOTH_SCO,
73f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_DTMF,
74f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_MUSIC,
75f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_NOTIFICATION,
76f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_RING,
77f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_SYSTEM,
78f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_SYSTEM_ENFORCED,
79f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_TTS,
80f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        AudioSystem.STREAM_VOICE_CALL,
81f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    };
82f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
83f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final HandlerThread mWorkerThread;
84f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final W mWorker;
85f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final Context mContext;
86f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final AudioManager mAudio;
87f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final NotificationManager mNoMan;
88f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final ComponentName mComponent;
89f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final SettingObserver mObserver;
90f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final Receiver mReceiver = new Receiver();
91f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final MediaSessions mMediaSessions;
92f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final VC mVolumeController = new VC();
93f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final C mCallbacks = new C();
94f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final State mState = new State();
95f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final String[] mStreamTitles;
96f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
97f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final Vibrator mVibrator;
98f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final boolean mHasVibrator;
99f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
100f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean mEnabled;
101f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean mDestroyed;
102b02c744f05b0cca05b77fcbaad4a546ad86081ebJohn Spurlock    private VolumePolicy mVolumePolicy;
103d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock    private boolean mShowDndTile = true;
104f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
105f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public VolumeDialogController(Context context, ComponentName component) {
106f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mContext = context.getApplicationContext();
107e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        Events.writeEvent(mContext, Events.EVENT_COLLECTION_STARTED);
108f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mComponent = component;
109f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorkerThread = new HandlerThread(VolumeDialogController.class.getSimpleName());
110f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorkerThread.start();
111f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker = new W(mWorkerThread.getLooper());
112f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(),
113f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mMediaSessionsCallbacksW);
114f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
115f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
116f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mObserver = new SettingObserver(mWorker);
117f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mObserver.init();
118f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mReceiver.init();
119f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mStreamTitles = mContext.getResources().getStringArray(R.array.volume_stream_titles);
120f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
121f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
122f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
123f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
12476b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock    public AudioManager getAudioManager() {
12576b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock        return mAudio;
12676b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock    }
12776b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock
128d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock    public ZenModeConfig getZenModeConfig() {
129d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        return mNoMan.getZenModeConfig();
130d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock    }
131d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock
132f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void dismiss() {
133f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
134f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
135f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
136f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void register() {
137f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        try {
138f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mAudio.setVolumeController(mVolumeController);
139f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        } catch (SecurityException e) {
140f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            Log.w(TAG, "Unable to set the volume controller", e);
141f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            return;
142f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
143f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        setVolumePolicy(mVolumePolicy);
144f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        showDndTile(mShowDndTile);
145f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        try {
146f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mMediaSessions.init();
147f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        } catch (SecurityException e) {
148f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            Log.w(TAG, "No access to media sessions", e);
149f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
150f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
151f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
152f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void setVolumePolicy(VolumePolicy policy) {
153f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mVolumePolicy = policy;
154b02c744f05b0cca05b77fcbaad4a546ad86081ebJohn Spurlock        if (mVolumePolicy == null) return;
155f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        try {
156f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mAudio.setVolumePolicy(mVolumePolicy);
157f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        } catch (NoSuchMethodError e) {
158f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            Log.w(TAG, "No volume policy api");
159f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
160f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
161f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
162f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    protected MediaSessions createMediaSessions(Context context, Looper looper,
163f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            MediaSessions.Callbacks callbacks) {
164f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return new MediaSessions(context, looper, callbacks);
165f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
166f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
167f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void destroy() {
168f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (D.BUG) Log.d(TAG, "destroy");
169f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
170f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mDestroyed = true;
171e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        Events.writeEvent(mContext, Events.EVENT_COLLECTION_STOPPED);
172f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mMediaSessions.destroy();
173f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mObserver.destroy();
174f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mReceiver.destroy();
175f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorkerThread.quitSafely();
176f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
177f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
178f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
179f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.println(VolumeDialogController.class.getSimpleName() + " state:");
180f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.print("  mEnabled: "); pw.println(mEnabled);
181f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.print("  mDestroyed: "); pw.println(mDestroyed);
182f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.print("  mVolumePolicy: "); pw.println(mVolumePolicy);
183e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock        pw.print("  mState: "); pw.println(mState.toString(4));
184f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.print("  mShowDndTile: "); pw.println(mShowDndTile);
185f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.print("  mHasVibrator: "); pw.println(mHasVibrator);
186f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.print("  mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
187f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                .values());
188f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        pw.println();
189f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mMediaSessions.dump(pw);
190f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
191f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
192f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void addCallback(Callbacks callback, Handler handler) {
193f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mCallbacks.add(callback, handler);
194f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
195f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
196f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void removeCallback(Callbacks callback) {
197f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mCallbacks.remove(callback);
198f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
199f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
200f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void getState() {
201f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
202f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.sendEmptyMessage(W.GET_STATE);
203f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
204f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
205f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void notifyVisible(boolean visible) {
206f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
207f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
208f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
209f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
210f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void userActivity() {
211f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
212f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.removeMessages(W.USER_ACTIVITY);
213f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.sendEmptyMessage(W.USER_ACTIVITY);
214f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
215f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
216f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void setRingerMode(int value, boolean external) {
217f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
218f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
219f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
220f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
221f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void setZenMode(int value) {
222f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
223f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
224f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
225f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
226f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void setExitCondition(Condition condition) {
227f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
228f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
229f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
230f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
231f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void setStreamMute(int stream, boolean mute) {
232f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
233f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
234f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
235f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
236f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void setStreamVolume(int stream, int level) {
237f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
238f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
239f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
240f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
241f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void setActiveStream(int stream) {
242f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
243f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
244f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
245f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
246f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void vibrate() {
247f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mHasVibrator) {
248f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mVibrator.vibrate(VIBRATE_HINT_DURATION);
249f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
250f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
251f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
252f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public boolean hasVibrator() {
253f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return mHasVibrator;
254f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
255f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
256f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onNotifyVisibleW(boolean visible) {
257f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mDestroyed) return;
258f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
259f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (!visible) {
260f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (updateActiveStreamW(-1)) {
261f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onStateChanged(mState);
262f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
263f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
264f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
265f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
266f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    protected void onUserActivityW() {
267f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        // hook for subclasses
268f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
269f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
27076b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock    private void onShowSafetyWarningW(int flags) {
27176b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock        mCallbacks.onShowSafetyWarning(flags);
27276b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock    }
27376b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock
274f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean checkRoutedToBluetoothW(int stream) {
275f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        boolean changed = false;
276f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (stream == AudioManager.STREAM_MUSIC) {
277f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final boolean routedToBluetooth =
278f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
279f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                            (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
280f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                            AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
281f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                            AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0;
282f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
283f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
284f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return changed;
285f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
286f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
287f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onVolumeChangedW(int stream, int flags) {
288f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
289f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
290f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
291f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
292f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        boolean changed = false;
293f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (showUI) {
294f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            changed |= updateActiveStreamW(stream);
295f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
296e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        int lastAudibleStreamVolume = mAudio.getLastAudibleStreamVolume(stream);
297e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
298f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
299f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (changed) {
300f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbacks.onStateChanged(mState);
301f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
302f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (showUI) {
303f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
304f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
305f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (showVibrateHint) {
306f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbacks.onShowVibrateHint();
307f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
308f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (showSilentHint) {
309f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbacks.onShowSilentHint();
310f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
311f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (changed && fromKey) {
312e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren            Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
313f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
314f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
315f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
316f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateActiveStreamW(int activeStream) {
317f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (activeStream == mState.activeStream) return false;
318f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mState.activeStream = activeStream;
319e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        Events.writeEvent(mContext, Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
320f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
321f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
322f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
323f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mAudio.forceVolumeControlStream(s);
324f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
325f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
326f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
327f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private StreamState streamStateW(int stream) {
328f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        StreamState ss = mState.states.get(stream);
329f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (ss == null) {
330f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            ss = new StreamState();
331f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mState.states.put(stream, ss);
332f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
333f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return ss;
334f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
335f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
336f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onGetStateW() {
337f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        for (int stream : STREAMS) {
338f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            updateStreamLevelW(stream, mAudio.getLastAudibleStreamVolume(stream));
339f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            streamStateW(stream).levelMin = mAudio.getStreamMinVolume(stream);
340f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            streamStateW(stream).levelMax = mAudio.getStreamMaxVolume(stream);
341f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            updateStreamMuteW(stream, mAudio.isStreamMute(stream));
342f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final StreamState ss = streamStateW(stream);
343f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
344f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            ss.name = mStreamTitles[stream];
345f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            checkRoutedToBluetoothW(stream);
346f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
347f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        updateRingerModeExternalW(mAudio.getRingerMode());
348f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        updateZenModeW();
349f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
350d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        updateZenModeConfigW();
351f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mCallbacks.onStateChanged(mState);
352f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
353f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
354f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateStreamRoutedToBluetoothW(int stream, boolean routedToBluetooth) {
355f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final StreamState ss = streamStateW(stream);
356f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (ss.routedToBluetooth == routedToBluetooth) return false;
357f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        ss.routedToBluetooth = routedToBluetooth;
358f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (D.BUG) Log.d(TAG, "updateStreamRoutedToBluetoothW stream=" + stream
359f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                + " routedToBluetooth=" + routedToBluetooth);
360f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
361f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
362f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
363f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateStreamLevelW(int stream, int level) {
364f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final StreamState ss = streamStateW(stream);
365f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (ss.level == level) return false;
366f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        ss.level = level;
367f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (isLogWorthy(stream)) {
368e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren            Events.writeEvent(mContext, Events.EVENT_LEVEL_CHANGED, stream, level);
369f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
370f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
371f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
372f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
373f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private static boolean isLogWorthy(int stream) {
374f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        switch (stream) {
375f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            case AudioSystem.STREAM_ALARM:
376f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            case AudioSystem.STREAM_BLUETOOTH_SCO:
377f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            case AudioSystem.STREAM_MUSIC:
378f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            case AudioSystem.STREAM_RING:
379f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            case AudioSystem.STREAM_SYSTEM:
380f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            case AudioSystem.STREAM_VOICE_CALL:
381f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                return true;
382f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
383f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return false;
384f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
385f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
386f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateStreamMuteW(int stream, boolean muted) {
387f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final StreamState ss = streamStateW(stream);
388f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (ss.muted == muted) return false;
389f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        ss.muted = muted;
390f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (isLogWorthy(stream)) {
391e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren            Events.writeEvent(mContext, Events.EVENT_MUTE_CHANGED, stream, muted);
392f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
393f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (muted && isRinger(stream)) {
394f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            updateRingerModeInternalW(mAudio.getRingerModeInternal());
395f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
396f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
397f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
398f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
399f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private static boolean isRinger(int stream) {
400f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
401f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
402f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
403d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock    private boolean updateZenModeConfigW() {
404d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        final ZenModeConfig zenModeConfig = getZenModeConfig();
405d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        if (Objects.equals(mState.zenModeConfig, zenModeConfig)) return false;
406d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        mState.zenModeConfig = zenModeConfig;
407f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
408f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
409f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
410f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
411f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
412f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mState.effectsSuppressor = effectsSuppressor;
413f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor);
414e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        Events.writeEvent(mContext, Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
415f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mState.effectsSuppressorName);
416f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
417f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
418f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
419f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private static String getApplicationName(Context context, ComponentName component) {
420f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (component == null) return null;
421f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final PackageManager pm = context.getPackageManager();
422f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final String pkg = component.getPackageName();
423f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        try {
424f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
425f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final String rt = Objects.toString(ai.loadLabel(pm), "").trim();
426f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (rt.length() > 0) {
427f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                return rt;
428f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
429f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        } catch (NameNotFoundException e) {}
430f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return pkg;
431f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
432f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
433f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateZenModeW() {
434f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        final int zen = Settings.Global.getInt(mContext.getContentResolver(),
435f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
436f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (mState.zenMode == zen) return false;
437f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mState.zenMode = zen;
438e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        Events.writeEvent(mContext, Events.EVENT_ZEN_MODE_CHANGED, zen);
439f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
440f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
441f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
442f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateRingerModeExternalW(int rm) {
443f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (rm == mState.ringerModeExternal) return false;
444f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mState.ringerModeExternal = rm;
445e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        Events.writeEvent(mContext, Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
446f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
447f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
448f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
449f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private boolean updateRingerModeInternalW(int rm) {
450f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (rm == mState.ringerModeInternal) return false;
451f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mState.ringerModeInternal = rm;
452e565ee6ccbac4e820eb2737890aadb5efe9d4e3bChris Wren        Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
453f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        return true;
454f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
455f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
456f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onSetRingerModeW(int mode, boolean external) {
457f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (external) {
458f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mAudio.setRingerMode(mode);
459f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        } else {
460f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mAudio.setRingerModeInternal(mode);
461f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
462f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
463f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
464f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onSetStreamMuteW(int stream, boolean mute) {
465f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE
466f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                : AudioManager.ADJUST_UNMUTE, 0);
467f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
468f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
469f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onSetStreamVolumeW(int stream, int level) {
470f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
471f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (stream >= DYNAMIC_STREAM_START_INDEX) {
472f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mMediaSessionsCallbacksW.setStreamVolume(stream, level);
473f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            return;
474f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
475f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mAudio.setStreamVolume(stream, level, 0);
476f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
477f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
478f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onSetActiveStreamW(int stream) {
479f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        boolean changed = updateActiveStreamW(stream);
480f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (changed) {
481f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbacks.onStateChanged(mState);
482f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
483f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
484f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
485f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onSetExitConditionW(Condition condition) {
486b2278d65714c0dd0a6f94d1913db1ebc8bfc8b06John Spurlock        mNoMan.setZenMode(mState.zenMode, condition != null ? condition.id : null, TAG);
487f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
488f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
489f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onSetZenModeW(int mode) {
490f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode);
491b2278d65714c0dd0a6f94d1913db1ebc8bfc8b06John Spurlock        mNoMan.setZenMode(mode, null, TAG);
492f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
493f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
494f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private void onDismissRequestedW(int reason) {
495f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        mCallbacks.onDismissRequested(reason);
496f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
497f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
498f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public void showDndTile(boolean visible) {
499f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        if (D.BUG) Log.d(TAG, "showDndTile");
500cd863ad094f38ab6b2339bb7961a34ae11414412John Spurlock        DndTile.setVisible(mContext, visible);
501f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
502f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
503f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final class VC extends IVolumeController.Stub {
504f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private final String TAG = VolumeDialogController.TAG + ".VC";
505f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
506f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
507f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void displaySafeVolumeWarning(int flags) throws RemoteException {
50876b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock            if (D.BUG) Log.d(TAG, "displaySafeVolumeWarning "
50976b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                    + Util.audioManagerFlagsToString(flags));
51076b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock            if (mDestroyed) return;
51176b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock            mWorker.obtainMessage(W.SHOW_SAFETY_WARNING, flags, 0).sendToTarget();
512f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
513f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
514f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
515f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void volumeChanged(int streamType, int flags) throws RemoteException {
516f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
517f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    + " " + Util.audioManagerFlagsToString(flags));
518f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (mDestroyed) return;
519f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
520f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
521f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
522f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
523f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void masterMuteChanged(int flags) throws RemoteException {
524f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (D.BUG) Log.d(TAG, "masterMuteChanged");
525f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
526f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
527f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
528f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void setLayoutDirection(int layoutDirection) throws RemoteException {
529f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (D.BUG) Log.d(TAG, "setLayoutDirection");
530f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (mDestroyed) return;
531f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
532f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
533f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
534f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
535f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void dismiss() throws RemoteException {
536f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (D.BUG) Log.d(TAG, "dismiss requested");
537f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (mDestroyed) return;
538f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
539f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    .sendToTarget();
540f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
541f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
542f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
543f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
544f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final class W extends Handler {
545f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int VOLUME_CHANGED = 1;
546f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int DISMISS_REQUESTED = 2;
547f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int GET_STATE = 3;
548f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int SET_RINGER_MODE = 4;
549f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int SET_ZEN_MODE = 5;
550f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int SET_EXIT_CONDITION = 6;
551f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int SET_STREAM_MUTE = 7;
552f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int LAYOUT_DIRECTION_CHANGED = 8;
553f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int CONFIGURATION_CHANGED = 9;
554f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int SET_STREAM_VOLUME = 10;
555f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int SET_ACTIVE_STREAM = 11;
556f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int NOTIFY_VISIBLE = 12;
557f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private static final int USER_ACTIVITY = 13;
55876b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock        private static final int SHOW_SAFETY_WARNING = 14;
559f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
560f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        W(Looper looper) {
561f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            super(looper);
562f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
563f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
564f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
565f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void handleMessage(Message msg) {
566f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            switch (msg.what) {
567f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
568f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
569f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case GET_STATE: onGetStateW(); break;
570f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
571f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
572f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
573f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
574f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
575f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
576f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
577f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
57876b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
57976b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                case USER_ACTIVITY: onUserActivityW(); break;
58076b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
581f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
582f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
583f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
584f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
585f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final class C implements Callbacks {
586f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
587f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
588f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void add(Callbacks callback, Handler handler) {
589f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (callback == null || handler == null) throw new IllegalArgumentException();
590f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbackMap.put(callback, handler);
591f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
592f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
593f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void remove(Callbacks callback) {
594f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbackMap.remove(callback);
595f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
596f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
597f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
598f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onShowRequested(final int reason) {
599f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
600f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
601f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
602f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
603f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onShowRequested(reason);
604f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
605f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
606f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
607f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
608f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
609f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
610f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onDismissRequested(final int reason) {
611f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
612f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
613f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
614f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
615f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onDismissRequested(reason);
616f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
617f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
618f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
619f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
620f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
621f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
622f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onStateChanged(final State state) {
623f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final long time = System.currentTimeMillis();
624f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final State copy = state.copy();
625f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
626f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
627f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
628f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
629f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onStateChanged(copy);
630f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
631f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
632f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
633f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            Events.writeState(time, copy);
634f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
635f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
636f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
637f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onLayoutDirectionChanged(final int layoutDirection) {
638f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
639f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
640f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
641f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
642f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onLayoutDirectionChanged(layoutDirection);
643f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
644f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
645f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
646f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
647f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
648f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
649f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onConfigurationChanged() {
650f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
651f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
652f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
653f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
654f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onConfigurationChanged();
655f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
656f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
657f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
658f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
659f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
660f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
661f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onShowVibrateHint() {
662f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
663f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
664f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
665f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
666f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onShowVibrateHint();
667f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
668f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
669f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
670f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
671f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
672f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
673f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onShowSilentHint() {
674f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
675f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
676f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
677f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
678f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onShowSilentHint();
679f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
680f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
681f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
682f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
683f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
684f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
685f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onScreenOff() {
686f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
687f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                entry.getValue().post(new Runnable() {
688f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    @Override
689f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    public void run() {
690f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        entry.getKey().onScreenOff();
691f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    }
692f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                });
693f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
694f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
69576b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock
69676b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock        @Override
69776b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock        public void onShowSafetyWarning(final int flags) {
69876b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
69976b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                entry.getValue().post(new Runnable() {
70076b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                    @Override
70176b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                    public void run() {
70276b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                        entry.getKey().onShowSafetyWarning(flags);
70376b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                    }
70476b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock                });
70576b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock            }
70676b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock        }
707f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
708f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
709f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
710f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final class SettingObserver extends ContentObserver {
711f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private final Uri SERVICE_URI = Settings.Secure.getUriFor(
712f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT);
713f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private final Uri ZEN_MODE_URI =
714f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
715f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private final Uri ZEN_MODE_CONFIG_URI =
716f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG);
717f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
718f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public SettingObserver(Handler handler) {
719f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            super(handler);
720f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
721f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
722f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void init() {
723f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mContext.getContentResolver().registerContentObserver(SERVICE_URI, false, this);
724f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
725f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);
726f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            onChange(true, SERVICE_URI);
727f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
728f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
729f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void destroy() {
730f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mContext.getContentResolver().unregisterContentObserver(this);
731f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
732f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
733f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
734f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onChange(boolean selfChange, Uri uri) {
735f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            boolean changed = false;
736f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (SERVICE_URI.equals(uri)) {
737f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final String setting = Settings.Secure.getString(mContext.getContentResolver(),
738f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT);
739f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final boolean enabled = setting != null && mComponent != null
740f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        && mComponent.equals(ComponentName.unflattenFromString(setting));
741f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (enabled == mEnabled) return;
742f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (enabled) {
743f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    register();
744f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                }
745f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mEnabled = enabled;
746f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
747f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (ZEN_MODE_URI.equals(uri)) {
748f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = updateZenModeW();
749f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
750f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (ZEN_MODE_CONFIG_URI.equals(uri)) {
751d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock                changed = updateZenModeConfigW();
752f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
753f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (changed) {
754f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onStateChanged(mState);
755f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
756f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
757f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
758f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
759f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final class Receiver extends BroadcastReceiver {
760f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
761f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void init() {
762f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final IntentFilter filter = new IntentFilter();
763f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
764f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
765f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
766f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
767f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
768f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
769f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
770f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            filter.addAction(Intent.ACTION_SCREEN_OFF);
771bc7233a8a37dd8090d6e04b8c92bc93c868e16b2John Spurlock            filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
772f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mContext.registerReceiver(this, filter, null, mWorker);
773f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
774f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
775f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void destroy() {
776f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mContext.unregisterReceiver(this);
777f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
778f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
779f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
780f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onReceive(Context context, Intent intent) {
781f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final String action = intent.getAction();
782f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            boolean changed = false;
783f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
784f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
785f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
786f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int oldLevel = intent
787f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
788f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
789f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        + " level=" + level + " oldLevel=" + oldLevel);
790f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = updateStreamLevelW(stream, level);
791f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
792f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
793f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int devices = intent
794f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
795f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int oldDevices = intent
796f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
797f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
798f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        + stream + " devices=" + devices + " oldDevices=" + oldDevices);
799f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = checkRoutedToBluetoothW(stream);
800f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
801f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
802f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
803f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        + Util.ringerModeToString(rm));
804f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = updateRingerModeExternalW(rm);
805f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
806f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
807f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
808f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        + Util.ringerModeToString(rm));
809f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = updateRingerModeInternalW(rm);
810f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
811f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
812f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final boolean muted = intent
813f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
814f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
815f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        + " muted=" + muted);
816f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = updateStreamMuteW(stream, muted);
817f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
818f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
819f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
820f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
821f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive ACTION_CONFIGURATION_CHANGED");
822f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onConfigurationChanged();
823f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
824f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onReceive ACTION_SCREEN_OFF");
825f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onScreenOff();
826bc7233a8a37dd8090d6e04b8c92bc93c868e16b2John Spurlock            } else if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
827bc7233a8a37dd8090d6e04b8c92bc93c868e16b2John Spurlock                if (D.BUG) Log.d(TAG, "onReceive ACTION_CLOSE_SYSTEM_DIALOGS");
828bc7233a8a37dd8090d6e04b8c92bc93c868e16b2John Spurlock                dismiss();
829f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
830f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (changed) {
831f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onStateChanged(mState);
832f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
833f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
834f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
835f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
836f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    private final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
837f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
838f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
839f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private int mNextStream = DYNAMIC_STREAM_START_INDEX;
840f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
841f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
842f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
843f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (!mRemoteStreams.containsKey(token)) {
844f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mRemoteStreams.put(token, mNextStream);
845f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + " is stream " + mNextStream);
846f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mNextStream++;
847f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
848f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final int stream = mRemoteStreams.get(token);
849f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            boolean changed = mState.states.indexOfKey(stream) < 0;
850f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final StreamState ss = streamStateW(stream);
851f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            ss.dynamic = true;
852f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            ss.levelMin = 0;
853f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            ss.levelMax = pi.getMaxVolume();
854f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (ss.level != pi.getCurrentVolume()) {
855f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                ss.level = pi.getCurrentVolume();
856f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = true;
857f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
858f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (!Objects.equals(ss.name, name)) {
859f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                ss.name = name;
860f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed = true;
861f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
862f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (changed) {
863f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level
864f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                        + " of " + ss.levelMax);
865f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onStateChanged(mState);
866f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
867f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
868f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
869f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
870f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onRemoteVolumeChanged(Token token, int flags) {
871f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final int stream = mRemoteStreams.get(token);
872f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
873f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            boolean changed = updateActiveStreamW(stream);
874f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (showUI) {
875f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
876f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
877f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (changed) {
878f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onStateChanged(mState);
879f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
880f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (showUI) {
881f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
882f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
883f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
884f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
885f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
886f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void onRemoteRemoved(Token token) {
887f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final int stream = mRemoteStreams.get(token);
888f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mState.states.remove(stream);
889f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (mState.activeStream == stream) {
890f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                updateActiveStreamW(-1);
891f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
892f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mCallbacks.onStateChanged(mState);
893f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
894f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
895f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public void setStreamVolume(int stream, int level) {
896f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final Token t = findToken(stream);
897f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (t == null) {
898f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
899f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                return;
900f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
901f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            mMediaSessions.setVolume(t, level);
902f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
903f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
904f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        private Token findToken(int stream) {
905f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
906f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (entry.getValue().equals(stream)) {
907f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                    return entry.getKey();
908f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                }
909f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
910f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            return null;
911f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
912f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
913f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
914f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public static final class StreamState {
915f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public boolean dynamic;
916f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public int level;
917f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public int levelMin;
918f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public int levelMax;
919f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public boolean muted;
920f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public boolean muteSupported;
921f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public String name;
922f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public boolean routedToBluetooth;
923f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
924f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public StreamState copy() {
925f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final StreamState rt = new StreamState();
926f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.dynamic = dynamic;
927f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.level = level;
928f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.levelMin = levelMin;
929f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.levelMax = levelMax;
930f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.muted = muted;
931f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.muteSupported = muteSupported;
932f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.name = name;
933f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.routedToBluetooth = routedToBluetooth;
934f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            return rt;
935f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
936f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
937f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
938f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public static final class State {
939f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public static int NO_ACTIVE_STREAM = -1;
940f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
941f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public final SparseArray<StreamState> states = new SparseArray<StreamState>();
942f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
943f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public int ringerModeInternal;
944f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public int ringerModeExternal;
945f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public int zenMode;
946f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public ComponentName effectsSuppressor;
947f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public String effectsSuppressorName;
948d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        public ZenModeConfig zenModeConfig;
949f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public int activeStream = NO_ACTIVE_STREAM;
950f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
951f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public State copy() {
952f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final State rt = new State();
953f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (int i = 0; i < states.size(); i++) {
954f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                rt.states.put(states.keyAt(i), states.valueAt(i).copy());
955f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
956f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.ringerModeExternal = ringerModeExternal;
957f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.ringerModeInternal = ringerModeInternal;
958f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.zenMode = zenMode;
959f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            if (effectsSuppressor != null) rt.effectsSuppressor = effectsSuppressor.clone();
960f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.effectsSuppressorName = effectsSuppressorName;
961d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock            if (zenModeConfig != null) rt.zenModeConfig = zenModeConfig.copy();
962f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            rt.activeStream = activeStream;
963f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            return rt;
964f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
965f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
966f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        @Override
967f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        public String toString() {
968e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            return toString(0);
969e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock        }
970e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock
971e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock        public String toString(int indent) {
972f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            final StringBuilder sb = new StringBuilder("{");
973e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            if (indent > 0) sep(sb, indent);
974f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            for (int i = 0; i < states.size(); i++) {
975e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                if (i > 0) {
976e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                    sep(sb, indent);
977e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                }
978f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final int stream = states.keyAt(i);
979f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                final StreamState ss = states.valueAt(i);
980f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                sb.append(AudioSystem.streamToString(stream)).append(":").append(ss.level)
981e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                        .append('[').append(ss.levelMin).append("..").append(ss.levelMax)
982e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                        .append(']');
983f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock                if (ss.muted) sb.append(" [MUTED]");
984f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            }
985e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            sep(sb, indent); sb.append("ringerModeExternal:").append(ringerModeExternal);
986e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            sep(sb, indent); sb.append("ringerModeInternal:").append(ringerModeInternal);
987e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            sep(sb, indent); sb.append("zenMode:").append(zenMode);
988e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor);
989e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName);
990e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            sep(sb, indent); sb.append("zenModeConfig:").append(zenModeConfig);
991e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            sep(sb, indent); sb.append("activeStream:").append(activeStream);
992e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            if (indent > 0) sep(sb, indent);
993f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock            return sb.append('}').toString();
994f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        }
995d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock
996e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock        private static void sep(StringBuilder sb, int indent) {
997e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            if (indent > 0) {
998e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                sb.append('\n');
999e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                for (int i = 0; i < indent; i++) {
1000e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                    sb.append(' ');
1001e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                }
1002e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            } else {
1003e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock                sb.append(',');
1004e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock            }
1005e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock        }
1006e56efa71bbb71b65ffdc37313c2745bff9ab912eJohn Spurlock
1007d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        public Condition getManualExitCondition() {
1008d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock            return zenModeConfig != null && zenModeConfig.manualRule != null
1009d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock                    ? zenModeConfig.manualRule.condition : null;
1010d9c75dba75ad4742dbaadc34db99d2b76cebca91John Spurlock        }
1011f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
1012f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock
1013f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    public interface Callbacks {
1014f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onShowRequested(int reason);
1015f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onDismissRequested(int reason);
1016f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onStateChanged(State state);
1017f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onLayoutDirectionChanged(int layoutDirection);
1018f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onConfigurationChanged();
1019f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onShowVibrateHint();
1020f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onShowSilentHint();
1021f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock        void onScreenOff();
102276b52b3f9b0dde7d7800508271cf5851d92b25d5John Spurlock        void onShowSafetyWarning(int flags);
1023f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock    }
1024f88d8082a86bee00c604cbbcfb5261f5573936feJohn Spurlock}
1025