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

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