1/*
2 * Copyright (C) 2007 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.internal.policy.impl.keyguard;
18
19import android.app.Activity;
20import android.content.Context;
21import android.graphics.Canvas;
22import android.graphics.ColorFilter;
23import android.graphics.PixelFormat;
24import android.graphics.PorterDuff;
25import android.graphics.drawable.Drawable;
26import android.media.AudioManager;
27import android.media.IAudioService;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.telephony.TelephonyManager;
31import android.util.AttributeSet;
32import android.util.Log;
33import android.util.Slog;
34import android.view.KeyEvent;
35import android.widget.FrameLayout;
36
37/**
38 * Base class for keyguard view.  {@link #reset} is where you should
39 * reset the state of your view.  Use the {@link KeyguardViewCallback} via
40 * {@link #getCallback()} to send information back (such as poking the wake lock,
41 * or finishing the keyguard).
42 *
43 * Handles intercepting of media keys that still work when the keyguard is
44 * showing.
45 */
46public abstract class KeyguardViewBase extends FrameLayout {
47
48    private static final int BACKGROUND_COLOR = 0x70000000;
49    private AudioManager mAudioManager;
50    private TelephonyManager mTelephonyManager = null;
51    protected KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
52
53    // Whether the volume keys should be handled by keyguard. If true, then
54    // they will be handled here for specific media types such as music, otherwise
55    // the audio service will bring up the volume dialog.
56    private static final boolean KEYGUARD_MANAGES_VOLUME = true;
57
58    // This is a faster way to draw the background on devices without hardware acceleration
59    private static final Drawable mBackgroundDrawable = new Drawable() {
60        @Override
61        public void draw(Canvas canvas) {
62            canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
63        }
64
65        @Override
66        public void setAlpha(int alpha) {
67        }
68
69        @Override
70        public void setColorFilter(ColorFilter cf) {
71        }
72
73        @Override
74        public int getOpacity() {
75            return PixelFormat.TRANSLUCENT;
76        }
77    };
78
79    public KeyguardViewBase(Context context) {
80        this(context, null);
81    }
82
83    public KeyguardViewBase(Context context, AttributeSet attrs) {
84        super(context, attrs);
85        resetBackground();
86    }
87
88    public void resetBackground() {
89        setBackground(mBackgroundDrawable);
90    }
91
92    /**
93     * Called when you need to reset the state of your view.
94     */
95    abstract public void reset();
96
97    /**
98     * Called when the screen turned off.
99     */
100    abstract public void onScreenTurnedOff();
101
102    /**
103     * Called when the screen turned on.
104     */
105    abstract public void onScreenTurnedOn();
106
107    /**
108     * Called when the view needs to be shown.
109     */
110    abstract public void show();
111
112    /**
113     * Called when a key has woken the device to give us a chance to adjust our
114     * state according the the key.  We are responsible for waking the device
115     * (by poking the wake lock) once we are ready.
116     *
117     * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
118     * Be sure not to take any action that takes a long time; any significant
119     * action should be posted to a handler.
120     *
121     * @param keyCode The wake key, which may be relevant for configuring the
122     *   keyguard.  May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking for a reason
123     *   other than a key press.
124     */
125    abstract public void wakeWhenReadyTq(int keyCode);
126
127    /**
128     * Verify that the user can get past the keyguard securely.  This is called,
129     * for example, when the phone disables the keyguard but then wants to launch
130     * something else that requires secure access.
131     *
132     * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
133     */
134    abstract public void verifyUnlock();
135
136    /**
137     * Called before this view is being removed.
138     */
139    abstract public void cleanUp();
140
141    /**
142     * Gets the desired user activity timeout in milliseconds, or -1 if the
143     * default should be used.
144     */
145    abstract public long getUserActivityTimeout();
146
147    @Override
148    public boolean dispatchKeyEvent(KeyEvent event) {
149        if (interceptMediaKey(event)) {
150            return true;
151        }
152        return super.dispatchKeyEvent(event);
153    }
154
155    /**
156     * Allows the media keys to work when the keyguard is showing.
157     * The media keys should be of no interest to the actual keyguard view(s),
158     * so intercepting them here should not be of any harm.
159     * @param event The key event
160     * @return whether the event was consumed as a media key.
161     */
162    private boolean interceptMediaKey(KeyEvent event) {
163        final int keyCode = event.getKeyCode();
164        if (event.getAction() == KeyEvent.ACTION_DOWN) {
165            switch (keyCode) {
166                case KeyEvent.KEYCODE_MEDIA_PLAY:
167                case KeyEvent.KEYCODE_MEDIA_PAUSE:
168                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
169                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
170                     * in-call to avoid music playback */
171                    if (mTelephonyManager == null) {
172                        mTelephonyManager = (TelephonyManager) getContext().getSystemService(
173                                Context.TELEPHONY_SERVICE);
174                    }
175                    if (mTelephonyManager != null &&
176                            mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
177                        return true;  // suppress key event
178                    }
179                case KeyEvent.KEYCODE_MUTE:
180                case KeyEvent.KEYCODE_HEADSETHOOK:
181                case KeyEvent.KEYCODE_MEDIA_STOP:
182                case KeyEvent.KEYCODE_MEDIA_NEXT:
183                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
184                case KeyEvent.KEYCODE_MEDIA_REWIND:
185                case KeyEvent.KEYCODE_MEDIA_RECORD:
186                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
187                    handleMediaKeyEvent(event);
188                    return true;
189                }
190
191                case KeyEvent.KEYCODE_VOLUME_UP:
192                case KeyEvent.KEYCODE_VOLUME_DOWN:
193                case KeyEvent.KEYCODE_VOLUME_MUTE: {
194                    if (KEYGUARD_MANAGES_VOLUME) {
195                        synchronized (this) {
196                            if (mAudioManager == null) {
197                                mAudioManager = (AudioManager) getContext().getSystemService(
198                                        Context.AUDIO_SERVICE);
199                            }
200                        }
201                        // Volume buttons should only function for music (local or remote).
202                        // TODO: Actually handle MUTE.
203                        mAudioManager.adjustLocalOrRemoteStreamVolume(
204                                AudioManager.STREAM_MUSIC,
205                                keyCode == KeyEvent.KEYCODE_VOLUME_UP
206                                        ? AudioManager.ADJUST_RAISE
207                                        : AudioManager.ADJUST_LOWER);
208                        // Don't execute default volume behavior
209                        return true;
210                    } else {
211                        return false;
212                    }
213                }
214            }
215        } else if (event.getAction() == KeyEvent.ACTION_UP) {
216            switch (keyCode) {
217                case KeyEvent.KEYCODE_MUTE:
218                case KeyEvent.KEYCODE_HEADSETHOOK:
219                case KeyEvent.KEYCODE_MEDIA_PLAY:
220                case KeyEvent.KEYCODE_MEDIA_PAUSE:
221                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
222                case KeyEvent.KEYCODE_MEDIA_STOP:
223                case KeyEvent.KEYCODE_MEDIA_NEXT:
224                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
225                case KeyEvent.KEYCODE_MEDIA_REWIND:
226                case KeyEvent.KEYCODE_MEDIA_RECORD:
227                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
228                    handleMediaKeyEvent(event);
229                    return true;
230                }
231            }
232        }
233        return false;
234    }
235
236    void handleMediaKeyEvent(KeyEvent keyEvent) {
237        IAudioService audioService = IAudioService.Stub.asInterface(
238                ServiceManager.checkService(Context.AUDIO_SERVICE));
239        if (audioService != null) {
240            try {
241                audioService.dispatchMediaKeyEvent(keyEvent);
242            } catch (RemoteException e) {
243                Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e);
244            }
245        } else {
246            Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event");
247        }
248    }
249
250    @Override
251    public void dispatchSystemUiVisibilityChanged(int visibility) {
252        super.dispatchSystemUiVisibilityChanged(visibility);
253
254        if (!(mContext instanceof Activity)) {
255            setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
256        }
257    }
258
259    public void setViewMediatorCallback(
260            KeyguardViewMediator.ViewMediatorCallback viewMediatorCallback) {
261        mViewMediatorCallback = viewMediatorCallback;
262    }
263
264}
265