PageRenderTime 69ms CodeModel.GetById 14ms app.highlight 50ms RepoModel.GetById 2ms app.codeStats 0ms

/documentation/RockLockTutorial/StepByStep/src/com/marvin/rocklock/RockLockActivity.java

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