PageRenderTime 30ms CodeModel.GetById 3ms app.highlight 21ms RepoModel.GetById 3ms app.codeStats 0ms

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

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