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

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