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