[npr-android-app] push by justinfr...@gmail.com - 2.3 updates including audio focus, service resume, and a number of bug... on 2012-04-25 21:16 GMT

21 views
Skip to first unread message

npr-and...@googlecode.com

unread,
Apr 25, 2012, 5:16:44 PM4/25/12
to npr-a...@googlegroups.com
Revision: dcb19f0dfcd9
Author: Justin Friberg <justin...@hotmail.com>
Date: Wed Apr 25 14:01:45 2012
Log: 2.3 updates including audio focus, service resume, and a number
of bug fixes

http://code.google.com/p/npr-android-app/source/detail?r=dcb19f0dfcd9

Added:
/Npr/libs/crittercism_v1_2_3.jar
/Npr/res/drawable-hdpi/stop_button_normal.png
/Npr/res/drawable-hdpi/stop_button_pressed.png
/Npr/src/org/npr/android/util/AudioBecomingNoisyReceiver.java
/Npr/src/org/npr/android/util/AudioManagerProxy.java
Deleted:
/Npr/.externalToolBuilders/org.npr.android.AddSvnRevisionToVersion.launch
/Npr/svn-revision.build.xml
Modified:
/Npr/AndroidManifest.xml
/Npr/Npr.iml
/Npr/res/drawable-hdpi/sponsorship_gradient_slice.png
/Npr/src/org/npr/android/news/BannerView.java
/Npr/src/org/npr/android/news/HourlyNewsActivity.java
/Npr/src/org/npr/android/news/NavigationView.java
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsStoryActivity.java
/Npr/src/org/npr/android/news/Playable.java
/Npr/src/org/npr/android/news/PlaybackService.java
/Npr/src/org/npr/android/news/PlaylistView.java
/Npr/src/org/npr/android/news/PodcastActivity.java
/Npr/src/org/npr/android/news/ProgramStoryListActivity.java
/Npr/src/org/npr/android/news/RootActivity.java
/Npr/src/org/npr/android/util/PlaylistRepository.java
/Npr/src/org/npr/api/Station.java
/Npr/src/org/npr/api/Story.java
/Npr_Test/Npr_Test.iml

=======================================
--- /dev/null
+++ /Npr/libs/crittercism_v1_2_3.jar Wed Apr 25 14:01:45 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/stop_button_normal.png Wed Apr 25 14:01:45 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/stop_button_pressed.png Wed Apr 25 14:01:45 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/util/AudioBecomingNoisyReceiver.java Wed Apr
25 14:01:45 2012
@@ -0,0 +1,19 @@
+package org.npr.android.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import org.npr.android.news.PlaybackService;
+
+public class AudioBecomingNoisyReceiver extends BroadcastReceiver {
+
+ private static final String LOG_TAG =
AudioBecomingNoisyReceiver.class.getName();
+
+ public void onReceive(Context context, Intent intent) {
+ Log.d(LOG_TAG, "Audio becoming noisy - pausing.");
+ Intent pauseIntent = new Intent(context, PlaybackService.class);
+ pauseIntent.setAction(PlaybackService.SERVICE_PAUSE);
+ context.startService(pauseIntent);
+ }
+}
=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/util/AudioManagerProxy.java Wed Apr 25
14:01:45 2012
@@ -0,0 +1,124 @@
+package org.npr.android.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.util.Log;
+import org.npr.android.news.PlaybackService;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class AudioManagerProxy {
+
+ private static final String LOG_TAG = AudioManagerProxy.class.getName();
+ private static final int AUDIOMANAGER_AUDIOFOCUS_GAIN = 1;
+ private static final int AUDIOMANAGER_AUDIOFOCUS_LOSS = -1;
+ private static final int AUDIOMANAGER_AUDIO_REQUEST_GRANTED = 1;
+
+ private AudioManager audioManager;
+ private Object myOnAudioFocusChangeListener = null;
+ private boolean hasFocus = false;
+
+ public AudioManagerProxy(Context context) {
+ audioManager = (AudioManager)
context.getSystemService(Context.AUDIO_SERVICE);
+
+ Log.v(LOG_TAG, "Looking for audio focus change support");
+ // instantiate the OnAudioFocusChangeListener using reflection (as it
only exists from Android 2.2 onwards)
+ Class<?>[] innerClasses = audioManager.getClass().getDeclaredClasses();
+ for (Class<?> classInterface : innerClasses) {
+ if
(classInterface.getSimpleName().equalsIgnoreCase("OnAudioFocusChangeListener"))
{
+ Class<?>[] classArray = new Class<?>[1];
+ classArray[0] = classInterface;
+ myOnAudioFocusChangeListener =
Proxy.newProxyInstance(classInterface.getClassLoader(),
+ classArray, new ProxyOnAudioFocusChangeListener(context));
+ Log.v(LOG_TAG, "Audio focus change support found");
+ }
+ }
+ }
+
+ public boolean getAudioFocus() {
+ if (myOnAudioFocusChangeListener != null) {
+ Log.v(LOG_TAG, "Getting audio focus");
+ try {
+ Method[] methods = audioManager.getClass().getDeclaredMethods();
+ for (Method method : methods) {
+ if (method.getName().equalsIgnoreCase("requestAudioFocus")) {
+ Object object = method.invoke(audioManager,
+ myOnAudioFocusChangeListener,
+ AudioManager.STREAM_MUSIC,
+ AUDIOMANAGER_AUDIOFOCUS_GAIN);
+ if (object != null && (Integer) object ==
AUDIOMANAGER_AUDIO_REQUEST_GRANTED) {
+ hasFocus = true;
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ return hasFocus;
+ }
+ // If audio focus isn't supported by the OS, just return true
+ return true;
+ }
+
+ public void releaseAudioFocus() {
+ if (myOnAudioFocusChangeListener != null && hasFocus) {
+ Log.v(LOG_TAG, "Releasing audio focus");
+ try {
+ Method[] methods = audioManager.getClass().getDeclaredMethods();
+ for (Method method : methods) {
+ if (method.getName().equalsIgnoreCase("abandonAudioFocus")) {
+ method.invoke(audioManager, myOnAudioFocusChangeListener);
+ hasFocus = false;
+ }
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ }
+ }
+
+ private class ProxyOnAudioFocusChangeListener implements
InvocationHandler {
+
+ private Context context;
+
+ public ProxyOnAudioFocusChangeListener(Context context) {
+ this.context = context;
+ }
+
+ public void onAudioFocusChange(int focusChange) {
+ Log.v(LOG_TAG, "Audio focus change. focusChange = " + focusChange);
+ if (hasFocus) {
+ if (focusChange > 0) {
+ Log.v(LOG_TAG, "Audio focus gained");
+ Intent intent = new Intent(context, PlaybackService.class);
+ intent.setAction(PlaybackService.SERVICE_RESUME_PLAYING);
+ context.startService(intent);
+ } else {
+ Log.v(LOG_TAG, "Audio focus lost");
+ Intent intent = new Intent(context, PlaybackService.class);
+ intent.setAction(PlaybackService.SERVICE_PAUSE);
+ intent.putExtra(PlaybackService.EXTRA_KEEP_AUDIO_FOCUS,
+ (focusChange != AUDIOMANAGER_AUDIOFOCUS_LOSS));
+ context.startService(intent);
+ }
+ }
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
+ Object result = null;
+ try {
+ if (args != null) {
+ if (method.getName().equalsIgnoreCase("onAudioFocusChange") &&
args[0] instanceof Integer) {
+ onAudioFocusChange((Integer) args[0]);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("unexpected invocation exception: " +
e.getMessage());
+ }
+ return result;
+ }
+ }
+}
=======================================
---
/Npr/.externalToolBuilders/org.npr.android.AddSvnRevisionToVersion.launch
Sun May 22 12:54:47 2011
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration
type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
-<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED"
value="true"/>
-<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL"
value="false"/>
-<booleanAttribute key="org.eclipse.debug.core.ATTR_REFRESH_RECURSIVE"
value="false"/>
-<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE"
value="${project}"/>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/Npr/svn-revision.build.xml"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="1"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.debug.core.capture_output"
value="false"/>
-<booleanAttribute key="org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON"
value="false"/>
-<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND"
value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER"
value="org.eclipse.ant.ui.AntClasspathProvider"/>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH"
value="true"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Npr"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION"
value="${project_loc}/svn-revision.build.xml"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS"
value="full,incremental,"/>
-<booleanAttribute
key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
-</launchConfiguration>
=======================================
--- /Npr/svn-revision.build.xml Mon Oct 4 11:32:32 2010
+++ /dev/null
@@ -1,30 +0,0 @@
-<project default="svn-revision">
- <target name="svn-revision">
- <property name="revision" value="HEAD"/>
-
- <!--
- Exec `svn info` to get the revision of the code being built.
- Note that the output of this command is in "name: value" format
- so we can pretend it's a java properties file.
- -->
- <exec executable="svn" output="svn-info.properties">
- <arg line="info"/>
- </exec>
- <property prefix="svn" file="svn-info.properties"/>
- <echo>Revision: ${svn.Revision}</echo>
-
- <!--
- Write the revision number into the Manifest as the last
- segment of the VersionName property
- -->
- <replaceregexp file="AndroidManifest.xml"
- match='android:versionName="([^".]+\.[^".]+)(\.[^"]*)?"'
- replace='android:versionName="\1.${svn.Revision}"'
- />
-
- <!--
- Clean up
- -->
- <delete file="svn-info.properties"/>
- </target>
-</project>
=======================================
--- /Npr/AndroidManifest.xml Thu Jan 19 16:46:32 2012
+++ /Npr/AndroidManifest.xml Wed Apr 25 14:01:45 2012
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.npr.android.news" android:versionName="2.2.2"
+ package="org.npr.android.news" android:versionName="2.3"
android:versionCode="3">
<application
android:label="@string/app_name"
@@ -74,12 +74,23 @@
<provider
android:name="org.npr.android.util.FavoriteStationsProvider"
android:authorities="org.npr.android.util.FavoriteStations"/>
-
+ <receiver
android:name="org.npr.android.util.AudioBecomingNoisyReceiver">
+ <intent-filter>
+ <action android:name="android.media.AUDIO_BECOMING_NOISY"/>
+ </intent-filter>
+ </receiver>
<service android:name="PlaybackService"/>

<uses-library
android:name="android.test.runner"/>

+ <!-- Start of Crittercism.com Code -->
+ <activity android:name="com.crittercism.FeedbackActivity"/>
+ <activity android:name="com.crittercism.FeedbackCreateActivity"/>
+ <activity android:name="com.crittercism.FeedbackDetailsActivity"/>
+ <activity android:name="com.crittercism.NotificationActivity"/>
+ <!-- End of Crittercism.com Code -->
+
<!-- declare the default searchable Activity for the whole app -->
<meta-data android:name="android.app.default_searchable"
android:value=".SearchResultsActivity"/>
@@ -96,6 +107,11 @@
android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission
android:name="android.permission.BROADCAST_STICKY"/>
+ <uses-permission
+ android:name="android.permission.BLUETOOTH"/>
+ <uses-permission
+ android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+
<uses-sdk
android:targetSdkVersion="7"
android:minSdkVersion="7"/>
=======================================
--- /Npr/Npr.iml Fri Jan 6 11:10:49 2012
+++ /Npr/Npr.iml Wed Apr 25 14:01:45 2012
@@ -19,6 +19,7 @@
<option name="LIBRARY_PROJECT" value="false" />
<option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="true" />
<option name="GENERATE_UNSIGNED_APK" value="false" />
+ <option name="CUSTOM_DEBUG_KEYSTORE_PATH" value="" />
</configuration>
</facet>
</component>
@@ -39,6 +40,7 @@
<SOURCES />
</library>
</orderEntry>
+ <orderEntry type="library" name="crittercism_v1_2_3" level="project" />
</component>
</module>

=======================================
--- /Npr/res/drawable-hdpi/sponsorship_gradient_slice.png Tue Oct 25
08:27:38 2011
+++ /Npr/res/drawable-hdpi/sponsorship_gradient_slice.png Wed Apr 25
14:01:45 2012
Binary file, no diff available.
=======================================
--- /Npr/src/org/npr/android/news/BannerView.java Thu Jan 19 16:15:42 2012
+++ /Npr/src/org/npr/android/news/BannerView.java Wed Apr 25 14:01:45 2012
@@ -299,12 +299,16 @@
}

sponsorshipWindowState = SponsorshipWindowStates.Closing;
- AnimationReturn r = getAnimationWindow();
- animationView = r.view;
- bannerHeight = r.bannerHeight;
- startY = screenHeight - r.bitmapHeight;
- animationStartTime = System.currentTimeMillis();
- handler.sendEmptyMessage(MSG_SCROLL_OUT);
+ try {
+ AnimationReturn r = getAnimationWindow();
+ animationView = r.view;
+ bannerHeight = r.bannerHeight;
+ startY = screenHeight - r.bitmapHeight;
+ animationStartTime = System.currentTimeMillis();
+ handler.sendEmptyMessage(MSG_SCROLL_OUT);
+ } catch (WindowManager.BadTokenException e) {
+ Log.e(LOG_TAG, "Bad token exception caught, bailing");
+ }
}

@Override
=======================================
--- /Npr/src/org/npr/android/news/HourlyNewsActivity.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/HourlyNewsActivity.java Wed Apr 25
14:01:45 2012
@@ -80,7 +80,6 @@
ViewGroup container = (ViewGroup) findViewById(R.id.TitleContent);
ViewGroup.inflate(this, R.layout.hourly_news, container);

-
hourlyPubDate = (TextView) findViewById(R.id.HourlyPubDate);
hourlyDuration = (TextView) findViewById(R.id.HourlyDuration);

@@ -125,7 +124,7 @@
Log.w(LOG_TAG, "Play hourly news podcast");
final Playable newsPlayable = Playable.PlayableFactory
.fromPodcastItem(podcast.getItems().get(0), "", "");
- newsPlayable.setActivity(HourlyNewsActivity.class);
+ newsPlayable.setActivityName(HourlyNewsActivity.class.getName());
playSingleNow(newsPlayable);
break;
}
=======================================
--- /Npr/src/org/npr/android/news/NavigationView.java Sun Sep 11 10:17:14
2011
+++ /Npr/src/org/npr/android/news/NavigationView.java Wed Apr 25 14:01:45
2012
@@ -111,7 +111,7 @@

String grouping = null;
String description = "Top Stories";
- String topicId = "1002";
+ String topicId = "1001";
Map<String, String> params = new HashMap<String, String>();
params.put("id", topicId);
params.put("fields", ApiConstants.STORY_FIELDS);
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Thu Jan 19 16:15:42
2012
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Wed Apr 25 14:01:45
2012
@@ -499,7 +499,7 @@
// When first starting up, load the top news stories
private void setDefaultIntent() {
Map<String, String> params = new HashMap<String, String>();
- params.put("id", "1002");
+ params.put("id", "1001");
params.put("fields", ApiConstants.STORY_FIELDS);
params.put("sort", "assigned");
String url = ApiConstants.instance().createUrl(ApiConstants.STORY_PATH,
=======================================
--- /Npr/src/org/npr/android/news/NewsStoryActivity.java Thu Jan 19
16:15:42 2012
+++ /Npr/src/org/npr/android/news/NewsStoryActivity.java Wed Apr 25
14:01:45 2012
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.text.Html;
@@ -486,7 +487,20 @@
private class PlaybackChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- Long playlistId = intent.getLongExtra(PlaybackService.EXTRA_ID, -1);
+ Long playlistId = -1L;
+ Playable playable = null;
+ try {
+ Context serviceContext =
context.createPackageContext(context.getPackageName(),
+ Context.CONTEXT_INCLUDE_CODE |
Context.CONTEXT_IGNORE_SECURITY);
+ Bundle bundle = intent.getExtras();
+ bundle.setClassLoader(serviceContext.getClassLoader());
+ playable = bundle.getParcelable(Playable.PLAYABLE_TYPE);
+ } catch (PackageManager.NameNotFoundException e)
+ {
+ }
+ if (playable != null) {
+ playlistId = playable.getId();
+ }
if (playlistId != -1) {
PlaylistEntry pe = playlistRepository.getPlaylistItemFromId
(playlistId);
=======================================
--- /Npr/src/org/npr/android/news/Playable.java Fri Jan 6 11:10:49 2012
+++ /Npr/src/org/npr/android/news/Playable.java Wed Apr 25 14:01:45 2012
@@ -27,12 +27,12 @@
private static final String LOG_TAG = Playable.class.getName();

private Playable(long id, String url, String title, boolean isStream,
- Class<?> activity, String activityData) {
+ String activityName, String activityData) {
setId(id);
setUrl(url);
setTitle(title);
setIsStream(isStream);
- setActivity(activity);
+ setActivityName(activityName);
setActivityData(activityData);
}

@@ -40,7 +40,7 @@
private String url;
private String title;
private boolean isStream;
- private Class<?> activity;
+ private String activityName;
private String activityData;

public static final String PLAYABLE_TYPE = "PLAYABLE_TYPE";
@@ -77,12 +77,12 @@
isStream = stream;
}

- public Class<?> getActivity() {
- return activity;
+ public String getActivityName() {
+ return activityName;
}

- public void setActivity(Class<?> activity) {
- this.activity = activity;
+ public void setActivityName(String activityName) {
+ this.activityName = activityName;
}

public String getActivityData() {
@@ -104,7 +104,7 @@
out.writeString(url);
out.writeString(title);
out.writeString(Boolean.toString(isStream));
- out.writeString(activity.getName());
+ out.writeString(activityName);
out.writeString(activityData);
}

@@ -119,16 +119,12 @@
}
};

- private Playable(Parcel in) {
+ public Playable(Parcel in) {
id = in.readLong();
url = in.readString();
title = in.readString();
isStream = Boolean.parseBoolean(in.readString());
- try {
- activity = Class.forName(in.readString());
- } catch (ClassNotFoundException e) {
- Log.e(LOG_TAG, "Class not found");
- }
+ activityName = in.readString();
activityData = in.readString();
}

@@ -136,28 +132,28 @@
public static Playable fromPlaylistEntry(PlaylistEntry playlistEntry) {
return new Playable(playlistEntry.id, playlistEntry.url,
playlistEntry.title, playlistEntry.isStream,
- NewsStoryActivity.class, playlistEntry.storyID);
+ NewsStoryActivity.class.getName(), playlistEntry.storyID);
}

public static Playable fromPodcastItem(Podcast.Item item, String url,
String title) {
return new Playable(-1, item.getUrl(), item.getTitle(), true,
- PodcastActivity.class, url + ' ' + title);
+ PodcastActivity.class.getName(), url + ' ' + title);
}

public static Playable fromStationStream(String stationId,
Station.AudioStream stream) {
return new Playable(-1, stream.getUrl(), stream.getTitle(), true,
- StationDetailsActivity.class, stationId);
+ StationDetailsActivity.class.getName(), stationId);
}

public static Playable fromStory(Story story) {
return new Playable(-1, story.getPlayableUrl(), story.getTitle(),
- false, NewsStoryActivity.class, null);
+ false, null, null);
}

- public static Playable fromURL(String url, String title, Class<?>
activity) {
- return new Playable(-1, url, title, false, activity, null);
+ public static Playable fromURL(String url, String title) {
+ return new Playable(-1, url, title, false, null, null);
}
}
}
=======================================
--- /Npr/src/org/npr/android/news/PlaybackService.java Thu Jan 19 16:15:42
2012
+++ /Npr/src/org/npr/android/news/PlaybackService.java Wed Apr 25 14:01:45
2012
@@ -34,11 +34,7 @@
import android.telephony.TelephonyManager;
import android.util.Log;

-import org.npr.android.util.M3uParser;
-import org.npr.android.util.PlaylistParser;
-import org.npr.android.util.PlaylistRepository;
-import org.npr.android.util.PlsParser;
-import org.npr.android.util.Tracker;
+import org.npr.android.util.*;

import java.io.File;
import java.io.FileOutputStream;
@@ -69,6 +65,9 @@
public static final String SERVICE_PLAY_ENTRY = SERVICE_PREFIX
+ "PLAY_ENTRY";
public static final String SERVICE_TOGGLE_PLAY = SERVICE_PREFIX +
"TOGGLE_PLAY";
+ public static final String SERVICE_RESUME_PLAYING = SERVICE_PREFIX +
+ "RESUME_PLAYING";
+ public static final String SERVICE_PAUSE = SERVICE_PREFIX + "PAUSE";
public static final String SERVICE_BACK_30 = SERVICE_PREFIX + "BACK_30";
public static final String SERVICE_FORWARD_30 = SERVICE_PREFIX
+ "FORWARD_30";
public static final String SERVICE_SEEK_TO = SERVICE_PREFIX + "SEEK_TO";
@@ -80,16 +79,16 @@
public static final String SERVICE_CLEAR_PLAYER = SERVICE_PREFIX +
"CLEAR_PLAYER";

- public static final String EXTRA_ID = SERVICE_PREFIX + "ID";
- public static final String EXTRA_TITLE = SERVICE_PREFIX + "TITLE";
public static final String EXTRA_DOWNLOADED = SERVICE_PREFIX
+ "DOWNLOADED";
public static final String EXTRA_DURATION = SERVICE_PREFIX + "DURATION";
public static final String EXTRA_POSITION = SERVICE_PREFIX + "POSITION";
public static final String EXTRA_SEEK_TO = SERVICE_PREFIX + "SEEK_TO";
public static final String EXTRA_IS_PLAYING = SERVICE_PREFIX
+ "IS_PLAYING";
public static final String EXTRA_IS_PREPARED = SERVICE_PREFIX
+ "IS_PREPARED";
+ public static final String EXTRA_KEEP_AUDIO_FOCUS = SERVICE_PREFIX
+ "KEEP_AUDIO_FOCUS";

public static final String EXTRA_ERROR = SERVICE_PREFIX + "ERROR";
+
public static enum PLAYBACK_SERVICE_ERROR {Connection, Playback}

private MediaPlayer mediaPlayer;
@@ -106,7 +105,7 @@
private PlaylistRepository playlist;
private int startId;
private String currentAction;
- private Playable current = null;
+ private Playable currentPlayable = null;
private List<String> playlistUrls;

// Error handling
@@ -122,6 +121,8 @@
private int lastBufferPercent = 0;
private Thread updateProgressThread;

+ private AudioManagerProxy audioManagerProxy;
+
// Amount of time to rewind playback when resuming after call
private final static int RESUME_REWIND_TIME = 3000;
private final static int ERROR_RETRY_COUNT = 3;
@@ -158,6 +159,8 @@
playlist = new PlaylistRepository(getApplicationContext(),
getContentResolver());

+ audioManagerProxy = new AudioManagerProxy(getApplicationContext());
+
Log.d(LOG_TAG, "Playback service created");

telephonyManager = (TelephonyManager)
getSystemService(TELEPHONY_SERVICE);
@@ -170,7 +173,7 @@
case TelephonyManager.CALL_STATE_RINGING:
// Phone going off-hook or ringing, pause the player.
if (isPlaying()) {
- pause();
+ pause(false);
isPausedInCall = true;
}
break;
@@ -198,7 +201,6 @@

@Override
public void onStart(Intent intent, int startId) {
- Log.d(LOG_TAG, "OnStart");
super.onStart(intent, startId);
Message message = serviceHandler.obtainMessage();
message.arg1 = startId;
@@ -207,30 +209,43 @@
}

protected void onHandleIntent(Intent intent) {
+ if (intent == null || intent.getAction() == null) {
+ Log.d(LOG_TAG, "Null intent received");
+ return;
+ }
String action = intent.getAction();
+ Log.d(LOG_TAG, "Playback service action received: " + action);
if (action.equals(SERVICE_PLAY_SINGLE) ||
action.equals(SERVICE_PLAY_ENTRY)) {
currentAction = action;
- current = intent.getParcelableExtra(Playable.PLAYABLE_TYPE);
+ currentPlayable = intent.getParcelableExtra(Playable.PLAYABLE_TYPE);
seekToPosition = intent.getIntExtra(EXTRA_SEEK_TO, 0);
playCurrent(0, 1);
} else if (action.equals(SERVICE_TOGGLE_PLAY)) {
if (isPlaying()) {
- pause();
+ pause(false);
// Get rid of the toggle intent, since we don't want it redelivered
// on restart
Intent emptyIntent = new Intent(intent);
emptyIntent.setAction("");
startService(emptyIntent);
- } else if (current != null) {
- if (isPrepared) {
- play();
+ } else {
+ if (currentPlayable == null) {
+ currentAction = action;
+ currentPlayable =
intent.getParcelableExtra(Playable.PLAYABLE_TYPE);
+ }
+ if (currentPlayable != null) {
+ resumePlaying();
} else {
- playCurrent(0, 1);
- }
- } else {
- currentAction = SERVICE_PLAY_ENTRY;
- errorCount = 0;
- playFirstUnreadEntry();
+ currentAction = SERVICE_PLAY_ENTRY;
+ errorCount = 0;
+ playFirstUnreadEntry();
+ }
+ }
+ } else if (action.equals(SERVICE_RESUME_PLAYING)) {
+ resumePlaying();
+ } else if (action.equals(SERVICE_PAUSE)) {
+ if (isPlaying()) {
+ pause(intent.getBooleanExtra(EXTRA_KEEP_AUDIO_FOCUS, false));
}
} else if (action.equals(SERVICE_BACK_30)) {
seekRelative(-30000);
@@ -260,13 +275,23 @@
Log.w(LOG_TAG, "onBind called, but binding no longer supported.");
return null;
}
+
+ private void resumePlaying() {
+ if (currentPlayable != null) {
+ if (isPrepared) {
+ play();
+ } else {
+ playCurrent(0, 1);
+ }
+ }
+ }

private boolean playCurrent(int startingErrorCount, int
startingWaitTime) {
errorCount = startingErrorCount;
connectionErrorWaitTime = startingWaitTime;
while (errorCount < ERROR_RETRY_COUNT) {
try {
- prepareThenPlay(current.getUrl(), current.isStream());
+ prepareThenPlay(currentPlayable.getUrl(),
currentPlayable.isStream());
return true;
} catch (UnknownHostException e) {
Log.w(LOG_TAG, "Unknown host in playCurrent");
@@ -275,8 +300,11 @@
Log.w(LOG_TAG, "Connect exception in playCurrent");
handleConnectionError();
} catch (IOException e) {
- Log.e(LOG_TAG, "IOException on playlist entry " + current.getId(),
e);
+ Log.e(LOG_TAG, "IOException on playlist entry " +
currentPlayable.getId(), e);
incrementErrorCount();
+ } catch (IllegalStateException e) {
+ Log.e(LOG_TAG, "Illegal state exception trying to play entry " +
currentPlayable.getId(), e);
+ incrementErrorCount();
}
}

@@ -285,41 +313,48 @@

private void playNextEntry() {
do {
- long id = current.getId();
- if (id != -1) {
- current = playlist.getNextEntry(current.getId());
+ if (currentPlayable != null && currentPlayable.getId() != -1) {
+ currentPlayable = playlist.getNextEntry(currentPlayable.getId());
} else {
- current = playlist.getFirstUnreadEntry();
- }
- } while (current != null && !playCurrent(0, 1));
+ currentPlayable = playlist.getFirstUnreadEntry();
+ }
+ } while (currentPlayable != null && !playCurrent(0, 1));
}

private void playPreviousEntry() {
do {
- current = playlist.getPreviousEntry(current.getId());
- } while (current != null && !playCurrent(0, 1));
+ if (currentPlayable != null && currentPlayable.getId() != -1) {
+ currentPlayable =
playlist.getPreviousEntry(currentPlayable.getId());
+ } else {
+ currentPlayable = playlist.getFirstUnreadEntry();
+ }
+ } while (currentPlayable != null && !playCurrent(0, 1));
}

private void playFirstUnreadEntry() {
do {
- current = playlist.getFirstUnreadEntry();
- } while (current != null && !playCurrent(0, 1));
-
- if (current == null) {
+ currentPlayable = playlist.getFirstUnreadEntry();
+ } while (currentPlayable != null && !playCurrent(0, 1));
+
+ if (currentPlayable == null) {
stopSelfResult(startId);
}
}

private void finishEntryAndPlayNext() {
- if (current.getId() >= 0 && !markedRead) {
- playlist.markAsRead(current.getId());
+ if (currentPlayable != null && currentPlayable.getId() >= 0
&& !markedRead) {
+ playlist.markAsRead(currentPlayable.getId());
}

do {
- current = playlist.getNextEntry(current.getId());
- } while (current != null && !playCurrent(0, 1));
-
- if (current == null) {
+ if (currentPlayable == null) {
+ currentPlayable = playlist.getFirstUnreadEntry();
+ } else {
+ currentPlayable = playlist.getNextEntry(currentPlayable.getId());
+ }
+ } while (currentPlayable != null && !playCurrent(0, 1));
+
+ if (currentPlayable == null) {
stopSelfResult(startId);
}
}
@@ -399,16 +434,21 @@
}

synchronized private void play() {
- if (!isPrepared || current == null) {
+ if (!isPrepared || currentPlayable == null) {
Log.e(LOG_TAG, "play - not prepared");
return;
}
- Log.d(LOG_TAG, "play " + current.getId());
+ Log.d(LOG_TAG, "play " + currentPlayable.getId());
+
+ if (!audioManagerProxy.getAudioFocus()) {
+ Log.d(LOG_TAG, "Unable to get audio focus, so stop");
+ return;
+ }

mediaPlayer.start();
mediaPlayerHasStarted = true;

- CharSequence contentText = current.getTitle();
+ CharSequence contentText = currentPlayable.getTitle();
Notification notification =
new Notification(R.drawable.stat_notify_musicplayer,
contentText,
@@ -417,15 +457,23 @@
| Notification.FLAG_ONGOING_EVENT;
Context context = getApplicationContext();
CharSequence title = getString(R.string.app_name);
- Intent notificationIntent;
- if (current.getActivityData() != null) {
- notificationIntent = new Intent(this, current.getActivity());
+
+ Class<?> notificationActivity;
+ if (currentPlayable.getActivityName() != null) {
+ try {
+ notificationActivity =
Class.forName(currentPlayable.getActivityName());
+ } catch (ClassNotFoundException e) {
+ notificationActivity = NewsListActivity.class;
+ }
+ } else {
+ notificationActivity = NewsListActivity.class;
+ }
+ Intent notificationIntent = new Intent(this, notificationActivity);
+ if (currentPlayable.getActivityData() != null) {
notificationIntent.putExtra(Constants.EXTRA_ACTIVITY_DATA,
- current.getActivityData());
+ currentPlayable.getActivityData());
notificationIntent.putExtra(Constants.EXTRA_DESCRIPTION,
R.string.msg_main_subactivity_nowplaying);
- } else {
- notificationIntent = new Intent(this, NewsListActivity.class);
}
notificationIntent.setAction(Intent.ACTION_VIEW);
notificationIntent.addCategory(Intent.CATEGORY_DEFAULT);
@@ -441,31 +489,43 @@
getApplicationContext().removeStickyBroadcast(lastChangeBroadcast);
}
lastChangeBroadcast = new Intent(SERVICE_CHANGE_NAME);
- lastChangeBroadcast.putExtra(EXTRA_TITLE, current.getTitle());
- lastChangeBroadcast.putExtra(EXTRA_ID, current.getId());
+ lastChangeBroadcast.putExtra(Playable.PLAYABLE_TYPE, currentPlayable);
getApplicationContext().sendStickyBroadcast(lastChangeBroadcast);

- if (current != null && current.getUrl() != null) {
- Tracker.PlayEvent e = new Tracker.PlayEvent(current.getUrl());
+ if (currentPlayable != null && currentPlayable.getUrl() != null) {
+ Tracker.PlayEvent e = new
Tracker.PlayEvent(currentPlayable.getUrl());
Tracker.instance(getApplication()).trackLink(e);
}
}

- synchronized private void pause() {
+ synchronized private void pause(boolean maintainFocus) {
Log.d(LOG_TAG, "pause");
if (isPrepared) {
- mediaPlayer.pause();
+ if (currentPlayable != null && currentPlayable.isStream()) {
+ isPrepared = false;
+ if (proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+ mediaPlayer.stop();
+ } else {
+ mediaPlayer.pause();
+ }
+ }
+ if (!maintainFocus) {
+ audioManagerProxy.releaseAudioFocus();
}
notificationManager.cancel(NOTIFICATION_ID);

- if (current != null) {
- Tracker.PauseEvent e = new Tracker.PauseEvent(current.getUrl());
+ if (currentPlayable != null) {
+ Tracker.PauseEvent e = new
Tracker.PauseEvent(currentPlayable.getUrl());
Tracker.instance(getApplication()).trackLink(e);
}
}

synchronized private void stop() {
Log.d(LOG_TAG, "stop");
+ audioManagerProxy.releaseAudioFocus();
if (isPrepared) {
isPrepared = false;
if (proxy != null) {
@@ -591,7 +651,9 @@
if (!markedRead) {
if (seekToPosition > duration / 10) {
markedRead = true;
- playlist.markAsRead(current.getId());
+ if (playlist != null && currentPlayable != null) {
+ playlist.markAsRead(currentPlayable.getId());
+ }
}
}

@@ -628,8 +690,8 @@
}

seekToPosition = 0;
- if (current != null) {
- Tracker.StopEvent e = new Tracker.StopEvent(current.getUrl());
+ if (currentPlayable != null) {
+ Tracker.StopEvent e = new
Tracker.StopEvent(currentPlayable.getUrl());
Tracker.instance(getApplication()).trackLink(e);
}

@@ -641,7 +703,7 @@
errorCount = 0;
while (errorCount < ERROR_RETRY_COUNT) {
try {
- prepareThenPlay(url, current.isStream());
+ prepareThenPlay(url, currentPlayable.isStream());
successfulPlay = true;
break;
} catch (UnknownHostException e) {
@@ -692,7 +754,7 @@
getApplicationContext().sendBroadcast(intent);

// If a stream, increment since it could be bad
- if (current.isStream()) {
+ if (currentPlayable.isStream()) {
errorCount++;
}

=======================================
--- /Npr/src/org/npr/android/news/PlaylistView.java Thu Jan 19 16:32:00 2012
+++ /Npr/src/org/npr/android/news/PlaylistView.java Wed Apr 25 14:01:45 2012
@@ -20,8 +20,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
@@ -88,7 +90,7 @@
private int startX;
private int startY;
private boolean cancelDown;
-
+ private Playable storedPlayable = null;

private enum ClickedItem {
rewind, rewind30, playPause, fastForward, contractedPlay, progressbar
@@ -276,8 +278,10 @@


private void refreshList() {
- playlistAdapter.getCursor().requery();
- playlistAdapter.notifyDataSetChanged();
+ if (playlistAdapter != null) {
+ playlistAdapter.getCursor().requery();
+ playlistAdapter.notifyDataSetChanged();
+ }
}

@Override
@@ -416,13 +420,23 @@
private class PlaybackChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- String title = intent.getStringExtra(PlaybackService.EXTRA_TITLE);
- newsItemText.setText(title);
- contractedNewsItemText.setText(title);
- playlistAdapter.setActiveId(Long.toString(intent.getLongExtra(
- PlaybackService.EXTRA_ID, -1)));
+ try {
+ Context serviceContext =
context.createPackageContext(context.getPackageName(),
+ Context.CONTEXT_INCLUDE_CODE |
Context.CONTEXT_IGNORE_SECURITY);
+ Bundle bundle = intent.getExtras();
+ bundle.setClassLoader(serviceContext.getClassLoader());
+ storedPlayable = bundle.getParcelable(Playable.PLAYABLE_TYPE);
+ if (storedPlayable != null) {
+ newsItemText.setText(storedPlayable.getTitle());
+ contractedNewsItemText.setText(storedPlayable.getTitle());
+
playlistAdapter.setActiveId(Long.toString(storedPlayable.getId()));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Name not found exception in playback change", e);
+ }
refreshList();
configurePlayerControls();
+ showPlayPause(false);
}
}

@@ -478,8 +492,13 @@

if (isPlaying == playPauseShowsPlay) {
if (isPlaying) {
- playPauseButton.setImageResource(R.drawable.pause_button_normal);
-
contractedPlayButton.setImageResource(R.drawable.pause_button_normal);
+ if (storedPlayable == null || !storedPlayable.isStream()) {
+
playPauseButton.setImageResource(R.drawable.pause_button_normal);
+
contractedPlayButton.setImageResource(R.drawable.pause_button_normal);
+ } else {
+
playPauseButton.setImageResource(R.drawable.stop_button_normal);
+
contractedPlayButton.setImageResource(R.drawable.stop_button_normal);
+ }
playPauseShowsPlay = false;
} else {
playPauseButton.setImageResource(R.drawable.play_button_normal);
@@ -630,12 +649,22 @@

contractedPlayButton.setImageResource(R.drawable.play_button_normal);
}
} else {
- if (showPressed) {
- playPauseButton.setImageResource(R.drawable.pause_button_pressed);
-
contractedPlayButton.setImageResource(R.drawable.pause_button_pressed);
+ if (storedPlayable == null || !storedPlayable.isStream()) {
+ if (showPressed) {
+
playPauseButton.setImageResource(R.drawable.pause_button_pressed);
+
contractedPlayButton.setImageResource(R.drawable.pause_button_pressed);
+ } else {
+ playPauseButton.setImageResource(R.drawable.pause_button_normal);
+
contractedPlayButton.setImageResource(R.drawable.pause_button_normal);
+ }
} else {
- playPauseButton.setImageResource(R.drawable.pause_button_normal);
-
contractedPlayButton.setImageResource(R.drawable.pause_button_normal);
+ if (showPressed) {
+ playPauseButton.setImageResource(R.drawable.stop_button_pressed);
+
contractedPlayButton.setImageResource(R.drawable.stop_button_pressed);
+ } else {
+ playPauseButton.setImageResource(R.drawable.stop_button_normal);
+
contractedPlayButton.setImageResource(R.drawable.stop_button_normal);
+ }
}
}
}
@@ -689,6 +718,7 @@
showPlayPause(false);
Intent intent = new Intent(context, PlaybackService.class);
intent.setAction(PlaybackService.SERVICE_TOGGLE_PLAY);
+ intent.putExtra(Playable.PLAYABLE_TYPE, storedPlayable);
context.startService(intent);
} else {
closeDrawerIfPastThreshold(y);
@@ -863,6 +893,7 @@
showPlayPause(false);
Intent intent = new Intent(context, PlaybackService.class);
intent.setAction(PlaybackService.SERVICE_TOGGLE_PLAY);
+ intent.putExtra(Playable.PLAYABLE_TYPE, storedPlayable);
context.startService(intent);
} else {
Rect r = new Rect();
=======================================
--- /Npr/src/org/npr/android/news/PodcastActivity.java Sun Sep 11 10:17:14
2011
+++ /Npr/src/org/npr/android/news/PodcastActivity.java Wed Apr 25 14:01:45
2012
@@ -111,7 +111,13 @@
titleText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
if (podcastLoaded) {
summary.setVisibility(View.VISIBLE);
- summary.setText(Html.fromHtml(podcast.getSummary()));
+ String podcastSummary = podcast.getSummary();
+ if (podcastSummary != null && podcastSummary.length() > 0) {
+ summary.setText(Html.fromHtml(podcastSummary));
+ } else {
+ Log.e(LOG_TAG, "Empty podcast summary");
+ summary.setText("");
+ }
summary.setTextColor(getResources().getColor(R.color.black));
summary.setTextAppearance(getContext(),
android.R.attr.textAppearanceSmall);
=======================================
--- /Npr/src/org/npr/android/news/ProgramStoryListActivity.java Fri Jan 6
11:10:49 2012
+++ /Npr/src/org/npr/android/news/ProgramStoryListActivity.java Wed Apr 25
14:01:45 2012
@@ -75,7 +75,6 @@
private NewsListAdapter.StoriesLoadedListener listener = new
NewsListAdapter.StoriesLoadedListener() {
@Override
public void storiesLoaded() {
- bannerView.startCloseTimer();
if (loadAll) {
loadAll = false;
PlaylistRepository playlistRepository =
=======================================
--- /Npr/src/org/npr/android/news/RootActivity.java Mon Aug 15 16:13:01 2011
+++ /Npr/src/org/npr/android/news/RootActivity.java Wed Apr 25 14:01:45 2012
@@ -45,6 +45,8 @@
import org.npr.android.util.PlaylistRepository;
import org.npr.api.ApiConstants;

+import com.crittercism.app.Crittercism;
+
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@@ -91,6 +93,12 @@
(ProgressBar) findViewById(R.id.WindowProgressIndicator);

trackNow();
+
+ // Initializes the crittercism library
+ /*Crittercism.init(getApplicationContext(),
+ "", // Code removed from open source
+ true);
+ */
}


=======================================
--- /Npr/src/org/npr/android/util/PlaylistRepository.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/util/PlaylistRepository.java Wed Apr 25
14:01:45 2012
@@ -101,6 +101,7 @@
}

public void markAsRead(long id) {
+ Log.d(LOG_TAG, "Item with id " + id + " is being marked as read.");
Uri update = ContentUris.withAppendedId(PlaylistProvider.CONTENT_URI,
id);
ContentValues values = new ContentValues();
values.put(Items.IS_READ, true);
@@ -188,11 +189,12 @@
return null;
}

- String selection = PlaylistProvider.Items.PLAY_ORDER + " = ?";
+ String selection = PlaylistProvider.Items.PLAY_ORDER + " < ?";
String[] selectionArgs = new String[1];
- selectionArgs[0] = Integer.toString(entry.playOrder - 1);
+ selectionArgs[0] = Integer.toString(entry.playOrder);
+ String sort = PlaylistProvider.Items.PLAY_ORDER + " desc";
PlaylistEntry playlistEntry = retrievePlaylistItem(selection,
- selectionArgs, null);
+ selectionArgs, sort);
if (playlistEntry == null) {
return null;
} else {
@@ -206,9 +208,10 @@
return null;
}

- String selection = PlaylistProvider.Items.PLAY_ORDER + " = ?";
+ String selection = PlaylistProvider.Items.PLAY_ORDER + " > ?";
String[] selectionArgs = new String[1];
- selectionArgs[0] = Integer.toString(entry.playOrder + 1);
+ selectionArgs[0] = Integer.toString(entry.playOrder);
+ String sort = PlaylistProvider.Items.PLAY_ORDER + " asc";
PlaylistEntry playlistEntry = retrievePlaylistItem(selection,
selectionArgs, null);
if (playlistEntry == null) {
=======================================
--- /Npr/src/org/npr/api/Station.java Fri Jan 6 15:16:14 2012
+++ /Npr/src/org/npr/api/Station.java Wed Apr 25 14:01:45 2012
@@ -245,7 +245,9 @@
NodeList stationList = rootNode.getChildNodes();
for (Node stationNode : new IterableNodeList(stationList)) {
Station station = createNprmlStation(stationNode);
- if (station != null) {
+ if (station != null &&
+ (station.getAudioStreams().size() > 0 ||
+ station.getPodcasts().size() > 0)) {
result.add(station);
}
}
=======================================
--- /Npr/src/org/npr/api/Story.java Fri Jan 6 15:16:14 2012
+++ /Npr/src/org/npr/api/Story.java Wed Apr 25 14:01:45 2012
@@ -771,7 +771,7 @@
Attr typeAttr = (Attr) node.getAttributes().getNamedItem("type");
if (typeAttr != null) {
type = typeAttr.getValue();
- if (typeAttr.equals("primaryTopic")) {
+ if (type.equals("primaryTopic")) {
isPrimary = true;
}
}
@@ -859,7 +859,8 @@
}

try {
- src = src.replaceAll("&s=[0-9]+", "&s=13");
+ src = src.replaceAll("&s=[0-9]+", "");
+ src = src.concat("&s=13");
} catch (NullPointerException e) {
Log.e(LOG_TAG, "Error replacing size in story image parsing");
}
=======================================
--- /Npr_Test/Npr_Test.iml Fri Jan 6 11:10:49 2012
+++ /Npr_Test/Npr_Test.iml Wed Apr 25 14:01:45 2012
@@ -19,6 +19,7 @@
<option name="LIBRARY_PROJECT" value="false" />
<option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="true" />
<option name="GENERATE_UNSIGNED_APK" value="false" />
+ <option name="CUSTOM_DEBUG_KEYSTORE_PATH" value="" />
</configuration>
</facet>
</component>
@@ -28,7 +29,7 @@
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
</content>
- <orderEntry type="jdk" jdkName="Android 2.1 Platform" jdkType="Android
SDK" />
+ <orderEntry type="jdk" jdkName="Android 2.2 Platform" jdkType="Android
SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Npr" />
</component>
Reply all
Reply to author
Forward
0 new messages