1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.preference.DialogPreference;
24import android.provider.Settings;
25import android.util.AttributeSet;
26import android.view.View;
27import android.view.accessibility.AccessibilityEvent;
28import android.view.accessibility.AccessibilityManager;
29
30/**
31 * Preference for enabling accessibility script injection. It displays a warning
32 * dialog before enabling the preference.
33 */
34public class AccessibilityEnableScriptInjectionPreference extends DialogPreference {
35
36    private boolean mInjectionAllowed;
37    private boolean mSendClickAccessibilityEvent;
38
39    public AccessibilityEnableScriptInjectionPreference(Context context, AttributeSet attrs) {
40        super(context, attrs);
41        updateSummary();
42    }
43
44    public void setInjectionAllowed(boolean injectionAllowed) {
45        if (mInjectionAllowed != injectionAllowed) {
46            mInjectionAllowed = injectionAllowed;
47            persistBoolean(injectionAllowed);
48            updateSummary();
49        }
50    }
51
52    public boolean isInjectionAllowed() {
53        return mInjectionAllowed;
54    }
55
56    @Override
57    protected void onBindView(View view) {
58        super.onBindView(view);
59        View summaryView = view.findViewById(com.android.internal.R.id.summary);
60        sendAccessibilityEvent(summaryView);
61    }
62
63    private void sendAccessibilityEvent(View view) {
64        // Since the view is still not attached we create, populate,
65        // and send the event directly since we do not know when it
66        // will be attached and posting commands is not as clean.
67        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getContext());
68        if (mSendClickAccessibilityEvent && accessibilityManager.isEnabled()) {
69            AccessibilityEvent event = AccessibilityEvent.obtain();
70            event.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
71            view.onInitializeAccessibilityEvent(event);
72            view.dispatchPopulateAccessibilityEvent(event);
73            accessibilityManager.sendAccessibilityEvent(event);
74        }
75        mSendClickAccessibilityEvent = false;
76    }
77
78    @Override
79    protected void onClick() {
80        if (isInjectionAllowed()) {
81            setInjectionAllowed(false);
82            // Update the system setting only upon user action.
83            setSystemSetting(false);
84            mSendClickAccessibilityEvent = true;
85        } else {
86            super.onClick();
87            mSendClickAccessibilityEvent = false;
88        }
89    }
90
91    @Override
92    protected Object onGetDefaultValue(TypedArray a, int index) {
93        return a.getBoolean(index, false);
94    }
95
96    @Override
97    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
98        setInjectionAllowed(restoreValue
99                ? getPersistedBoolean(mInjectionAllowed)
100                : (Boolean) defaultValue);
101    }
102
103    @Override
104    protected void onDialogClosed(boolean result) {
105        setInjectionAllowed(result);
106        if (result) {
107            // Update the system setting only upon user action.
108            setSystemSetting(true);
109        }
110    }
111
112    @Override
113    protected Parcelable onSaveInstanceState() {
114        Parcelable superState = super.onSaveInstanceState();
115        if (isPersistent()) {
116            return superState;
117        }
118        SavedState myState = new SavedState(superState);
119        myState.mInjectionAllowed = mInjectionAllowed;
120        return myState;
121    }
122
123    @Override
124    protected void onRestoreInstanceState(Parcelable state) {
125        if (state == null || !state.getClass().equals(SavedState.class)) {
126            super.onRestoreInstanceState(state);
127            return;
128        }
129        SavedState myState = (SavedState) state;
130        super.onRestoreInstanceState(myState.getSuperState());
131        setInjectionAllowed(myState.mInjectionAllowed);
132    }
133
134    private void updateSummary() {
135        setSummary(mInjectionAllowed
136                ? getContext().getString(R.string.accessibility_script_injection_allowed)
137                : getContext().getString(R.string.accessibility_script_injection_disallowed));
138    }
139
140    private void setSystemSetting(boolean enabled) {
141        Settings.Secure.putInt(getContext().getContentResolver(),
142                Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, enabled ? 1 : 0);
143    }
144
145    private static class SavedState extends BaseSavedState {
146        private boolean mInjectionAllowed;
147
148        public SavedState(Parcel source) {
149            super(source);
150            mInjectionAllowed = (source.readInt() == 1);
151        }
152
153        @Override
154        public void writeToParcel(Parcel parcel, int flags) {
155            super.writeToParcel(parcel, flags);
156            parcel.writeInt(mInjectionAllowed ? 1 : 0);
157        }
158
159        public SavedState(Parcelable superState) {
160            super(superState);
161        }
162
163        @SuppressWarnings("all")
164        public static final Parcelable.Creator<SavedState> CREATOR =
165                new Parcelable.Creator<SavedState>() {
166            public SavedState createFromParcel(Parcel in) {
167                return new SavedState(in);
168            }
169
170            public SavedState[] newArray(int size) {
171                return new SavedState[size];
172            }
173        };
174    }
175}
176