/MusicLockTutorial/src/com/marvin/rocklock/RockLockActivity.java

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