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.keyguard;
18
19import android.app.Activity;
20import android.content.Context;
21import android.media.AudioManager;
22import android.media.IAudioService;
23import android.os.RemoteException;
24import android.os.ServiceManager;
25import android.telephony.TelephonyManager;
26import android.util.AttributeSet;
27import android.util.Log;
28import android.util.Slog;
29import android.view.KeyEvent;
30import android.widget.FrameLayout;
31
32/**
33 * Base class for keyguard view.  {@link #reset} is where you should
34 * reset the state of your view.  Use the {@link KeyguardViewCallback} via
35 * {@link #getCallback()} to send information back (such as poking the wake lock,
36 * or finishing the keyguard).
37 *
38 * Handles intercepting of media keys that still work when the keyguard is
39 * showing.
40 */
41public abstract class KeyguardViewBase extends FrameLayout {
42
43    private AudioManager mAudioManager;
44    private TelephonyManager mTelephonyManager = null;
45    protected KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
46
47    // Whether the volume keys should be handled by keyguard. If true, then
48    // they will be handled here for specific media types such as music, otherwise
49    // the audio service will bring up the volume dialog.
50    private static final boolean KEYGUARD_MANAGES_VOLUME = true;
51
52    public KeyguardViewBase(Context context) {
53        this(context, null);
54    }
55
56    public KeyguardViewBase(Context context, AttributeSet attrs) {
57        super(context, attrs);
58    }
59
60    /**
61     * Called when the screen turned off.
62     */
63    abstract public void onScreenTurnedOff();
64
65    /**
66     * Called when the screen turned on.
67     */
68    abstract public void onScreenTurnedOn();
69
70    /**
71     * Called when the view needs to be shown.
72     */
73    abstract public void show();
74
75    /**
76     * Verify that the user can get past the keyguard securely.  This is called,
77     * for example, when the phone disables the keyguard but then wants to launch
78     * something else that requires secure access.
79     *
80     * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
81     */
82    abstract public void verifyUnlock();
83
84    /**
85     * Called before this view is being removed.
86     */
87    abstract public void cleanUp();
88
89    /**
90     * Gets the desired user activity timeout in milliseconds, or -1 if the
91     * default should be used.
92     */
93    abstract public long getUserActivityTimeout();
94
95    @Override
96    public boolean dispatchKeyEvent(KeyEvent event) {
97        if (interceptMediaKey(event)) {
98            return true;
99        }
100        return super.dispatchKeyEvent(event);
101    }
102
103    /**
104     * Allows the media keys to work when the keyguard is showing.
105     * The media keys should be of no interest to the actual keyguard view(s),
106     * so intercepting them here should not be of any harm.
107     * @param event The key event
108     * @return whether the event was consumed as a media key.
109     */
110    private boolean interceptMediaKey(KeyEvent event) {
111        final int keyCode = event.getKeyCode();
112        if (event.getAction() == KeyEvent.ACTION_DOWN) {
113            switch (keyCode) {
114                case KeyEvent.KEYCODE_MEDIA_PLAY:
115                case KeyEvent.KEYCODE_MEDIA_PAUSE:
116                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
117                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
118                     * in-call to avoid music playback */
119                    if (mTelephonyManager == null) {
120                        mTelephonyManager = (TelephonyManager) getContext().getSystemService(
121                                Context.TELEPHONY_SERVICE);
122                    }
123                    if (mTelephonyManager != null &&
124                            mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
125                        return true;  // suppress key event
126                    }
127                case KeyEvent.KEYCODE_MUTE:
128                case KeyEvent.KEYCODE_HEADSETHOOK:
129                case KeyEvent.KEYCODE_MEDIA_STOP:
130                case KeyEvent.KEYCODE_MEDIA_NEXT:
131                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
132                case KeyEvent.KEYCODE_MEDIA_REWIND:
133                case KeyEvent.KEYCODE_MEDIA_RECORD:
134                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
135                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
136                    handleMediaKeyEvent(event);
137                    return true;
138                }
139
140                case KeyEvent.KEYCODE_VOLUME_UP:
141                case KeyEvent.KEYCODE_VOLUME_DOWN:
142                case KeyEvent.KEYCODE_VOLUME_MUTE: {
143                    if (KEYGUARD_MANAGES_VOLUME) {
144                        synchronized (this) {
145                            if (mAudioManager == null) {
146                                mAudioManager = (AudioManager) getContext().getSystemService(
147                                        Context.AUDIO_SERVICE);
148                            }
149                        }
150                        // Volume buttons should only function for music (local or remote).
151                        // TODO: Actually handle MUTE.
152                        mAudioManager.adjustLocalOrRemoteStreamVolume(
153                                AudioManager.STREAM_MUSIC,
154                                keyCode == KeyEvent.KEYCODE_VOLUME_UP
155                                        ? AudioManager.ADJUST_RAISE
156                                        : AudioManager.ADJUST_LOWER);
157                        // Don't execute default volume behavior
158                        return true;
159                    } else {
160                        return false;
161                    }
162                }
163            }
164        } else if (event.getAction() == KeyEvent.ACTION_UP) {
165            switch (keyCode) {
166                case KeyEvent.KEYCODE_MUTE:
167                case KeyEvent.KEYCODE_HEADSETHOOK:
168                case KeyEvent.KEYCODE_MEDIA_PLAY:
169                case KeyEvent.KEYCODE_MEDIA_PAUSE:
170                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
171                case KeyEvent.KEYCODE_MEDIA_STOP:
172                case KeyEvent.KEYCODE_MEDIA_NEXT:
173                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
174                case KeyEvent.KEYCODE_MEDIA_REWIND:
175                case KeyEvent.KEYCODE_MEDIA_RECORD:
176                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
177                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
178                    handleMediaKeyEvent(event);
179                    return true;
180                }
181            }
182        }
183        return false;
184    }
185
186    void handleMediaKeyEvent(KeyEvent keyEvent) {
187        IAudioService audioService = IAudioService.Stub.asInterface(
188                ServiceManager.checkService(Context.AUDIO_SERVICE));
189        if (audioService != null) {
190            try {
191                audioService.dispatchMediaKeyEvent(keyEvent);
192            } catch (RemoteException e) {
193                Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e);
194            }
195        } else {
196            Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event");
197        }
198    }
199
200    @Override
201    public void dispatchSystemUiVisibilityChanged(int visibility) {
202        super.dispatchSystemUiVisibilityChanged(visibility);
203
204        if (!(mContext instanceof Activity)) {
205            setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
206        }
207    }
208
209    public void setViewMediatorCallback(
210            KeyguardViewMediator.ViewMediatorCallback viewMediatorCallback) {
211        mViewMediatorCallback = viewMediatorCallback;
212    }
213}
214