PageRenderTime 77ms CodeModel.GetById 21ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 0ms

/MusicLockTutorial/src/com/marvin/rocklock/RockLockActivity.java

http://eyes-free.googlecode.com/
Java | 409 lines | 317 code | 52 blank | 40 comment | 43 complexity | 58882a61e0e8e604397f8337439f2a96 MD5 | raw file
  1/*
  2 * Copyright (C) 2010 Google Inc.
  3 * 
  4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5 * use this file except in compliance with the License. You may obtain a copy of
  6 * 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, WITHOUT
 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 13 * License for the specific language governing permissions and limitations under
 14 * the License.
 15 */
 16
 17package com.marvin.rocklock;
 18
 19import com.google.marvin.widget.GestureOverlay;
 20import com.google.marvin.widget.GestureOverlay.Gesture;
 21import com.google.marvin.widget.GestureOverlay.GestureListener;
 22
 23import android.app.Activity;
 24import android.app.KeyguardManager;
 25import android.app.KeyguardManager.KeyguardLock;
 26import android.content.BroadcastReceiver;
 27import android.content.Context;
 28import android.content.Intent;
 29import android.content.IntentFilter;
 30import android.os.Bundle;
 31import android.os.Vibrator;
 32import android.speech.tts.TextToSpeech;
 33import android.speech.tts.TextToSpeech.OnInitListener;
 34import android.telephony.PhoneStateListener;
 35import android.telephony.TelephonyManager;
 36import android.view.KeyEvent;
 37import android.view.View;
 38import android.view.WindowManager;
 39import android.view.View.OnClickListener;
 40import android.view.accessibility.AccessibilityEvent;
 41import android.widget.Button;
 42import android.widget.FrameLayout;
 43import android.widget.TextView;
 44
 45import java.text.SimpleDateFormat;
 46import java.util.Calendar;
 47
 48/**
 49 * The main Rock Lock application that runs as an alternate lock screen which
 50 * enables the user to use stroke gestures to play music. If there is no lock
 51 * pattern, Rock Lock will replace the lock screen entirely; dismissing Rock
 52 * Lock will unlock the phone. If there is a lock pattern, Rock Lock will put up
 53 * the default pattern locked screen when the user dismisses Rock Lock.
 54 * 
 55 * @author clchen@google.com (Charles L. Chen)
 56 */
 57public class RockLockActivity extends Activity {
 58    public static final String EXTRA_STARTED_BY_SERVICE = "STARTED_BY_SERVICE";
 59
 60    public static final String TICK_EARCON = "[TICK]";
 61
 62    private static final long[] VIBE_PATTERN = {
 63            0, 10, 70, 80
 64    };
 65
 66    private RockLockActivity self;
 67
 68    private boolean poked = false;
 69
 70    private KeyguardManager keyguardManager;
 71
 72    private KeyguardLock keyguard;
 73
 74    private FrameLayout contentFrame;
 75
 76    private Button unlockButton;
 77
 78    private MusicPlayer mp;
 79
 80    private boolean isSeeking = false;
 81
 82    private boolean seekingStopped = true;
 83
 84    private GestureOverlay gestureOverlay;
 85
 86    private AnimationLayer uiAnimation;
 87
 88    private TextView dateText;
 89
 90    private TextView statusText;
 91
 92    private TextView infoText;
 93
 94    private Vibrator vibe;
 95
 96    private TextToSpeech tts;
 97
 98    // Catch media button events so that controls from plugged in headsets and
 99    // BlueTooth headsets will work.
100    //
101    // Note that this only works if there are NO other apps that are trying to
102    // consume the media button events and aborting the broadcasts; otherwise,
103    // whether it works or not is a function of the order in which the
104    // broadcasts are sent.
105    private BroadcastReceiver mediaButtonReceiver = new BroadcastReceiver() {
106        @Override
107        public void onReceive(Context ctx, Intent data) {
108            this.abortBroadcast();
109            KeyEvent event = data.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
110            int keyCode = event.getKeyCode();
111            if (event.getAction() == KeyEvent.ACTION_DOWN) {
112                if ((keyCode == KeyEvent.KEYCODE_HEADSETHOOK)
113                        || (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)) {
114                    mp.togglePlayPause();
115                    updateDisplayText(null, null, false);
116                } else if (keyCode == KeyEvent.KEYCODE_MEDIA_NEXT) {
117                    mp.nextTrack();
118                    updateDisplayText(null, null, false);
119                } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS) {
120                    mp.prevTrack();
121                    updateDisplayText(null, null, false);
122                }
123            }
124        }
125    };
126
127    // Don't send any accessibility events since this is a fully self voicing
128    // app.
129    @Override
130    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent evt) {
131        return true;
132    }
133
134    /** Called when the activity is first created. */
135    @Override
136    public void onCreate(Bundle savedInstanceState) {
137        super.onCreate(savedInstanceState);
138
139        // Start the service in case it is not already running
140        startService(new Intent(this, ScreenOnHandlerService.class));
141
142        self = this;
143        keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
144        keyguard = keyguardManager.newKeyguardLock("RockLock");
145
146        requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
147        // Need to use FLAG_TURN_SCREEN_ON to make sure that the status bar
148        // stays locked
149        getWindow().addFlags(
150                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
151                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
152                        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
153
154        setContentView(R.layout.main);
155
156        mp = new MusicPlayer(this);
157
158        final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
159        tm.listen(new PhoneStateListener() {
160            @Override
161            public void onCallStateChanged(int state, String incomingNumber) {
162                // If the phone is not idle, immediately quit and let the
163                // default lock screen handle it.
164                if (state != TelephonyManager.CALL_STATE_IDLE) {
165                    finish();
166                    return;
167                }
168            }
169        }, PhoneStateListener.LISTEN_CALL_STATE);
170
171        unlockButton = (Button) findViewById(R.id.unlockButton);
172        unlockButton.setOnClickListener(new OnClickListener() {
173            @Override
174            public void onClick(View arg0) {
175                finish();
176            }
177        });
178
179        IntentFilter filter = new IntentFilter();
180        filter.addAction(Intent.ACTION_MEDIA_BUTTON);
181        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
182        registerReceiver(mediaButtonReceiver, filter);
183
184        vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
185
186        uiAnimation = new AnimationLayer(this);
187
188        gestureOverlay = new GestureOverlay(self, new GestureListener() {
189
190            @Override
191            public void onGestureChange(int g) {
192                isSeeking = false;
193                vibe.vibrate(VIBE_PATTERN, -1);
194                uiAnimation.setDirection(g);
195
196                switch (g) {
197                    case Gesture.UPLEFT:
198                        updateDisplayText(getString(R.string.previous_artist), mp
199                                .getPrevArtistName(), true);
200                        break;
201                    case Gesture.UP:
202                        updateDisplayText(getString(R.string.previous_album),
203                                mp.getPrevAlbumName(), true);
204                        break;
205                    case Gesture.UPRIGHT:
206                        updateDisplayText(getString(R.string.next_artist), mp.getNextArtistName(),
207                                true);
208                        break;
209                    case Gesture.LEFT:
210                        updateDisplayText(getString(R.string.previous_track),
211                                mp.getPrevTrackName(), true);
212                        break;
213                    case Gesture.CENTER:
214                        if (mp.isPlaying()) {
215                            updateDisplayText(getString(R.string.pause), mp.getCurrentSongInfo(),
216                                    false);
217                        } else {
218                            updateDisplayText(getString(R.string.play), mp.getCurrentSongInfo(),
219                                    false);
220                        }
221                        break;
222                    case Gesture.RIGHT:
223                        updateDisplayText(getString(R.string.next_track), mp.getNextTrackName(),
224                                true);
225                        break;
226                    case Gesture.DOWNLEFT:
227                        if (seekingStopped) {
228                            updateDisplayText(getString(R.string.rewind), mp.getCurrentSongInfo(),
229                                    false);
230                            isSeeking = true;
231                            new Thread(new Seeker(-1)).start();
232                        }
233                        break;
234                    case Gesture.DOWN:
235                        updateDisplayText(getString(R.string.next_album), mp.getNextAlbumName(),
236                                true);
237                        break;
238                    case Gesture.DOWNRIGHT:
239                        if (seekingStopped) {
240                            updateDisplayText(getString(R.string.fast_forward), mp
241                                    .getCurrentSongInfo(), false);
242                            isSeeking = true;
243                            new Thread(new Seeker(1)).start();
244                        }
245                        break;
246                }
247            }
248
249            @Override
250            public void onGestureFinish(int g) {
251                isSeeking = false;
252                vibe.vibrate(VIBE_PATTERN, -1);
253                uiAnimation.setDirection(-1);
254                switch (g) {
255                    case Gesture.UPLEFT:
256                        mp.prevArtist();
257                        break;
258                    case Gesture.UP:
259                        mp.prevAlbum();
260                        break;
261                    case Gesture.UPRIGHT:
262                        mp.nextArtist();
263                        break;
264                    case Gesture.LEFT:
265                        mp.prevTrack();
266                        break;
267                    case Gesture.CENTER:
268                        mp.togglePlayPause();
269                        break;
270                    case Gesture.RIGHT:
271                        mp.nextTrack();
272                        break;
273                    case Gesture.DOWN:
274                        mp.nextAlbum();
275                        break;
276                }
277                tts.playEarcon(TICK_EARCON, TextToSpeech.QUEUE_FLUSH, null);
278                updateDisplayText(null, null, false);
279            }
280
281            @Override
282            public void onGestureStart(int g) {
283                poked = true;
284                isSeeking = false;
285                vibe.vibrate(VIBE_PATTERN, -1);
286            }
287
288        });
289
290        contentFrame = (FrameLayout) findViewById(R.id.contentFrame);
291        contentFrame.addView(uiAnimation);
292        View textLayer = this.getLayoutInflater().inflate(R.layout.textlayer, null);
293        dateText = (TextView) textLayer.findViewById(R.id.dateText);
294        statusText = (TextView) textLayer.findViewById(R.id.statusText);
295        infoText = (TextView) textLayer.findViewById(R.id.infoText);
296        contentFrame.addView(textLayer);
297        contentFrame.addView(gestureOverlay);
298
299        tts = new TextToSpeech(this, new OnInitListener() {
300            @Override
301            public void onInit(int arg0) {
302                tts.addEarcon(TICK_EARCON, self.getPackageName(), R.raw.tick_snd);
303                tts.playEarcon(TICK_EARCON, TextToSpeech.QUEUE_FLUSH, null);
304            }
305        });
306    }
307
308    @Override
309    public void onResume() {
310        super.onResume();
311        poked = false;
312        Calendar cal = Calendar.getInstance();
313        int day = cal.get(Calendar.DAY_OF_MONTH);
314        SimpleDateFormat monthFormat = new SimpleDateFormat("MMMM");
315        String monthStr = monthFormat.format(cal.getTime());
316        int year = cal.get(Calendar.YEAR);
317        dateText.setText(monthStr + " " + Integer.toString(day) + ", " + year);
318        keyguard.disableKeyguard();
319        new Thread(new PokeWatcher()).start();
320        tts.playEarcon(TICK_EARCON, TextToSpeech.QUEUE_FLUSH, null);
321    }
322
323    @Override
324    public boolean onKeyDown(int keyCode, KeyEvent event) {
325        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
326            finish();
327            return true;
328        }
329        if (keyCode == KeyEvent.KEYCODE_MENU) {
330            mp.stop();
331            int songPickerType = mp.cycleSongPicker();
332            int songPickerTextResId = R.string.tagged_music_playlist;
333            if (songPickerType == MusicPlayer.ROCKLOCK_PLAYLIST) {
334                songPickerTextResId = R.string.rock_lock_playlist;
335            }
336            updateDisplayText(getString(R.string.app_name), getString(songPickerTextResId), true);
337            return true;
338        }
339        return super.onKeyDown(keyCode, event);
340    }
341
342    @Override
343    public void onDestroy() {
344        super.onDestroy();
345        mp.stop();
346        tts.shutdown();
347        unregisterReceiver(mediaButtonReceiver);
348        keyguardManager.exitKeyguardSecurely(null);
349    }
350
351    public void updateDisplayText(String status, String info, boolean speak) {
352        if ((status == null) || (info == null)) {
353            if (mp.isPlaying()) {
354                statusText.setText(R.string.playing);
355                infoText.setText(mp.getCurrentSongInfo());
356            } else {
357                statusText.setText(R.string.app_name);
358                infoText.setText("The lock that rocks!");
359            }
360            return;
361        }
362        statusText.setText(status);
363        infoText.setText(info);
364        if (speak) {
365            tts.speak(info, TextToSpeech.QUEUE_FLUSH, null);
366        }
367    }
368
369    private class Seeker implements Runnable {
370        private int seekMode = 0;
371
372        public Seeker(int seekDirection) {
373            seekMode = seekDirection;
374        }
375
376        @Override
377        public void run() {
378            while (isSeeking) {
379                try {
380                    Thread.sleep(100);
381                } catch (InterruptedException e) {
382                    // TODO Auto-generated catch block
383                    e.printStackTrace();
384                }
385                if (seekMode == 1) {
386                    mp.seekForward();
387                } else if (seekMode == -1) {
388                    mp.seekBackward();
389                }
390            }
391            seekingStopped = true;
392        }
393    }
394
395    private class PokeWatcher implements Runnable {
396        @Override
397        public void run() {
398            try {
399                Thread.sleep(5000);
400            } catch (InterruptedException e) {
401                e.printStackTrace();
402            }
403            if (!poked && (mp != null) && !mp.isPlaying()) {
404                keyguard.reenableKeyguard();
405                finish();
406            }
407        }
408    }
409}