KeyguardSelectorView.java revision f4d922b2d9c2011bcdc0543b5cdf2dd53ca04b25
1/*
2 * Copyright (C) 2012 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 */
16package com.android.keyguard;
17
18import android.animation.ObjectAnimator;
19import android.app.SearchManager;
20import android.app.admin.DevicePolicyManager;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.graphics.drawable.Drawable;
25import android.os.Build;
26import android.os.PowerManager;
27import android.os.UserHandle;
28import android.provider.Settings;
29import android.telephony.TelephonyManager;
30import android.util.AttributeSet;
31import android.util.Log;
32import android.util.Slog;
33import android.view.View;
34import android.widget.LinearLayout;
35
36import com.android.internal.telephony.IccCardConstants.State;
37import com.android.internal.widget.LockPatternUtils;
38import com.android.internal.widget.multiwaveview.GlowPadView;
39import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
40import com.android.keyguard.KeyguardHostView.OnDismissAction;
41
42public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView {
43    private static final boolean DEBUG = KeyguardHostView.DEBUG;
44    private static final String TAG = "SecuritySelectorView";
45    private static final String ASSIST_ICON_METADATA_NAME =
46        "com.android.systemui.action_assist_icon";
47    // Flag to enable/disable hotword detection on lock screen.
48    private static final boolean FLAG_HOTWORD = true;
49
50    private KeyguardSecurityCallback mCallback;
51    private GlowPadView mGlowPadView;
52    private ObjectAnimator mAnim;
53    private View mFadeView;
54    private boolean mIsBouncing;
55    private boolean mCameraDisabled;
56    private boolean mSearchDisabled;
57    private LockPatternUtils mLockPatternUtils;
58    private SecurityMessageDisplay mSecurityMessageDisplay;
59    private Drawable mBouncerFrame;
60    private HotwordServiceClient mHotwordClient;
61
62    OnTriggerListener mOnTriggerListener = new OnTriggerListener() {
63
64        public void onTrigger(View v, int target) {
65            final int resId = mGlowPadView.getResourceIdForTarget(target);
66            if (FLAG_HOTWORD) {
67                maybeStopHotwordDetector();
68            }
69            switch (resId) {
70                case R.drawable.ic_action_assist_generic:
71                    Intent assistIntent =
72                            ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
73                            .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
74                    if (assistIntent != null) {
75                        mActivityLauncher.launchActivity(assistIntent, false, true, null, null);
76                    } else {
77                        Log.w(TAG, "Failed to get intent for assist activity");
78                    }
79                    mCallback.userActivity(0);
80                    break;
81
82                case R.drawable.ic_lockscreen_camera:
83                    mActivityLauncher.launchCamera(null, null);
84                    mCallback.userActivity(0);
85                    break;
86
87                case R.drawable.ic_lockscreen_unlock_phantom:
88                case R.drawable.ic_lockscreen_unlock:
89                    mCallback.userActivity(0);
90                    mCallback.dismiss(false);
91                break;
92            }
93        }
94
95        public void onReleased(View v, int handle) {
96            if (!mIsBouncing) {
97                doTransition(mFadeView, 1.0f);
98            }
99        }
100
101        public void onGrabbed(View v, int handle) {
102            mCallback.userActivity(0);
103            doTransition(mFadeView, 0.0f);
104        }
105
106        public void onGrabbedStateChange(View v, int handle) {
107
108        }
109
110        public void onFinishFinalAnimation() {
111
112        }
113
114    };
115
116    KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
117
118        @Override
119        public void onDevicePolicyManagerStateChanged() {
120            updateTargets();
121        }
122
123        @Override
124        public void onSimStateChanged(State simState) {
125            updateTargets();
126        }
127
128        @Override
129        public void onPhoneStateChanged(int phoneState) {
130            if (FLAG_HOTWORD) {
131                // We need to stop the hotwording when a phone call comes in
132                // TODO(sansid): This is not really needed if onPause triggers
133                // when we navigate away from the keyguard
134                if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
135                    if (DEBUG) Log.d(TAG, "Stopping due to CALL_STATE_RINGING");
136                    maybeStopHotwordDetector();
137                }
138            }
139        }
140
141        @Override
142        public void onUserSwitching(int userId) {
143            maybeStopHotwordDetector();
144        }
145    };
146
147    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
148
149        @Override
150        KeyguardSecurityCallback getCallback() {
151            return mCallback;
152        }
153
154        @Override
155        LockPatternUtils getLockPatternUtils() {
156            return mLockPatternUtils;
157        }
158
159        @Override
160        Context getContext() {
161            return mContext;
162        }};
163
164    public KeyguardSelectorView(Context context) {
165        this(context, null);
166    }
167
168    public KeyguardSelectorView(Context context, AttributeSet attrs) {
169        super(context, attrs);
170        mLockPatternUtils = new LockPatternUtils(getContext());
171    }
172
173    @Override
174    protected void onFinishInflate() {
175        super.onFinishInflate();
176        mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
177        mGlowPadView.setOnTriggerListener(mOnTriggerListener);
178        updateTargets();
179
180        mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
181        View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame);
182        mBouncerFrame = bouncerFrameView.getBackground();
183        if (FLAG_HOTWORD) {
184            mHotwordClient = new HotwordServiceClient(getContext(), mHotwordCallback);
185        }
186    }
187
188    public void setCarrierArea(View carrierArea) {
189        mFadeView = carrierArea;
190    }
191
192    public boolean isTargetPresent(int resId) {
193        return mGlowPadView.getTargetPosition(resId) != -1;
194    }
195
196    @Override
197    public void showUsabilityHint() {
198        mGlowPadView.ping();
199    }
200
201    private void updateTargets() {
202        int currentUserHandle = mLockPatternUtils.getCurrentUser();
203        DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
204        int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUserHandle);
205        boolean secureCameraDisabled = mLockPatternUtils.isSecure()
206                && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
207        boolean cameraDisabledByAdmin = dpm.getCameraDisabled(null, currentUserHandle)
208                || secureCameraDisabled;
209        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext());
210        boolean disabledBySimState = monitor.isSimLocked();
211        boolean cameraTargetPresent =
212            isTargetPresent(R.drawable.ic_lockscreen_camera);
213        boolean searchTargetPresent =
214            isTargetPresent(R.drawable.ic_action_assist_generic);
215
216        if (cameraDisabledByAdmin) {
217            Log.v(TAG, "Camera disabled by Device Policy");
218        } else if (disabledBySimState) {
219            Log.v(TAG, "Camera disabled by Sim State");
220        }
221        boolean currentUserSetup = 0 != Settings.Secure.getIntForUser(
222                mContext.getContentResolver(),
223                Settings.Secure.USER_SETUP_COMPLETE,
224                0 /*default */,
225                currentUserHandle);
226        boolean searchActionAvailable =
227                ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
228                .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
229        mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent
230                || !currentUserSetup;
231        mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent
232                || !currentUserSetup;
233        updateResources();
234    }
235
236    public void updateResources() {
237        // Update the search icon with drawable from the search .apk
238        if (!mSearchDisabled) {
239            Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
240                    .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
241            if (intent != null) {
242                // XXX Hack. We need to substitute the icon here but haven't formalized
243                // the public API. The "_google" metadata will be going away, so
244                // DON'T USE IT!
245                ComponentName component = intent.getComponent();
246                boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component,
247                        ASSIST_ICON_METADATA_NAME + "_google", R.drawable.ic_action_assist_generic);
248
249                if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component,
250                            ASSIST_ICON_METADATA_NAME, R.drawable.ic_action_assist_generic)) {
251                        Slog.w(TAG, "Couldn't grab icon from package " + component);
252                }
253            }
254        }
255
256        mGlowPadView.setEnableTarget(R.drawable.ic_lockscreen_camera, !mCameraDisabled);
257        mGlowPadView.setEnableTarget(R.drawable.ic_action_assist_generic, !mSearchDisabled);
258    }
259
260    void doTransition(View view, float to) {
261        if (mAnim != null) {
262            mAnim.cancel();
263        }
264        mAnim = ObjectAnimator.ofFloat(view, "alpha", to);
265        mAnim.start();
266    }
267
268    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
269        mCallback = callback;
270    }
271
272    public void setLockPatternUtils(LockPatternUtils utils) {
273        mLockPatternUtils = utils;
274    }
275
276    @Override
277    public void reset() {
278        mGlowPadView.reset(false);
279    }
280
281    @Override
282    public boolean needsInput() {
283        return false;
284    }
285
286    @Override
287    public void onPause() {
288        KeyguardUpdateMonitor.getInstance(getContext()).removeCallback(mUpdateCallback);
289    }
290
291    @Override
292    public void onResume(int reason) {
293        KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mUpdateCallback);
294        // TODO: Figure out if there's a better way to do it.
295        // Right now we don't get onPause at all, and onResume gets called
296        // multiple times (even when the screen is turned off with VIEW_REVEALED)
297        if (reason == SCREEN_ON) {
298            if (!KeyguardUpdateMonitor.getInstance(getContext()).isSwitchingUser()) {
299                maybeStartHotwordDetector();
300            }
301        } else {
302            maybeStopHotwordDetector();
303        }
304    }
305
306    @Override
307    public KeyguardSecurityCallback getCallback() {
308        return mCallback;
309    }
310
311    @Override
312    public void showBouncer(int duration) {
313        mIsBouncing = true;
314        KeyguardSecurityViewHelper.
315                showBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
316    }
317
318    @Override
319    public void hideBouncer(int duration) {
320        mIsBouncing = false;
321        KeyguardSecurityViewHelper.
322                hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
323    }
324
325    /**
326     * Start the hotword detector if:
327     * <li> HOTWORDING_ENABLED is true and
328     * <li> HotwordUnlock is initialized and
329     * <li> TelephonyManager is in CALL_STATE_IDLE
330     *
331     * If this method is called when the screen is off,
332     * it attempts to stop hotwording if it's running.
333     */
334    private void maybeStartHotwordDetector() {
335        if (FLAG_HOTWORD) {
336            if (DEBUG) Log.d(TAG, "maybeStartHotwordDetector()");
337            // Don't start it if the screen is off or not showing
338            PowerManager powerManager = (PowerManager) getContext().getSystemService(
339                    Context.POWER_SERVICE);
340            if (!powerManager.isScreenOn()) {
341                if (DEBUG) Log.d(TAG, "screen was off, not starting");
342                return;
343            }
344
345            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext());
346            if (monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE) {
347                if (DEBUG) Log.d(TAG, "Call underway, not starting");
348                return;
349            }
350            if (!mHotwordClient.start()) {
351                Log.w(TAG, "Failed to start the hotword detector");
352            }
353        }
354    }
355
356    /**
357     * Stop hotword detector if HOTWORDING_ENABLED is true.
358     */
359    private void maybeStopHotwordDetector() {
360        if (FLAG_HOTWORD) {
361            if (DEBUG) Log.d(TAG, "maybeStopHotwordDetector()");
362            mHotwordClient.stop();
363        }
364    }
365
366    private final HotwordServiceClient.Callback mHotwordCallback =
367            new HotwordServiceClient.Callback() {
368        private static final String TAG = "HotwordServiceClient.Callback";
369
370        @Override
371        public void onServiceConnected() {
372            if (DEBUG) Log.d(TAG, "onServiceConnected()");
373        }
374
375        @Override
376        public void onServiceDisconnected() {
377            if (DEBUG) Log.d(TAG, "onServiceDisconnected()");
378        }
379
380        @Override
381        public void onHotwordDetectionStarted() {
382            if (DEBUG) Log.d(TAG, "onHotwordDetectionStarted()");
383            // TODO: Change the usage of SecurityMessageDisplay to a better visual indication.
384            mSecurityMessageDisplay.setMessage("\"Ok Google...\"", true);
385        }
386
387        @Override
388        public void onHotwordDetectionStopped() {
389            if (DEBUG) Log.d(TAG, "onHotwordDetectionStopped()");
390            // TODO: Change the usage of SecurityMessageDisplay to a better visual indication.
391        }
392
393        @Override
394        public void onHotwordDetected(String action) {
395            if (DEBUG) Log.d(TAG, "onHotwordDetected(" + action + ")");
396            if (action != null) {
397                Intent intent = new Intent(action);
398                mActivityLauncher.launchActivity(intent, true, true, null, null);
399            }
400            mCallback.userActivity(0);
401        }
402    };
403}
404