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