1af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/*
2af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Copyright (C) 2014 The Android Open Source Project
3af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
4af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Licensed under the Apache License, Version 2.0 (the "License");
5af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * you may not use this file except in compliance with the License.
6af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * You may obtain a copy of the License at
7af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
8af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *      http://www.apache.org/licenses/LICENSE-2.0
9af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
10af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Unless required by applicable law or agreed to in writing, software
11af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * distributed under the License is distributed on an "AS IS" BASIS,
12af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * See the License for the specific language governing permissions and
14af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * limitations under the License.
15af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock */
16af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
17af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockpackage com.android.systemui.qs;
18af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
19af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.Context;
20af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.Intent;
212d695813b44690ca146f95939acb00f0c14dac29John Spurlockimport android.graphics.drawable.AnimatedVectorDrawable;
22444eb2e13032c78b23b4a371ce6164faf1a3512bJohn Spurlockimport android.graphics.drawable.Drawable;
23af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Handler;
24af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Looper;
25af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Message;
26af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.util.Log;
272d695813b44690ca146f95939acb00f0c14dac29John Spurlockimport android.util.SparseArray;
28af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.view.View;
29af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.view.ViewGroup;
30af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
31af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.qs.QSTile.State;
32af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.statusbar.policy.BluetoothController;
33af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.statusbar.policy.CastController;
34b83777ba5c12295224cd566bba59a44b8860e449Adrian Roosimport com.android.systemui.statusbar.policy.FlashlightController;
35657c62c31ecdade22f67f13102c2fb19765cbe13John Spurlockimport com.android.systemui.statusbar.policy.KeyguardMonitor;
36ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlockimport com.android.systemui.statusbar.policy.Listenable;
37af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.statusbar.policy.LocationController;
38af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.statusbar.policy.NetworkController;
39af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.statusbar.policy.RotationLockController;
4051e4dc0a153bd0da197728ce6fe34df662b1cea2Jason Monkimport com.android.systemui.statusbar.policy.HotspotController;
41af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.statusbar.policy.ZenModeController;
42af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
43bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlockimport java.util.Collection;
44af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.util.Objects;
45af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
46af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/**
47af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Base quick-settings tile, extend this to create a new tile.
48af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
49af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * State management done on a looper provided by the host.  Tiles should update state in
50af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * handleUpdateState.  Callbacks affecting state should use refreshState to trigger another
51af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * state update pass on tile looper.
52af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock */
53ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlockpublic abstract class QSTile<TState extends State> implements Listenable {
54ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock    protected final String TAG = "QSTile." + getClass().getSimpleName();
55486b78e42652466f6241eb87d5bed60040db7a25John Spurlock    protected static final boolean DEBUG = Log.isLoggable("QSTile", Log.DEBUG);
56af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
57af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected final Host mHost;
58af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected final Context mContext;
59af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected final H mHandler;
6076c43b9ee1fb91016265b3758d3d635105091fbeJohn Spurlock    protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
61af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
62af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private Callback mCallback;
63af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected final TState mState = newTileState();
64af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private final TState mTmpState = newTileState();
654fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek    private boolean mAnnounceNextStateChange;
66af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
67af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    abstract protected TState newTileState();
68af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    abstract protected void handleClick();
69af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    abstract protected void handleUpdateState(TState state, Object arg);
70af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
71af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected QSTile(Host host) {
72af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHost = host;
73af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mContext = host.getContext();
74af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHandler = new H(host.getLooper());
75af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
76af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
77ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock    public boolean supportsDualTargets() {
78ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock        return false;
79ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock    }
80ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock
81af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public Host getHost() {
82af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        return mHost;
83af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
84af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
85af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public QSTileView createTileView(Context context) {
86af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        return new QSTileView(context);
87af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
88af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
897f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    public DetailAdapter getDetailAdapter() {
90af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        return null; // optional
91af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
92af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
937f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    public interface DetailAdapter {
947f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        int getTitle();
957f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        Boolean getToggleState();
967f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        View createDetailView(Context context, View convertView, ViewGroup parent);
977f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        Intent getSettingsIntent();
987f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        void setToggleState(boolean state);
997f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    }
1007f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock
101af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    // safe to call from any thread
102af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
103af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void setCallback(Callback callback) {
104af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
105af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
106af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
107af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void click() {
108af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHandler.sendEmptyMessage(H.CLICK);
109af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
110af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
111af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void secondaryClick() {
112af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
113af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
114af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
115c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock    public void longClick() {
116c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        mHandler.sendEmptyMessage(H.LONG_CLICK);
117c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock    }
118c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock
119af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void showDetail(boolean show) {
120af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
121af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
122af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
123af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected final void refreshState() {
124af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        refreshState(null);
125af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
126af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
127af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected final void refreshState(Object arg) {
128af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
129af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
130af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
131af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void userSwitch(int newUserId) {
13232d88e8ead4ccf6b698cac343461cfe12ef168b8Adrian Roos        mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
133af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
134af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1357f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    public void fireToggleStateChanged(boolean state) {
1367f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
1377f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    }
1387f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock
139486b78e42652466f6241eb87d5bed60040db7a25John Spurlock    public void fireScanStateChanged(boolean state) {
140486b78e42652466f6241eb87d5bed60040db7a25John Spurlock        mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
141486b78e42652466f6241eb87d5bed60040db7a25John Spurlock    }
142486b78e42652466f6241eb87d5bed60040db7a25John Spurlock
143bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    public void destroy() {
144bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        mHandler.sendEmptyMessage(H.DESTROY);
145bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    }
146bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock
147bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    public TState getState() {
148bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        return mState;
149bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    }
150bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock
151af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    // call only on tile worker looper
152af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
153af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private void handleSetCallback(Callback callback) {
154af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mCallback = callback;
155af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        handleRefreshState(null);
156af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
157af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
158af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected void handleSecondaryClick() {
159af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        // optional
160af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
161af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
162c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock    protected void handleLongClick() {
163c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        // optional
164c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock    }
165c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock
166af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected void handleRefreshState(Object arg) {
167af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        handleUpdateState(mTmpState, arg);
168af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        final boolean changed = mTmpState.copyTo(mState);
169af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        if (changed) {
170af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            handleStateChanged();
171af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
172af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
173af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
174af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private void handleStateChanged() {
1754fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek        boolean delayAnnouncement = shouldAnnouncementBeDelayed();
176af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        if (mCallback != null) {
177af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            mCallback.onStateChanged(mState);
1784fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek            if (mAnnounceNextStateChange && !delayAnnouncement) {
1794fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek                String announcement = composeChangeAnnouncement();
1804fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek                if (announcement != null) {
1814fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek                    mCallback.onAnnouncementRequested(announcement);
1824fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek                }
1834fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek            }
184af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1854fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek        mAnnounceNextStateChange = mAnnounceNextStateChange && delayAnnouncement;
1864fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek    }
1874fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek
1884fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek    protected boolean shouldAnnouncementBeDelayed() {
1894fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek        return false;
1904fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek    }
1914fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek
1924fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek    protected String composeChangeAnnouncement() {
1934fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek        return null;
194af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
195af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
196af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private void handleShowDetail(boolean show) {
197af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        if (mCallback != null) {
198af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            mCallback.onShowDetail(show);
199af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
200af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
201af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
2027f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    private void handleToggleStateChanged(boolean state) {
2037f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        if (mCallback != null) {
2047f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mCallback.onToggleStateChanged(state);
2057f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        }
2067f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    }
2077f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock
208486b78e42652466f6241eb87d5bed60040db7a25John Spurlock    private void handleScanStateChanged(boolean state) {
209486b78e42652466f6241eb87d5bed60040db7a25John Spurlock        if (mCallback != null) {
210486b78e42652466f6241eb87d5bed60040db7a25John Spurlock            mCallback.onScanStateChanged(state);
211486b78e42652466f6241eb87d5bed60040db7a25John Spurlock        }
212486b78e42652466f6241eb87d5bed60040db7a25John Spurlock    }
213486b78e42652466f6241eb87d5bed60040db7a25John Spurlock
214af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected void handleUserSwitch(int newUserId) {
215af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        handleRefreshState(null);
216af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
217af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
218bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    protected void handleDestroy() {
219bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        setListening(false);
220bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        mCallback = null;
221bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    }
222bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock
223af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected final class H extends Handler {
224af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        private static final int SET_CALLBACK = 1;
225af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        private static final int CLICK = 2;
226af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        private static final int SECONDARY_CLICK = 3;
227c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        private static final int LONG_CLICK = 4;
228c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        private static final int REFRESH_STATE = 5;
229c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        private static final int SHOW_DETAIL = 6;
230c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        private static final int USER_SWITCH = 7;
231c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        private static final int TOGGLE_STATE_CHANGED = 8;
232c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        private static final int SCAN_STATE_CHANGED = 9;
233c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        private static final int DESTROY = 10;
234af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
235af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        private H(Looper looper) {
236af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            super(looper);
237af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
238af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
239af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        @Override
240af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public void handleMessage(Message msg) {
241af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            String name = null;
242af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            try {
243af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                if (msg.what == SET_CALLBACK) {
244af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    name = "handleSetCallback";
245af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    handleSetCallback((QSTile.Callback)msg.obj);
246af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                } else if (msg.what == CLICK) {
247af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    name = "handleClick";
2484fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek                    mAnnounceNextStateChange = true;
249af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    handleClick();
250af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                } else if (msg.what == SECONDARY_CLICK) {
251af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    name = "handleSecondaryClick";
252af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    handleSecondaryClick();
253c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock                } else if (msg.what == LONG_CLICK) {
254c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock                    name = "handleLongClick";
255c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock                    handleLongClick();
256af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                } else if (msg.what == REFRESH_STATE) {
257af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    name = "handleRefreshState";
258af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    handleRefreshState(msg.obj);
259af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                } else if (msg.what == SHOW_DETAIL) {
260af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    name = "handleShowDetail";
261af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    handleShowDetail(msg.arg1 != 0);
262af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                } else if (msg.what == USER_SWITCH) {
263af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    name = "handleUserSwitch";
264af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    handleUserSwitch(msg.arg1);
2657f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                } else if (msg.what == TOGGLE_STATE_CHANGED) {
2667f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                    name = "handleToggleStateChanged";
2677f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                    handleToggleStateChanged(msg.arg1 != 0);
268486b78e42652466f6241eb87d5bed60040db7a25John Spurlock                } else if (msg.what == SCAN_STATE_CHANGED) {
269486b78e42652466f6241eb87d5bed60040db7a25John Spurlock                    name = "handleScanStateChanged";
270486b78e42652466f6241eb87d5bed60040db7a25John Spurlock                    handleScanStateChanged(msg.arg1 != 0);
271bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock                } else if (msg.what == DESTROY) {
272bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock                    name = "handleDestroy";
273bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock                    handleDestroy();
274bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock                } else {
275bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock                    throw new IllegalArgumentException("Unknown msg: " + msg.what);
276af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
277af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            } catch (Throwable t) {
278af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                final String error = "Error in " + name;
279af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                Log.w(TAG, error, t);
280af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                mHost.warn(error, t);
281af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
282af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
283af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
284af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
285af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public interface Callback {
286af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        void onStateChanged(State state);
287af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        void onShowDetail(boolean show);
2887f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        void onToggleStateChanged(boolean state);
289486b78e42652466f6241eb87d5bed60040db7a25John Spurlock        void onScanStateChanged(boolean state);
2904fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek        void onAnnouncementRequested(CharSequence announcement);
291af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
292af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
293af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public interface Host {
294af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        void startSettingsActivity(Intent intent);
295af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        void warn(String message, Throwable t);
296af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        void collapsePanels();
297af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        Looper getLooper();
298af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        Context getContext();
299bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        Collection<QSTile<?>> getTiles();
300bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        void setCallback(Callback callback);
301af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        BluetoothController getBluetoothController();
302af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        LocationController getLocationController();
303af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        RotationLockController getRotationLockController();
304af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        NetworkController getNetworkController();
305af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        ZenModeController getZenModeController();
30651e4dc0a153bd0da197728ce6fe34df662b1cea2Jason Monk        HotspotController getHotspotController();
307af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        CastController getCastController();
308b83777ba5c12295224cd566bba59a44b8860e449Adrian Roos        FlashlightController getFlashlightController();
309657c62c31ecdade22f67f13102c2fb19765cbe13John Spurlock        KeyguardMonitor getKeyguardMonitor();
310bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock
311bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        public interface Callback {
312bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock            void onTilesChanged();
313bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        }
314af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
315af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
3162d695813b44690ca146f95939acb00f0c14dac29John Spurlock    public static abstract class Icon {
3172d695813b44690ca146f95939acb00f0c14dac29John Spurlock        abstract public Drawable getDrawable(Context context);
3182d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3192d695813b44690ca146f95939acb00f0c14dac29John Spurlock        @Override
3202d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public int hashCode() {
3212d695813b44690ca146f95939acb00f0c14dac29John Spurlock            return Icon.class.hashCode();
3222d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3232d695813b44690ca146f95939acb00f0c14dac29John Spurlock    }
3242d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3252d695813b44690ca146f95939acb00f0c14dac29John Spurlock    public static class ResourceIcon extends Icon {
3262d695813b44690ca146f95939acb00f0c14dac29John Spurlock        private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
3272d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3282d695813b44690ca146f95939acb00f0c14dac29John Spurlock        private final int mResId;
3292d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3302d695813b44690ca146f95939acb00f0c14dac29John Spurlock        private ResourceIcon(int resId) {
3312d695813b44690ca146f95939acb00f0c14dac29John Spurlock            mResId = resId;
3322d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3332d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3342d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public static Icon get(int resId) {
3352d695813b44690ca146f95939acb00f0c14dac29John Spurlock            Icon icon = ICONS.get(resId);
3362d695813b44690ca146f95939acb00f0c14dac29John Spurlock            if (icon == null) {
3372d695813b44690ca146f95939acb00f0c14dac29John Spurlock                icon = new ResourceIcon(resId);
3382d695813b44690ca146f95939acb00f0c14dac29John Spurlock                ICONS.put(resId, icon);
3392d695813b44690ca146f95939acb00f0c14dac29John Spurlock            }
3402d695813b44690ca146f95939acb00f0c14dac29John Spurlock            return icon;
3412d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3422d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3432d695813b44690ca146f95939acb00f0c14dac29John Spurlock        @Override
3442d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public Drawable getDrawable(Context context) {
3452d695813b44690ca146f95939acb00f0c14dac29John Spurlock            return context.getDrawable(mResId);
3462d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3472d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3482d695813b44690ca146f95939acb00f0c14dac29John Spurlock        @Override
3492d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public boolean equals(Object o) {
3502d695813b44690ca146f95939acb00f0c14dac29John Spurlock            return o instanceof ResourceIcon && ((ResourceIcon) o).mResId == mResId;
3512d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3522d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3532d695813b44690ca146f95939acb00f0c14dac29John Spurlock        @Override
3542d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public String toString() {
3552d695813b44690ca146f95939acb00f0c14dac29John Spurlock            return String.format("ResourceIcon[resId=0x%08x]", mResId);
3562d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3572d695813b44690ca146f95939acb00f0c14dac29John Spurlock    }
3582d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3592d695813b44690ca146f95939acb00f0c14dac29John Spurlock    protected class AnimationIcon extends ResourceIcon {
3602d695813b44690ca146f95939acb00f0c14dac29John Spurlock        private boolean mAllowAnimation;
3612d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3622d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public AnimationIcon(int resId) {
3632d695813b44690ca146f95939acb00f0c14dac29John Spurlock            super(resId);
3642d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3652d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3662d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public void setAllowAnimation(boolean allowAnimation) {
3672d695813b44690ca146f95939acb00f0c14dac29John Spurlock            mAllowAnimation = allowAnimation;
3682d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3692d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3702d695813b44690ca146f95939acb00f0c14dac29John Spurlock        @Override
3712d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public Drawable getDrawable(Context context) {
3722d695813b44690ca146f95939acb00f0c14dac29John Spurlock            // workaround: get a clean state for every new AVD
3732d695813b44690ca146f95939acb00f0c14dac29John Spurlock            final AnimatedVectorDrawable d = (AnimatedVectorDrawable) super.getDrawable(context)
3742d695813b44690ca146f95939acb00f0c14dac29John Spurlock                    .getConstantState().newDrawable();
3752d695813b44690ca146f95939acb00f0c14dac29John Spurlock            d.start();
3762d695813b44690ca146f95939acb00f0c14dac29John Spurlock            if (mAllowAnimation) {
3772d695813b44690ca146f95939acb00f0c14dac29John Spurlock                mAllowAnimation = false;
3782d695813b44690ca146f95939acb00f0c14dac29John Spurlock            } else {
3792d695813b44690ca146f95939acb00f0c14dac29John Spurlock                d.stop(); // skip directly to end state
3802d695813b44690ca146f95939acb00f0c14dac29John Spurlock            }
3812d695813b44690ca146f95939acb00f0c14dac29John Spurlock            return d;
3822d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3832d695813b44690ca146f95939acb00f0c14dac29John Spurlock    }
3842d695813b44690ca146f95939acb00f0c14dac29John Spurlock
3852d695813b44690ca146f95939acb00f0c14dac29John Spurlock    protected enum UserBoolean {
3862d695813b44690ca146f95939acb00f0c14dac29John Spurlock        USER_TRUE(true, true),
3872d695813b44690ca146f95939acb00f0c14dac29John Spurlock        USER_FALSE(true, false),
3882d695813b44690ca146f95939acb00f0c14dac29John Spurlock        BACKGROUND_TRUE(false, true),
3892d695813b44690ca146f95939acb00f0c14dac29John Spurlock        BACKGROUND_FALSE(false, false);
3902d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public final boolean value;
3912d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public final boolean userInitiated;
3922d695813b44690ca146f95939acb00f0c14dac29John Spurlock        private UserBoolean(boolean userInitiated, boolean value) {
3932d695813b44690ca146f95939acb00f0c14dac29John Spurlock            this.value = value;
3942d695813b44690ca146f95939acb00f0c14dac29John Spurlock            this.userInitiated = userInitiated;
3952d695813b44690ca146f95939acb00f0c14dac29John Spurlock        }
3962d695813b44690ca146f95939acb00f0c14dac29John Spurlock    }
3972d695813b44690ca146f95939acb00f0c14dac29John Spurlock
398af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public static class State {
399af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean visible;
4002d695813b44690ca146f95939acb00f0c14dac29John Spurlock        public Icon icon;
401af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public String label;
402af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public String contentDescription;
403e5557a972ca190cb82026a5dd0c53f4d119fa05aSelim Cinek        public String dualLabelContentDescription;
40406d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek        public boolean autoMirrorDrawable = true;
405af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
406af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean copyTo(State other) {
407af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            if (other == null) throw new IllegalArgumentException();
408af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
409af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final boolean changed = other.visible != visible
410af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    || !Objects.equals(other.icon, icon)
411af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    || !Objects.equals(other.label, label)
412e5557a972ca190cb82026a5dd0c53f4d119fa05aSelim Cinek                    || !Objects.equals(other.contentDescription, contentDescription)
41306d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek                    || !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
414e5557a972ca190cb82026a5dd0c53f4d119fa05aSelim Cinek                    || !Objects.equals(other.dualLabelContentDescription,
415e5557a972ca190cb82026a5dd0c53f4d119fa05aSelim Cinek                    dualLabelContentDescription);
416af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            other.visible = visible;
417af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            other.icon = icon;
418af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            other.label = label;
419af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            other.contentDescription = contentDescription;
420e5557a972ca190cb82026a5dd0c53f4d119fa05aSelim Cinek            other.dualLabelContentDescription = dualLabelContentDescription;
42106d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            other.autoMirrorDrawable = autoMirrorDrawable;
422af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            return changed;
423af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
424af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
425af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        @Override
426af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public String toString() {
427af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            return toStringBuilder().toString();
428af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
429af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
430af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        protected StringBuilder toStringBuilder() {
4312d695813b44690ca146f95939acb00f0c14dac29John Spurlock            final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
432af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            sb.append("visible=").append(visible);
433af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            sb.append(",icon=").append(icon);
434af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            sb.append(",label=").append(label);
435af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            sb.append(",contentDescription=").append(contentDescription);
436e5557a972ca190cb82026a5dd0c53f4d119fa05aSelim Cinek            sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
43706d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
438af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            return sb.append(']');
439af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
440af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
441af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
442af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public static class BooleanState extends State {
443af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean value;
444af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
445af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        @Override
446af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean copyTo(State other) {
447af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final BooleanState o = (BooleanState) other;
448af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final boolean changed = super.copyTo(other) || o.value != value;
449af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            o.value = value;
450af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            return changed;
451af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
452af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
453af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        @Override
454af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        protected StringBuilder toStringBuilder() {
455af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final StringBuilder rt = super.toStringBuilder();
456af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            rt.insert(rt.length() - 1, ",value=" + value);
457af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            return rt;
458af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
459af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
460af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
461af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public static final class SignalState extends State {
462af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean enabled;
463af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean connected;
464af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean activityIn;
465af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean activityOut;
466af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public int overlayIconId;
467899f439a29ab1e609e7ba2aea3e9de3afef35c50John Spurlock        public boolean filter;
468c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi        public boolean isOverlayIconWide;
469af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
470af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        @Override
471af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public boolean copyTo(State other) {
472af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final SignalState o = (SignalState) other;
473af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final boolean changed = o.enabled != enabled
474af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    || o.connected != connected || o.activityIn != activityIn
475af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    || o.activityOut != activityOut
476c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi                    || o.overlayIconId != overlayIconId
477c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi                    || o.isOverlayIconWide != isOverlayIconWide;
478af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            o.enabled = enabled;
479af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            o.connected = connected;
480af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            o.activityIn = activityIn;
481af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            o.activityOut = activityOut;
482af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            o.overlayIconId = overlayIconId;
483899f439a29ab1e609e7ba2aea3e9de3afef35c50John Spurlock            o.filter = filter;
484c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi            o.isOverlayIconWide = isOverlayIconWide;
485af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            return super.copyTo(other) || changed;
486af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
487af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
488af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        @Override
489af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        protected StringBuilder toStringBuilder() {
490af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final StringBuilder rt = super.toStringBuilder();
491af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            rt.insert(rt.length() - 1, ",enabled=" + enabled);
492af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            rt.insert(rt.length() - 1, ",connected=" + connected);
493af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
494af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
495af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
496899f439a29ab1e609e7ba2aea3e9de3afef35c50John Spurlock            rt.insert(rt.length() - 1, ",filter=" + filter);
497c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi            rt.insert(rt.length() - 1, ",wideOverlayIcon=" + isOverlayIconWide);
498af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            return rt;
499af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
500af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
501af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock}
502