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

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