/MusicLockTutorial/src/com/marvin/rocklock/RockLockActivity.java
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}