/documentation/RockLockTutorial/RockLock_04/src/com/marvin/rocklock/RockLockActivity.java

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