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