PageRenderTime 34ms CodeModel.GetById 2ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

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

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