[npr-android-app] 19 new revisions pushed by justinfr...@gmail.com on 2012-01-06 19:22 GMT

7 views
Skip to first unread message

npr-and...@googlecode.com

unread,
Jan 6, 2012, 2:23:02 PM1/6/12
to npr-a...@googlegroups.com
19 new revisions:

Revision: 6af085af197e
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jun 2 19:56:22 2011
Log: Creating branch for next release...
http://code.google.com/p/npr-android-app/source/detail?r=6af085af197e

Revision: 7fb1caa3ec8c
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jun 9 13:25:31 2011
Log: Ad window in list header. Scrolling window code still present....
http://code.google.com/p/npr-android-app/source/detail?r=7fb1caa3ec8c

Revision: 476810e8ac18
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Jun 15 13:39:57 2011
Log: ...
http://code.google.com/p/npr-android-app/source/detail?r=476810e8ac18

Revision: ac4c79808617
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Jun 22 17:39:11 2011
Log: - Rearrange list item layout...
http://code.google.com/p/npr-android-app/source/detail?r=ac4c79808617

Revision: abcdeb15beec
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sat Jul 2 19:50:10 2011
Log: Added updated time to news story list title bar. Added Suuport
Comes F...
http://code.google.com/p/npr-android-app/source/detail?r=abcdeb15beec

Revision: ab4a04185a19
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Jul 6 16:05:40 2011
Log: Changed sponsorship banner text to white on black to match mobile
site...
http://code.google.com/p/npr-android-app/source/detail?r=ab4a04185a19

Revision: 95027ab60d27
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jul 21 13:43:44 2011
Log: Set banner background to black. Changed banner to load
android_npr tag...
http://code.google.com/p/npr-android-app/source/detail?r=95027ab60d27

Revision: 5e538bd94a93
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jul 21 13:49:41 2011
Log: Removed debug logging. Reformatted....
http://code.google.com/p/npr-android-app/source/detail?r=5e538bd94a93

Revision: 286aee96419f
Author: marshgosnell <marshgosnell@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Jul 25 20:11:55 2011
Log: don't FC if multiple Playback updates received...
http://code.google.com/p/npr-android-app/source/detail?r=286aee96419f

Revision: e92b6348cf49
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Jul 25 21:36:24 2011
Log: Use Thumbnail for news story list, rather than first story
image....
http://code.google.com/p/npr-android-app/source/detail?r=e92b6348cf49

Revision: d7c51a7de1cb
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Aug 15 16:13:01 2011
Log: Move sponsorship banner below player....
http://code.google.com/p/npr-android-app/source/detail?r=d7c51a7de1cb

Revision: 6c45c298199d
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Aug 15 16:26:58 2011
Log: Missing files from previous commit...
http://code.google.com/p/npr-android-app/source/detail?r=6c45c298199d

Revision: 05cc27e2583d
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Aug 17 17:50:22 2011
Log: Prevent media player callbacks from being invoked after playback
servi...
http://code.google.com/p/npr-android-app/source/detail?r=05cc27e2583d

Revision: b90c55b74c69
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sat Sep 10 13:37:45 2011
Log: QA feedback for new banner...
http://code.google.com/p/npr-android-app/source/detail?r=b90c55b74c69

Revision: 3f24a91b4484
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sat Sep 10 15:07:21 2011
Log: Lock portrait & minor topic fix...
http://code.google.com/p/npr-android-app/source/detail?r=3f24a91b4484

Revision: 84ee2840dd49
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sun Sep 11 10:17:14 2011
Log: 2.1 Graphics Updates...
http://code.google.com/p/npr-android-app/source/detail?r=84ee2840dd49

Revision: aa15e2d80bd3
Author: justinfriberg <justinfriberg@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Tue Oct 25 08:27:38 2011
Log: - Ensures the banner only shows up every two mins (or time
specified i...
http://code.google.com/p/npr-android-app/source/detail?r=aa15e2d80bd3

Revision: 45034751fb63
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Nov 3 12:23:50 2011
Log: noBannerUntilSystemTime should be static because we want it to
apply t...
http://code.google.com/p/npr-android-app/source/detail?r=45034751fb63

Revision: 8ffcd2cf54f5
Author: Justin Friberg <justin...@hotmail.com>
Date: Fri Jan 6 11:10:49 2012
Log: 2.2 updates
http://code.google.com/p/npr-android-app/source/detail?r=8ffcd2cf54f5

==============================================================================
Revision: 6af085af197e
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jun 2 19:56:22 2011
Log: Creating branch for next release

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@124
0a442a4a-40cc-11de-998b-8fedbbe774c7

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



==============================================================================
Revision: 7fb1caa3ec8c
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jun 9 13:25:31 2011
Log: Ad window in list header. Scrolling window code still present.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@125
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Added:
/Npr/res/anim/scroll_in_from_bottom.xml
/Npr/res/anim/scroll_out_bottom.xml
/Npr/res/drawable-hdpi/close_button.png
/Npr/res/layout/list_header.xml
Modified:
/Npr/res/layout/news_item.xml
/Npr/res/layout/playlist.xml
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java
/Npr/src/org/npr/android/news/PlaylistView.java
/Npr/src/org/npr/android/news/RootActivity.java

=======================================
--- /dev/null
+++ /Npr/res/anim/scroll_in_from_bottom.xml Thu Jun 9 13:25:31 2011
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:interpolator="@android:anim/linear_interpolator"
+ android:fromYDelta="0"
+ android:toYDelta="100%"
+ android:duration="1"/>
+ <alpha android:fromAlpha="0"
+ android:toAlpha="1"
+ android:duration="1"
+ android:startOffset="1"/>
+ <translate android:interpolator="@android:anim/linear_interpolator"
+ android:fromYDelta="0"
+ android:toYDelta="-100%"
+ android:startOffset="2"
+ android:duration="1000"/>
+</set>
=======================================
--- /dev/null
+++ /Npr/res/anim/scroll_out_bottom.xml Thu Jun 9 13:25:31 2011
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:interpolator="@android:anim/linear_interpolator"
+ android:fromYDelta="0"
+ android:toYDelta="100%"
+ android:duration="1000"/>
+</set>
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/close_button.png Thu Jun 9 13:25:31 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/layout/list_header.xml Thu Jun 9 13:25:31 2011
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<WebView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/adWindowHeader"
+ android:layout_width="fill_parent"
+ android:layout_height="50dip" />
=======================================
--- /Npr/res/layout/news_item.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/news_item.xml Thu Jun 9 13:25:31 2011
@@ -7,37 +7,23 @@
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeight">
<ImageView
- android:id="@+id/NewsItemImage"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:layout_marginRight="5dp"
- android:scaleType="centerInside"/>
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical">
- <TextView
- android:id="@+id/NewsItemNameText"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:gravity="center_vertical"
- android:lines="2"
- android:ellipsize="end"
- android:textAppearance="@style/NewsItemTitle"/>
- <TextView
- android:id="@+id/NewsItemTeaserText"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:lines="2"
- android:ellipsize="end"
- android:textAppearance="@style/NewsItemTeaserText"/>
- </LinearLayout>
- <ImageView
android:id="@+id/NewsItemIcon"
- android:layout_alignParentRight="true"
android:layout_width="35dp"
android:layout_height="35dp"
- android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
android:scaleType="center"/>
+ <TextView
+ android:id="@+id/NewsItemNameText"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/NewsItemTitle"/>
+ <ImageView
+ android:id="@+id/NewsItemImage"
+ android:layout_alignParentRight="true"
+ android:layout_width="60dp"
+ android:layout_height="60dp"
+ android:layout_marginLeft="5dp"
+ android:scaleType="centerInside"/>
</LinearLayout>
=======================================
--- /Npr/res/layout/playlist.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/playlist.xml Thu Jun 9 13:25:31 2011
@@ -1,57 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
-<SlidingDrawer
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/drawer"
android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:handle="@+id/handle"
- android:content="@+id/playlist_content"
- android:orientation="vertical"
- android:allowSingleTap="false">
- <RelativeLayout
- android:id="@id/handle"
+ android:layout_height="fill_parent">
+ <SlidingDrawer
+ android:id="@+id/drawer"
android:layout_width="fill_parent"
- android:layout_height="95dp"
- android:background="@drawable/contracted_player_bg">
- <include
- layout="@layout/player_contracted"/>
- <include
- layout="@layout/player_expanded"/>
- </RelativeLayout>
+ android:layout_height="fill_parent"
+ android:handle="@+id/handle"
+ android:content="@+id/playlist_content"
+ android:orientation="vertical"
+ android:allowSingleTap="false">
+ <RelativeLayout
+ android:id="@id/handle"
+ android:layout_width="fill_parent"
+ android:layout_height="95dp"
+ android:background="@drawable/contracted_player_bg">
+ <include
+ layout="@layout/player_contracted"/>
+ <include
+ layout="@layout/player_expanded"/>
+ </RelativeLayout>
+ <RelativeLayout
+ android:id="@id/playlist_content"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <org.npr.android.widget.DragNDropListView
+ android:id="@+id/playlist"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginBottom="50dip"/>
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="50dip"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:gravity="center_vertical"
+ android:padding="10dip"
+ android:background="@drawable/app_title_background"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/clear_played_segments"
+ android:text="@string/msg_clear_played_segments"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ style="@style/actionButton"/>
+ <Button
+ android:id="@+id/clear_playlist"
+ android:text="@string/msg_clear_playlist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ style="@style/actionButton"/>
+ </LinearLayout>
+ </RelativeLayout>
+ </SlidingDrawer>
<RelativeLayout
- android:id="@id/playlist_content"
+ android:id="@+id/adWindow"
+ android:layout_height="65dip"
android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <org.npr.android.widget.DragNDropListView
- android:id="@+id/playlist"
+ android:layout_gravity="bottom">
+ <WebView
+ android:id="@+id/adView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layout_alignParentLeft="true"
+ android:background="@drawable/contracted_player_bg"/>
+ <ImageButton
+ android:id="@+id/dismissAdView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="7dip"
android:layout_alignParentTop="true"
- android:layout_marginBottom="50dip"/>
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="50dip"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:gravity="center_vertical"
- android:padding="10dip"
- android:background="@drawable/app_title_background"
- android:orientation="horizontal">
- <Button
- android:id="@+id/clear_played_segments"
- android:text="@string/msg_clear_played_segments"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- style="@style/actionButton"/>
- <Button
- android:id="@+id/clear_playlist"
- android:text="@string/msg_clear_playlist"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- style="@style/actionButton"/>
- </LinearLayout>
+ android:layout_alignParentRight="true"
+ android:src="@drawable/close_button"
+ android:background="@android:color/transparent"/>
</RelativeLayout>
-</SlidingDrawer>
+</FrameLayout>
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Sun May 22 12:54:47
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Thu Jun 9 13:25:31
2011
@@ -24,11 +24,14 @@
import android.os.Message;
import android.util.Log;
import android.view.GestureDetector;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
@@ -179,6 +182,30 @@


listView = (ListView) findViewById(R.id.ListView01);
+
+
+ View header = LayoutInflater.from(this)
+ .inflate(R.layout.list_header, listView, false);
+
+ WebView adWindowHeader =
(WebView)header.findViewById(R.id.adWindowHeader);
+
+ WebSettings webSettings = adWindowHeader.getSettings();
+ webSettings.setSavePassword(false);
+ webSettings.setSaveFormData(false);
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setSupportZoom(false);
+
+ adWindowHeader.loadUrl("www.fark.com");
+ adWindowHeader.loadDataWithBaseURL(null,
+ "<html><head><style type='text/css'>body
{padding:0;margin:0} " +
+ "p {display:none}</style>" +
+ "</head><body><script type='text/javascript' " +
+ "src='http://ad.doubleclick.net/adj/" +
+ "n6735.NPR.MOBILE/android;sz=320x50' />" +
+ "</body></html>",
+ "text/html", "utf-8", null);
+ listView.addHeaderView(header);
+
listView.setOnItemClickListener(this);
listAdapter = new NewsListAdapter(this);
listView.setAdapter(listAdapter);
=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Sun May 22 12:54:47
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Thu Jun 9 13:25:31
2011
@@ -116,7 +116,6 @@
Story story = getItem(position);

ImageView icon = (ImageView)
convertView.findViewById(R.id.NewsItemIcon);
- TextView teaser = (TextView)
convertView.findViewById(R.id.NewsItemTeaserText);
TextView name = (TextView)
convertView.findViewById(R.id.NewsItemNameText);
final ImageView image = (ImageView)
convertView.findViewById(R.id.NewsItemImage);

@@ -145,16 +144,6 @@
// view and will be in italics
name.setTypeface(name.getTypeface(), Typeface.BOLD);

- String teaserText = story.getMiniTeaser();
- if (teaserText == null) {
- teaserText = story.getTeaser();
- }
- if (teaserText != null && teaserText.length() > 0) {
- teaser.setText(Html.fromHtml(teaserText));
- teaser.setVisibility(View.VISIBLE);
- } else {
- teaser.setVisibility(View.GONE);
- }
if (story.getImages().size() > 0) {
final String url = story.getImages().get(0).getSrc();
Drawable cachedImage = null;
@@ -190,7 +179,6 @@
} else {
// null marker means it's the end of the list.
icon.setVisibility(View.INVISIBLE);
- teaser.setVisibility(View.INVISIBLE);
image.setVisibility(View.GONE);
name.setTypeface(name.getTypeface(), Typeface.ITALIC);
name.setText(R.string.msg_load_more);
=======================================
--- /Npr/src/org/npr/android/news/PlaylistView.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/android/news/PlaylistView.java Thu Jun 9 13:25:31 2011
@@ -33,6 +33,8 @@
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.FrameLayout;
@@ -102,6 +104,15 @@
private int startY;
private boolean cancelDown;

+ private RelativeLayout adWindow;
+
+ private enum adWindowStates {
+ WindowNeedsMeasurement, WindowTooNarrow, WindowReadyToBeShown,
+ WindowVisible, WindowHasBeenShown
+ }
+
+ private adWindowStates adWindowState = adWindowStates.WindowTooNarrow;
+
private enum ClickedItem {
rewind, rewind30, playPause, fastForward, contractedPlay, progressbar
}
@@ -113,6 +124,7 @@
private BroadcastReceiver closeReceiver;
private BroadcastReceiver playlistChangedReceiver;

+ private static final int MSG_AD_CLOSE = 0;
private GestureDetector gestureDetector;
private final Handler handler = new Handler() {
@Override
@@ -137,14 +149,22 @@
}

@Override
- public void onAnimationStart(Animation animation) {}
+ public void onAnimationStart(Animation animation) {
+ }

@Override
- public void onAnimationRepeat(Animation animation) {}
+ public void onAnimationRepeat(Animation animation) {
+ }
});

listItem.startAnimation(fling);
break;
+
+ case MSG_AD_CLOSE:
+ if (adWindowState == adWindowStates.WindowVisible) {
+ hideAdWindow();
+ }
+ break;
}
}
};
@@ -173,6 +193,9 @@
touchSlop = ViewConfiguration.getTouchSlop();
handle = (RelativeLayout) findViewById(R.id.handle);

+ adWindow = (RelativeLayout) findViewById(R.id.adWindow);
+ adWindow.setVisibility(View.INVISIBLE);
+
playerContracted = (RelativeLayout)
findViewById(R.id.player_contracted);
playerExpanded = (RelativeLayout) findViewById(R.id.player_expanded);

@@ -215,6 +238,7 @@
if (intent != null) {
changeReceiver.onReceive(context, intent);
} else {
+ Log.d(LOG_TAG, "Call clearPlayer from init");
clearPlayer();
}

@@ -255,6 +279,40 @@

refreshList();
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (adWindowState == adWindowStates.WindowNeedsMeasurement) {
+ if (adWindow.getMeasuredWidth() >= 240) {
+ adWindowState = adWindowStates.WindowReadyToBeShown;
+ WebView adView = (WebView) findViewById(R.id.adView);
+ adView.setBackgroundColor(0);
+
+ WebSettings webSettings = adView.getSettings();
+ webSettings.setSavePassword(false);
+ webSettings.setSaveFormData(false);
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setSupportZoom(false);
+
+ adView.loadDataWithBaseURL(null,
+ "<html><head><style type='text/css'>body
{padding:0;margin:0} " +
+ "p {padding:0 0 3px 0;margin:0;color:white;" +
+ "font-size:10px;font-family:Helvetica,Arial," +
+ "sans-serif;text-align:center}</style>" +
+ "</head><body><script type='text/javascript' " +
+ "src='http://ad.doubleclick.net/adj/" +
+ "n6735.NPR.MOBILE/android;sz=320x50' />" +
+ "</body></html>",
+ "text/html", "utf-8", null);
+ ImageButton dismissAdView = (ImageButton)
findViewById(R.id.dismissAdView);
+ dismissAdView.setOnClickListener(this);
+ } else {
+ adWindowState = adWindowStates.WindowTooNarrow;
+ }
+ }
+ }

private void refreshList() {
playlistAdapter.getCursor().requery();
@@ -306,6 +364,10 @@
refreshList();
configurePlayerControls();
break;
+
+ case R.id.dismissAdView:
+ hideAdWindow();
+ break;
}
}

@@ -321,6 +383,9 @@
}

private void playNow(final Playable playable, String action) {
+ if (adWindowState == adWindowStates.WindowReadyToBeShown) {
+ showAdWindow();
+ }
startPlaylistSpinners();
Intent intent = new Intent(context, PlaybackService.class);
intent.setAction(action);
@@ -408,8 +473,16 @@
@Override
public void onReceive(Context context, Intent intent) {
int duration = intent.getIntExtra(PlaybackService.EXTRA_DURATION, 1);
+ Log.d(LOG_TAG, "Playback update; duration = " + duration + "
millsecs");
+ // Drop out if no duration is given (flicker?)
+ if (duration == 1) {
+ return;
+ }
+
int position = intent.getIntExtra(PlaybackService.EXTRA_POSITION, 0);
int downloaded =
intent.getIntExtra(PlaybackService.EXTRA_DOWNLOADED, 1);
+ Log.d(LOG_TAG, "Playback update; position = " + position + "
millsecs; " +
+ "downloaded = " + duration + " millsecs");
boolean isPlaying = intent.getBooleanExtra(PlaybackService
.EXTRA_IS_PLAYING, false);
if (!changingProgress) {
@@ -463,6 +536,7 @@
private class PlaybackCloseReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
+ Log.d(LOG_TAG, "Playback close received - calling clear player");
clearPlayer();
refreshList();
}
@@ -964,4 +1038,67 @@
public String getActiveId() {
return playlistAdapter.getActiveId();
}
-}
+
+ private void showAdWindow() {
+ if (adWindowState != adWindowStates.WindowReadyToBeShown) {
+ return;
+ }
+
+ Animation scroll_in_from_bottom = AnimationUtils.loadAnimation(
+ context,
+ R.anim.scroll_in_from_bottom
+ );
+ scroll_in_from_bottom.setFillAfter(true);
+ scroll_in_from_bottom.setAnimationListener(new
Animation.AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ handler.sendEmptyMessageDelayed(MSG_AD_CLOSE, 10000);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+
+ });
+ adWindow.startAnimation(scroll_in_from_bottom);
+ adWindowState = adWindowStates.WindowVisible;
+ }
+
+ private void hideAdWindow() {
+ if (adWindowState != adWindowStates.WindowVisible) {
+ return;
+ }
+
+ adWindowState = adWindowStates.WindowReadyToBeShown;
+
+ Animation scroll_out_bottom = AnimationUtils.loadAnimation(
+ context,
+ R.anim.scroll_out_bottom
+ );
+ scroll_out_bottom.setFillAfter(true);
+ scroll_out_bottom.setAnimationListener(new
Animation.AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+
+ });
+ adWindow.startAnimation(scroll_out_bottom);
+ adWindow.setVisibility(View.INVISIBLE);
+ }
+}
=======================================
--- /Npr/src/org/npr/android/news/RootActivity.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/android/news/RootActivity.java Thu Jun 9 13:25:31 2011
@@ -18,8 +18,11 @@
import android.app.Activity;
import android.app.ActivityGroup;
import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
@@ -56,6 +59,7 @@
private NavigationView navigationView;
private PlaylistView playlistView;
private ProgressBar progressIndicator;
+ private BroadcastReceiver updateReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -191,8 +195,30 @@
* @param playable A Playable stream or podcast to play.
*/
protected void playSingleNow(Playable playable) {
+ startIndeterminateProgressIndicator();
+
+ if (updateReceiver != null) {
+ unregisterReceiver(updateReceiver);
+ updateReceiver = null;
+ }
+ updateReceiver = new PlaybackUpdateReceiver();
+ registerReceiver(updateReceiver,
+ new IntentFilter(PlaybackService.SERVICE_UPDATE_NAME));
+
playlistView.playSingleNow(playable);
}
+
+ private class PlaybackUpdateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int duration = intent.getIntExtra(PlaybackService.EXTRA_DURATION, 1);
+ if (duration != 1) {
+ stopIndeterminateProgressIndicator();
+ unregisterReceiver(updateReceiver);
+ updateReceiver = null;
+ }
+ }
+ }

@Override
public boolean onCreateOptionsMenu(Menu menu) {

==============================================================================
Revision: 476810e8ac18
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Jun 15 13:39:57 2011
Log:
Long press and swipe have been fixed. (The sponsorship header was causing
an off by one error)
Top stories shows the primary topic as a subtitle.
The story list by topic shows the story date as a subtitle.
Buttons on the story page are disabled when applicable (in playlist or
playing).
Text sizes have been converted to SP to support the visually impaired.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@126
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/res/layout/list_header.xml
/Npr/res/layout/news_item.xml
/Npr/res/layout/playlist.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java
/Npr/src/org/npr/android/news/NewsStoryActivity.java
/Npr/src/org/npr/android/news/PlaylistView.java
/Npr/src/org/npr/api/Story.java

=======================================
--- /Npr/res/layout/list_header.xml Thu Jun 9 13:25:31 2011
+++ /Npr/res/layout/list_header.xml Wed Jun 15 13:39:57 2011
@@ -2,6 +2,6 @@

<WebView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/adWindowHeader"
+ android:id="@+id/sponsorshipWindowHeader"
android:layout_width="fill_parent"
android:layout_height="50dip" />
=======================================
--- /Npr/res/layout/news_item.xml Thu Jun 9 13:25:31 2011
+++ /Npr/res/layout/news_item.xml Wed Jun 15 13:39:57 2011
@@ -12,13 +12,23 @@
android:layout_height="35dp"
android:layout_marginRight="5dp"
android:scaleType="center"/>
- <TextView
- android:id="@+id/NewsItemNameText"
- android:layout_height="wrap_content"
+ <LinearLayout
android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center_vertical"
- android:textAppearance="@style/NewsItemTitle"/>
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/NewsItemNameText"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/NewsItemTitle"/>
+ <TextView
+ android:id="@+id/NewsItemSubtitleText"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:textAppearance="@style/NewsItemSubtitleText"/>
+ </LinearLayout>
<ImageView
android:id="@+id/NewsItemImage"
android:layout_alignParentRight="true"
=======================================
--- /Npr/res/layout/playlist.xml Thu Jun 9 13:25:31 2011
+++ /Npr/res/layout/playlist.xml Wed Jun 15 13:39:57 2011
@@ -59,17 +59,17 @@
</RelativeLayout>
</SlidingDrawer>
<RelativeLayout
- android:id="@+id/adWindow"
+ android:id="@+id/sponsorshipWindow"
android:layout_height="65dip"
android:layout_width="fill_parent"
android:layout_gravity="bottom">
<WebView
- android:id="@+id/adView"
+ android:id="@+id/sponsorshipView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/contracted_player_bg"/>
<ImageButton
- android:id="@+id/dismissAdView"
+ android:id="@+id/dismissSponsorshipView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="7dip"
=======================================
--- /Npr/res/values/styles.xml Sun May 22 12:54:47 2011
+++ /Npr/res/values/styles.xml Wed Jun 15 13:39:57 2011
@@ -30,46 +30,45 @@
<item name="android:paddingRight">5dip</item>
</style>
<style name="ViewTitleText" parent="@android:style/TextAppearance">
- <item name="android:textSize">14dip</item>
+ <item name="android:textSize">14sp</item>
<item name="android:textColor">#333</item>
<item name="android:textStyle">bold</item>
<item name="android:ellipsize">end</item>
<item name="android:singleLine">true</item>
</style>
<style name="NewsItemTitle" parent="@android:style/TextAppearance">
- <item name="android:textSize">15dip</item>
+ <item name="android:textSize">15sp</item>
<item name="android:typeface">serif</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@android:color/black</item>
</style>
- <style name="NewsItemTeaserText"
- parent="@android:style/TextAppearance">
- <item name="android:textSize">12dip</item>
+ <style name="NewsItemSubtitleText"
parent="@android:style/TextAppearance">
+ <item name="android:textSize">12sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#737373</item>
</style>
<style name="PlayerStatusText"
parent="@android:style/TextAppearance">
- <item name="android:textSize">10dip</item>
+ <item name="android:textSize">10sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#f5f5f5</item>
</style>
<style name="PlayerText"
parent="@android:style/TextAppearance">
- <item name="android:textSize">12dip</item>
+ <item name="android:textSize">12sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#f5f5f5</item>
</style>
<style name="PlaylistItemTitle"
parent="@android:style/TextAppearance">
- <item name="android:textSize">12dip</item>
+ <item name="android:textSize">12sp</item>
<item name="android:typeface">sans</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">#333</item>
</style>
<style name="PlaylistItemDuration"
parent="@android:style/TextAppearance">
- <item name="android:textSize">10dip</item>
+ <item name="android:textSize">10sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#333</item>
</style>
@@ -84,7 +83,7 @@
</style>
<style name="NewsStorySubtext"
parent="@android:style/TextAppearance">
- <item name="android:textSize">10dip</item>
+ <item name="android:textSize">10sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#737373</item>
</style>
@@ -96,7 +95,7 @@
</style>
<style name="NewsStoryIndex"
parent="@android:style/TextAppearance">
- <item name="android:textSize">12dip</item>
+ <item name="android:textSize">12sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#737373</item>
</style>
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Thu Jun 9 13:25:31
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Wed Jun 15 13:39:57
2011
@@ -78,8 +78,13 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case ListItemGestureListener.MSG_LONG_PRESS:
+ {
lastLongPressPosition = msg.arg1;
- Story longPressStory = listAdapter.getItem(msg.arg1);
+
+ // Offset 1 for header
+ int storyPosition = msg.arg1 - 1;
+
+ Story longPressStory = listAdapter.getItem(storyPosition);
if (longPressStory != null && longPressStory.getPlayable() !=
null) {
PlaylistRepository playlistRepository =
new PlaylistRepository(getApplicationContext(),
@@ -91,7 +96,7 @@
if (playlistEntry == null) {
addAndPulseIcon(listView.getChildAt(msg.arg1 -
listView.getFirstVisiblePosition()));
- addStory(msg.arg1, true);
+ addStory(storyPosition, true);
} else {
PlaylistEntry activeEntry =
playlistRepository.getPlaylistItemFromId(getActiveId());
@@ -102,9 +107,15 @@
playEntryNow(playlistEntry);
}
}
- break;
+ }
+ break;
+
case ListItemGestureListener.MSG_FLING:
- flungStory = listAdapter.getItem(msg.arg1);
+ {
+ // Offset 1 for header
+ int storyPosition = msg.arg1 - 1;
+
+ flungStory = listAdapter.getItem(storyPosition);
if ( flungStory != null && flungStory.getPlayable() != null) {
PlaylistRepository playlistRepository =
new PlaylistRepository(getApplicationContext(),
@@ -119,7 +130,7 @@
msg.arg2,
true
);
- addStory(msg.arg1, false);
+ addStory(storyPosition, false);
} else {
animateListItemFling(
listView.getChildAt(msg.arg1 -
@@ -129,7 +140,8 @@
);
}
}
- break;
+ }
+ break;
}
}
};
@@ -180,37 +192,34 @@
ViewGroup container = (ViewGroup) findViewById(R.id.Content);
ViewGroup.inflate(this, R.layout.news, container);

-
listView = (ListView) findViewById(R.id.ListView01);

-
View header = LayoutInflater.from(this)
.inflate(R.layout.list_header, listView, false);

- WebView adWindowHeader =
(WebView)header.findViewById(R.id.adWindowHeader);
-
- WebSettings webSettings = adWindowHeader.getSettings();
+ WebView sponsorshipWindow =
+ (WebView)header.findViewById(R.id.sponsorshipWindowHeader);
+
+ WebSettings webSettings = sponsorshipWindow.getSettings();
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(false);

- adWindowHeader.loadUrl("www.fark.com");
- adWindowHeader.loadDataWithBaseURL(null,
- "<html><head><style type='text/css'>body
{padding:0;margin:0} " +
- "p {display:none}</style>" +
- "</head><body><script type='text/javascript' " +
- "src='http://ad.doubleclick.net/adj/" +
- "n6735.NPR.MOBILE/android;sz=320x50' />" +
- "</body></html>",
- "text/html", "utf-8", null);
+ sponsorshipWindow.loadDataWithBaseURL(null,
+ "<html><head><style type='text/css'>body {padding:0;margin:0} " +
+ "p {display:none}</style>" +
+ "</head><body><script type='text/javascript' " +
+ "src='http://ad.doubleclick.net/adj/" +
+ "n6735.NPR.MOBILE/android;sz=320x50' />" +
+ "</body></html>",
+ "text/html", "utf-8", null);
listView.addHeaderView(header);

listView.setOnItemClickListener(this);
listAdapter = new NewsListAdapter(this);
listView.setAdapter(listAdapter);

-
// Gesture detection
gestureDetector = new GestureDetector(
new ListItemGestureListener(listView, handler)
@@ -430,6 +439,7 @@
* @return A URL for the NPR API.
*/
protected String getApiUrl() {
+ Log.e(LOG_TAG, getIntent().getStringExtra(Constants.EXTRA_QUERY_URL));
return getIntent().getStringExtra(Constants.EXTRA_QUERY_URL);
}

=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Thu Jun 9 13:25:31
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Wed Jun 15 13:39:57
2011
@@ -41,6 +41,10 @@

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
+import java.sql.Time;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.List;


@@ -50,11 +54,19 @@
private final ImageThreadLoader imageLoader;
private RootActivity rootActivity = null;
private final PlaylistRepository repository;
+ private String grouping = null;
+ // Sample date from api: Tue, 09 Jun 2009 15:20:00 -0400
+ public static final SimpleDateFormat apiDateFormat
+ = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
+ private final DateFormat longDateFormat
+ = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT);

public NewsListAdapter(Context context) {
super(context, R.layout.news_item);
if (context instanceof RootActivity) {
rootActivity = (RootActivity) context;
+ grouping =
+
rootActivity.getIntent().getStringExtra(Constants.EXTRA_GROUPING);
}
inflater = LayoutInflater.from(getContext());
imageLoader = ImageThreadLoader.getOnDiskInstance(context);
@@ -116,6 +128,7 @@
Story story = getItem(position);

ImageView icon = (ImageView)
convertView.findViewById(R.id.NewsItemIcon);
+ TextView subtitle =
(TextView)convertView.findViewById(R.id.NewsItemSubtitleText);
TextView name = (TextView)
convertView.findViewById(R.id.NewsItemNameText);
final ImageView image = (ImageView)
convertView.findViewById(R.id.NewsItemImage);

@@ -144,6 +157,23 @@
// view and will be in italics
name.setTypeface(name.getTypeface(), Typeface.BOLD);

+ if (grouping == null) {
+ for (Story.Parent storyParent : story.getParentTopics()) {
+ if (storyParent.isPrimary()) {
+ subtitle.setText(storyParent.getTitle());
+ }
+ }
+ } else {
+ try {
+ subtitle.setText(
+
longDateFormat.format(apiDateFormat.parse(story.getStoryDate()))
+ );
+ } catch (ParseException e) {
+ Log.e(LOG_TAG, "date format:", e);
+ subtitle.setVisibility(View.GONE);
+ }
+ }
+
if (story.getImages().size() > 0) {
final String url = story.getImages().get(0).getSrc();
Drawable cachedImage = null;
@@ -179,6 +209,7 @@
} else {
// null marker means it's the end of the list.
icon.setVisibility(View.INVISIBLE);
+ subtitle.setVisibility(View.GONE);
image.setVisibility(View.GONE);
name.setTypeface(name.getTypeface(), Typeface.ITALIC);
name.setText(R.string.msg_load_more);
=======================================
--- /Npr/src/org/npr/android/news/NewsStoryActivity.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/NewsStoryActivity.java Wed Jun 15
13:39:57 2011
@@ -15,8 +15,10 @@

package org.npr.android.news;

+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Environment;
import android.text.Html;
@@ -68,12 +70,16 @@
private TrackerItem trackerItem = null;
private List<Story> stories;
private boolean externalStorageAvailable = false;
-
+ private PlaylistRepository playlistRepository;
+ private BroadcastReceiver playlistChangedReceiver;
+ private BroadcastReceiver playbackChangedReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
stories = new ArrayList<Story>();
+ playlistRepository = new
+ PlaylistRepository(getApplicationContext(), getContentResolver());

final String storyIdsString =
getIntent().getStringExtra(Constants.EXTRA_STORY_ID_LIST);
Log.d(LOG_TAG, "Got the following id's: " + storyIdsString);
@@ -132,9 +138,33 @@
trackNow();
}
}
+
+ playlistChangedReceiver = new PlaylistChangedReceiver();
+ this.registerReceiver(playlistChangedReceiver,
+ new IntentFilter(PlaylistRepository.PLAYLIST_CHANGED));
+
+ playbackChangedReceiver = new PlaybackChangedReceiver();
+ Intent intent = this.registerReceiver(playbackChangedReceiver,
+ new IntentFilter(PlaybackService.SERVICE_CHANGE_NAME));
+ if (intent != null) {
+ playbackChangedReceiver.onReceive(this, intent);
+ }

workspace.setOnScreenChangeListener(this);
}
+
+ @Override
+ protected void onStop() {
+ if (playlistChangedReceiver != null) {
+ unregisterReceiver(playlistChangedReceiver);
+ playlistChangedReceiver = null;
+ }
+ if (playbackChangedReceiver != null) {
+ unregisterReceiver(playbackChangedReceiver);
+ playbackChangedReceiver = null;
+ }
+ super.onStop();
+ }

private void layoutStory(Story story, int position, int total) {
if (position >= stories.size()) {
@@ -260,7 +290,8 @@
listenNow.setVisibility(isListenable ? View.VISIBLE : View.INVISIBLE);
listenNow.setEnabled(isListenable);
enqueue.setVisibility(isListenable ? View.VISIBLE : View.INVISIBLE);
- enqueue.setEnabled(isListenable);
+ enqueue.setEnabled(isListenable &&
+ playlistRepository.getPlaylistItemFromStoryId(story.getId()) ==
null);
}

private void playStory(boolean playNow, int position) {
@@ -279,8 +310,6 @@


Tracker.LinkEvent e;
- PlaylistRepository playlistRepository =
- new PlaylistRepository(getApplicationContext(),
getContentResolver());
if (playNow) {
PlaylistEntry activeEntry =
playlistRepository.getPlaylistItemFromId(getActiveId());
@@ -408,4 +437,37 @@
}
}
}
-}
+
+ private class PlaylistChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int len = stories.size();
+ for (int i = 0 ; i < len ; i++) {
+ View v = workspace.getChildAt(i);
+ Button enqueue =
+ (Button) v.findViewById(R.id.NewsStoryListenEnqueueButton);
+ enqueue.setEnabled(
+
playlistRepository.getPlaylistItemFromStoryId(stories.get(i).getId()) ==
null);
+ }
+ }
+ }
+
+ private class PlaybackChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Long playlistId = intent.getLongExtra(PlaybackService.EXTRA_ID, -1);
+ if (playlistId != -1) {
+ PlaylistEntry pe = playlistRepository.getPlaylistItemFromId
+ (playlistId);
+ if (pe == null) return;
+ int len = stories.size();
+ for (int i = 0 ; i < len ; i++) {
+ View v = workspace.getChildAt(i);
+ Button listenNow =
+ (Button) v.findViewById(R.id.NewsStoryListenNowButton);
+ listenNow.setEnabled(!stories.get(i).getId().equals(pe.storyID));
+ }
+ }
+ }
+ }
+}
=======================================
--- /Npr/src/org/npr/android/news/PlaylistView.java Thu Jun 9 13:25:31 2011
+++ /Npr/src/org/npr/android/news/PlaylistView.java Wed Jun 15 13:39:57 2011
@@ -104,14 +104,14 @@
private int startY;
private boolean cancelDown;

- private RelativeLayout adWindow;
-
- private enum adWindowStates {
+ private RelativeLayout sponsorshipWindow;
+
+ private enum sponsorshipWindowStates {
WindowNeedsMeasurement, WindowTooNarrow, WindowReadyToBeShown,
WindowVisible, WindowHasBeenShown
}

- private adWindowStates adWindowState = adWindowStates.WindowTooNarrow;
+ private sponsorshipWindowStates sponsorshipWindowState =
sponsorshipWindowStates.WindowTooNarrow;

private enum ClickedItem {
rewind, rewind30, playPause, fastForward, contractedPlay, progressbar
@@ -161,8 +161,8 @@
break;

case MSG_AD_CLOSE:
- if (adWindowState == adWindowStates.WindowVisible) {
- hideAdWindow();
+ if (sponsorshipWindowState ==
sponsorshipWindowStates.WindowVisible) {
+ hideSponsorshipWindow();
}
break;
}
@@ -193,8 +193,8 @@
touchSlop = ViewConfiguration.getTouchSlop();
handle = (RelativeLayout) findViewById(R.id.handle);

- adWindow = (RelativeLayout) findViewById(R.id.adWindow);
- adWindow.setVisibility(View.INVISIBLE);
+ sponsorshipWindow = (RelativeLayout)
findViewById(R.id.sponsorshipWindow);
+ sponsorshipWindow.setVisibility(View.INVISIBLE);

playerContracted = (RelativeLayout)
findViewById(R.id.player_contracted);
playerExpanded = (RelativeLayout) findViewById(R.id.player_expanded);
@@ -284,19 +284,19 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

- if (adWindowState == adWindowStates.WindowNeedsMeasurement) {
- if (adWindow.getMeasuredWidth() >= 240) {
- adWindowState = adWindowStates.WindowReadyToBeShown;
- WebView adView = (WebView) findViewById(R.id.adView);
- adView.setBackgroundColor(0);
-
- WebSettings webSettings = adView.getSettings();
+ if (sponsorshipWindowState ==
sponsorshipWindowStates.WindowNeedsMeasurement) {
+ if (sponsorshipWindow.getMeasuredWidth() >= 240) {
+ sponsorshipWindowState =
sponsorshipWindowStates.WindowReadyToBeShown;
+ WebView sponsorshipView = (WebView)
findViewById(R.id.sponsorshipView);
+ sponsorshipView.setBackgroundColor(0);
+
+ WebSettings webSettings = sponsorshipView.getSettings();
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(false);

- adView.loadDataWithBaseURL(null,
+ sponsorshipView.loadDataWithBaseURL(null,
"<html><head><style type='text/css'>body
{padding:0;margin:0} " +
"p {padding:0 0 3px 0;margin:0;color:white;" +
"font-size:10px;font-family:Helvetica,Arial," +
@@ -306,10 +306,11 @@
"n6735.NPR.MOBILE/android;sz=320x50' />" +
"</body></html>",
"text/html", "utf-8", null);
- ImageButton dismissAdView = (ImageButton)
findViewById(R.id.dismissAdView);
- dismissAdView.setOnClickListener(this);
+ ImageButton dismissSponsorshipView =
+ (ImageButton) findViewById(R.id.dismissSponsorshipView);
+ dismissSponsorshipView.setOnClickListener(this);
} else {
- adWindowState = adWindowStates.WindowTooNarrow;
+ sponsorshipWindowState = sponsorshipWindowStates.WindowTooNarrow;
}
}
}
@@ -365,8 +366,8 @@
configurePlayerControls();
break;

- case R.id.dismissAdView:
- hideAdWindow();
+ case R.id.dismissSponsorshipView:
+ hideSponsorshipWindow();
break;
}
}
@@ -383,8 +384,8 @@
}

private void playNow(final Playable playable, String action) {
- if (adWindowState == adWindowStates.WindowReadyToBeShown) {
- showAdWindow();
+ if (sponsorshipWindowState ==
sponsorshipWindowStates.WindowReadyToBeShown) {
+ showSponsorshipWindow();
}
startPlaylistSpinners();
Intent intent = new Intent(context, PlaybackService.class);
@@ -1039,8 +1040,8 @@
return playlistAdapter.getActiveId();
}

- private void showAdWindow() {
- if (adWindowState != adWindowStates.WindowReadyToBeShown) {
+ private void showSponsorshipWindow() {
+ if (sponsorshipWindowState !=
sponsorshipWindowStates.WindowReadyToBeShown) {
return;
}

@@ -1066,16 +1067,16 @@
}

});
- adWindow.startAnimation(scroll_in_from_bottom);
- adWindowState = adWindowStates.WindowVisible;
+ sponsorshipWindow.startAnimation(scroll_in_from_bottom);
+ sponsorshipWindowState = sponsorshipWindowStates.WindowVisible;
}

- private void hideAdWindow() {
- if (adWindowState != adWindowStates.WindowVisible) {
+ private void hideSponsorshipWindow() {
+ if (sponsorshipWindowState != sponsorshipWindowStates.WindowVisible) {
return;
}

- adWindowState = adWindowStates.WindowReadyToBeShown;
+ sponsorshipWindowState = sponsorshipWindowStates.WindowReadyToBeShown;

Animation scroll_out_bottom = AnimationUtils.loadAnimation(
context,
@@ -1098,7 +1099,7 @@
}

});
- adWindow.startAnimation(scroll_out_bottom);
- adWindow.setVisibility(View.INVISIBLE);
+ sponsorshipWindow.startAnimation(scroll_out_bottom);
+ sponsorshipWindow.setVisibility(View.INVISIBLE);
}
}
=======================================
--- /Npr/src/org/npr/api/Story.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/api/Story.java Wed Jun 15 13:39:57 2011
@@ -284,6 +284,10 @@
public boolean isPrimary() {
return isPrimary;
}
+
+ public String getTitle() {
+ return title;
+ }
}

public Story(String id, String link, String shortLink, String title,

==============================================================================
Revision: ac4c79808617
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Jun 22 17:39:11 2011
Log: - Rearrange list item layout

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@127
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/res/layout/news_item.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/NavigationView.java
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java
/Npr/src/org/npr/api/ApiConstants.java
/Npr/src/org/npr/api/Story.java

=======================================
--- /Npr/res/layout/news_item.xml Wed Jun 15 13:39:57 2011
+++ /Npr/res/layout/news_item.xml Wed Jun 22 17:39:11 2011
@@ -3,14 +3,13 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical"
- android:padding="8dp"
+ android:padding="3dp"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeight">
<ImageView
android:id="@+id/NewsItemIcon"
android:layout_width="35dp"
android:layout_height="35dp"
- android:layout_marginRight="5dp"
android:scaleType="center"/>
<LinearLayout
android:layout_width="wrap_content"
@@ -18,22 +17,21 @@
android:layout_weight="1"
android:orientation="vertical">
<TextView
- android:id="@+id/NewsItemNameText"
+ android:id="@+id/NewsItemTopicText"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
- android:gravity="center_vertical"
- android:textAppearance="@style/NewsItemTitle"/>
+ android:textAppearance="@style/NewsItemTopicText"/>
<TextView
- android:id="@+id/NewsItemSubtitleText"
+ android:id="@+id/NewsItemNameText"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
- android:textAppearance="@style/NewsItemSubtitleText"/>
+ android:gravity="center_vertical"
+ android:textAppearance="@style/NewsItemTitle"/>
</LinearLayout>
<ImageView
android:id="@+id/NewsItemImage"
android:layout_alignParentRight="true"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:layout_marginLeft="5dp"
- android:scaleType="centerInside"/>
+ android:layout_width="75dp"
+ android:layout_height="75dp"
+ android:scaleType="centerCrop"/>
</LinearLayout>
=======================================
--- /Npr/res/values/styles.xml Wed Jun 15 13:39:57 2011
+++ /Npr/res/values/styles.xml Wed Jun 22 17:39:11 2011
@@ -37,13 +37,13 @@
<item name="android:singleLine">true</item>
</style>
<style name="NewsItemTitle" parent="@android:style/TextAppearance">
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">13sp</item>
<item name="android:typeface">serif</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@android:color/black</item>
</style>
- <style name="NewsItemSubtitleText"
parent="@android:style/TextAppearance">
- <item name="android:textSize">12sp</item>
+ <style name="NewsItemTopicText" parent="@android:style/TextAppearance">
+ <item name="android:textSize">10sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#737373</item>
</style>
=======================================
--- /Npr/src/org/npr/android/news/NavigationView.java Sun May 22 12:54:47
2011
+++ /Npr/src/org/npr/android/news/NavigationView.java Wed Jun 22 17:39:11
2011
@@ -127,7 +127,7 @@
.putExtra(Constants.EXTRA_QUERY_URL, newsUrl)
.putExtra(Constants.EXTRA_DESCRIPTION, description)
.putExtra(Constants.EXTRA_GROUPING, grouping)
- .putExtra(Constants.EXTRA_SIZE, 5)),
+ .putExtra(Constants.EXTRA_SIZE, 10)),
new SubActivity(new Intent(getContext(), NewsTopicActivity.class)
.putExtra(Constants.EXTRA_SUBACTIVITY_ID,
R.string.msg_main_subactivity_topics)),
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Wed Jun 15 13:39:57
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Wed Jun 22 17:39:11
2011
@@ -439,7 +439,6 @@
* @return A URL for the NPR API.
*/
protected String getApiUrl() {
- Log.e(LOG_TAG, getIntent().getStringExtra(Constants.EXTRA_QUERY_URL));
return getIntent().getStringExtra(Constants.EXTRA_QUERY_URL);
}

@@ -502,7 +501,7 @@
.putExtra(Constants.EXTRA_QUERY_URL, url)
.putExtra(Constants.EXTRA_DESCRIPTION, "Top Stories")
.putExtra(Constants.EXTRA_GROUPING, grouping)
- .putExtra(Constants.EXTRA_SIZE, 5);
+ .putExtra(Constants.EXTRA_SIZE, 10);
setIntent(i);
}

=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Wed Jun 15 13:39:57
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Wed Jun 22 17:39:11
2011
@@ -41,10 +41,6 @@

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
-import java.sql.Time;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
import java.util.List;


@@ -54,19 +50,11 @@
private final ImageThreadLoader imageLoader;
private RootActivity rootActivity = null;
private final PlaylistRepository repository;
- private String grouping = null;
- // Sample date from api: Tue, 09 Jun 2009 15:20:00 -0400
- public static final SimpleDateFormat apiDateFormat
- = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
- private final DateFormat longDateFormat
- = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT);

public NewsListAdapter(Context context) {
super(context, R.layout.news_item);
if (context instanceof RootActivity) {
rootActivity = (RootActivity) context;
- grouping =
-
rootActivity.getIntent().getStringExtra(Constants.EXTRA_GROUPING);
}
inflater = LayoutInflater.from(getContext());
imageLoader = ImageThreadLoader.getOnDiskInstance(context);
@@ -128,7 +116,7 @@
Story story = getItem(position);

ImageView icon = (ImageView)
convertView.findViewById(R.id.NewsItemIcon);
- TextView subtitle =
(TextView)convertView.findViewById(R.id.NewsItemSubtitleText);
+ TextView topic = (TextView)
convertView.findViewById(R.id.NewsItemTopicText);
TextView name = (TextView)
convertView.findViewById(R.id.NewsItemNameText);
final ImageView image = (ImageView)
convertView.findViewById(R.id.NewsItemImage);

@@ -157,22 +145,8 @@
// view and will be in italics
name.setTypeface(name.getTypeface(), Typeface.BOLD);

- if (grouping == null) {
- for (Story.Parent storyParent : story.getParentTopics()) {
- if (storyParent.isPrimary()) {
- subtitle.setText(storyParent.getTitle());
- }
- }
- } else {
- try {
- subtitle.setText(
-
longDateFormat.format(apiDateFormat.parse(story.getStoryDate()))
- );
- } catch (ParseException e) {
- Log.e(LOG_TAG, "date format:", e);
- subtitle.setVisibility(View.GONE);
- }
- }
+ topic.setText(story.getSlug());
+ topic.setVisibility(View.VISIBLE);

if (story.getImages().size() > 0) {
final String url = story.getImages().get(0).getSrc();
@@ -181,18 +155,20 @@
url,
new ImageThreadLoader.ImageLoadedListener() {
public void imageLoaded(Drawable imageBitmap) {
- View itemView = parent.getChildAt(position -
+ // Offset 1 for header
+ int storyPosition = position - 1;
+ View itemView = parent.getChildAt(storyPosition -
((ListView) parent).getFirstVisiblePosition());
if (itemView == null) {
Log.w(LOG_TAG, "Could not find list item at position " +
- position);
+ storyPosition);
return;
}
ImageView img = (ImageView)
itemView.findViewById(R.id.NewsItemImage);
if (img == null) {
Log.w(LOG_TAG, "Could not find image for list item at " +
- "position " + position);
+ "position " + storyPosition);
return;
}
img.setImageDrawable(imageBitmap);
@@ -209,7 +185,7 @@
} else {
// null marker means it's the end of the list.
icon.setVisibility(View.INVISIBLE);
- subtitle.setVisibility(View.GONE);
+ topic.setVisibility(View.GONE);
image.setVisibility(View.GONE);
name.setTypeface(name.getTypeface(), Typeface.ITALIC);
name.setText(R.string.msg_load_more);
=======================================
--- /Npr/src/org/npr/api/ApiConstants.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/api/ApiConstants.java Wed Jun 22 17:39:11 2011
@@ -47,7 +47,7 @@
public static final String PARAM_SORT = "sort";
public static final String PARAM_DATE = "date";

- public static final String STORY_FIELDS
= "title,miniTeaser,teaser,storyDate,byline,text,audio,textWithHtml,image,organization,parent";
+ public static final String STORY_FIELDS
= "titles,teasers,storyDate,byline,text,audio,textWithHtml,image,organization,parent";
private final String apiKey;
private static ApiConstants instance;

=======================================
--- /Npr/src/org/npr/api/Story.java Wed Jun 15 13:39:57 2011
+++ /Npr/src/org/npr/api/Story.java Wed Jun 22 17:39:11 2011
@@ -701,6 +701,8 @@
sb.withTeaser(NodeUtils.getTextContent(n));
} else if (nodeName.equals("miniTeaser")) {
sb.withMiniTeaser(NodeUtils.getTextContent(n));
+ } else if (nodeName.equals("slug")) {
+ sb.withSlug(NodeUtils.getTextContent(n));
} else if (nodeName.equals("storyDate")) {
sb.withStoryDate(NodeUtils.getTextContent(n));
} else if (nodeName.equals("pubDate")) {

==============================================================================
Revision: abcdeb15beec
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sat Jul 2 19:50:10 2011
Log: Added updated time to news story list title bar. Added Suuport
Comes From to the banner. Increased story slug font size.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@128
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Added:
/Npr/src/org/npr/android/util/TimeUnit.java
/Npr/src/org/npr/android/util/TimeUtils.java
Modified:
/Npr/res/layout/list_header.xml
/Npr/res/layout/title.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java
/Npr/src/org/npr/android/news/TitleActivity.java

=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/util/TimeUnit.java Sat Jul 2 19:50:10 2011
@@ -0,0 +1,341 @@
+package org.npr.android.util;
+
+/**
+ * Backport because Android JDK doesn't include DAYS as a valid TimeUnit
+ */
+
+
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+
+/**
+ * A <tt>TimeUnit</tt> represents time durations at a given unit of
+ * granularity and provides utility methods to convert across units,
+ * and to perform timing and delay operations in these units. A
+ * <tt>TimeUnit</tt> does not maintain time information, but only
+ * helps organize and use time representations that may be maintained
+ * separately across various contexts. A nanosecond is defined as one
+ * thousandth of a microsecond, a microsecond as one thousandth of a
+ * millisecond, a millisecond as one thousandth of a second, a minute
+ * as sixty seconds, an hour as sixty minutes, and a day as twenty four
+ * hours.
+ *
+ * <p>A <tt>TimeUnit</tt> is mainly used to inform time-based methods
+ * how a given timing parameter should be interpreted. For example,
+ * the following code will timeout in 50 milliseconds if the {@link
+ * java.util.concurrent.locks.Lock lock} is not available:
+ *
+ * <pre> Lock lock = ...;
+ * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...
+ * </pre>
+ * while this code will timeout in 50 seconds:
+ * <pre>
+ * Lock lock = ...;
+ * if (lock.tryLock(50L, TimeUnit.SECONDS)) ...
+ * </pre>
+ *
+ * Note however, that there is no guarantee that a particular timeout
+ * implementation will be able to notice the passage of time at the
+ * same granularity as the given <tt>TimeUnit</tt>.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ */
+public enum TimeUnit {
+ NANOSECONDS {
+ public long toNanos(long d) { return d; }
+ public long toMicros(long d) { return d/(C1/C0); }
+ public long toMillis(long d) { return d/(C2/C0); }
+ public long toSeconds(long d) { return d/(C3/C0); }
+ public long toMinutes(long d) { return d/(C4/C0); }
+ public long toHours(long d) { return d/(C5/C0); }
+ public long toDays(long d) { return d/(C6/C0); }
+ public long convert(long d, TimeUnit u) { return u.toNanos(d); }
+ int excessNanos(long d, long m) { return (int)(d - (m*C2)); }
+ },
+ MICROSECONDS {
+ public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); }
+ public long toMicros(long d) { return d; }
+ public long toMillis(long d) { return d/(C2/C1); }
+ public long toSeconds(long d) { return d/(C3/C1); }
+ public long toMinutes(long d) { return d/(C4/C1); }
+ public long toHours(long d) { return d/(C5/C1); }
+ public long toDays(long d) { return d/(C6/C1); }
+ public long convert(long d, TimeUnit u) { return u.toMicros(d); }
+ int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); }
+ },
+ MILLISECONDS {
+ public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); }
+ public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); }
+ public long toMillis(long d) { return d; }
+ public long toSeconds(long d) { return d/(C3/C2); }
+ public long toMinutes(long d) { return d/(C4/C2); }
+ public long toHours(long d) { return d/(C5/C2); }
+ public long toDays(long d) { return d/(C6/C2); }
+ public long convert(long d, TimeUnit u) { return u.toMillis(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+ SECONDS {
+ public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); }
+ public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); }
+ public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); }
+ public long toSeconds(long d) { return d; }
+ public long toMinutes(long d) { return d/(C4/C3); }
+ public long toHours(long d) { return d/(C5/C3); }
+ public long toDays(long d) { return d/(C6/C3); }
+ public long convert(long d, TimeUnit u) { return u.toSeconds(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+ MINUTES {
+ public long toNanos(long d) { return x(d, C4/C0, MAX/(C4/C0)); }
+ public long toMicros(long d) { return x(d, C4/C1, MAX/(C4/C1)); }
+ public long toMillis(long d) { return x(d, C4/C2, MAX/(C4/C2)); }
+ public long toSeconds(long d) { return x(d, C4/C3, MAX/(C4/C3)); }
+ public long toMinutes(long d) { return d; }
+ public long toHours(long d) { return d/(C5/C4); }
+ public long toDays(long d) { return d/(C6/C4); }
+ public long convert(long d, TimeUnit u) { return u.toMinutes(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+ HOURS {
+ public long toNanos(long d) { return x(d, C5/C0, MAX/(C5/C0)); }
+ public long toMicros(long d) { return x(d, C5/C1, MAX/(C5/C1)); }
+ public long toMillis(long d) { return x(d, C5/C2, MAX/(C5/C2)); }
+ public long toSeconds(long d) { return x(d, C5/C3, MAX/(C5/C3)); }
+ public long toMinutes(long d) { return x(d, C5/C4, MAX/(C5/C4)); }
+ public long toHours(long d) { return d; }
+ public long toDays(long d) { return d/(C6/C5); }
+ public long convert(long d, TimeUnit u) { return u.toHours(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+ DAYS {
+ public long toNanos(long d) { return x(d, C6/C0, MAX/(C6/C0)); }
+ public long toMicros(long d) { return x(d, C6/C1, MAX/(C6/C1)); }
+ public long toMillis(long d) { return x(d, C6/C2, MAX/(C6/C2)); }
+ public long toSeconds(long d) { return x(d, C6/C3, MAX/(C6/C3)); }
+ public long toMinutes(long d) { return x(d, C6/C4, MAX/(C6/C4)); }
+ public long toHours(long d) { return x(d, C6/C5, MAX/(C6/C5)); }
+ public long toDays(long d) { return d; }
+ public long convert(long d, TimeUnit u) { return u.toDays(d); }
+ int excessNanos(long d, long m) { return 0; }
+ };
+
+ // Handy constants for conversion methods
+ static final long C0 = 1L;
+ static final long C1 = C0 * 1000L;
+ static final long C2 = C1 * 1000L;
+ static final long C3 = C2 * 1000L;
+ static final long C4 = C3 * 60L;
+ static final long C5 = C4 * 60L;
+ static final long C6 = C5 * 24L;
+
+ static final long MAX = Long.MAX_VALUE;
+
+ /**
+ * Scale d by m, checking for overflow.
+ * This has a short name to make above code more readable.
+ */
+ static long x(long d, long m, long over) {
+ if (d > over) return Long.MAX_VALUE;
+ if (d < -over) return Long.MIN_VALUE;
+ return d * m;
+ }
+
+ // To maintain full signature compatibility with 1.5, and to improve
the
+ // clarity of the generated javadoc (see 6287639: Abstract methods in
+ // enum classes should not be listed as abstract), method convert
+ // etc. are not declared abstract but otherwise act as abstract
methods.
+
+ /**
+ * Convert the given time duration in the given unit to this
+ * unit. Conversions from finer to coarser granularities
+ * truncate, so lose precision. For example converting
+ * <tt>999</tt> milliseconds to seconds results in
+ * <tt>0</tt>. Conversions from coarser to finer granularities
+ * with arguments that would numerically overflow saturate to
+ * <tt>Long.MIN_VALUE</tt> if negative or <tt>Long.MAX_VALUE</tt>
+ * if positive.
+ *
+ * <p>For example, to convert 10 minutes to milliseconds, use:
+ * <tt>TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)</tt>
+ *
+ * @param sourceDuration the time duration in the given
<tt>sourceUnit</tt>
+ * @param sourceUnit the unit of the <tt>sourceDuration</tt> argument
+ * @return the converted duration in this unit,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively
overflow.
+ */
+ public long convert(long sourceDuration, TimeUnit sourceUnit) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>NANOSECONDS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively
overflow.
+ * @see #convert
+ */
+ public long toNanos(long duration) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>MICROSECONDS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively
overflow.
+ * @see #convert
+ */
+ public long toMicros(long duration) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>MILLISECONDS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively
overflow.
+ * @see #convert
+ */
+ public long toMillis(long duration) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>SECONDS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively
overflow.
+ * @see #convert
+ */
+ public long toSeconds(long duration) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>MINUTES.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively
overflow.
+ * @see #convert
+ * @since 1.6
+ */
+ public long toMinutes(long duration) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>HOURS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively
overflow.
+ * @see #convert
+ * @since 1.6
+ */
+ public long toHours(long duration) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>DAYS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration
+ * @see #convert
+ * @since 1.6
+ */
+ public long toDays(long duration) {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Utility to compute the excess-nanosecond argument to wait,
+ * sleep, join.
+ * @param d the duration
+ * @param m the number of milliseconds
+ * @return the number of nanoseconds
+ */
+ abstract int excessNanos(long d, long m);
+
+ /**
+ * Performs a timed {@link Object#wait(long, int) Object.wait}
+ * using this time unit.
+ * This is a convenience method that converts timeout arguments
+ * into the form required by the <tt>Object.wait</tt> method.
+ *
+ * <p>For example, you could implement a blocking <tt>poll</tt>
+ * method (see {@link BlockingQueue#poll BlockingQueue.poll})
+ * using:
+ *
+ * <pre> {@code
+ * public synchronized Object poll(long timeout, TimeUnit unit)
+ * throws InterruptedException {
+ * while (empty) {
+ * unit.timedWait(this, timeout);
+ * ...
+ * }
+ * }}</pre>
+ *
+ * @param obj the object to wait on
+ * @param timeout the maximum time to wait. If less than
+ * or equal to zero, do not wait at all.
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public void timedWait(Object obj, long timeout)
+ throws InterruptedException {
+ if (timeout > 0) {
+ long ms = toMillis(timeout);
+ int ns = excessNanos(timeout, ms);
+ obj.wait(ms, ns);
+ }
+ }
+
+ /**
+ * Performs a timed {@link Thread#join(long, int) Thread.join}
+ * using this time unit.
+ * This is a convenience method that converts time arguments into the
+ * form required by the <tt>Thread.join</tt> method.
+ *
+ * @param thread the thread to wait for
+ * @param timeout the maximum time to wait. If less than
+ * or equal to zero, do not wait at all.
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public void timedJoin(Thread thread, long timeout)
+ throws InterruptedException {
+ if (timeout > 0) {
+ long ms = toMillis(timeout);
+ int ns = excessNanos(timeout, ms);
+ thread.join(ms, ns);
+ }
+ }
+
+ /**
+ * Performs a {@link Thread#sleep(long, int) Thread.sleep} using
+ * this time unit.
+ * This is a convenience method that converts time arguments into the
+ * form required by the <tt>Thread.sleep</tt> method.
+ *
+ * @param timeout the minimum time to sleep. If less than
+ * or equal to zero, do not sleep at all.
+ * @throws InterruptedException if interrupted while sleeping
+ */
+ public void sleep(long timeout) throws InterruptedException {
+ if (timeout > 0) {
+ long ms = toMillis(timeout);
+ int ns = excessNanos(timeout, ms);
+ Thread.sleep(ms, ns);
+ }
+ }
+
+}
=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/util/TimeUtils.java Sat Jul 2 19:50:10 2011
@@ -0,0 +1,60 @@
+package org.npr.android.util;
+
+/**
+ *
http://stackoverflow.com/questions/3859288/how-to-calculate-time-ago-in-java#5062810
+ * Author: David Blevins
(http://stackoverflow.com/users/190816/david-blevins)
+ */
+
+
+import static org.npr.android.util.TimeUnit.MILLISECONDS;
+
+public class TimeUtils {
+
+ /**
+ * Converts time to a human readable format within the specified range
+ *
+ *
+ *
+ * @param duration the time in milliseconds to be converted
+ * @param max the highest time unit of interest
+ * @param min the lowest time unit of interest
+ * @return A human readable time expression
+ */
+ public static String formatMillis(long duration, TimeUnit max, TimeUnit
min) {
+ StringBuilder res = new StringBuilder();
+
+ TimeUnit current = max;
+
+ while (duration > 0) {
+ long temp = current.convert(duration, MILLISECONDS);
+
+ if (temp > 0) {
+ duration -= current.toMillis(temp);
+ res.append(temp).append(" ").append(current.name().toLowerCase());
+ if (temp < 2) res.deleteCharAt(res.length() - 1);
+ res.append(", ");
+ }
+
+ if (current == min) break;
+
+ current = TimeUnit.values()[current.ordinal() - 1];
+ }
+
+ // clean up our formatting....
+
+ // we never got a hit, the time is lower than we care about
+ if (res.lastIndexOf(", ") < 0) return "0 " + min.name().toLowerCase();
+
+ // yank trailing ", "
+ res.deleteCharAt(res.length() - 2);
+
+ // convert last ", " to " and"
+ int i = res.lastIndexOf(", ");
+ if (i > 0) {
+ res.deleteCharAt(i);
+ res.insert(i, " and");
+ }
+
+ return res.toString();
+ }
+}
=======================================
--- /Npr/res/layout/list_header.xml Wed Jun 15 13:39:57 2011
+++ /Npr/res/layout/list_header.xml Sat Jul 2 19:50:10 2011
@@ -4,4 +4,4 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sponsorshipWindowHeader"
android:layout_width="fill_parent"
- android:layout_height="50dip" />
+ android:layout_height="62dip" />
=======================================
--- /Npr/res/layout/title.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/title.xml Sat Jul 2 19:50:10 2011
@@ -11,13 +11,21 @@
android:orientation="horizontal">
<TextView
android:id="@+id/TitleText"
- android:layout_width="fill_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_below="@+id/LogoBar"
+ android:layout_weight="2"
android:lines="1"
android:ellipsize="end"
android:textAppearance="@style/ViewTitleText"/>
+ <TextView
+ android:id="@+id/TitleRight"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:lines="1"
+ android:ellipsize="end"
+ android:gravity="right"
+ android:textAppearance="@style/ViewTitleSubText"/>
</LinearLayout>
<FrameLayout
android:id="@+id/Content"
=======================================
--- /Npr/res/values/styles.xml Wed Jun 22 17:39:11 2011
+++ /Npr/res/values/styles.xml Sat Jul 2 19:50:10 2011
@@ -31,11 +31,15 @@
</style>
<style name="ViewTitleText" parent="@android:style/TextAppearance">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">#333</item>
+ <item name="android:textColor">#ffffffff</item>
<item name="android:textStyle">bold</item>
<item name="android:ellipsize">end</item>
<item name="android:singleLine">true</item>
</style>
+ <style name="ViewTitleSubText" parent="@style/ViewTitleText">
+ <item name="android:textSize">10sp</item>
+ <item name="android:textStyle">normal</item>
+ </style>
<style name="NewsItemTitle" parent="@android:style/TextAppearance">
<item name="android:textSize">13sp</item>
<item name="android:typeface">serif</item>
@@ -43,7 +47,7 @@
<item name="android:textColor">@android:color/black</item>
</style>
<style name="NewsItemTopicText" parent="@android:style/TextAppearance">
- <item name="android:textSize">10sp</item>
+ <item name="android:textSize">10.5sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#737373</item>
</style>
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Wed Jun 22 17:39:11
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Sat Jul 2 19:50:10
2011
@@ -23,11 +23,7 @@
import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
+import android.view.*;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.webkit.WebSettings;
@@ -37,11 +33,7 @@
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
-
-import org.npr.android.util.DisplayUtils;
-import org.npr.android.util.PlaylistEntry;
-import org.npr.android.util.PlaylistRepository;
-import org.npr.android.util.Tracker;
+import org.npr.android.util.*;
import org.npr.android.util.Tracker.StoryListMeasurement;
import org.npr.api.ApiConstants;
import org.npr.api.Story;
@@ -72,13 +64,13 @@

private BroadcastReceiver playlistChangedReceiver;

+
// Message handler to communicate between the gestures and the activity
private final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case ListItemGestureListener.MSG_LONG_PRESS:
- {
+ case ListItemGestureListener.MSG_LONG_PRESS: {
lastLongPressPosition = msg.arg1;

// Offset 1 for header
@@ -110,13 +102,12 @@
}
break;

- case ListItemGestureListener.MSG_FLING:
- {
+ case ListItemGestureListener.MSG_FLING: {
// Offset 1 for header
int storyPosition = msg.arg1 - 1;

flungStory = listAdapter.getItem(storyPosition);
- if ( flungStory != null && flungStory.getPlayable() != null) {
+ if (flungStory != null && flungStory.getPlayable() != null) {
PlaylistRepository playlistRepository =
new PlaylistRepository(getApplicationContext(),
getContentResolver());
@@ -171,7 +162,7 @@
protected void onCreate(Bundle savedInstanceState) {
if (getIntent() == null ||
!(getIntent().hasExtra(Constants.EXTRA_QUERY_URL)
- || getIntent().hasExtra(Constants.EXTRA_PODCAST_URL))) {
+ || getIntent().hasExtra(Constants.EXTRA_PODCAST_URL))) {
setDefaultIntent();
}
grouping = getIntent().getStringExtra(Constants.EXTRA_GROUPING);
@@ -198,7 +189,7 @@
.inflate(R.layout.list_header, listView, false);

WebView sponsorshipWindow =
- (WebView)header.findViewById(R.id.sponsorshipWindowHeader);
+ (WebView) header.findViewById(R.id.sponsorshipWindowHeader);

WebSettings webSettings = sponsorshipWindow.getSettings();
webSettings.setSavePassword(false);
@@ -206,14 +197,19 @@
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(false);

- sponsorshipWindow.loadDataWithBaseURL(null,
- "<html><head><style type='text/css'>body {padding:0;margin:0} " +
- "p {display:none}</style>" +
- "</head><body><script type='text/javascript' " +
- "src='http://ad.doubleclick.net/adj/" +
- "n6735.NPR.MOBILE/android;sz=320x50' />" +
+ long ord = (long) (Math.random() * 10000000000000000L);
+ String html = String.format(
+ "<html><head><style type=\"text/css\">body
{padding:0;margin:0;text-align:center;}" +
+ "p {margin:0;padding:0;font-family:sans-serif;" +
+ "font-size:x-small}</style></head>" +
+ "<body><script type=\"text/javascript\" " +
+ "src=\"http://ad.doubleclick.net/adj/n6735.NPR.MOBILE/android;sz=320x50;ord=%1$d?\">"
+
+ "</script>" +
"</body></html>",
- "text/html", "utf-8", null);
+ ord);
+ Log.d(LOG_TAG, html);
+ sponsorshipWindow.loadDataWithBaseURL(null, html, "text/html", "utf-8",
+ null);
listView.addHeaderView(header);

listView.setOnItemClickListener(this);
@@ -237,6 +233,13 @@

addStories();
}
+
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ handler.postDelayed(updateTime, UPDATE_SHORT_PERIOD);
+ }

@Override
protected void onStop() {
@@ -244,6 +247,7 @@
unregisterReceiver(playlistChangedReceiver);
playlistChangedReceiver = null;
}
+ handler.removeCallbacks(updateTime);
super.onStop();
}

@@ -274,7 +278,7 @@
private void addStories() {
String url = getApiUrl();
if (url != null) {
- // Adding these parameters to podcast urls (like WNYC) can break them
+ // Adding these parameters to podcast urls (like WNYC) can break them
Map<String, String> params = new HashMap<String, String>();
params.put("startNum", "" + listAdapter.getCount());
params.put("numResults", "" + initialSize);
@@ -446,7 +450,7 @@
* Gets a podcast RSS feed used for looking up the items for the list.
This
* will only be queried if getApiUrl returns null. No additional
parameters
* are added to the podcast list and all stories are shown immediately.
- *
+ * <p/>
* The default implementation pulls the URL from the Intent's
* EXTRA_PODCAST_URL value.
*
@@ -460,6 +464,7 @@
public CharSequence getMainTitle() {
return description;
}
+

@Override
public void trackNow() {
@@ -504,6 +509,35 @@
.putExtra(Constants.EXTRA_SIZE, 10);
setIntent(i);
}
+
+ // Update every five seconds until we have a result
+ private static final long UPDATE_SHORT_PERIOD = 5000L;
+
+ // Update once a minute once we have a result
+ private static final long UPDATE_LONG_PERIOD = 60000L;
+
+ private Runnable updateTime = new Runnable() {
+ public void run() {
+ if (listAdapter == null) {
+ handler.postDelayed(this, UPDATE_SHORT_PERIOD);
+ return;
+ }
+ long lastUpdate = listAdapter.getLastUpdate();
+ if (lastUpdate < 0) {
+ handler.postDelayed(this, UPDATE_SHORT_PERIOD);
+ return;
+ }
+ String label =
+ String.format("Updated %1$s ago",
+ TimeUtils.formatMillis(
+ System.currentTimeMillis() - lastUpdate,
+ TimeUnit.DAYS,
+ TimeUnit.MINUTES
+ ));
+ setTitleRight(label);
+ handler.postDelayed(this, UPDATE_LONG_PERIOD);
+ }
+ };

private class PlaylistChangedReceiver extends BroadcastReceiver {
@Override
=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Wed Jun 22 17:39:11
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Sat Jul 2 19:50:10
2011
@@ -25,12 +25,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
+import android.widget.*;
import org.npr.android.util.PlaylistRepository;
import org.npr.api.Client;
import org.npr.api.Story;
@@ -50,6 +45,7 @@
private final ImageThreadLoader imageLoader;
private RootActivity rootActivity = null;
private final PlaylistRepository repository;
+ private long lastUpdate = -1;

public NewsListAdapter(Context context) {
super(context, R.layout.news_item);
@@ -70,6 +66,7 @@
public void handleMessage(Message msg) {
if (msg.what >= 0) {
if (moreStories != null) {
+ lastUpdate = System.currentTimeMillis();
remove(null);
for (Story s : moreStories) {
if (getPosition(s) < 0) {
@@ -258,4 +255,14 @@
}
return result;
}
-}
+
+ /**
+ * Returns the time (in milliseconds since the epoch) of when
+ * the last update was.
+ *
+ * @return A time unit in milliseconds since the epoch
+ */
+ public long getLastUpdate() {
+ return lastUpdate;
+ }
+}
=======================================
--- /Npr/src/org/npr/android/news/TitleActivity.java Sun May 22 12:54:47
2011
+++ /Npr/src/org/npr/android/news/TitleActivity.java Sat Jul 2 19:50:10
2011
@@ -35,6 +35,19 @@
* @return A string for the title bar.
*/
protected abstract CharSequence getMainTitle();
+
+ /**
+ * This allows implementing classes to set text that appears
+ * on the right side of the title bar.
+ *
+ * @param rightText The text to render on the right of the title bar
+ */
+ protected void setTitleRight(CharSequence rightText) {
+ if (rightText != null && rightText.length() > 0) {
+ TextView titleRight = (TextView) findViewById(R.id.TitleRight);
+ titleRight.setText(rightText);
+ }
+ }

@Override
protected void onCreate(Bundle savedInstanceState) {

==============================================================================
Revision: ab4a04185a19
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Jul 6 16:05:40 2011
Log: Changed sponsorship banner text to white on black to match mobile
site experience.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@129
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/src/org/npr/android/news/NewsListActivity.java

=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Sat Jul 2 19:50:10
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Wed Jul 6 16:05:40
2011
@@ -199,9 +199,8 @@

long ord = (long) (Math.random() * 10000000000000000L);
String html = String.format(
- "<html><head><style type=\"text/css\">body
{padding:0;margin:0;text-align:center;}" +
- "p {margin:0;padding:0;font-family:sans-serif;" +
- "font-size:x-small}</style></head>" +
+ "<html><head><style type=\"text/css\">body
{padding:0;margin:0;text-align:center;background-color:black;}" +
+ "p
{margin:0;padding:0;font-family:sans-serif;font-size:x-small;color:white;}</style></head>"
+
"<body><script type=\"text/javascript\" " +
"src=\"http://ad.doubleclick.net/adj/n6735.NPR.MOBILE/android;sz=320x50;ord=%1$d?\">"
+
"</script>" +

==============================================================================
Revision: 95027ab60d27
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jul 21 13:43:44 2011
Log: Set banner background to black. Changed banner to load
android_npr tag and (mostly) hide when no image is presented. Fixed up
image loading that had an off-by-two error in the news list. Fixed text
color for news topic list header.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@130
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/res/layout/list_header.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/ImageThreadLoader.java
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java

=======================================
--- /Npr/res/layout/list_header.xml Sat Jul 2 19:50:10 2011
+++ /Npr/res/layout/list_header.xml Thu Jul 21 13:43:44 2011
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>

<WebView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/sponsorshipWindowHeader"
- android:layout_width="fill_parent"
- android:layout_height="62dip" />
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sponsorshipWindowHeader"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="#FF000000"/>
=======================================
--- /Npr/res/values/styles.xml Sat Jul 2 19:50:10 2011
+++ /Npr/res/values/styles.xml Thu Jul 21 13:43:44 2011
@@ -31,7 +31,7 @@
</style>
<style name="ViewTitleText" parent="@android:style/TextAppearance">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">#ffffffff</item>
+ <item name="android:textColor">#ff000000</item>
<item name="android:textStyle">bold</item>
<item name="android:ellipsize">end</item>
<item name="android:singleLine">true</item>
=======================================
--- /Npr/src/org/npr/android/news/ImageThreadLoader.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/ImageThreadLoader.java Thu Jul 21
13:43:44 2011
@@ -133,6 +133,7 @@
@Override
public void run() {
if (item.listener != null) {
+ Log.d(LOG_TAG, "Loaded image from " + item.url);
item.listener.imageLoaded(new BitmapDrawable(bmp));
}
}
@@ -153,6 +154,7 @@
// re-run the network load or something.
Bitmap ref = cache.get(item.url);
if (ref != null) {
+ Log.d(LOG_TAG, "Loaded image from cache");
item.listener.imageLoaded(new BitmapDrawable(ref));
} else {
Log.w(LOG_TAG, "Image loader lost the image to GC.");
@@ -177,6 +179,7 @@
if (cache.containsKey(uri)) {
Bitmap ref = cache.get(uri);
if (ref != null) {
+ Log.d(LOG_TAG, "Found image in cache!");
return new BitmapDrawable(ref);
}
}
@@ -187,7 +190,6 @@
queue.add(item);

// start the thread if needed
- // NB: this might not work right; it's untested
if (thread.getState() == State.NEW) {
thread.start();
} else if (thread.getState() == State.TERMINATED) {
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Wed Jul 6 16:05:40
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Thu Jul 21 13:43:44
2011
@@ -179,6 +179,8 @@
titleBar.getLayoutParams().height = DisplayUtils.convertToDIP(this,
20);
TextView titleText = (TextView) findViewById(R.id.TitleText);

titleText.setTextColor(getResources().getColor(R.color.news_title_text));
+ TextView titleRight = (TextView) findViewById(R.id.TitleRight);
+
titleRight.setTextColor(getResources().getColor(R.color.news_title_text));

ViewGroup container = (ViewGroup) findViewById(R.id.Content);
ViewGroup.inflate(this, R.layout.news, container);
@@ -202,7 +204,7 @@
"<html><head><style type=\"text/css\">body
{padding:0;margin:0;text-align:center;background-color:black;}" +
"p
{margin:0;padding:0;font-family:sans-serif;font-size:x-small;color:white;}</style></head>"
+
"<body><script type=\"text/javascript\" " +
- "src=\"http://ad.doubleclick.net/adj/n6735.NPR.MOBILE/android;sz=320x50;ord=%1$d?\">"
+
+ "src=\"http://ad.doubleclick.net/adj/n6735.NPR.MOBILE/android_npr;sz=320x50;ord=%1$d?\">"
+
"</script>" +
"</body></html>",
ord);
=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Sat Jul 2 19:50:10
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Thu Jul 21 13:43:44
2011
@@ -147,30 +147,9 @@

if (story.getImages().size() > 0) {
final String url = story.getImages().get(0).getSrc();
- Drawable cachedImage = null;
- cachedImage = imageLoader.loadImage(
+ Drawable cachedImage = imageLoader.loadImage(
url,
- new ImageThreadLoader.ImageLoadedListener() {
- public void imageLoaded(Drawable imageBitmap) {
- // Offset 1 for header
- int storyPosition = position - 1;
- View itemView = parent.getChildAt(storyPosition -
- ((ListView) parent).getFirstVisiblePosition());
- if (itemView == null) {
- Log.w(LOG_TAG, "Could not find list item at position " +
- storyPosition);
- return;
- }
- ImageView img = (ImageView)
- itemView.findViewById(R.id.NewsItemImage);
- if (img == null) {
- Log.w(LOG_TAG, "Could not find image for list item at " +
- "position " + storyPosition);
- return;
- }
- img.setImageDrawable(imageBitmap);
- }
- }
+ new ImageLoadListener(position, (ListView) parent)
);

image.setImageDrawable(cachedImage);
@@ -265,4 +244,36 @@
public long getLastUpdate() {
return lastUpdate;
}
-}
+
+ private class ImageLoadListener implements
ImageThreadLoader.ImageLoadedListener {
+
+ private int position;
+ private ListView parent;
+
+ public ImageLoadListener(int position, ListView parent) {
+ this.position = position;
+ this.parent = parent;
+ }
+
+ public void imageLoaded(Drawable imageBitmap) {
+ // Offset 1 for header
+ int storyPosition = position + 1;
+ View itemView = parent.getChildAt(storyPosition -
+ parent.getFirstVisiblePosition());
+ if (itemView == null) {
+ Log.w(LOG_TAG, "Could not find list item at position " +
+ storyPosition);
+ return;
+ }
+ ImageView img = (ImageView)
+ itemView.findViewById(R.id.NewsItemImage);
+ if (img == null) {
+ Log.w(LOG_TAG, "Could not find image for list item at " +
+ "position " + storyPosition);
+ return;
+ }
+ Log.d(LOG_TAG, "Drawing image at position " + storyPosition);
+ img.setImageDrawable(imageBitmap);
+ }
+ }
+}

==============================================================================
Revision: 5e538bd94a93
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Jul 21 13:49:41 2011
Log: Removed debug logging. Reformatted.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@131
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/res/layout/list_header.xml
/Npr/src/org/npr/android/news/ImageThreadLoader.java

=======================================
--- /Npr/res/layout/list_header.xml Thu Jul 21 13:43:44 2011
+++ /Npr/res/layout/list_header.xml Thu Jul 21 13:49:41 2011
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>

<WebView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/sponsorshipWindowHeader"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="#FF000000"/>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sponsorshipWindowHeader"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="#FF000000"/>
=======================================
--- /Npr/src/org/npr/android/news/ImageThreadLoader.java Thu Jul 21
13:43:44 2011
+++ /Npr/src/org/npr/android/news/ImageThreadLoader.java Thu Jul 21
13:49:41 2011
@@ -133,7 +133,6 @@
@Override
public void run() {
if (item.listener != null) {
- Log.d(LOG_TAG, "Loaded image from " + item.url);
item.listener.imageLoaded(new BitmapDrawable(bmp));
}
}
@@ -154,7 +153,6 @@
// re-run the network load or something.
Bitmap ref = cache.get(item.url);
if (ref != null) {
- Log.d(LOG_TAG, "Loaded image from cache");
item.listener.imageLoaded(new BitmapDrawable(ref));
} else {
Log.w(LOG_TAG, "Image loader lost the image to GC.");
@@ -179,7 +177,6 @@
if (cache.containsKey(uri)) {
Bitmap ref = cache.get(uri);
if (ref != null) {
- Log.d(LOG_TAG, "Found image in cache!");
return new BitmapDrawable(ref);
}
}

==============================================================================
Revision: 286aee96419f
Author: marshgosnell <marshgosnell@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Jul 25 20:11:55 2011
Log: don't FC if multiple Playback updates received

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@132
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/src/org/npr/android/news/RootActivity.java

=======================================
--- /Npr/src/org/npr/android/news/RootActivity.java Thu Jun 9 13:25:31 2011
+++ /Npr/src/org/npr/android/news/RootActivity.java Mon Jul 25 20:11:55 2011
@@ -214,8 +214,10 @@
int duration = intent.getIntExtra(PlaybackService.EXTRA_DURATION, 1);
if (duration != 1) {
stopIndeterminateProgressIndicator();
- unregisterReceiver(updateReceiver);
- updateReceiver = null;
+ if (updateReceiver != null) {
+ unregisterReceiver(updateReceiver);
+ updateReceiver = null;
+ }
}
}
}

==============================================================================
Revision: e92b6348cf49
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Jul 25 21:36:24 2011
Log: Use Thumbnail for news story list, rather than first story image.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@133
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/src/org/npr/android/news/NewsListAdapter.java
/Npr/src/org/npr/api/Story.java

=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Thu Jul 21 13:43:44
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Mon Jul 25 21:36:24
2011
@@ -145,10 +145,15 @@
topic.setText(story.getSlug());
topic.setVisibility(View.VISIBLE);

- if (story.getImages().size() > 0) {
- final String url = story.getImages().get(0).getSrc();
+ String imageUrl = null;
+ if (story.getThumbnails().size() > 0) {
+ imageUrl = story.getThumbnails().get(0).getMedium();
+ } else if (story.getImages().size() > 0) {
+ imageUrl = story.getImages().get(0).getSrc();
+ }
+ if (imageUrl != null) {
Drawable cachedImage = imageLoader.loadImage(
- url,
+ imageUrl,
new ImageLoadListener(position, (ListView) parent)
);

=======================================
--- /Npr/src/org/npr/api/Story.java Wed Jun 22 17:39:11 2011
+++ /Npr/src/org/npr/api/Story.java Mon Jul 25 21:36:24 2011
@@ -70,6 +70,10 @@
public Thumbnail(String medium) {
this.medium = medium;
}
+
+ public String getMedium() {
+ return medium;
+ }
}

public static class Toenail {

==============================================================================
Revision: d7c51a7de1cb
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Aug 15 16:13:01 2011
Log: Move sponsorship banner below player.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@134
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Added:
/Npr/res/drawable/sponsorship_close.xml
/Npr/res/layout/banner.xml
/Npr/src/org/npr/android/news/BannerView.java
Deleted:
/Npr/res/layout/list_header.xml
Modified:
/Npr/res/layout/main.xml
/Npr/res/layout/playlist.xml
/Npr/res/values/strings.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/PlaylistView.java
/Npr/src/org/npr/android/news/RootActivity.java

=======================================
--- /dev/null
+++ /Npr/res/drawable/sponsorship_close.xml Mon Aug 15 16:13:01 2011
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/sponsorship_close_icon_hit_state" />
+
+ <item
+ android:drawable="@drawable/sponsorship_close_icon_normal" />
+
+</selector>
=======================================
--- /dev/null
+++ /Npr/res/layout/banner.xml Mon Aug 15 16:13:01 2011
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<org.npr.android.news.BannerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@style/Banner"/>
=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/news/BannerView.java Mon Aug 15 16:13:01 2011
@@ -0,0 +1,365 @@
+// Copyright 2011 NPR
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.npr.android.news;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.widget.*;
+import org.npr.android.util.DisplayUtils;
+
+
+/**
+ * Encapsulates the logic and layout of the sponsorship banner
+ */
+public class BannerView extends LinearLayout implements
View.OnClickListener {
+ private static final String LOG_TAG = BannerView.class.getName();
+ // Durations in milliseconds
+ private static final int BANNER_VISIBLE = 10000;
+ private static final int FRAME_DURATION = 50;
+ private static final float ANIMATION_DURATION = 500f;
+ private static final String LAYOUT =
+ "<html>" +
+ "<head>" +
+ "<meta name=\"viewport\"
content=\"target-densitydpi=device-dpi, " +
+ "width=device-width\" />" +
+ "<style type=\"text/css\">" +
+ "body
{padding:0;margin:0;text-align:center;background-color:#333;}" +
+ "p {display:none;}" +
+ "</style>" +
+ "</head>" +
+ "<body>" +
+ "<script type=\"text/javascript\"" +
+ "src=\"http://ad.doubleclick.net/adj/n6735.NPR.MOBILE/android_npr;"
+
+ "sz=320x50;ord=%1$d?\">" +
+ "</script>" +
+ "</body>" +
+ "</html>";
+
+ private ImageView animationView;
+ private int startY;
+ private PlaylistView dependentView;
+ private Context context;
+ private WebView webView;
+
+ private enum SponsorshipWindowStates {
+ NeedsMeasurement,
+ TooNarrow,
+ ReadyToBeShown,
+ Visible,
+ Opening,
+ Closing
+ }
+
+ private SponsorshipWindowStates sponsorshipWindowState =
SponsorshipWindowStates.TooNarrow;
+
+ private static final int MSG_START_CLOSE = 0;
+ private static final int MSG_SCROLL_IN = 1;
+ private static final int MSG_SCROLL_OUT = 2;
+
+ private Handler handler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ float input;
+ switch (msg.what) {
+ case MSG_SCROLL_OUT:
+ input = (System.currentTimeMillis() - animationStartTime) /
+ ANIMATION_DURATION;
+ if (input < 1) {
+ placeImage(startY +
+ (int) (accelerator.getInterpolation(input) *
bannerHeight));
+ handler.sendEmptyMessageDelayed(MSG_SCROLL_OUT,
FRAME_DURATION);
+ } else {
+ sponsorshipWindowState =
+ SponsorshipWindowStates.ReadyToBeShown;
+ animationView.setVisibility(GONE);
+ WindowManager wm =
+ (WindowManager)
getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.removeView(animationView);
+ animationView.setImageDrawable(null);
+ animationView = null;
+ }
+ setVisibility(View.GONE);
+ break;
+ case MSG_SCROLL_IN:
+ sponsorshipWindowState = SponsorshipWindowStates.Visible;
+ setVisibility(View.VISIBLE);
+ break;
+ case MSG_START_CLOSE:
+ hideSponsorshipWindow();
+ break;
+ }
+ }
+ };
+
+ private void placeImage(int y) {
+ WindowManager.LayoutParams layoutParams =
+ (WindowManager.LayoutParams) animationView.getLayoutParams();
+ layoutParams.x = 0;
+ layoutParams.y = y;
+
+ WindowManager mWindowManager =
+ (WindowManager)
getContext().getSystemService(Context.WINDOW_SERVICE);
+ mWindowManager.updateViewLayout(animationView, layoutParams);
+ }
+
+ private Interpolator accelerator = new AccelerateInterpolator();
+ private long animationStartTime = 0L;
+ private int bannerHeight;
+ private int screenHeight;
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ public BannerView(Context context) {
+ super(context);
+ this.context = context;
+ init();
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ public BannerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.context = context;
+ init();
+ }
+
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ handler.removeMessages(MSG_SCROLL_IN);
+ handler.removeMessages(MSG_SCROLL_OUT);
+ }
+
+ /**
+ * Allows assignment of the play list view so that the
+ * banner can slide the player drawer down as it scrolls
+ * off screen.
+ *
+ * This enables the drawing cache for the drawer view.
+ *
+ * @param playlistView The view to animate down.
+ */
+ public void setPlayerView(PlaylistView playlistView) {
+ // NB We need to store the PlaylistView here because
+ // the sliding drawer isn't instantiated until after
+ // the view is attached to the window.
+ dependentView = playlistView;
+ }
+
+ void init() {
+ DisplayMetrics metrics = new DisplayMetrics();
+ ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay()
+ .getMetrics(metrics);
+ screenHeight = metrics.heightPixels;
+
+ setDrawingCacheEnabled(true);
+
+ setOrientation(LinearLayout.HORIZONTAL);
+
+ // Some calculated dimensions
+ int dim6 = DisplayUtils.convertToDIP(context, 6);
+ int dim53 = DisplayUtils.convertToDIP(context, 53);
+ int dim214 = DisplayUtils.convertToDIP(context, 214);
+
+ TextView left = new TextView(context);
+ LayoutParams textLayout = new LayoutParams(
+ dim53,
+ LayoutParams.FILL_PARENT,
+ 1
+ );
+ textLayout.gravity = Gravity.CENTER;
+ left.setLayoutParams(textLayout);
+ left.setText(context.getString(R.string.banner_support));
+ left.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL);
+ left.setTextAppearance(context, R.style.BannerText);
+ addView(left);
+
+ webView = new WebView(context);
+ LayoutParams webLayout = new LayoutParams(dim214, 50, 0);
+ webLayout.setMargins(dim6, dim6, dim6, dim6);
+ webLayout.gravity = Gravity.CENTER;
+ webView.setLayoutParams(webLayout);
+ addView(webView);
+
+ ImageButton right = new ImageButton(context);
+ LayoutParams closeLayout = new LayoutParams(
+ dim53,
+ LayoutParams.WRAP_CONTENT,
+ 1
+ );
+ closeLayout.gravity = Gravity.CENTER;
+ right.setLayoutParams(closeLayout);
+ right.setBackgroundDrawable(null);
+ right.setImageResource(R.drawable.sponsorship_close);
+ right.setScaleType(ImageView.ScaleType.CENTER);
+ right.setOnClickListener(this);
+ addView(right);
+
+
+ WebSettings webSettings = webView.getSettings();
+ webSettings.setSavePassword(false);
+ webSettings.setSaveFormData(false);
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setSupportZoom(false);
+
+ sponsorshipWindowState = SponsorshipWindowStates.NeedsMeasurement;
+ }
+
+ @Override
+ public void onClick(View v) {
+ hideSponsorshipWindow();
+ }
+
+
+ private void showSponsorshipWindow() {
+ if (sponsorshipWindowState != SponsorshipWindowStates.ReadyToBeShown) {
+ Log.w(LOG_TAG, "Window is not ready to be shown: " +
+ sponsorshipWindowState);
+ return;
+ }
+
+ long ord = (long) (Math.random() * 10000000000000000L);
+ webView.loadDataWithBaseURL(null, String.format(LAYOUT,
ord), "text/html",
+ "utf-8", null);
+
+ sponsorshipWindowState = SponsorshipWindowStates.Opening;
+ handler.sendEmptyMessageDelayed(MSG_START_CLOSE, BANNER_VISIBLE);
+ handler.sendEmptyMessage(MSG_SCROLL_IN);
+ }
+
+ private void hideSponsorshipWindow() {
+ if (sponsorshipWindowState != SponsorshipWindowStates.Visible) {
+ return;
+ }
+
+ sponsorshipWindowState = SponsorshipWindowStates.Closing;
+ AnimationReturn r = getAnimationWindow();
+ animationView = r.view;
+ bannerHeight = r.bannerHeight;
+ startY = screenHeight - r.bitmapHeight;
+ animationStartTime = System.currentTimeMillis();
+ handler.sendEmptyMessage(MSG_SCROLL_OUT);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (sponsorshipWindowState ==
SponsorshipWindowStates.NeedsMeasurement) {
+ if (getMeasuredWidth() < 320) {
+ sponsorshipWindowState = SponsorshipWindowStates.TooNarrow;
+ } else {
+ sponsorshipWindowState = SponsorshipWindowStates.ReadyToBeShown;
+ showSponsorshipWindow();
+ }
+ }
+ }
+
+ private static class AnimationReturn {
+ public ImageView view;
+ public int bannerHeight;
+ public int bitmapHeight;
+ }
+
+ private AnimationReturn getAnimationWindow() {
+ AnimationReturn ret = new AnimationReturn();
+
+ WindowManager.LayoutParams windowParams = new
WindowManager.LayoutParams();
+ windowParams.gravity = Gravity.TOP;
+ windowParams.x = 0;
+ windowParams.y = getTop();
+ windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ windowParams.windowAnimations = 0;
+
+ ImageView view = new ImageView(context);
+ Bitmap bitmap = getViewBitmap(this);
+ ret.bannerHeight = bitmap.getHeight();
+
+ if (dependentView != null) {
+ SlidingDrawer playerDrawer = dependentView.getPlayerDrawer();
+ if (playerDrawer != null && !playerDrawer.isOpened()) {
+
+ // Ugh. This is bad. The BannerView shouldn't have to know this
+ // much about the player in order to properly render it. But if
+ // I try to render to PlaylistView or the SlidingDrawer it draws
+ // the entire frame all the way to the top of the window
+ // Also, for some very strange reason, just getting the handle
image
+ // seems to get everything rendered.
+ View handle = playerDrawer.findViewById(R.id.handle);
+
+ Bitmap player = getViewBitmap(handle);
+
+ int width = Math.max(bitmap.getWidth(), player.getWidth());
+ int height = bitmap.getHeight() + player.getHeight();
+
+ Bitmap combined = Bitmap.createBitmap(
+ width,
+ height,
+ Bitmap.Config.ARGB_8888
+ );
+ Canvas canvas = new Canvas(combined);
+
+ canvas.drawBitmap(player, 0, 0, null);
+ canvas.drawBitmap(bitmap, 0, player.getHeight(), null);
+ Log.d(LOG_TAG, "Building combined view " + combined.getHeight());
+ bitmap = combined;
+ }
+ }
+ // Need to use createBitmap to make a copy or else it gets recycled
+ view.setImageBitmap(Bitmap.createBitmap(bitmap));
+ ret.bitmapHeight = bitmap.getHeight();
+
+ WindowManager windowManager =
+ (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ windowManager.addView(view, windowParams);
+
+ ret.view = view;
+ return ret;
+ }
+
+ private static Bitmap getViewBitmap(View view) {
+ Bitmap bitmap = view.getDrawingCache();
+ if (bitmap == null) {
+ Log.d(LOG_TAG, "Making drawable bitmap for " + view);
+ bitmap = Bitmap.createBitmap(
+ view.getMeasuredWidth(),
+ view.getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ }
+ return bitmap;
+ }
+
+}
=======================================
--- /Npr/res/layout/list_header.xml Thu Jul 21 13:49:41 2011
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<WebView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/sponsorshipWindowHeader"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="#FF000000"/>
=======================================
--- /Npr/res/layout/main.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/main.xml Mon Aug 15 16:13:01 2011
@@ -47,10 +47,19 @@
style="@android:style/Widget.ProgressBar.Small"
/>
</LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/SponsorshipBanner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"/>
+
<FrameLayout
android:id="@+id/TitleContent"
android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_below="@id/LogoBar">
+ android:layout_height="wrap_content"
+ android:layout_below="@id/LogoBar"
+ android:layout_above="@id/SponsorshipBanner">
</FrameLayout>
+
</RelativeLayout>
=======================================
--- /Npr/res/layout/playlist.xml Wed Jun 15 13:39:57 2011
+++ /Npr/res/layout/playlist.xml Mon Aug 15 16:13:01 2011
@@ -58,24 +58,4 @@
</LinearLayout>
</RelativeLayout>
</SlidingDrawer>
- <RelativeLayout
- android:id="@+id/sponsorshipWindow"
- android:layout_height="65dip"
- android:layout_width="fill_parent"
- android:layout_gravity="bottom">
- <WebView
- android:id="@+id/sponsorshipView"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="@drawable/contracted_player_bg"/>
- <ImageButton
- android:id="@+id/dismissSponsorshipView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="7dip"
- android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
- android:src="@drawable/close_button"
- android:background="@android:color/transparent"/>
- </RelativeLayout>
</FrameLayout>
=======================================
--- /Npr/res/values/strings.xml Sun May 22 12:54:47 2011
+++ /Npr/res/values/strings.xml Mon Aug 15 16:13:01 2011
@@ -163,4 +163,5 @@
<item>Special Series</item>
<item>Other</item>
</array>
+ <string name="banner_support">support comes from</string>
</resources>
=======================================
--- /Npr/res/values/styles.xml Thu Jul 21 13:43:44 2011
+++ /Npr/res/values/styles.xml Mon Aug 15 16:13:01 2011
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
- name="MyTheme"
- parent="android:Theme.Light.NoTitleBar">
+ name="MyTheme"
+ parent="android:Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/light_grey</item>
<item name="android:colorBackground">@color/light_grey</item>
<item name="android:panelColorBackground">@color/light_grey</item>
@@ -21,6 +21,10 @@
<item
name="android:listSelector">@drawable/list_selector_background</item>
</style>

+ <style name="Banner">
+ <item
name="android:background">@drawable/sponsorship_gradient_slice</item>
+ <item name="android:textAppearance">@style/BannerText</item>
+ </style>
<style name="ViewTitle">
<item name="android:background">@drawable/title_background</item>
<item name="android:gravity">center_vertical</item>
@@ -98,9 +102,16 @@
<item name="android:textColor">@color/light_blue</item>
</style>
<style name="NewsStoryIndex"
- parent="@android:style/TextAppearance">
+ parent="@android:style/TextAppearance">
<item name="android:textSize">12sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#737373</item>
</style>
+ <style name="BannerText"
+ parent="@android:style/TextAppearance">
+ <item name="android:textSize">11sp</item>
+ <item name="android:typeface">sans</item>
+ <item name="android:textColor">#989898</item>
+ <item name="android:lineSpacingExtra">5sp</item>
+ </style>
</resources>
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Thu Jul 21 13:43:44
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Mon Aug 15 16:13:01
2011
@@ -23,11 +23,12 @@
import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import android.view.*;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
@@ -182,37 +183,16 @@
TextView titleRight = (TextView) findViewById(R.id.TitleRight);

titleRight.setTextColor(getResources().getColor(R.color.news_title_text));

+ ViewGroup bannerHolder = (ViewGroup)
findViewById(R.id.SponsorshipBanner);
+ ViewGroup.inflate(this, R.layout.banner, bannerHolder);
+ ((BannerView) bannerHolder.getChildAt(0))
+ .setPlayerView(getPlaylistView());
+
ViewGroup container = (ViewGroup) findViewById(R.id.Content);
ViewGroup.inflate(this, R.layout.news, container);

listView = (ListView) findViewById(R.id.ListView01);

- View header = LayoutInflater.from(this)
- .inflate(R.layout.list_header, listView, false);
-
- WebView sponsorshipWindow =
- (WebView) header.findViewById(R.id.sponsorshipWindowHeader);
-
- WebSettings webSettings = sponsorshipWindow.getSettings();
- webSettings.setSavePassword(false);
- webSettings.setSaveFormData(false);
- webSettings.setJavaScriptEnabled(true);
- webSettings.setSupportZoom(false);
-
- long ord = (long) (Math.random() * 10000000000000000L);
- String html = String.format(
- "<html><head><style type=\"text/css\">body
{padding:0;margin:0;text-align:center;background-color:black;}" +
- "p
{margin:0;padding:0;font-family:sans-serif;font-size:x-small;color:white;}</style></head>"
+
- "<body><script type=\"text/javascript\" " +
- "src=\"http://ad.doubleclick.net/adj/n6735.NPR.MOBILE/android_npr;sz=320x50;ord=%1$d?\">"
+
- "</script>" +
- "</body></html>",
- ord);
- Log.d(LOG_TAG, html);
- sponsorshipWindow.loadDataWithBaseURL(null, html, "text/html", "utf-8",
- null);
- listView.addHeaderView(header);
-
listView.setOnItemClickListener(this);
listAdapter = new NewsListAdapter(this);
listView.setAdapter(listAdapter);
@@ -234,12 +214,14 @@

addStories();
}
+


@Override
protected void onStart() {
super.onStart();
handler.postDelayed(updateTime, UPDATE_SHORT_PERIOD);
+
}

@Override
=======================================
--- /Npr/src/org/npr/android/news/PlaylistView.java Wed Jun 15 13:39:57 2011
+++ /Npr/src/org/npr/android/news/PlaylistView.java Mon Aug 15 16:13:01 2011
@@ -24,31 +24,16 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
+import android.util.AttributeSet;
import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
+import android.view.*;
import android.view.View.OnClickListener;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.widget.AdapterView;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.ListView;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-import android.widget.SeekBar;
+import android.widget.*;
import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.SlidingDrawer;
import android.widget.SlidingDrawer.OnDrawerCloseListener;
import android.widget.SlidingDrawer.OnDrawerOpenListener;
-import android.widget.TextView;
-
import org.npr.android.util.DisplayUtils;
import org.npr.android.util.PlaylistEntry;
import org.npr.android.util.PlaylistProvider;
@@ -104,14 +89,6 @@
private int startY;
private boolean cancelDown;

- private RelativeLayout sponsorshipWindow;
-
- private enum sponsorshipWindowStates {
- WindowNeedsMeasurement, WindowTooNarrow, WindowReadyToBeShown,
- WindowVisible, WindowHasBeenShown
- }
-
- private sponsorshipWindowStates sponsorshipWindowState =
sponsorshipWindowStates.WindowTooNarrow;

private enum ClickedItem {
rewind, rewind30, playPause, fastForward, contractedPlay, progressbar
@@ -124,7 +101,6 @@
private BroadcastReceiver closeReceiver;
private BroadcastReceiver playlistChangedReceiver;

- private static final int MSG_AD_CLOSE = 0;
private GestureDetector gestureDetector;
private final Handler handler = new Handler() {
@Override
@@ -159,20 +135,37 @@

listItem.startAnimation(fling);
break;
-
- case MSG_AD_CLOSE:
- if (sponsorshipWindowState ==
sponsorshipWindowStates.WindowVisible) {
- hideSponsorshipWindow();
- }
- break;
}
}
};

+ @SuppressWarnings({"UnusedDeclaration"})
public PlaylistView(Context context) {
super(context);
this.context = context;
}
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ public PlaylistView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.context = context;
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ public PlaylistView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ this.context = context;
+ }
+
+ /**
+ * Returns a pointer to the SlidingDrawer for the
+ * player window.
+ *
+ * @return The player's SlidingDrawer
+ */
+ public SlidingDrawer getPlayerDrawer() {
+ return drawer;
+ }

@Override
protected void onAttachedToWindow() {
@@ -193,9 +186,6 @@
touchSlop = ViewConfiguration.getTouchSlop();
handle = (RelativeLayout) findViewById(R.id.handle);

- sponsorshipWindow = (RelativeLayout)
findViewById(R.id.sponsorshipWindow);
- sponsorshipWindow.setVisibility(View.INVISIBLE);
-
playerContracted = (RelativeLayout)
findViewById(R.id.player_contracted);
playerExpanded = (RelativeLayout) findViewById(R.id.player_expanded);

@@ -280,40 +270,6 @@
refreshList();
}

- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (sponsorshipWindowState ==
sponsorshipWindowStates.WindowNeedsMeasurement) {
- if (sponsorshipWindow.getMeasuredWidth() >= 240) {
- sponsorshipWindowState =
sponsorshipWindowStates.WindowReadyToBeShown;
- WebView sponsorshipView = (WebView)
findViewById(R.id.sponsorshipView);
- sponsorshipView.setBackgroundColor(0);
-
- WebSettings webSettings = sponsorshipView.getSettings();
- webSettings.setSavePassword(false);
- webSettings.setSaveFormData(false);
- webSettings.setJavaScriptEnabled(true);
- webSettings.setSupportZoom(false);
-
- sponsorshipView.loadDataWithBaseURL(null,
- "<html><head><style type='text/css'>body
{padding:0;margin:0} " +
- "p {padding:0 0 3px 0;margin:0;color:white;" +
- "font-size:10px;font-family:Helvetica,Arial," +
- "sans-serif;text-align:center}</style>" +
- "</head><body><script type='text/javascript' " +
- "src='http://ad.doubleclick.net/adj/" +
- "n6735.NPR.MOBILE/android;sz=320x50' />" +
- "</body></html>",
- "text/html", "utf-8", null);
- ImageButton dismissSponsorshipView =
- (ImageButton) findViewById(R.id.dismissSponsorshipView);
- dismissSponsorshipView.setOnClickListener(this);
- } else {
- sponsorshipWindowState = sponsorshipWindowStates.WindowTooNarrow;
- }
- }
- }

private void refreshList() {
playlistAdapter.getCursor().requery();
@@ -323,7 +279,6 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- Log.d(LOG_TAG, "detached from window");
// Emulator calls detach twice, so clear receiver
if (changeReceiver != null) {
context.unregisterReceiver(changeReceiver);
@@ -365,10 +320,6 @@
refreshList();
configurePlayerControls();
break;
-
- case R.id.dismissSponsorshipView:
- hideSponsorshipWindow();
- break;
}
}

@@ -384,9 +335,6 @@
}

private void playNow(final Playable playable, String action) {
- if (sponsorshipWindowState ==
sponsorshipWindowStates.WindowReadyToBeShown) {
- showSponsorshipWindow();
- }
startPlaylistSpinners();
Intent intent = new Intent(context, PlaybackService.class);
intent.setAction(action);
@@ -1040,66 +988,4 @@
return playlistAdapter.getActiveId();
}

- private void showSponsorshipWindow() {
- if (sponsorshipWindowState !=
sponsorshipWindowStates.WindowReadyToBeShown) {
- return;
- }
-
- Animation scroll_in_from_bottom = AnimationUtils.loadAnimation(
- context,
- R.anim.scroll_in_from_bottom
- );
- scroll_in_from_bottom.setFillAfter(true);
- scroll_in_from_bottom.setAnimationListener(new
Animation.AnimationListener() {
-
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- handler.sendEmptyMessageDelayed(MSG_AD_CLOSE, 10000);
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
-
- }
-
- });
- sponsorshipWindow.startAnimation(scroll_in_from_bottom);
- sponsorshipWindowState = sponsorshipWindowStates.WindowVisible;
- }
-
- private void hideSponsorshipWindow() {
- if (sponsorshipWindowState != sponsorshipWindowStates.WindowVisible) {
- return;
- }
-
- sponsorshipWindowState = sponsorshipWindowStates.WindowReadyToBeShown;
-
- Animation scroll_out_bottom = AnimationUtils.loadAnimation(
- context,
- R.anim.scroll_out_bottom
- );
- scroll_out_bottom.setFillAfter(true);
- scroll_out_bottom.setAnimationListener(new
Animation.AnimationListener() {
-
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
-
- }
-
- });
- sponsorshipWindow.startAnimation(scroll_out_bottom);
- sponsorshipWindow.setVisibility(View.INVISIBLE);
- }
-}
+}
=======================================
--- /Npr/src/org/npr/android/news/RootActivity.java Mon Jul 25 20:11:55 2011
+++ /Npr/src/org/npr/android/news/RootActivity.java Mon Aug 15 16:13:01 2011
@@ -71,8 +71,9 @@
// when a stream is not playing.
setVolumeControlStream(AudioManager.STREAM_MUSIC);

+ ViewGroup titleFrame = (ViewGroup) findViewById(R.id.TitleContent);
navigationView = new NavigationView(this);
- ((ViewGroup) findViewById(R.id.TitleContent)).addView(navigationView,
+ titleFrame.addView(navigationView,
new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
navigationView.setVisibility(View.GONE);
@@ -82,9 +83,10 @@
mainSearchButton.setOnClickListener(this);

playlistView = new PlaylistView(this);
- ((ViewGroup) findViewById(R.id.TitleContent)).addView(playlistView,
+ titleFrame.addView(playlistView,
new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
+
progressIndicator =
(ProgressBar) findViewById(R.id.WindowProgressIndicator);

@@ -92,6 +94,10 @@
}


+ protected PlaylistView getPlaylistView() {
+ return playlistView;
+ }
+
protected void startIndeterminateProgressIndicator() {
progressIndicator.setVisibility(View.VISIBLE);
}

==============================================================================
Revision: 6c45c298199d
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Mon Aug 15 16:26:58 2011
Log: Missing files from previous commit

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@135
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Added:
/Npr/res/drawable-hdpi/sponsorship_close_icon_hit_state.png
/Npr/res/drawable-hdpi/sponsorship_close_icon_normal.png
/Npr/res/drawable-hdpi/sponsorship_gradient_slice.png

=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/sponsorship_close_icon_hit_state.png Mon Aug 15
16:26:58 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/sponsorship_close_icon_normal.png Mon Aug 15
16:26:58 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/sponsorship_gradient_slice.png Mon Aug 15
16:26:58 2011
@@ -0,0 +1,11 @@
+‰PNG
+
+
+IHDR F ÿfãn sBIT ÛáOà $PLTE333000---))*'''%%%"""
+
+
+
+
+ %- tRNSÿÿÿÿÿÿÿÿÿÿÿÿ?Ý!C pHYs ÒÝ~ü
+tEXtSoftware Adobe Fireworks CS5qµã6
+IDAT ™cØÀ ‚ À° ÀÐ €P é 3™ a-{É® IEND®B`‚

==============================================================================
Revision: 05cc27e2583d
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Wed Aug 17 17:50:22 2011
Log: Prevent media player callbacks from being invoked after playback
service is destroyed.

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@136
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/src/org/npr/android/news/PlaybackService.java

=======================================
--- /Npr/src/org/npr/android/news/PlaybackService.java Sun May 22 12:54:47
2011
+++ /Npr/src/org/npr/android/news/PlaybackService.java Wed Aug 17 17:50:22
2011
@@ -502,6 +502,12 @@
if (mediaPlayer != null) {
if (mediaPlayerHasStarted) {
mediaPlayer.release();
+ } else {
+ mediaPlayer.setOnBufferingUpdateListener(null);
+ mediaPlayer.setOnCompletionListener(null);
+ mediaPlayer.setOnErrorListener(null);
+ mediaPlayer.setOnInfoListener(null);
+ mediaPlayer.setOnPreparedListener(null);
}
mediaPlayer = null;
}

==============================================================================
Revision: b90c55b74c69
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sat Sep 10 13:37:45 2011
Log: QA feedback for new banner

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@137
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Added:
/Npr/res/drawable/banner_item_background.xml
Modified:
/Npr/AndroidManifest.xml
/Npr/res/drawable-hdpi/sponsorship_close_icon_normal.png
/Npr/res/drawable-hdpi/sponsorship_gradient_slice.png
/Npr/res/layout/banner.xml
/Npr/res/values/strings.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/BannerView.java
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java

=======================================
--- /dev/null
+++ /Npr/res/drawable/banner_item_background.xml Sat Sep 10 13:37:45 2011
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#333"/>
+ <stroke android:width="1dp"
+ android:color="#4c4c4c"/>
+ <padding android:left="1dp"
+ android:top="1dp"
+ android:right="1dp"
+ android:bottom="1dp"/>
+</shape>
=======================================
--- /Npr/AndroidManifest.xml Sun May 22 12:54:47 2011
+++ /Npr/AndroidManifest.xml Sat Sep 10 13:37:45 2011
@@ -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="1.9.1"
+ package="org.npr.android.news" android:versionName="2.1.0"
android:versionCode="3">
<application
android:label="@string/app_name"
=======================================
--- /Npr/res/drawable-hdpi/sponsorship_close_icon_normal.png Mon Aug 15
16:26:58 2011
+++ /Npr/res/drawable-hdpi/sponsorship_close_icon_normal.png Sat Sep 10
13:37:45 2011
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/sponsorship_gradient_slice.png Mon Aug 15
16:26:58 2011
+++ /Npr/res/drawable-hdpi/sponsorship_gradient_slice.png Sat Sep 10
13:37:45 2011
@@ -3,9 +3,12 @@

IHDR F ÿfãn sBIT ÛáOà $PLTE333000---))*'''%%%"""

+



- %- tRNSÿÿÿÿÿÿÿÿÿÿÿÿ?Ý!C pHYs ÒÝ~ü
+ þµ :
+tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿ êÍ pHYs ÒÝ~ü
tEXtSoftware Adobe Fireworks CS5qµã6
-IDAT ™cØÀ ‚ À° ÀÐ €P é 3™ a-{É® IEND®B`‚
+€Ð ÀP€> eì
+Árvñe IEND®B`‚
=======================================
--- /Npr/res/layout/banner.xml Mon Aug 15 16:13:01 2011
+++ /Npr/res/layout/banner.xml Sat Sep 10 13:37:45 2011
@@ -3,5 +3,6 @@
<org.npr.android.news.BannerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
+ android:paddingTop="1dp"
style="@style/Banner"/>
=======================================
--- /Npr/res/values/strings.xml Mon Aug 15 16:13:01 2011
+++ /Npr/res/values/strings.xml Sat Sep 10 13:37:45 2011
@@ -156,6 +156,7 @@
that is on air.</string>
<string name="format_default_station_name">%s Listen Online</string>

+ <string name="msg_update_format">Updated %1$s ago</string>
<array name="program_categories">
<item>News</item>
<item>Arts &amp; Life</item>
@@ -163,5 +164,5 @@
<item>Special Series</item>
<item>Other</item>
</array>
- <string name="banner_support">support comes from</string>
+ <string name="banner_support">support\ncomes from</string>
</resources>
=======================================
--- /Npr/res/values/styles.xml Mon Aug 15 16:13:01 2011
+++ /Npr/res/values/styles.xml Sat Sep 10 13:37:45 2011
@@ -109,9 +109,9 @@
</style>
<style name="BannerText"
parent="@android:style/TextAppearance">
- <item name="android:textSize">11sp</item>
+ <item name="android:textSize">7sp</item>
<item name="android:typeface">sans</item>
<item name="android:textColor">#989898</item>
- <item name="android:lineSpacingExtra">5sp</item>
+ <item name="android:lineSpacingExtra">8sp</item>
</style>
</resources>
=======================================
--- /Npr/src/org/npr/android/news/BannerView.java Mon Aug 15 16:13:01 2011
+++ /Npr/src/org/npr/android/news/BannerView.java Sat Sep 10 13:37:45 2011
@@ -151,9 +151,12 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();

+ handler.removeMessages(MSG_START_CLOSE);
handler.removeMessages(MSG_SCROLL_IN);
handler.removeMessages(MSG_SCROLL_OUT);
}
+
+

/**
* Allows assignment of the play list view so that the
@@ -183,7 +186,9 @@
setOrientation(LinearLayout.HORIZONTAL);

// Some calculated dimensions
+ int dim3 = DisplayUtils.convertToDIP(context, 3);
int dim6 = DisplayUtils.convertToDIP(context, 6);
+ int dim33 = DisplayUtils.convertToDIP(context, 33);
int dim53 = DisplayUtils.convertToDIP(context, 53);
int dim214 = DisplayUtils.convertToDIP(context, 214);

@@ -201,10 +206,12 @@
addView(left);

webView = new WebView(context);
- LayoutParams webLayout = new LayoutParams(dim214, 50, 0);
+ LayoutParams webLayout = new LayoutParams(dim214, dim33, 0);
webLayout.setMargins(dim6, dim6, dim6, dim6);
webLayout.gravity = Gravity.CENTER;
webView.setLayoutParams(webLayout);
+ webView.setBackgroundColor(0); // Transparent to show resource
+ webView.setBackgroundResource(R.drawable.banner_item_background);
addView(webView);

ImageButton right = new ImageButton(context);
@@ -213,6 +220,8 @@
LayoutParams.WRAP_CONTENT,
1
);
+ // Set padding so it feels more centered
+ right.setPadding(0, dim3, dim3, 0);
closeLayout.gravity = Gravity.CENTER;
right.setLayoutParams(closeLayout);
right.setBackgroundDrawable(null);
@@ -236,6 +245,15 @@
hideSponsorshipWindow();
}

+ /**
+ * Starts the ten-second timer for when the sponsorship
+ * view will auto-close.
+ */
+ public void startCloseTimer() {
+ if (sponsorshipWindowState == SponsorshipWindowStates.Visible) {
+ handler.sendEmptyMessageDelayed(MSG_START_CLOSE, BANNER_VISIBLE);
+ }
+ }

private void showSponsorshipWindow() {
if (sponsorshipWindowState != SponsorshipWindowStates.ReadyToBeShown) {
@@ -249,7 +267,6 @@
"utf-8", null);

sponsorshipWindowState = SponsorshipWindowStates.Opening;
- handler.sendEmptyMessageDelayed(MSG_START_CLOSE, BANNER_VISIBLE);
handler.sendEmptyMessage(MSG_SCROLL_IN);
}

=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Mon Aug 15 16:13:01
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Sat Sep 10 13:37:45
2011
@@ -54,6 +54,7 @@

protected NewsListAdapter listAdapter;
private ListView listView;
+ private BannerView bannerView;

private static final Map<String, Story> storyCache = new HashMap<String,
Story>();

@@ -185,8 +186,8 @@

ViewGroup bannerHolder = (ViewGroup)
findViewById(R.id.SponsorshipBanner);
ViewGroup.inflate(this, R.layout.banner, bannerHolder);
- ((BannerView) bannerHolder.getChildAt(0))
- .setPlayerView(getPlaylistView());
+ bannerView = (BannerView) bannerHolder.getChildAt(0);
+ bannerView.setPlayerView(getPlaylistView());

ViewGroup container = (ViewGroup) findViewById(R.id.Content);
ViewGroup.inflate(this, R.layout.news, container);
@@ -195,6 +196,7 @@

listView.setOnItemClickListener(this);
listAdapter = new NewsListAdapter(this);
+ listAdapter.setStoriesLoadedListener(listener);
listView.setAdapter(listAdapter);

// Gesture detection
@@ -215,6 +217,12 @@
addStories();
}

+ private NewsListAdapter.StoriesLoadedListener listener = new
NewsListAdapter.StoriesLoadedListener() {
+ @Override
+ public void storiesLoaded() {
+ bannerView.startCloseTimer();
+ }
+ };


@Override
@@ -511,7 +519,7 @@
return;
}
String label =
- String.format("Updated %1$s ago",
+ String.format(getString(R.string.msg_update_format),
TimeUtils.formatMillis(
System.currentTimeMillis() - lastUpdate,
TimeUnit.DAYS,
=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Mon Jul 25 21:36:24
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Sat Sep 10 13:37:45
2011
@@ -46,6 +46,7 @@
private RootActivity rootActivity = null;
private final PlaylistRepository repository;
private long lastUpdate = -1;
+ private StoriesLoadedListener storiesLoadedListener;

public NewsListAdapter(Context context) {
super(context, R.layout.news_item);
@@ -77,6 +78,9 @@
add(null);
}
}
+ if (storiesLoadedListener != null) {
+ storiesLoadedListener.storiesLoaded();
+ }
} else {
Toast.makeText(rootActivity,
rootActivity.getResources()
@@ -142,7 +146,13 @@
// view and will be in italics
name.setTypeface(name.getTypeface(), Typeface.BOLD);

- topic.setText(story.getSlug());
+ String topicText = story.getSlug();
+ for (Story.Parent p : story.getParentTopics()) {
+ if (p.isPrimary()) {
+ topicText = p.getTitle();
+ }
+ }
+ topic.setText(topicText.toLowerCase());
topic.setVisibility(View.VISIBLE);

String imageUrl = null;
@@ -249,6 +259,23 @@
public long getLastUpdate() {
return lastUpdate;
}
+
+
+ /**
+ * A call back that can be used to be notified when stories are done
+ * loading.
+ */
+ public interface StoriesLoadedListener {
+ void storiesLoaded();
+ }
+
+ /**
+ * Sets a listener to be notified when stories are done loading
+ * @param listener A {@link StoriesLoadedListener}
+ */
+ public void setStoriesLoadedListener(StoriesLoadedListener listener) {
+ storiesLoadedListener = listener;
+ }

private class ImageLoadListener implements
ImageThreadLoader.ImageLoadedListener {


==============================================================================
Revision: 3f24a91b4484
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sat Sep 10 15:07:21 2011
Log: Lock portrait & minor topic fix

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@138
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/AndroidManifest.xml
/Npr/src/org/npr/android/news/NewsListAdapter.java

=======================================
--- /Npr/AndroidManifest.xml Sat Sep 10 13:37:45 2011
+++ /Npr/AndroidManifest.xml Sat Sep 10 15:07:21 2011
@@ -10,13 +10,17 @@
android:name="NewsApplication">
<activity
android:name="StationListActivity"
- android:windowSoftInputMode="adjustPan"/>
+ android:windowSoftInputMode="adjustPan"
+ android:screenOrientation="portrait"/>
<activity
- android:name="StationDetailsActivity"/>
+ android:name="StationDetailsActivity"
+ android:screenOrientation="portrait"/>
<activity
- android:name="NewsStoryActivity"/>
+ android:name="NewsStoryActivity"
+ android:screenOrientation="portrait"/>
<activity
- android:name="NewsListActivity">
+ android:name="NewsListActivity"
+ android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -27,27 +31,37 @@
</intent-filter>
</activity>
<activity
- android:name="NewsTopicActivity"/>
+ android:name="NewsTopicActivity"
+ android:screenOrientation="portrait"/>
<activity
- android:name="AllProgramsActivity"/>
+ android:name="AllProgramsActivity"
+ android:screenOrientation="portrait"/>
<activity
- android:name="AboutActivity"/>
+ android:name="AboutActivity"
+ android:screenOrientation="portrait"/>
<activity
android:name="SearchActivity"
- android:windowSoftInputMode="adjustPan|stateVisible"/>
+ android:windowSoftInputMode="adjustPan|stateVisible"
+ android:screenOrientation="portrait"/>
<activity
- android:name="PodcastActivity"/>
+ android:name="PodcastActivity"
+ android:screenOrientation="portrait"/>
<activity
android:name="SearchResultsActivity"
- android:windowSoftInputMode="adjustPan|stateHidden">
+ android:windowSoftInputMode="adjustPan|stateHidden"
+ android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable"/>
</activity>
- <activity android:name="ProgramStoryListActivity"/>
- <activity android:name="HourlyNewsActivity"/>
+ <activity
+ android:name="ProgramStoryListActivity"
+ android:screenOrientation="portrait"/>
+ <activity
+ android:name="HourlyNewsActivity"
+ android:screenOrientation="portrait"/>
<provider
android:name="org.npr.android.util.PlaylistProvider"
android:authorities="org.npr.android.util.Playlist"/>
=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Sat Sep 10 13:37:45
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Sat Sep 10 15:07:21
2011
@@ -152,7 +152,11 @@
topicText = p.getTitle();
}
}
- topic.setText(topicText.toLowerCase());
+ if (topicText != null) {
+ topic.setText(topicText.toLowerCase());
+ } else {
+ topic.setText("");
+ }
topic.setVisibility(View.VISIBLE);

String imageUrl = null;

==============================================================================
Revision: 84ee2840dd49
Author: jeremywadsack <jeremywadsack@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Sun Sep 11 10:17:14 2011
Log: 2.1 Graphics Updates

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@139
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Added:
/Npr/res/drawable-hdpi/list_selector_background_pressed.png
/Npr/res/drawable-hdpi/logo_bar_back_normal.png
/Npr/res/drawable-hdpi/logo_bar_back_pressed.png
/Npr/res/drawable-hdpi/logo_bar_back_selected.png
/Npr/res/drawable-hdpi/logo_bar_divider.png
/Npr/res/drawable-hdpi/search_icon_normal.png
/Npr/res/drawable-hdpi/search_icon_pressed.png
/Npr/res/drawable-hdpi/speaker_icon.png
/Npr/res/drawable-hdpi/top_stories_title_background.png
Deleted:
/Npr/assets/fonts/Georgia.ttf
/Npr/res/drawable-hdpi/header.png
/Npr/res/drawable-hdpi/list_selector_background_focus.9.png
/Npr/res/drawable-hdpi/list_selector_background_pressed.9.png
/Npr/res/drawable-hdpi/nav_header_background.png
/Npr/res/drawable-hdpi/nav_menu_button_normal.png
/Npr/res/drawable-hdpi/nav_menu_button_pressed.png
/Npr/res/drawable-hdpi/news_list_title_background.png
/Npr/res/drawable-hdpi/search_button_normal.png
/Npr/res/drawable-hdpi/search_button_pressed.png
/Npr/res/drawable-hdpi/speaker.png
/Npr/res/drawable/nav_menu_button.xml
Modified:
/Npr/res/drawable/list_selector_background.xml
/Npr/res/drawable/search_button.xml
/Npr/res/layout/hourly_news.xml
/Npr/res/layout/main.xml
/Npr/res/layout/news_story.xml
/Npr/res/layout/station_item.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/AllProgramsActivity.java
/Npr/src/org/npr/android/news/NavigationView.java
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java
/Npr/src/org/npr/android/news/PlaylistAdapter.java
/Npr/src/org/npr/android/news/PodcastActivity.java
/Npr/src/org/npr/android/news/StationDetailsActivity.java

=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/list_selector_background_pressed.png Sun Sep 11
10:17:14 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/logo_bar_back_normal.png Sun Sep 11 10:17:14 2011
@@ -0,0 +1,20 @@
+‰PNG
+
+
+IHDR F :– o sBIT ÛáOà ™PLTE<<<;;;:::999888777666555444333222111///...---,,,+++*))((('''&&&%%%$$$"""!!!

+
+
+
+
+
+
+
+
+
+
+
+
+
+
Õ §e 3tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\œR
pHYs ÒÝ~ü
+tEXtSoftware Adobe Fireworks CS5qµã6 4IDAT ™c0b0b€ f0d CN0ä C~
C 0 C)0” C E$¨Š 51 
+6 # ¯u“mÔ IEND®B`‚
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/logo_bar_back_pressed.png Sun Sep 11 10:17:14
2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/logo_bar_back_selected.png Sun Sep 11 10:17:14
2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/logo_bar_divider.png Sun Sep 11 10:17:14 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/search_icon_normal.png Sun Sep 11 10:17:14 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/search_icon_pressed.png Sun Sep 11 10:17:14 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/speaker_icon.png Sun Sep 11 10:17:14 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/top_stories_title_background.png Sun Sep 11
10:17:14 2011
Binary file, no diff available.
=======================================
--- /Npr/assets/fonts/Georgia.ttf Tue Mar 2 17:52:36 2010
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/header.png Tue Mar 2 17:52:36 2010
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/list_selector_background_focus.9.png Sun May 22
12:54:47 2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/list_selector_background_pressed.9.png Sun May
22 12:54:47 2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/nav_header_background.png Sun May 22 12:54:47
2011
+++ /dev/null
@@ -1,12 +0,0 @@
-‰PNG
-
-
-IHDR Æ H ãÍÂb tEXtSoftware Adobe ImageReadyqÉe< ïIDATxÚìØA
AÂàßJ,b¢/Ø•À#s%m N ¯ 0b{ “
-`R Þå/ @¥ ¨T •
-€I pø ¨T •
-€J P© * •
- R T*€I Àá  R T* * À¤ 8ü T* * @¥ ˜T
-þ * À¤ 8ü P© & Àá €J 0© •
-€I pø ¨T L*€Ã @¥ ¨T T*€I pø  R L*€Ã @¥  R T*€J P©* @¥ ¨T •
-€J P© & Àá €J P© * “
-àð P© Ÿº &J [‚šÏ, IEND®B`‚
=======================================
--- /Npr/res/drawable-hdpi/nav_menu_button_normal.png Sun May 22 12:54:47
2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/nav_menu_button_pressed.png Sun May 22 12:54:47
2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/news_list_title_background.png Sun May 22
12:54:47 2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/search_button_normal.png Sun May 22 12:54:47 2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/search_button_pressed.png Sun May 22 12:54:47
2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/speaker.png Sun May 22 12:54:47 2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /Npr/res/drawable/nav_menu_button.xml Sun May 22 12:54:47 2011
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_pressed="true"
- android:drawable="@drawable/nav_menu_button_pressed"/>
- <item
- android:state_pressed="false"
- android:drawable="@drawable/nav_menu_button_normal"/>
-</selector>
=======================================
--- /Npr/res/drawable/list_selector_background.xml Sun May 22 12:54:47 2011
+++ /Npr/res/drawable/list_selector_background.xml Sun Sep 11 10:17:14 2011
@@ -22,6 +22,6 @@
android:drawable="@drawable/list_selector_background_transition"/>

<item android:state_focused="true"
- android:drawable="@drawable/list_selector_background_focus"/>
+ android:drawable="@drawable/list_selector_background_pressed"/>

</selector>
=======================================
--- /Npr/res/drawable/search_button.xml Sun May 22 12:54:47 2011
+++ /Npr/res/drawable/search_button.xml Sun Sep 11 10:17:14 2011
@@ -3,8 +3,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
- android:drawable="@drawable/search_button_pressed"/>
+ android:drawable="@drawable/search_icon_pressed"/>
<item
- android:state_pressed="false"
- android:drawable="@drawable/search_button_normal"/>
+ android:drawable="@drawable/search_icon_normal"/>
</selector>
=======================================
--- /Npr/res/layout/hourly_news.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/hourly_news.xml Sun Sep 11 10:17:14 2011
@@ -58,7 +58,7 @@
android:layout_gravity="center_horizontal"
android:layout_margin="15dip"
android:gravity="center"
- android:drawableLeft="@drawable/speaker"
+ android:drawableLeft="@drawable/speaker_icon"
android:text="@string/msg_listen_now"
android:background="@drawable/story_button_background"/>
</LinearLayout>
=======================================
--- /Npr/res/layout/main.xml Mon Aug 15 16:13:01 2011
+++ /Npr/res/layout/main.xml Sun Sep 11 10:17:14 2011
@@ -8,16 +8,25 @@
android:layout_width="fill_parent"
android:id="@+id/LogoBar"
android:orientation="horizontal"
- android:background="@drawable/header"
+ android:background="@drawable/logo_bar_back_normal"
android:gravity="center">
<ImageButton
android:id="@+id/MainSearchButton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:layout_margin="5dip"
+ android:layout_marginTop="14dip"
+ android:layout_marginBottom="13dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
android:src="@drawable/search_button"
android:background="@android:color/transparent"
/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="1dip"
+ android:src="@drawable/logo_bar_divider"
+ />
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -42,7 +51,8 @@
android:id="@+id/WindowProgressIndicator"
android:layout_width="20dp"
android:layout_height="20dp"
- android:layout_margin="5dp"
+ android:layout_marginRight="20dp"
+ android:layout_marginLeft="20dp"
android:visibility="invisible"
style="@android:style/Widget.ProgressBar.Small"
/>
=======================================
--- /Npr/res/layout/news_story.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/news_story.xml Sun Sep 11 10:17:14 2011
@@ -59,7 +59,7 @@
android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_weight="1"
- android:drawableLeft="@drawable/speaker"
+ android:drawableLeft="@drawable/speaker_icon"
android:text="@string/msg_listen_now"
android:background="@drawable/story_button_background"/>
<Button
=======================================
--- /Npr/res/layout/station_item.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/station_item.xml Sun Sep 11 10:17:14 2011
@@ -32,7 +32,7 @@
android:id="@+id/StationItemPlayableImage"
android:layout_width="27dip"
android:layout_height="22dip"
- android:src="@drawable/speaker"
+ android:src="@drawable/speaker_icon"
android:layout_marginLeft="5dip"
android:layout_gravity="center_vertical"
android:scaleType="centerInside"/>
=======================================
--- /Npr/res/values/styles.xml Sat Sep 10 13:37:45 2011
+++ /Npr/res/values/styles.xml Sun Sep 11 10:17:14 2011
@@ -30,8 +30,10 @@
<item name="android:gravity">center_vertical</item>
<item name="android:layout_height">34dip</item>
<item name="android:layout_width">fill_parent</item>
- <item name="android:paddingLeft">5dip</item>
- <item name="android:paddingRight">5dip</item>
+ <item name="android:paddingLeft">8dip</item>
+ <item name="android:paddingRight">8dip</item>
+ <item name="android:paddingTop">4dip</item>
+ <item name="android:paddingBottom">2dip</item>
</style>
<style name="ViewTitleText" parent="@android:style/TextAppearance">
<item name="android:textSize">14sp</item>
=======================================
--- /Npr/src/org/npr/android/news/AllProgramsActivity.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/AllProgramsActivity.java Sun Sep 11
10:17:14 2011
@@ -262,7 +262,7 @@
if (item != null && item.isHeader()) {
v.setEnabled(false);
v.setBackgroundDrawable(
-
getResources().getDrawable(R.drawable.news_list_title_background)
+
getResources().getDrawable(R.drawable.top_stories_title_background)
);
v.getLayoutParams().height =
DisplayUtils.convertToDIP(getContext(), 30);
=======================================
--- /Npr/src/org/npr/android/news/NavigationView.java Wed Jun 22 17:39:11
2011
+++ /Npr/src/org/npr/android/news/NavigationView.java Sun Sep 11 10:17:14
2011
@@ -76,7 +76,7 @@
if (activity != null && activity.isHeader()) {
convertView.setEnabled(false);
convertView.setBackgroundDrawable(getResources().getDrawable(
- R.drawable.news_list_title_background));
+ R.drawable.top_stories_title_background));
convertView.getLayoutParams().height =
DisplayUtils.convertToDIP(getContext(), 30);
((TextView) convertView.findViewById(android.R.id.text1))
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Sat Sep 10 13:37:45
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Sun Sep 11 10:17:14
2011
@@ -177,8 +177,7 @@
// TODO: move this to a layout?
View titleBar = findViewById(R.id.TitleBar);
titleBar.setBackgroundDrawable(getResources().getDrawable(
- R.drawable.news_list_title_background));
- titleBar.getLayoutParams().height = DisplayUtils.convertToDIP(this,
20);
+ R.drawable.top_stories_title_background));
TextView titleText = (TextView) findViewById(R.id.TitleText);

titleText.setTextColor(getResources().getColor(R.color.news_title_text));
TextView titleRight = (TextView) findViewById(R.id.TitleRight);
@@ -370,8 +369,9 @@

@Override
public void onAnimationEnd(Animation animation) {
- icon.setImageDrawable(getResources().getDrawable(R.drawable
- .speaker));
+ icon.setImageDrawable(getResources().getDrawable(
+ R.drawable.speaker_icon
+ ));
// Need to update the list after animation so the correct item is
animated
if (flungStory != null) {
PlaylistRepository playlistRepository =
=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Sat Sep 10 15:07:21
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Sun Sep 11 10:17:14
2011
@@ -125,7 +125,7 @@
if (isPlayable(story)) {
if (repository.getPlaylistItemFromStoryId(story.getId()) == null) {
icon.setImageDrawable(
- getContext().getResources().getDrawable(R.drawable.speaker)
+
getContext().getResources().getDrawable(R.drawable.speaker_icon)
);
} else {
icon.setImageDrawable(
=======================================
--- /Npr/src/org/npr/android/news/PlaylistAdapter.java Sun May 22 12:54:47
2011
+++ /Npr/src/org/npr/android/news/PlaylistAdapter.java Sun Sep 11 10:17:14
2011
@@ -59,7 +59,7 @@
if (id.equals(activeId)) {

view.setBackgroundDrawable(context.getResources().getDrawable(R.drawable
.playlist_entry_background_active));
- playlistItemState.setImageResource(R.drawable.speaker);
+ playlistItemState.setImageResource(R.drawable.speaker_icon);
} else {
view.setBackgroundDrawable(null);
view.setBackgroundColor(android.R.color.transparent);
=======================================
--- /Npr/src/org/npr/android/news/PodcastActivity.java Sun May 22 12:54:47
2011
+++ /Npr/src/org/npr/android/news/PodcastActivity.java Sun Sep 11 10:17:14
2011
@@ -121,7 +121,7 @@
} else if (listItem.isHeader()) {
convertView.setEnabled(false);
convertView.setBackgroundDrawable(getResources().getDrawable(
- R.drawable.news_list_title_background));
+ R.drawable.top_stories_title_background));
convertView.getLayoutParams().height =
DisplayUtils.convertToDIP(getContext(), 20);
convertView.setPadding(10, 0, 10, 0);
=======================================
--- /Npr/src/org/npr/android/news/StationDetailsActivity.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/StationDetailsActivity.java Sun Sep 11
10:17:14 2011
@@ -161,7 +161,7 @@
} else if (listItem.isHeader()) {
convertView.setEnabled(false);
convertView.setBackgroundDrawable(getResources().getDrawable(
- R.drawable.news_list_title_background));
+ R.drawable.top_stories_title_background));
convertView.getLayoutParams().height =
DisplayUtils.convertToDIP(getContext(), 20);
convertView.setPadding(10, 0, 10, 0);

==============================================================================
Revision: aa15e2d80bd3
Author: justinfriberg <justinfriberg@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Tue Oct 25 08:27:38 2011
Log: - Ensures the banner only shows up every two mins (or time
specified in the conf file)
- For programs - only show story teasers
- For topics - only show full stories
- Minor chrome updates
- Right margin on list item story title 6dp
- Min list item height to size w/ image

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@140
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Added:
/Npr/src/org/npr/api/IPhoneTimersConfProvider.java
Modified:
/Npr/AndroidManifest.xml
/Npr/res/drawable-hdpi/contracted_player_bg.png
/Npr/res/drawable-hdpi/sponsorship_gradient_slice.png
/Npr/res/layout/news_item.xml
/Npr/res/values/styles.xml
/Npr/src/org/npr/android/news/AllProgramsActivity.java
/Npr/src/org/npr/android/news/BannerView.java
/Npr/src/org/npr/android/news/Constants.java
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsStoryActivity.java
/Npr/src/org/npr/android/news/NewsTopicActivity.java
/Npr/src/org/npr/android/news/PlaylistView.java
/Npr/src/org/npr/api/ApiConstants.java

=======================================
--- /dev/null
+++ /Npr/src/org/npr/api/IPhoneTimersConfProvider.java Tue Oct 25 08:27:38
2011
@@ -0,0 +1,132 @@
+package org.npr.api;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.util.Log;
+import org.npr.android.util.ArrayUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class IPhoneTimersConfProvider extends ContentProvider {
+
+ public static final Uri CONTENT_URL =
+ Uri.parse("content://org.npr.apr.IPhoneTimersConf");
+ private static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.npr.timers";
+ private static final String CONF_URL =
+ "http://www.npr.org/services/apps/iphone_timers.conf";
+
+ private static final String LOG_TAG =
IPhoneTimersConfProvider.class.getName();
+ private static List<String[]> data;
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projections, String selection,
+ String[] selectionArgs, String sortOrder) {
+ if (selection != null && !selection.equals(Items.NAME + " = ?")) {
+ return null;
+ }
+
+ if (data == null) {
+ data = new ArrayList<String[]>();
+ if (!load()) {
+ return null;
+ }
+ }
+
+ MatrixCursor cursor = new MatrixCursor(Items.COLUMNS);
+ for (String[] row : data) {
+ if (selection == null) {
+ cursor.addRow(row);
+ } else if (row[6].equals(selectionArgs[0])) {
+ cursor.addRow(row);
+ }
+ }
+
+ return cursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return CONTENT_TYPE;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues contentValues) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String s, String[] strings) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues contentValues, String s,
String[] strings) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Parses the CSV data and loads the data member table.
+ * <p/>
+ * TODO: Use a real CSV parser or even better a CSV database
implementation.
+ * e.g. http://sourceforge.net/projects/javacsv/develop
+ * or import into SQLite in-memory?
+ *
+ * @return true on success; false if the stream is null or there is an
+ * exception (which is logged)
+ */
+ private boolean load() {
+ try {
+ InputStream stream = HttpHelper.download(CONF_URL);
+ if (stream == null) {
+ return false;
+ }
+
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(stream),
+ 8192
+ );
+ String buffer;
+ while ((buffer = reader.readLine()) != null) {
+ String[] rowData = buffer.split(",");
+ if (rowData.length > 0) {
+ if (rowData.length < Items.COLUMNS.length) {
+ rowData = ArrayUtils.copyOf(rowData,
Items.COLUMNS.length);
+ }
+ data.add(rowData);
+ }
+ }
+ reader.close();
+ stream.close();
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "", e);
+ return false;
+ }
+ return true;
+ }
+
+ public static class Items implements BaseColumns {
+ public static final String NAME = "name";
+ public static final String TIMER_LENGTH = "timer_length";
+
+ public static final String[] COLUMNS = {NAME, TIMER_LENGTH};
+
+ // This class cannot be instantiated
+ private Items() {
+ }
+ }
+}
=======================================
--- /Npr/AndroidManifest.xml Sat Sep 10 15:07:21 2011
+++ /Npr/AndroidManifest.xml Tue Oct 25 08:27:38 2011
@@ -69,6 +69,9 @@
android:name="org.npr.api.IPhoneNewsAppProgramsConfProvider"
android:authorities="org.npr.apr.IPhoneNewsAppProgramsConf"/>
<provider
+ android:name="org.npr.api.IPhoneTimersConfProvider"
+ android:authorities="org.npr.apr.IPhoneTimersConf"/>
+ <provider
android:name="org.npr.android.util.FavoriteStationsProvider"
android:authorities="org.npr.android.util.FavoriteStations"/>

=======================================
--- /Npr/res/drawable-hdpi/contracted_player_bg.png Sun May 22 12:54:47 2011
+++ /Npr/res/drawable-hdpi/contracted_player_bg.png Tue Oct 25 08:27:38 2011
Binary file, no diff available.
=======================================
--- /Npr/res/drawable-hdpi/sponsorship_gradient_slice.png Sat Sep 10
13:37:45 2011
+++ /Npr/res/drawable-hdpi/sponsorship_gradient_slice.png Tue Oct 25
08:27:38 2011
Binary file, no diff available.
=======================================
--- /Npr/res/layout/news_item.xml Wed Jun 22 17:39:11 2011
+++ /Npr/res/layout/news_item.xml Tue Oct 25 08:27:38 2011
@@ -5,7 +5,7 @@
android:gravity="center_vertical"
android:padding="3dp"
android:orientation="horizontal"
- android:minHeight="?android:attr/listPreferredItemHeight">
+ android:minHeight="81dp">
<ImageView
android:id="@+id/NewsItemIcon"
android:layout_width="35dp"
@@ -25,6 +25,7 @@
android:id="@+id/NewsItemNameText"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
+ android:layout_marginRight="6dp"
android:gravity="center_vertical"
android:textAppearance="@style/NewsItemTitle"/>
</LinearLayout>
=======================================
--- /Npr/res/values/styles.xml Sun Sep 11 10:17:14 2011
+++ /Npr/res/values/styles.xml Tue Oct 25 08:27:38 2011
@@ -22,18 +22,18 @@
</style>

<style name="Banner">
- <item
name="android:background">@drawable/sponsorship_gradient_slice</item>
+ <item name="android:background">@color/black</item>
<item name="android:textAppearance">@style/BannerText</item>
</style>
<style name="ViewTitle">
<item name="android:background">@drawable/title_background</item>
<item name="android:gravity">center_vertical</item>
- <item name="android:layout_height">34dip</item>
+ <item name="android:layout_height">24dip</item>
<item name="android:layout_width">fill_parent</item>
<item name="android:paddingLeft">8dip</item>
<item name="android:paddingRight">8dip</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">2dip</item>
+ <item name="android:paddingTop">1dip</item>
+ <item name="android:paddingBottom">1dip</item>
</style>
<style name="ViewTitleText" parent="@android:style/TextAppearance">
<item name="android:textSize">14sp</item>
=======================================
--- /Npr/src/org/npr/android/news/AllProgramsActivity.java Sun Sep 11
10:17:14 2011
+++ /Npr/src/org/npr/android/news/AllProgramsActivity.java Tue Oct 25
08:27:38 2011
@@ -144,7 +144,7 @@
}
Intent i = new Intent(this, ProgramStoryListActivity.class);
i.putExtra(Constants.EXTRA_LIVE_STREAM_RSS_URL,
item.getLiveStreamUrl());
-
+ i.putExtra(Constants.EXTRA_TEASER_ONLY, true);

String grouping = getString(type);
String description = item.getTitle();
@@ -171,7 +171,6 @@
i.putExtra(Constants.EXTRA_ON_AIR, true);
}
}
-

i.putExtra(Constants.EXTRA_DESCRIPTION, description);
i.putExtra(Constants.EXTRA_GROUPING, grouping);
=======================================
--- /Npr/src/org/npr/android/news/BannerView.java Sat Sep 10 13:37:45 2011
+++ /Npr/src/org/npr/android/news/BannerView.java Tue Oct 25 08:27:38 2011
@@ -15,6 +15,7 @@
package org.npr.android.news;

import android.content.Context;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Handler;
@@ -31,7 +32,7 @@
import android.webkit.WebView;
import android.widget.*;
import org.npr.android.util.DisplayUtils;
-
+import org.npr.api.IPhoneTimersConfProvider;

/**
* Encapsulates the logic and layout of the sponsorship banner
@@ -43,22 +44,25 @@
private static final int FRAME_DURATION = 50;
private static final float ANIMATION_DURATION = 500f;
private static final String LAYOUT =
- "<html>" +
- "<head>" +
- "<meta name=\"viewport\"
content=\"target-densitydpi=device-dpi, " +
- "width=device-width\" />" +
- "<style type=\"text/css\">" +
- "body
{padding:0;margin:0;text-align:center;background-color:#333;}" +
- "p {display:none;}" +
- "</style>" +
- "</head>" +
- "<body>" +
- "<script type=\"text/javascript\"" +
+ "<html>" +
+ "<head>" +
+ "<meta name=\"viewport\" content=\"target-densitydpi=device-dpi, "
+
+ "width=device-width\" />" +
+ "<style type=\"text/css\">" +
+ "body
{padding:0;margin:0;text-align:center;background-color:#333;}" +
+ "p {display:none;}" +
+ "</style>" +
+ "</head>" +
+ "<body>" +
+ "<script type=\"text/javascript\"" +
"src=\"http://ad.doubleclick.net/adj/n6735.NPR.MOBILE/android_npr;"
+
"sz=320x50;ord=%1$d?\">" +
- "</script>" +
- "</body>" +
- "</html>";
+ "</script>" +
+ "</body>" +
+ "</html>";
+
+ private long noBannerUntilSystemTime = 0;
+ private long noBannerTimeIncrement = 120000;

private ImageView animationView;
private int startY;
@@ -155,14 +159,13 @@
handler.removeMessages(MSG_SCROLL_IN);
handler.removeMessages(MSG_SCROLL_OUT);
}
-


/**
* Allows assignment of the play list view so that the
* banner can slide the player drawer down as it scrolls
* off screen.
- *
+ * <p/>
* This enables the drawing cache for the drawer view.
*
* @param playlistView The view to animate down.
@@ -175,6 +178,22 @@
}

void init() {
+ Cursor cursor =
context.getContentResolver().query(IPhoneTimersConfProvider.CONTENT_URL,
null, null, null, null);
+ while (cursor.moveToNext()) {
+ String id =
cursor.getString(cursor.getColumnIndex(IPhoneTimersConfProvider.Items.NAME));
+ if (id.equals("banners")) {
+ noBannerTimeIncrement =
cursor.getInt(cursor.getColumnIndex(IPhoneTimersConfProvider.Items.TIMER_LENGTH))
* 1000;
+ break;
+ }
+ }
+
+ // Don't shown until increment has elapsed
+ if (System.currentTimeMillis() < noBannerUntilSystemTime) {
+ setVisibility(View.GONE);
+ return;
+ }
+ noBannerUntilSystemTime = System.currentTimeMillis() +
noBannerTimeIncrement;
+
DisplayMetrics metrics = new DisplayMetrics();
((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay()
@@ -378,5 +397,4 @@
}
return bitmap;
}
-
-}
+}
=======================================
--- /Npr/src/org/npr/android/news/Constants.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/android/news/Constants.java Tue Oct 25 08:27:38 2011
@@ -34,7 +34,7 @@
"mode_for_station_list_activity";
public static final String EXTRA_ON_AIR = "program_is_on_air";
public static final String EXTRA_PODCAST_URL = "podcast_rss_feed_url";
-
+ public static final String EXTRA_TEASER_ONLY = "teaser_only";
private Constants() {
// no instantiation
}
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Sun Sep 11 10:17:14
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Tue Oct 25 08:27:38
2011
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -73,12 +74,10 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case ListItemGestureListener.MSG_LONG_PRESS: {
- lastLongPressPosition = msg.arg1;
-
- // Offset 1 for header
- int storyPosition = msg.arg1 - 1;
-
- Story longPressStory = listAdapter.getItem(storyPosition);
+
+ lastLongPressPosition = msg.arg1;
+
+ Story longPressStory = listAdapter.getItem(msg.arg1);
if (longPressStory != null && longPressStory.getPlayable() !=
null) {
PlaylistRepository playlistRepository =
new PlaylistRepository(getApplicationContext(),
@@ -90,7 +89,7 @@
if (playlistEntry == null) {
addAndPulseIcon(listView.getChildAt(msg.arg1 -
listView.getFirstVisiblePosition()));
- addStory(storyPosition, true);
+ addStory(msg.arg1, true);
} else {
PlaylistEntry activeEntry =
playlistRepository.getPlaylistItemFromId(getActiveId());
@@ -105,10 +104,8 @@
break;

case ListItemGestureListener.MSG_FLING: {
- // Offset 1 for header
- int storyPosition = msg.arg1 - 1;
-
- flungStory = listAdapter.getItem(storyPosition);
+
+ flungStory = listAdapter.getItem(msg.arg1);
if (flungStory != null && flungStory.getPlayable() != null) {
PlaylistRepository playlistRepository =
new PlaylistRepository(getApplicationContext(),
@@ -123,7 +120,7 @@
msg.arg2,
true
);
- addStory(storyPosition, false);
+ addStory(msg.arg1, false);
} else {
animateListItemFling(
listView.getChildAt(msg.arg1 -
@@ -261,6 +258,10 @@
listAdapter.getStoryIdList()
);
i.putExtra(Constants.EXTRA_STORY_ID, s.getId());
+ if (getIntent().hasExtra(Constants.EXTRA_TEASER_ONLY)) {
+ i.putExtra(Constants.EXTRA_TEASER_ONLY,
+ getIntent().getBooleanExtra(Constants.EXTRA_TEASER_ONLY,
false));
+ }
startActivityWithoutAnimation(i);
}
}
=======================================
--- /Npr/src/org/npr/android/news/NewsStoryActivity.java Wed Jun 15
13:39:57 2011
+++ /Npr/src/org/npr/android/news/NewsStoryActivity.java Tue Oct 25
08:27:38 2011
@@ -114,13 +114,13 @@
);
layout.setMargins(0, 0, 0, DisplayUtils.convertToDIP(this, 95));
((ViewGroup) findViewById(R.id.TitleContent)).addView(workspace,
layout);
-
+ boolean teaserOnly =
getIntent().getBooleanExtra(Constants.EXTRA_TEASER_ONLY, false);

for (int i = 0; i < storyIds.length; i++) {
String storyId = storyIds[i];
Story story = NewsListActivity.getStoryFromCache(storyId);
stories.add(story);
- layoutStory(story, i, storyIds.length);
+ layoutStory(story, i, storyIds.length, teaserOnly);
if (storyId.equals(currentStoryId)) {
trackerItem = new TrackerItem();
workspace.setCurrentScreen(i);
@@ -166,7 +166,7 @@
super.onStop();
}

- private void layoutStory(Story story, int position, int total) {
+ private void layoutStory(Story story, int position, int total, boolean
teaserOnly) {
if (position >= stories.size()) {
Log.e(LOG_TAG, "Attempt to get story view for position " + position +
" beyond loaded stories");
@@ -251,7 +251,7 @@

TextWithHtml text = story.getTextWithHtml();
String textHtml;
- if (text != null) {
+ if (!teaserOnly && text != null) {
StringBuilder sb = new StringBuilder();
for (String paragraph : text.getParagraphs()) {
sb.append("<p>").append(paragraph).append("</p>");
=======================================
--- /Npr/src/org/npr/android/news/NewsTopicActivity.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/NewsTopicActivity.java Tue Oct 25
08:27:38 2011
@@ -174,6 +174,7 @@
params.put(ApiConstants.PARAM_ID, topicId);
params.put(ApiConstants.PARAM_FIELDS, ApiConstants.STORY_FIELDS);
params.put(ApiConstants.PARAM_SORT, "assigned");
+ params.put(ApiConstants.PARAM_REQUIRED_ASSETS, "text");
String url =
ApiConstants.instance().createUrl(ApiConstants.STORY_PATH, params);

=======================================
--- /Npr/src/org/npr/android/news/PlaylistView.java Mon Aug 15 16:13:01 2011
+++ /Npr/src/org/npr/android/news/PlaylistView.java Tue Oct 25 08:27:38 2011
@@ -422,15 +422,15 @@
@Override
public void onReceive(Context context, Intent intent) {
int duration = intent.getIntExtra(PlaybackService.EXTRA_DURATION, 1);
- Log.d(LOG_TAG, "Playback update; duration = " + duration + "
millsecs");
// Drop out if no duration is given (flicker?)
if (duration == 1) {
+ Log.v(LOG_TAG, "Playback update; no duration dropout");
return;
}

int position = intent.getIntExtra(PlaybackService.EXTRA_POSITION, 0);
int downloaded =
intent.getIntExtra(PlaybackService.EXTRA_DOWNLOADED, 1);
- Log.d(LOG_TAG, "Playback update; position = " + position + "
millsecs; " +
+ Log.v(LOG_TAG, "Playback update; position = " + position + "
millsecs; " +
"downloaded = " + duration + " millsecs");
boolean isPlaying = intent.getBooleanExtra(PlaybackService
.EXTRA_IS_PLAYING, false);
=======================================
--- /Npr/src/org/npr/api/ApiConstants.java Wed Jun 22 17:39:11 2011
+++ /Npr/src/org/npr/api/ApiConstants.java Tue Oct 25 08:27:38 2011
@@ -46,6 +46,7 @@
public static final String PARAM_FIELDS = "fields";
public static final String PARAM_SORT = "sort";
public static final String PARAM_DATE = "date";
+ public static final String PARAM_REQUIRED_ASSETS = "requiredAssets";

public static final String STORY_FIELDS
= "titles,teasers,storyDate,byline,text,audio,textWithHtml,image,organization,parent";
private final String apiKey;

==============================================================================
Revision: 45034751fb63
Author: justin...@gmail.com
<justin...@gmail.com@0a442a4a-40cc-11de-998b-8fedbbe774c7>
Date: Thu Nov 3 12:23:50 2011
Log: noBannerUntilSystemTime should be static because we want it to
apply to all instances of the class

git-svn-id:
https://npr-android-app.googlecode.com/svn/branches/release_2.1@141
0a442a4a-40cc-11de-998b-8fedbbe774c7

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

Modified:
/Npr/src/org/npr/android/news/BannerView.java

=======================================
--- /Npr/src/org/npr/android/news/BannerView.java Tue Oct 25 08:27:38 2011
+++ /Npr/src/org/npr/android/news/BannerView.java Thu Nov 3 12:23:50 2011
@@ -61,7 +61,7 @@
"</body>" +
"</html>";

- private long noBannerUntilSystemTime = 0;
+ private static long noBannerUntilSystemTime = 0;
private long noBannerTimeIncrement = 120000;

private ImageView animationView;

==============================================================================
Revision: 8ffcd2cf54f5
Author: Justin Friberg <justin...@hotmail.com>
Date: Fri Jan 6 11:10:49 2012
Log: 2.2 updates

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

Added:
/Npr/res/drawable-hdpi/preset.9.png
/Npr/src/org/npr/android/util/FavoriteStationEntry.java
/Npr/src/org/npr/android/util/FavoriteStationsRepository.java
/Npr/src/org/npr/android/util/StationCache.java
/Npr/src/org/npr/api/Book.java
Modified:
/Npr/AndroidManifest.xml
/Npr/Npr.iml
/Npr/res/layout/station_item.xml
/Npr/res/layout/station_list.xml
/Npr/res/values/strings.xml
/Npr/src/org/npr/android/news/AllProgramsActivity.java
/Npr/src/org/npr/android/news/BannerView.java
/Npr/src/org/npr/android/news/NewsListActivity.java
/Npr/src/org/npr/android/news/NewsListAdapter.java
/Npr/src/org/npr/android/news/NewsStoryActivity.java
/Npr/src/org/npr/android/news/NewsTopicActivity.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/ProgramStoryListActivity.java
/Npr/src/org/npr/android/news/StationDetailsActivity.java
/Npr/src/org/npr/android/news/StationListActivity.java
/Npr/src/org/npr/android/news/StationListAdapter.java
/Npr/src/org/npr/android/news/TitleActivity.java
/Npr/src/org/npr/android/util/FavoriteStationsProvider.java
/Npr/src/org/npr/api/ApiConstants.java
/Npr/src/org/npr/api/Program.java
/Npr/src/org/npr/api/Station.java
/Npr/src/org/npr/api/Story.java
/Npr_Test/Npr_Test.iml
/Npr_Test/src/org/npr/android/news/StationDetailsActivityTest.java

=======================================
--- /dev/null
+++ /Npr/res/drawable-hdpi/preset.9.png Fri Jan 6 11:10:49 2012
@@ -0,0 +1,4 @@
+‰PNG
+
+
+IHDR ÄRWÓ "IDATxÚc`@ ÿ °€ÿX0B
KþÇ'É@P Ÿ X U5Ë´rˉ IEND®B`‚
=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/util/FavoriteStationEntry.java Fri Jan 6
11:10:49 2012
@@ -0,0 +1,14 @@
+package org.npr.android.util;
+
+public class FavoriteStationEntry {
+
+ public final int id;
+ public final String stationId;
+ public final String preset;
+
+ public FavoriteStationEntry(int id, String stationId, String preset) {
+ this.id = id;
+ this.stationId = stationId;
+ this.preset = preset;
+ }
+}
=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/util/FavoriteStationsRepository.java Fri Jan
6 11:10:49 2012
@@ -0,0 +1,164 @@
+package org.npr.android.util;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+import org.npr.android.util.FavoriteStationsProvider.Items;
+import org.npr.api.Station;
+
+public class FavoriteStationsRepository {
+
+ private final ContentResolver contentResolver;
+ private static final String LOG_TAG =
FavoriteStationsRepository.class.getName();
+
+ public FavoriteStationsRepository(ContentResolver contentResolver) {
+ this.contentResolver = contentResolver;
+ }
+
+ public long add(Station station, String preset) {
+ ContentValues values = new ContentValues();
+ values.put(Items.NAME, station.getName());
+ values.put(Items.MARKET, station.getMarketCity());
+ values.put(Items.FREQUENCY, station.getFrequency());
+ values.put(Items.BAND, station.getBand());
+ values.put(Items.STATION_ID, station.getId());
+ values.put(Items.PRESET, preset);
+ Log.d(LOG_TAG, "Adding favorite station to db");
+ Uri uri = contentResolver.insert(
+ FavoriteStationsProvider.CONTENT_URI, values);
+
+ return ContentUris.parseId(uri);
+ }
+
+ public long update(long id, String preset) {
+ Uri update =
ContentUris.withAppendedId(FavoriteStationsProvider.CONTENT_URI, id);
+ ContentValues values = new ContentValues();
+ values.put(FavoriteStationsProvider.Items.PRESET, preset);
+ return contentResolver.update(update, values, null, null);
+ }
+
+ public void removePreset(String stationId) {
+ String selection = Items.STATION_ID + " = ?";
+ String[] selectionArgs = new String[1];
+ selectionArgs[0] = stationId;
+
+ ContentValues values = new ContentValues();
+ values.put(Items.PRESET, (String)null);
+ contentResolver.update(
+ FavoriteStationsProvider.CONTENT_URI, values, selection,
selectionArgs);
+ }
+
+ public int getItemCount() {
+ Cursor c = contentResolver.query(
+ FavoriteStationsProvider.CONTENT_URI, null, null, null, null);
+ int count = c.getCount();
+ c.close();
+ return count;
+ }
+
+ public int getPresetCount() {
+ String selection = Items.PRESET + " IS NOT NULL";
+ Cursor c = contentResolver.query(
+ FavoriteStationsProvider.CONTENT_URI, null, selection, null, null);
+ int count = c.getCount();
+ c.close();
+ return count;
+ }
+
+ public FavoriteStationEntry getFirstFavorite() {
+ Cursor c = contentResolver.query(FavoriteStationsProvider.CONTENT_URI,
+ null, null, null, Items.PRESET);
+ FavoriteStationEntry favoriteStationEntry = null;
+ if (c.moveToFirst()) {
+ favoriteStationEntry = new FavoriteStationEntry(
+ c.getInt(c.getColumnIndex(Items._ID)),
+ c.getString(c.getColumnIndex(Items.STATION_ID)),
+ c.getString(c.getColumnIndex(Items.PRESET)));
+ }
+
+ c.close();
+ return favoriteStationEntry;
+ }
+
+ public FavoriteStationEntry getFavoriteStationForPreset(String preset) {
+
+ String selection = FavoriteStationsProvider.Items.PRESET + " = ?";
+ String[] selectionArgs = new String[1];
+ selectionArgs[0] = preset;
+
+ Cursor c = contentResolver.query(FavoriteStationsProvider.CONTENT_URI,
+ null, selection, selectionArgs, null);
+
+ FavoriteStationEntry favoriteStationEntry = null;
+ if (c.moveToFirst()) {
+ favoriteStationEntry = new FavoriteStationEntry(
+ c.getInt(c.getColumnIndex(Items._ID)),
+ c.getString(c.getColumnIndex(Items.STATION_ID)),
+ c.getString(c.getColumnIndex(Items.PRESET)));
+ }
+
+ c.close();
+ return favoriteStationEntry;
+ }
+
+ public FavoriteStationEntry getFavoriteStationForStationId(String
stationId) {
+
+ String selection = Items.STATION_ID + " = ?";
+ String[] selectionArgs = new String[1];
+ selectionArgs[0] = stationId;
+
+ Cursor c = contentResolver.query(FavoriteStationsProvider.CONTENT_URI,
+ null, selection, selectionArgs, null);
+
+ FavoriteStationEntry favoriteStationEntry = null;
+ if (c.moveToFirst()) {
+ favoriteStationEntry = new FavoriteStationEntry(
+ c.getInt(c.getColumnIndex(Items._ID)),
+
c.getString(c.getColumnIndex(FavoriteStationsProvider.Items.STATION_ID)),
+
c.getString(c.getColumnIndex(FavoriteStationsProvider.Items.PRESET)));
+ }
+
+ c.close();
+ return favoriteStationEntry;
+ }
+
+ public long setPreset(Station station, String preset) {
+
+ // Does this preset already have a station?
+ FavoriteStationEntry oldPreset = getFavoriteStationForPreset(preset);
+
+ // Does this station already have a preset number?
+ FavoriteStationEntry matchingStationPreset =
+ getFavoriteStationForStationId(station.getId());
+
+ if (oldPreset == null) {
+ if (matchingStationPreset == null) {
+ return add(station, preset);
+ } else {
+ // Just update the number to the new number
+ return update(matchingStationPreset.id, preset);
+ }
+ } else {
+ update(oldPreset.id, null);
+ long newId;
+ if (matchingStationPreset == null) {
+ newId = add(station, preset);
+ } else {
+ newId = update(matchingStationPreset.id, preset);
+ }
+ // Move the existing item to the first open slot
+ int newNumber = 1;
+ while (getFavoriteStationForPreset(Integer.toString(newNumber)) !=
null && newNumber <= 10)
+ newNumber++;
+
+ if (newNumber <= 10) {
+ update(oldPreset.id, Integer.toString(newNumber));
+ }
+
+ return newId;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /Npr/src/org/npr/android/util/StationCache.java Fri Jan 6 11:10:49 2012
@@ -0,0 +1,53 @@
+package org.npr.android.util;
+
+import org.npr.api.Station;
+
+import java.util.Hashtable;
+import java.util.List;
+
+public class StationCache {
+
+ private static class StationEntry {
+
+ public StationEntry(Station station) {
+ this.station = station;
+ this.expiration = System.currentTimeMillis() + 86400000; // 24 hours
+ }
+
+ public Station station;
+ public long expiration;
+
+ public boolean isExpired() {
+ return expiration < System.currentTimeMillis();
+ }
+ }
+
+ private static final Hashtable<String, StationEntry> stationCache =
+ new Hashtable<String, StationEntry>();
+
+ public static void addAll(List<Station> stations) {
+ for (Station station : stations) {
+ stationCache.put(station.getId(), new StationEntry(station));
+ }
+ }
+
+ public static Station getStation(String stationId) {
+ StationEntry stationEntry = stationCache.get(stationId);
+ if (stationEntry == null ||
+ stationEntry.expiration < System.currentTimeMillis()) {
+ stationEntry = new StationEntry(
+ Station.StationFactory.downloadStation(stationId));
+ stationCache.put(stationId, stationEntry);
+ }
+ return stationEntry.station;
+ }
+
+ public static boolean entryPresentAndNotExpired(String stationId) {
+ StationEntry stationEntry = stationCache.get(stationId);
+ return (stationEntry != null && !stationEntry.isExpired());
+ }
+
+ public static void clear() {
+ stationCache.clear();
+ }
+}
=======================================
--- /dev/null
+++ /Npr/src/org/npr/api/Book.java Fri Jan 6 11:10:49 2012
@@ -0,0 +1,172 @@
+package org.npr.api;
+
+import android.util.Log;
+import org.apache.http.client.ClientProtocolException;
+import org.npr.android.util.NodeUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+public class Book extends ApiElement {
+ private static final String LOG_TAG = org.npr.api.Book.class.getName();
+
+ public Book(String id) {
+ super(id);
+ }
+
+ private String title;
+ private String promoArt;
+ private String author;
+ private String text;
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getPromoArt() {
+ return promoArt;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ private static Book parseBook(Node memberNode, String id) {
+ Book book = new Book(id);
+ for (Node node : new IterableNodeList(memberNode.getChildNodes())) {
+ String nodeName = node.getNodeName();
+ if (nodeName.equals("title")) {
+ book.title = NodeUtils.getTextContent(node);
+
+ } else if (nodeName.equals("promoArt")) {
+ Attr refIdAttr = (Attr)node.getAttributes().getNamedItem("refId");
+ if (refIdAttr != null) {
+ book.promoArt = refIdAttr.getValue();
+ }
+
+ } else if (nodeName.equals("author")) {
+ for (Node authorNode : new IterableNodeList(node.getChildNodes()))
{
+ if (authorNode.getNodeName().equals("title")) {
+ if (book.author != null) {
+ book.author += ", " + NodeUtils.getTextContent(authorNode);
+ } else {
+ book.author = NodeUtils.getTextContent(authorNode);
+ }
+ break;
+ }
+ }
+
+ } else if (nodeName.equals("introText")) {
+ book.text = NodeUtils.getTextContent(node);
+ }
+ }
+
+ return book;
+ }
+
+ private static List<Book> parseStory(Node storyNode) {
+
+ Hashtable<String, Book> books = new Hashtable<String, Book>();
+ Hashtable<String, String> promoArts = new Hashtable<String, String>();
+ Hashtable<Integer, String> collection = new Hashtable<Integer,
String>();
+
+ NodeList storyList = storyNode.getChildNodes();
+ for (Node node : new IterableNodeList(storyList)) {
+ String nodeName = node.getNodeName();
+
+ if (nodeName.equals("promoArt")) {
+ Attr idAttr = (Attr) node.getAttributes().getNamedItem("id");
+ Attr srcAttr = (Attr) node.getAttributes().getNamedItem("src");
+ if (idAttr != null && srcAttr != null) {
+ promoArts.put(idAttr.getValue(), srcAttr.getValue());
+ }
+
+ } else if (nodeName.equals("collection")) {
+ for (Node collectionNode : new
IterableNodeList(node.getChildNodes())) {
+ if (collectionNode.getNodeName().equals("member")) {
+ Attr refIdAttr = (Attr)
collectionNode.getAttributes().getNamedItem("refId");
+ Attr numAttr = (Attr)
collectionNode.getAttributes().getNamedItem("num");
+ if (refIdAttr != null && numAttr != null) {
+ collection.put(Integer.parseInt(numAttr.getValue()),
refIdAttr.getValue());
+ }
+ }
+ }
+
+ } else if (nodeName.equals("member")) {
+ Attr idAttr = (Attr) node.getAttributes().getNamedItem("id");
+ books.put(idAttr.getValue(), parseBook(node, idAttr.getValue()));
+ }
+ }
+
+ if (books.size() == 0) {
+ return null;
+ } else {
+ ArrayList<Book> result = new ArrayList<Book>(books.size());
+ for (int i = 1 ; i <= collection.size() ; i++) {
+ String id = collection.get(i);
+ if (id != null) {
+ Book book = books.get(id);
+ if (book != null) {
+ book.promoArt = promoArts.get(book.promoArt);
+ result.add(book);
+ }
+ }
+ }
+ return result;
+ }
+ }
+
+ private static List<Book> parseBooks(Node rootNode, String storyId) {
+
+ for (Node listNode : new IterableNodeList(rootNode.getChildNodes())) {
+ if (listNode.getNodeName().equals("list")) {
+
+ for (Node storyNode : new
IterableNodeList(listNode.getChildNodes())) {
+ if (storyNode.getNodeName().equals("story")) {
+
+ Attr idAttr = (Attr)
storyNode.getAttributes().getNamedItem("id");
+ if (idAttr.getValue().equals(storyId)) {
+ return parseStory(storyNode);
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static List<Book> downloadBooks(String url, String storyId) {
+ if (url == null || storyId == null)
+ return null;
+
+ Node books = null;
+ try {
+ books = new Client(url).execute();
+ } catch (ClientProtocolException e) {
+ Log.e(LOG_TAG, "", e);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "", e);
+ } catch (SAXException e) {
+ Log.e(LOG_TAG, "", e);
+ } catch (ParserConfigurationException e) {
+ Log.e(LOG_TAG, "", e);
+ }
+
+ if (books == null) {
+ return null;
+ }
+ return parseBooks(books, storyId);
+ }
+}
=======================================
--- /Npr/AndroidManifest.xml Tue Oct 25 08:27:38 2011
+++ /Npr/AndroidManifest.xml Fri Jan 6 11:10:49 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.1.0"
+ package="org.npr.android.news" android:versionName="2.2.0"
android:versionCode="3">
<application
android:label="@string/app_name"
=======================================
--- /Npr/Npr.iml Sun May 22 12:54:47 2011
+++ /Npr/Npr.iml Fri Jan 6 11:10:49 2012
@@ -28,6 +28,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="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
@@ -38,8 +39,6 @@
<SOURCES />
</library>
</orderEntry>
- <orderEntry type="jdk" jdkName="Android 1.6 Platform" jdkType="Android
SDK" />
- <orderEntry type="library" scope="PROVIDED" name="Android 1.6
Platform" level="application" />
</component>
</module>

=======================================
--- /Npr/res/layout/station_item.xml Sun Sep 11 10:17:14 2011
+++ /Npr/res/layout/station_item.xml Fri Jan 6 11:10:49 2012
@@ -7,39 +7,48 @@
android:paddingRight="10dp"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal">
- <TextView
- android:id="@+id/StationItemNameText"
- android:layout_height="wrap_content"
- android:layout_width="90dp"
- android:layout_gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
- <TextView
- android:id="@+id/StationItemLocationText"
- android:paddingLeft="5dip"
- android:layout_height="wrap_content"
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_height="fill_parent"
android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/StationItemNameText"
+ android:layout_height="wrap_content"
+ android:layout_width="90dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+ <TextView
+ android:id="@+id/StationItemLocationText"
+ android:paddingLeft="5dip"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+ </LinearLayout>
<TextView
- android:id="@+id/StationItemFrequencyText"
- android:paddingLeft="5dip"
+ android:id="@+id/StationPresetView"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:textAppearance="@style/StationFrequencyText"/>
- <ImageView
- android:id="@+id/StationItemPlayableImage"
- android:layout_width="27dip"
- android:layout_height="22dip"
- android:src="@drawable/speaker_icon"
- android:layout_marginLeft="5dip"
- android:layout_gravity="center_vertical"
- android:scaleType="centerInside"/>
- <ImageView
- android:id="@+id/StationItemPlaceholderImage"
- android:layout_width="27dip"
- android:layout_height="33dip"
- android:layout_marginLeft="5dip"
- android:layout_gravity="center_vertical"/>
+ android:visibility="invisible"
+ android:background="@drawable/preset"/>
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:gravity="right"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/StationItemFrequencyText"
+ android:paddingLeft="5dip"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="@style/StationFrequencyText"/>
+ </LinearLayout>
</LinearLayout>
=======================================
--- /Npr/res/layout/station_list.xml Sun May 22 12:54:47 2011
+++ /Npr/res/layout/station_list.xml Fri Jan 6 11:10:49 2012
@@ -66,6 +66,13 @@
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
+ <TextView
+ android:id="@+id/preset_instructions"
+ android:padding="10dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/msg_preset_instructions"
+ android:visibility="gone"/>
<ListView
android:id="@+id/ListView01"
android:layout_width="fill_parent"
=======================================
--- /Npr/res/values/strings.xml Sat Sep 10 13:37:45 2011
+++ /Npr/res/values/strings.xml Fri Jan 6 11:10:49 2012
@@ -31,6 +31,8 @@

<string name="msg_playback_error">Unable to play media. Moving to the
next item.</string>
+ <string name="msg_playback_connection_error">Connection error. Retrying
+ in 30 seconds.</string>
<string name="msg_check_connection">Check connection and try
again.</string>

<string name="msg_share_story">Share this story</string>
@@ -134,6 +136,7 @@
<string name="msg_station_search_help">Couldn\'t determine what you
are looking for. Try searching for call letter (e.g. WAMU),
zip code (e.g. 90210) or city and state (e.g. San Francisco,
CA).</string>
+ <string name="msg_preset_instructions">Press and hold to assign car
presets</string>


<string name="msg_hourly_news_title">Hourly Newscast</string>
=======================================
--- /Npr/src/org/npr/android/news/AllProgramsActivity.java Tue Oct 25
08:27:38 2011
+++ /Npr/src/org/npr/android/news/AllProgramsActivity.java Fri Jan 6
11:10:49 2012
@@ -328,7 +328,7 @@
} else if (id.equals("35") || id.equals("18")) {
return 1; // Arts & Life
} else if (id.equals("37") || id.equals("20") || id.equals("24")
- || id.equals("39") || id.equals("36")) {
+ || id.equals("39")) {
return 2; // Music
} else if (id.equals("")) {
return 3; // Special Series
@@ -388,7 +388,6 @@
"Song of the Day",
"Thistle & Shamrock",
"World Cafe",
- "World of Opera",
"StoryCorps",
"Planet Money",
"Picture Show",
=======================================
--- /Npr/src/org/npr/android/news/BannerView.java Thu Nov 3 12:23:50 2011
+++ /Npr/src/org/npr/android/news/BannerView.java Fri Jan 6 11:10:49 2012
@@ -179,7 +179,7 @@

void init() {
Cursor cursor =
context.getContentResolver().query(IPhoneTimersConfProvider.CONTENT_URL,
null, null, null, null);
- while (cursor.moveToNext()) {
+ while (cursor != null && cursor.moveToNext()) {
String id =
cursor.getString(cursor.getColumnIndex(IPhoneTimersConfProvider.Items.NAME));
if (id.equals("banners")) {
noBannerTimeIncrement =
cursor.getInt(cursor.getColumnIndex(IPhoneTimersConfProvider.Items.TIMER_LENGTH))
* 1000;
=======================================
--- /Npr/src/org/npr/android/news/NewsListActivity.java Tue Oct 25 08:27:38
2011
+++ /Npr/src/org/npr/android/news/NewsListActivity.java Fri Jan 6 11:10:49
2012
@@ -19,7 +19,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -38,6 +37,7 @@
import org.npr.android.util.*;
import org.npr.android.util.Tracker.StoryListMeasurement;
import org.npr.api.ApiConstants;
+import org.npr.api.Book;
import org.npr.api.Story;

import java.util.HashMap;
@@ -55,9 +55,11 @@

protected NewsListAdapter listAdapter;
private ListView listView;
- private BannerView bannerView;
+ protected BannerView bannerView;

private static final Map<String, Story> storyCache = new HashMap<String,
Story>();
+ private static final Map<String, List<Book>> bookCache =
+ new HashMap<String, List<Book>>();

private GestureDetector gestureDetector;
private Story flungStory;
@@ -75,7 +77,7 @@
switch (msg.what) {
case ListItemGestureListener.MSG_LONG_PRESS: {

- lastLongPressPosition = msg.arg1;
+ lastLongPressPosition = msg.arg1;

Story longPressStory = listAdapter.getItem(msg.arg1);
if (longPressStory != null && longPressStory.getPlayable() !=
null) {
@@ -105,7 +107,7 @@

case ListItemGestureListener.MSG_FLING: {

- flungStory = listAdapter.getItem(msg.arg1);
+ flungStory = listAdapter.getItem(msg.arg1);
if (flungStory != null && flungStory.getPlayable() != null) {
PlaylistRepository playlistRepository =
new PlaylistRepository(getApplicationContext(),
@@ -150,6 +152,14 @@
storyCache.put(story.getId(), story);
}
}
+
+ public static List<Book> getBooksFromCache(String storyId) {
+ return bookCache.get(storyId);
+ }
+
+ public static void addBooksToCache(String storyId, List<Book> books) {
+ bookCache.put(storyId, books);
+ }

@Override
public void onLowMemory() {
@@ -259,8 +269,8 @@
);
i.putExtra(Constants.EXTRA_STORY_ID, s.getId());
if (getIntent().hasExtra(Constants.EXTRA_TEASER_ONLY)) {
- i.putExtra(Constants.EXTRA_TEASER_ONLY,
- getIntent().getBooleanExtra(Constants.EXTRA_TEASER_ONLY,
false));
+ i.putExtra(Constants.EXTRA_TEASER_ONLY,
+ getIntent().getBooleanExtra(Constants.EXTRA_TEASER_ONLY,
false));
}
startActivityWithoutAnimation(i);
}
=======================================
--- /Npr/src/org/npr/android/news/NewsListAdapter.java Sun Sep 11 10:17:14
2011
+++ /Npr/src/org/npr/android/news/NewsListAdapter.java Fri Jan 6 11:10:49
2012
@@ -27,6 +27,7 @@
import android.view.ViewGroup;
import android.widget.*;
import org.npr.android.util.PlaylistRepository;
+import org.npr.api.Book;
import org.npr.api.Client;
import org.npr.api.Story;
import org.npr.api.Story.Audio;
@@ -94,7 +95,7 @@
}
};

- private boolean isPlayable(Story story) {
+ public boolean isPlayable(Story story) {
for (Audio a : story.getAudios()) {
if (a.getType().equals("primary")) {
for (Audio.Format f : a.getFormats()) {
@@ -147,7 +148,7 @@
name.setTypeface(name.getTypeface(), Typeface.BOLD);

String topicText = story.getSlug();
- for (Story.Parent p : story.getParentTopics()) {
+ for (Story.Parent p : story.getParents()) {
if (p.isPrimary()) {
topicText = p.getTitle();
}
@@ -204,6 +205,10 @@
}
}).start();
}
+
+ public void addAllStories(final String url) {
+ addMoreStories(url, Integer.MAX_VALUE);
+ }

private boolean getMoreStories(String url, int count) {

@@ -230,6 +235,16 @@
endReached = true;
}
NewsListActivity.addAllToStoryCache(moreStories);
+ for (Story story : moreStories) {
+ for (Story.Parent parent : story.getParents()) {
+ if (parent.getType().equals("book")) {
+ List<Book> books = Book.downloadBooks(parent.getApiLink(),
story.getId());
+ if (books != null) {
+ NewsListActivity.addBooksToCache(story.getId(), books);
+ }
+ }
+ }
+ }
}
}

@@ -275,6 +290,7 @@

/**
* Sets a listener to be notified when stories are done loading
+ *
* @param listener A {@link StoriesLoadedListener}
*/
public void setStoriesLoadedListener(StoriesLoadedListener listener) {
@@ -292,23 +308,21 @@
}

public void imageLoaded(Drawable imageBitmap) {
- // Offset 1 for header
- int storyPosition = position + 1;
- View itemView = parent.getChildAt(storyPosition -
+ View itemView = parent.getChildAt(position -
parent.getFirstVisiblePosition());
if (itemView == null) {
Log.w(LOG_TAG, "Could not find list item at position " +
- storyPosition);
+ position);
return;
}
ImageView img = (ImageView)
itemView.findViewById(R.id.NewsItemImage);
if (img == null) {
Log.w(LOG_TAG, "Could not find image for list item at " +
- "position " + storyPosition);
+ "position " + position);
return;
}
- Log.d(LOG_TAG, "Drawing image at position " + storyPosition);
+ Log.d(LOG_TAG, "Drawing image at position " + position);
img.setImageDrawable(imageBitmap);
}
}
=======================================
--- /Npr/src/org/npr/android/news/NewsStoryActivity.java Tue Oct 25
08:27:38 2011
+++ /Npr/src/org/npr/android/news/NewsStoryActivity.java Fri Jan 6
11:10:49 2012
@@ -37,6 +37,7 @@
import org.npr.android.util.Tracker;
import org.npr.android.util.Tracker.StoryDetailsMeasurement;
import org.npr.android.widget.WorkspaceView;
+import org.npr.api.Book;
import org.npr.api.Story;
import org.npr.api.Story.Byline;
import org.npr.api.Story.TextWithHtml;
@@ -91,8 +92,8 @@
currentStoryId =
getIntent().getStringExtra(Constants.EXTRA_ACTIVITY_DATA);
}
String[] storyIds;
- if (storyIdsString == null) {
- storyIds = new String[] {currentStoryId};
+ if (storyIdsString == null) {
+ storyIds = new String[]{currentStoryId};
} else {
storyIds = storyIdsString.split(",");
}
@@ -127,7 +128,7 @@
trackerItem.orgId = story.getOrganizations().size() > 0 ?
story.getOrganizations().get(0).getId() : null;

- for (Story.Parent p : story.getParentTopics()) {
+ for (Story.Parent p : story.getParents()) {
if (p.isPrimary()) {
trackerItem.topicId = p.getId();
break;
@@ -256,10 +257,38 @@
for (String paragraph : text.getParagraphs()) {
sb.append("<p>").append(paragraph).append("</p>");
}
+
textHtml = String.format(HTML_FORMAT, sb.toString());
// WebView can't load external images, so we need to strip them or it
// may not render.
textHtml = textHtml.replaceAll("<img .*/>", "");
+
+ // Load any book parents
+ for (Story.Parent parent : story.getParents()) {
+ if (parent.getType().equals("book")) {
+ sb = new StringBuilder(textHtml);
+ List<Book> books =
NewsListActivity.getBooksFromCache(story.getId());
+ if (books != null) {
+ for (Book book : books) {
+ sb.append("<hr/>");
+ if (book.getPromoArt() != null) {
+ sb.append(
+ String.format(
+ "<div id=\"story-icon\"><img src=\"%s\" /></div>",
+ book.getPromoArt())
+ );
+ }
+ sb.append(String.format("<p><b>%s</b><br/>",
book.getTitle()));
+ sb.append(String.format("<i>By %s</i></p>",
book.getAuthor()));
+ sb.append(book.getText());
+ }
+ }
+ textHtml = String.format(HTML_FORMAT, sb.toString());
+ // Book loads all books for the story, so break after one is
found
+ break;
+ }
+ }
+
} else {
// Only show the teaser if there is no full-text.
textHtml =
@@ -377,7 +406,7 @@
trackerItem.orgId = story.getOrganizations().size() > 0 ?
story.getOrganizations().get(0).getId() : null;

- for (Story.Parent p : story.getParentTopics()) {
+ for (Story.Parent p : story.getParents()) {
if (p.isPrimary()) {
trackerItem.topicId = p.getId();
break;
@@ -442,7 +471,7 @@
@Override
public void onReceive(Context context, Intent intent) {
int len = stories.size();
- for (int i = 0 ; i < len ; i++) {
+ for (int i = 0; i < len; i++) {
View v = workspace.getChildAt(i);
Button enqueue =
(Button) v.findViewById(R.id.NewsStoryListenEnqueueButton);
@@ -461,10 +490,10 @@
(playlistId);
if (pe == null) return;
int len = stories.size();
- for (int i = 0 ; i < len ; i++) {
+ for (int i = 0; i < len; i++) {
View v = workspace.getChildAt(i);
Button listenNow =
- (Button) v.findViewById(R.id.NewsStoryListenNowButton);
+ (Button) v.findViewById(R.id.NewsStoryListenNowButton);
listenNow.setEnabled(!stories.get(i).getId().equals(pe.storyID));
}
}
=======================================
--- /Npr/src/org/npr/android/news/NewsTopicActivity.java Tue Oct 25
08:27:38 2011
+++ /Npr/src/org/npr/android/news/NewsTopicActivity.java Fri Jan 6
11:10:49 2012
@@ -177,6 +177,7 @@
params.put(ApiConstants.PARAM_REQUIRED_ASSETS, "text");
String url =
ApiConstants.instance().createUrl(ApiConstants.STORY_PATH, params);
+ Log.d(LOG_TAG, "Loading topic url: " + url);

Intent i = new Intent(this, NewsListActivity.class);
i.putExtra(Constants.EXTRA_QUERY_URL, url);
=======================================
--- /Npr/src/org/npr/android/news/Playable.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/android/news/Playable.java Fri Jan 6 11:10:49 2012
@@ -21,12 +21,13 @@
import org.npr.android.util.PlaylistEntry;
import org.npr.api.Podcast;
import org.npr.api.Station;
+import org.npr.api.Story;

public class Playable implements Parcelable {
private static final String LOG_TAG = Playable.class.getName();

private Playable(long id, String url, String title, boolean isStream,
- Class<?> activity, String activityData) {
+ Class<?> activity, String activityData) {
setId(id);
setUrl(url);
setTitle(title);
@@ -149,5 +150,14 @@
return new Playable(-1, stream.getUrl(), stream.getTitle(), true,
StationDetailsActivity.class, stationId);
}
+
+ public static Playable fromStory(Story story) {
+ return new Playable(-1, story.getPlayableUrl(), story.getTitle(),
+ false, NewsStoryActivity.class, null);
+ }
+
+ public static Playable fromURL(String url, String title, Class<?>
activity) {
+ return new Playable(-1, url, title, false, activity, null);
+ }
}
}
=======================================
--- /Npr/src/org/npr/android/news/PlaybackService.java Wed Aug 17 17:50:22
2011
+++ /Npr/src/org/npr/android/news/PlaybackService.java Fri Jan 6 11:10:49
2012
@@ -28,16 +28,11 @@
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
+import android.media.MediaPlayer.OnSeekCompleteListener;
+import android.os.*;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.widget.Toast;

import org.npr.android.util.M3uParser;
import org.npr.android.util.PlaylistParser;
@@ -49,21 +44,25 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.ConnectException;
import java.net.URL;
import java.net.URLConnection;
+import java.net.UnknownHostException;
import java.util.List;

public class PlaybackService extends Service implements
- OnPreparedListener,
+ OnPreparedListener, OnSeekCompleteListener,
OnBufferingUpdateListener, OnCompletionListener, OnErrorListener,
OnInfoListener {

private static final String LOG_TAG = PlaybackService.class.getName();

private static final String SERVICE_PREFIX = "org.npr.android.news.";
+
public static final String SERVICE_CHANGE_NAME = SERVICE_PREFIX
+ "CHANGE";
public static final String SERVICE_CLOSE_NAME = SERVICE_PREFIX + "CLOSE";
public static final String SERVICE_UPDATE_NAME = SERVICE_PREFIX
+ "UPDATE";
+ public static final String SERVICE_ERROR_NAME = SERVICE_PREFIX + "ERROR";

public static final String SERVICE_PLAY_SINGLE = SERVICE_PREFIX +
"PLAY_SINGLE";
@@ -71,10 +70,12 @@
public static final String SERVICE_TOGGLE_PLAY = SERVICE_PREFIX +
"TOGGLE_PLAY";
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";
public static final String SERVICE_PLAY_NEXT = SERVICE_PREFIX
+ "PLAYNEXT";
public static final String SERVICE_PLAY_PREVIOUS = SERVICE_PREFIX +
"PLAYPREVIOUS";
+ public static final String SERVICE_STOP_PLAYBACK = SERVICE_PREFIX
+ "STOP_PLAYBACK";
public static final String SERVICE_STATUS = SERVICE_PREFIX + "STATUS";
public static final String SERVICE_CLEAR_PLAYER = SERVICE_PREFIX +
"CLEAR_PLAYER";
@@ -86,6 +87,8 @@
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_ERROR_MESSAGE = SERVICE_PREFIX
+ "ERROR_MESSAGE";

private MediaPlayer mediaPlayer;
private boolean isPrepared = false;
@@ -103,7 +106,11 @@
private String currentAction;
private Playable current = null;
private List<String> playlistUrls;
- private int errorCt;
+
+ // Error handling
+ private int errorCount;
+ private int connectionErrorWaitTime;
+ private int seekToPosition;

private TelephonyManager telephonyManager;
private PhoneStateListener listener;
@@ -116,6 +123,7 @@
// 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;
+ private final static int RETRY_SLEEP_TIME = 30000;

private Looper serviceLooper;
private ServiceHandler serviceHandler;
@@ -142,6 +150,7 @@
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setOnPreparedListener(this);
+ mediaPlayer.setOnSeekCompleteListener(this);
notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
playlist = new PlaylistRepository(getApplicationContext(),
@@ -200,7 +209,8 @@
if (action.equals(SERVICE_PLAY_SINGLE) ||
action.equals(SERVICE_PLAY_ENTRY)) {
currentAction = action;
current = intent.getParcelableExtra(Playable.PLAYABLE_TYPE);
- playCurrent(0);
+ seekToPosition = intent.getIntExtra(EXTRA_SEEK_TO, 0);
+ playCurrent(0, 1);
} else if (action.equals(SERVICE_TOGGLE_PLAY)) {
if (isPlaying()) {
pause();
@@ -213,21 +223,25 @@
if (isPrepared) {
play();
} else {
- playCurrent(0);
+ playCurrent(0, 1);
}
} else {
currentAction = SERVICE_PLAY_ENTRY;
- errorCt = 0;
+ errorCount = 0;
playFirstUnreadEntry();
}
} else if (action.equals(SERVICE_BACK_30)) {
seekRelative(-30000);
+ } else if (action.equals(SERVICE_FORWARD_30)) {
+ seekRelative(30000);
} else if (action.equals(SERVICE_SEEK_TO)) {
seekTo(intent.getIntExtra(EXTRA_SEEK_TO, 0));
} else if (action.equals(SERVICE_PLAY_NEXT)) {
playNextEntry();
} else if (action.equals(SERVICE_PLAY_PREVIOUS)) {
playPreviousEntry();
+ } else if (action.equals(SERVICE_STOP_PLAYBACK)) {
+ stopSelfResult(startId);
} else if (action.equals(SERVICE_STATUS)) {
updateProgress();
} else if (action.equals(SERVICE_CLEAR_PLAYER)) {
@@ -243,20 +257,22 @@
return null;
}

- private boolean playCurrent(int startingErrorCount) {
- errorCt = startingErrorCount;
- while (errorCt < ERROR_RETRY_COUNT) {
+ private boolean playCurrent(int startingErrorCount, int
startingWaitTime) {
+ errorCount = startingErrorCount;
+ connectionErrorWaitTime = startingWaitTime;
+ while (errorCount < ERROR_RETRY_COUNT) {
try {
prepareThenPlay(current.getUrl(), current.isStream());
return true;
+ } catch (UnknownHostException e) {
+ Log.w(LOG_TAG, "Unknown host in playCurrent");
+ handleConnectionError();
+ } catch (ConnectException e) {
+ Log.w(LOG_TAG, "Connect exception in playCurrent");
+ handleConnectionError();
} catch (IOException e) {
Log.e(LOG_TAG, "IOException on playlist entry " + current.getId(),
e);
- errorCt++;
- if (errorCt >= ERROR_RETRY_COUNT) {
- Toast.makeText(getApplicationContext(),
- getResources().getString(R.string.msg_playback_error),
- Toast.LENGTH_LONG).show();
- }
+ incrementErrorCount();
}
}

@@ -271,19 +287,19 @@
} else {
current = playlist.getFirstUnreadEntry();
}
- } while (current != null && !playCurrent(0));
+ } while (current != null && !playCurrent(0, 1));
}

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

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

if (current == null) {
stopSelfResult(startId);
@@ -297,7 +313,7 @@

do {
current = playlist.getNextEntry(current.getId());
- } while (current != null && !playCurrent(0));
+ } while (current != null && !playCurrent(0, 1));

if (current == null) {
stopSelfResult(startId);
@@ -317,12 +333,14 @@

synchronized private void seekRelative(int pos) {
if (isPrepared) {
+ seekToPosition = 0;
mediaPlayer.seekTo(mediaPlayer.getCurrentPosition() + pos);
}
}

synchronized private void seekTo(int pos) {
if (isPrepared) {
+ seekToPosition = 0;
mediaPlayer.seekTo(pos);
}
}
@@ -362,7 +380,9 @@
proxy.getPort(), url);
}

- markedRead = false;
+ // We only have to mark an item read on playlist items,
+ // so set markedRead to false only when a playlist entry
+ markedRead = !currentAction.equals(SERVICE_PLAY_ENTRY);
synchronized (this) {
Log.d(LOG_TAG, "reset: " + playUrl);
mediaPlayer.reset();
@@ -380,16 +400,18 @@
return;
}
Log.d(LOG_TAG, "play " + current.getId());
+
mediaPlayer.start();
mediaPlayerHasStarted = true;

- int icon = R.drawable.stat_notify_musicplayer;
CharSequence contentText = current.getTitle();
- long when = System.currentTimeMillis();
- Notification notification = new Notification(icon, contentText, when);
+ Notification notification =
+ new Notification(R.drawable.stat_notify_musicplayer,
+ contentText,
+ System.currentTimeMillis());
notification.flags = Notification.FLAG_NO_CLEAR
| Notification.FLAG_ONGOING_EVENT;
- Context c = getApplicationContext();
+ Context context = getApplicationContext();
CharSequence title = getString(R.string.app_name);
Intent notificationIntent;
if (current.getActivityData() != null) {
@@ -404,9 +426,9 @@
notificationIntent.setAction(Intent.ACTION_VIEW);
notificationIntent.addCategory(Intent.CATEGORY_DEFAULT);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent contentIntent = PendingIntent.getActivity(c, 0,
+ PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
- notification.setLatestEventInfo(c, title, contentText, contentIntent);
+ notification.setLatestEventInfo(context, title, contentText,
contentIntent);
notificationManager.notify(NOTIFICATION_ID, notification);

// Change broadcasts are sticky, so when a new receiver connects, it
will
@@ -458,17 +480,28 @@
isPrepared = true;
}
}
- play();
-
+
+ if (seekToPosition > 0) {
+ Log.d(LOG_TAG, "Seeking to starting position: " + seekToPosition);
+ mp.seekTo(seekToPosition);
+ } else {
+ startPlaying();
+ }
+ }
+
+ @Override
+ public void onSeekComplete(MediaPlayer mp) {
+ Log.d(LOG_TAG, "Seek complete");
+ if (seekToPosition > 0) {
+ seekToPosition = 0;
+ startPlaying();
+ }
+ }
+
+ private void startPlaying() {
+ play();
updateProgressThread = new Thread(new Runnable() {
public void run() {
- // Initially, don't send any updates, since it takes a while for
the
- // media player to settle down.
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- return;
- }
while (true) {
updateProgress();
try {
@@ -492,7 +525,7 @@
if (updateProgressThread != null) {
updateProgressThread.interrupt();
try {
- updateProgressThread.join(3000);
+ updateProgressThread.join(1000);
} catch (InterruptedException e) {
Log.e(LOG_TAG, "", e);
}
@@ -508,6 +541,7 @@
mediaPlayer.setOnErrorListener(null);
mediaPlayer.setOnInfoListener(null);
mediaPlayer.setOnPreparedListener(null);
+ mediaPlayer.setOnSeekCompleteListener(null);
}
mediaPlayer = null;
}
@@ -536,16 +570,22 @@
* Sends an UPDATE broadcast with the latest info.
*/
private void updateProgress() {
- if (lastUpdateBroadcast != null) {
- getApplicationContext().removeStickyBroadcast(lastUpdateBroadcast);
- }
-
- if (mediaPlayer != null && isPrepared) {
-
- int duration = mediaPlayer.getDuration(),
- position = mediaPlayer.getCurrentPosition();
+
+ // Stop updating after mediaplayer is released
+ if (mediaPlayer == null)
+ return;
+
+ if (isPrepared) {
+
+ if (lastUpdateBroadcast != null) {
+ getApplicationContext().removeStickyBroadcast(lastUpdateBroadcast);
+ lastUpdateBroadcast = null;
+ }
+
+ int duration = mediaPlayer.getDuration();
+ seekToPosition = mediaPlayer.getCurrentPosition();
if (!markedRead) {
- if (position > duration / 10) {
+ if (seekToPosition > duration / 10) {
markedRead = true;
playlist.markAsRead(current.getId());
}
@@ -555,16 +595,19 @@
tempUpdateBroadcast.putExtra(EXTRA_DURATION, duration);
tempUpdateBroadcast.putExtra(EXTRA_DOWNLOADED,
(int) ((lastBufferPercent / 100.0) * duration));
- tempUpdateBroadcast.putExtra(EXTRA_POSITION, position);
+ tempUpdateBroadcast.putExtra(EXTRA_POSITION, seekToPosition);
tempUpdateBroadcast.putExtra(EXTRA_IS_PLAYING,
mediaPlayer.isPlaying());
+ tempUpdateBroadcast.putExtra(EXTRA_IS_PREPARED, isPrepared);

// Update broadcasts while playing are not sticky, due to concurrency
// issues. These fire very often, so this shouldn't be a problem.
getApplicationContext().sendBroadcast(tempUpdateBroadcast);
} else {
- lastUpdateBroadcast = new Intent(SERVICE_UPDATE_NAME);
- lastUpdateBroadcast.putExtra(EXTRA_IS_PLAYING, false);
- getApplicationContext().sendStickyBroadcast(lastUpdateBroadcast);
+ if (lastUpdateBroadcast == null) {
+ lastUpdateBroadcast = new Intent(SERVICE_UPDATE_NAME);
+ lastUpdateBroadcast.putExtra(EXTRA_IS_PLAYING, false);
+ getApplicationContext().sendStickyBroadcast(lastUpdateBroadcast);
+ }
}
}

@@ -590,29 +633,29 @@
boolean successfulPlay = false;
while (!successfulPlay && playlistUrls.size() > 0) {
String url = playlistUrls.remove(0);
- errorCt = 0;
- while (errorCt < ERROR_RETRY_COUNT) {
+ errorCount = 0;
+ while (errorCount < ERROR_RETRY_COUNT) {
try {
prepareThenPlay(url, current.isStream());
successfulPlay = true;
break;
+ } catch (UnknownHostException e) {
+ Log.w(LOG_TAG, "Unknown host in onCompletion");
+ handleConnectionError();
+ } catch (ConnectException e) {
+ Log.w(LOG_TAG, "Connect exception in onCompletion");
+ handleConnectionError();
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, "", e);
- errorCt++;
+ incrementErrorCount();
} catch (IllegalStateException e) {
Log.e(LOG_TAG, "", e);
- errorCt++;
+ incrementErrorCount();
} catch (IOException e) {
Log.e(LOG_TAG, "", e);
- errorCt++;
+ incrementErrorCount();
}
}
-
- if (errorCt >= ERROR_RETRY_COUNT) {
- Toast.makeText(getApplicationContext(),
- getResources().getString(R.string.msg_playback_error),
- Toast.LENGTH_LONG).show();
- }
}
}

@@ -622,6 +665,44 @@
stopSelfResult(startId);
}
}
+
+ private void incrementErrorCount() {
+ errorCount++;
+ Log.e(LOG_TAG, "Media player increment error count:" + errorCount);
+ if (errorCount >= ERROR_RETRY_COUNT) {
+ Intent intent = new Intent(SERVICE_ERROR_NAME);
+ intent.putExtra(EXTRA_ERROR_MESSAGE,
+ getString(R.string.msg_playback_error));
+ getApplicationContext().sendBroadcast(intent);
+ }
+ }
+
+ private void handleConnectionError() {
+ connectionErrorWaitTime *= 5;
+ if (connectionErrorWaitTime > RETRY_SLEEP_TIME) {
+ Log.e(LOG_TAG, "Connection failed. Resetting mediaPlayer" +
+ " and trying again in 30 seconds.");
+
+ Intent intent = new Intent(SERVICE_ERROR_NAME);
+ intent.putExtra(EXTRA_ERROR_MESSAGE,
+ getString(R.string.msg_playback_connection_error));
+ getApplicationContext().sendBroadcast(intent);
+
+ // If a stream, increment since it could be bad
+ if (current.isStream()) {
+ errorCount++;
+ }
+
+ connectionErrorWaitTime = RETRY_SLEEP_TIME;
+ // Send error notification and keep waiting
+ isPrepared = false;
+ mediaPlayer.reset();
+ } else {
+ Log.w(LOG_TAG, "Connection error. Waiting for " +
+ connectionErrorWaitTime + " milliseconds.");
+ }
+ SystemClock.sleep(connectionErrorWaitTime);
+ }

@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
@@ -634,14 +715,11 @@
}
}
isPrepared = false;
- if (mediaPlayerHasStarted) {
- mediaPlayer.reset();
- }
-
- Log.e(LOG_TAG, "Media player onError, ct:" + errorCt);
- errorCt++;
- if (errorCt < ERROR_RETRY_COUNT) {
- playCurrent(errorCt);
+ mediaPlayer.reset();
+
+ incrementErrorCount();
+ if (errorCount < ERROR_RETRY_COUNT) {
+ playCurrent(errorCount, 1);
// Returning true means we handled the error, false causes the
// onCompletion handler to be called
return true;
@@ -657,7 +735,7 @@
}

private boolean isPlaylist(String url) {
- return url.indexOf("m3u") > -1 || url.indexOf("pls") > -1;
+ return url.contains("m3u") || url.contains("pls");
}

private boolean downloadPlaylist(String url) throws IOException {
@@ -681,9 +759,9 @@
stream.close();
out.close();
PlaylistParser parser;
- if (url.indexOf("m3u") > -1) {
+ if (url.contains("m3u")) {
parser = new M3uParser(downloadingMediaFile);
- } else if (url.indexOf("pls") > -1) {
+ } else if (url.contains("pls")) {
parser = new PlsParser(downloadingMediaFile);
} else {
return false;
=======================================
--- /Npr/src/org/npr/android/news/PlaylistView.java Tue Oct 25 08:27:38 2011
+++ /Npr/src/org/npr/android/news/PlaylistView.java Fri Jan 6 11:10:49 2012
@@ -99,6 +99,7 @@
private BroadcastReceiver changeReceiver;
private BroadcastReceiver updateReceiver;
private BroadcastReceiver closeReceiver;
+ private BroadcastReceiver errorReceiver;
private BroadcastReceiver playlistChangedReceiver;

private GestureDetector gestureDetector;
@@ -241,6 +242,9 @@
closeReceiver = new PlaybackCloseReceiver();
context.registerReceiver(closeReceiver,
new IntentFilter(PlaybackService.SERVICE_CLOSE_NAME));
+ errorReceiver = new PlaybackErrorReceiver();
+ context.registerReceiver(errorReceiver,
+ new IntentFilter(PlaybackService.SERVICE_ERROR_NAME));

playlistChangedReceiver = new PlaylistChangedReceiver();
context.registerReceiver(playlistChangedReceiver,
@@ -292,6 +296,10 @@
context.unregisterReceiver(closeReceiver);
closeReceiver = null;
}
+ if (errorReceiver != null) {
+ context.unregisterReceiver(errorReceiver);
+ errorReceiver = null;
+ }
if (playlistChangedReceiver != null) {
context.unregisterReceiver(playlistChangedReceiver);
playlistChangedReceiver = null;
@@ -490,6 +498,18 @@
refreshList();
}
}
+
+ private class PlaybackErrorReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(LOG_TAG, "Playback error received - toasting message");
+ String error =
intent.getStringExtra(PlaybackService.EXTRA_ERROR_MESSAGE);
+ if (error == null || error.length() == 0) {
+ error = "Unknown error occurred.";
+ }
+ Toast.makeText(context, error, Toast.LENGTH_LONG).show();
+ }
+ }

private class PlaylistChangedReceiver extends BroadcastReceiver {
@Override
=======================================
--- /Npr/src/org/npr/android/news/ProgramStoryListActivity.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/ProgramStoryListActivity.java Fri Jan 6
11:10:49 2012
@@ -22,19 +22,24 @@

import org.npr.android.util.PlaylistRepository;
import org.npr.android.util.Tracker;
+import org.npr.api.ApiConstants;
import org.npr.api.Story;

+import java.util.HashMap;
+import java.util.Map;
+
/**
* Extends NewsListActivity to add a tool bar of buttons to Add all items
to
* playlist or to Find live stream of this program.
*
* Author: Jeremy Wadsack
*/
-public class ProgramStoryListActivity extends NewsListActivity{
+public class ProgramStoryListActivity extends NewsListActivity {
private static final String LOG_TAG =
ProgramStoryListActivity.class.getName();

private String liveStreamRss;
+ private boolean loadAll;


@Override
@@ -48,14 +53,16 @@
// the story list
ViewGroup container = (ViewGroup) findViewById(R.id.TitleGroup);
container.addView(
- ViewGroup.inflate(
- this,
- R.layout.program_action_buttons,
- null
- ),
- 1
+ ViewGroup.inflate(
+ this,
+ R.layout.program_action_buttons,
+ null
+ ),
+ 1
);

+ listAdapter.setStoriesLoadedListener(listener);
+ loadAll = false;
Button addAll = (Button)
container.findViewById(R.id.add_all_to_playlist);
addAll.setOnClickListener(this);
Button findLiveStream =
@@ -65,31 +72,52 @@

}

-
- @Override
- public void onClick(View v) {
- super.onClick(v);
- switch (v.getId()) {
- case R.id.add_all_to_playlist:
+ private NewsListAdapter.StoriesLoadedListener listener = new
NewsListAdapter.StoriesLoadedListener() {
+ @Override
+ public void storiesLoaded() {
+ bannerView.startCloseTimer();
+ if (loadAll) {
+ loadAll = false;
PlaylistRepository playlistRepository =
new PlaylistRepository(getApplicationContext(),
getContentResolver());
for (int i = 0; i < listAdapter.getCount(); i++) {
Story story = listAdapter.getItem(i);
if (story != null &&
- story.getPlayable() != null) {
+ listAdapter.isPlayable(story)) {
playlistRepository.add(story);
Tracker.LinkEvent e =
new Tracker.AddToPlaylistEvent(story.getPlayableUrl());
Tracker.instance(getApplication()).trackLink(e);
}
}
+ }
+ }
+ };
+
+ @Override
+ public void onClick(View v) {
+ super.onClick(v);
+ switch (v.getId()) {
+ case R.id.add_all_to_playlist:
+ loadAll = true;
+ String url = getApiUrl();
+ if (url != null) {
+ // Adding these parameters to podcast urls (like WNYC) can break
them
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("startNum", "" + listAdapter.getCount());
+ url = ApiConstants.instance().addParams(url, params);
+ } else {
+ url = getPodcastUrl();
+ }
+ listAdapter.addAllStories(url);
break;
+
case R.id.find_live_stream:
if (liveStreamRss != null) {
Intent intent = new Intent(this, StationListActivity.class);
intent.putExtra(
- Constants.EXTRA_LIVE_STREAM_RSS_URL,
- liveStreamRss
+ Constants.EXTRA_LIVE_STREAM_RSS_URL,
+ liveStreamRss
);
startActivity(intent);
}
=======================================
--- /Npr/src/org/npr/android/news/StationDetailsActivity.java Sun Sep 11
10:17:14 2011
+++ /Npr/src/org/npr/android/news/StationDetailsActivity.java Fri Jan 6
11:10:49 2012
@@ -25,16 +25,10 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
+import android.widget.*;
import org.npr.android.util.DisplayUtils;
import org.npr.android.util.FavoriteStationsProvider;
+import org.npr.android.util.StationCache;
import org.npr.android.util.Tracker;
import org.npr.android.util.Tracker.StationDetailsMeasurement;
import org.npr.api.Station;
@@ -199,7 +193,7 @@
} else if (getIntent().hasExtra(Constants.EXTRA_ACTIVITY_DATA)) {
stationId =
getIntent().getStringExtra(Constants.EXTRA_ACTIVITY_DATA);
}
- station = StationListActivity.getStationFromCache(stationId);
+ station = StationCache.getStation(stationId);

String selection = FavoriteStationsProvider.Items.STATION_ID + " = ?";
String[] selectionArgs = new String[1];
=======================================
--- /Npr/src/org/npr/android/news/StationListActivity.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/StationListActivity.java Fri Jan 6
11:10:49 2012
@@ -15,7 +15,9 @@

package org.npr.android.news;

+import android.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
@@ -31,28 +33,18 @@
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
+import android.widget.*;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import org.npr.android.util.DisplayUtils;
-import org.npr.android.util.FavoriteStationsProvider;
-import org.npr.android.util.Tracker;
+import org.npr.android.util.*;
import org.npr.android.util.Tracker.StationListMeasurement;
import org.npr.api.ApiConstants;
import org.npr.api.Station;

import java.util.HashMap;
-import java.util.List;
import java.util.Map;

public class StationListActivity extends TitleActivity implements
- OnItemClickListener, OnClickListener, View.OnKeyListener {
+ OnItemClickListener, AdapterView.OnItemLongClickListener,
OnClickListener, View.OnKeyListener {
private static final String LOG_TAG =
StationListActivity.class.getName();

private EditText searchTermField;
@@ -61,44 +53,26 @@
public enum Mode {
allStations, favoriteStations, locateStations, liveStreams,
nearestStations
}
+
private Mode mode = Mode.allStations;

- private static final Map<String, Station> stationCache =
- new HashMap<String, Station>();
private String query;

- public static Station getStationFromCache(String stationId) {
- Station result = stationCache.get(stationId);
- if (result == null ||
- // Stations pulled from the live stream list don't have stream
names
- (result.getAudioStreams().size() > 0 &&
- result.getAudioStreams().get(0).getTitle() == null) ) {
- Station nprStation =
Station.StationFactory.downloadStation(stationId);
- if (nprStation != null) {
- stationCache.put(stationId, nprStation);
- result = nprStation;
- }
- }
- return result;
- }
-
- public static void addAllToStationCache(List<Station> stations) {
- for (Station station : stations) {
- stationCache.put(station.getId(), station);
- }
- }
+ private FavoriteStationsRepository favoriteStationsRepository;
+ private int selectedPosition;
+ private final String presets[] = new
String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "None"};

@Override
public void onLowMemory() {
super.onLowMemory();
listAdapter.clear();
- stationCache.clear();
+ StationCache.clear();
}

private final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if(msg.what == 0) {
+ if (msg.what == 0) {
listAdapter.showData();
TextView emptyText = (TextView) findViewById(R.id.Empty);
if (listAdapter.getCount() == 0) {
@@ -134,16 +108,21 @@
ViewGroup.inflate(this, R.layout.station_list, container);
final ListView lv = (ListView) findViewById(R.id.ListView01);

- listAdapter =
- new StationListAdapter(this
- );
+ favoriteStationsRepository = new
FavoriteStationsRepository(getContentResolver());
+ listAdapter = new StationListAdapter(this, (mode ==
Mode.favoriteStations));
lv.setAdapter(listAdapter);
lv.setOnItemClickListener(StationListActivity.this);
-
- if (mode == Mode.favoriteStations) {
- loadFromFavorites();
- } else {
+ lv.setOnItemLongClickListener(StationListActivity.this);
+
+ if (mode != Mode.favoriteStations) {
loadFromQuery(queryUrl);
+ } else {
+ TextView presetInstructions = (TextView)
findViewById(R.id.preset_instructions);
+ if (favoriteStationsRepository.getItemCount() > 0) {
+ presetInstructions.setVisibility(View.VISIBLE);
+ } else {
+ presetInstructions.setVisibility(View.GONE);
+ }
}

Button locateButton = (Button) findViewById(R.id.StationSearchButton);
@@ -180,12 +159,11 @@
@Override
public void onResume() {
super.onResume();
- SharedPreferences preferences = getSharedPreferences("StationSearch",
0);
- searchTermField.setText(preferences.getString("lastSearch", ""));
-
if (mode == Mode.favoriteStations) {
loadFromFavorites();
}
+ SharedPreferences preferences = getSharedPreferences("StationSearch",
0);
+ searchTermField.setText(preferences.getString("lastSearch", ""));
}

private void loadFromFavorites() {
@@ -205,6 +183,7 @@

/**
* Loads the list adapter and the list from the provided query.
+ *
* @param queryUrl The URL to fetch the data from.
*/
private void loadFromQuery(String queryUrl) {
@@ -227,8 +206,7 @@
* ensure that the title is properly selected.
*
* @param intent The Intent the activity was started with or the intent
- * returned to the activity as a result.
- *
+ * returned to the activity as a result.
* @return The URL to fetch the list data from.
*/
private String parseIntent(Intent intent) {
@@ -236,11 +214,11 @@
if (intent != null) {
query = intent.getStringExtra(Constants.EXTRA_QUERY_TERM);
queryUrl = intent.getStringExtra(Constants
- .EXTRA_LIVE_STREAM_RSS_URL);
+ .EXTRA_LIVE_STREAM_RSS_URL);
if (queryUrl != null) {
mode = Mode.liveStreams;
} else {
- mode = (Mode)intent.getSerializableExtra(
+ mode = (Mode) intent.getSerializableExtra(
Constants.EXTRA_STATION_LIST_MODE
);
if (mode == Mode.nearestStations) {
@@ -291,7 +269,7 @@

StringBuilder builder = new StringBuilder(parts[0]);
for (int i = 1, length = parts.length; i < length - 1; i++) {
- builder.append(" ").append(parts[i]);
+ builder.append(" ").append(parts[i]);
}
String city = builder.toString();
params.put(ApiConstants.PARAM_CITY, city);
@@ -334,15 +312,58 @@

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
- long id) {
+ long id) {
Station station = (Station) parent.getItemAtPosition(position);

Intent i =
- new Intent(this, StationDetailsActivity.class).putExtra(
- Constants.EXTRA_STATION_ID, station.getId());
+ new Intent(this, StationDetailsActivity.class).putExtra(
+ Constants.EXTRA_STATION_ID, station.getId());

startActivityWithoutAnimation(i);
}
+
+ private void setPreset(int item) {
+ if (item == 10) {
+ Station station = listAdapter.getItem(selectedPosition);
+ favoriteStationsRepository.removePreset(station.getId());
+ } else {
+ favoriteStationsRepository.setPreset(
+ listAdapter.getItem(selectedPosition), Integer.toString(item +
1));
+ }
+ listAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int
position, long id) {
+
+ if (mode == Mode.favoriteStations) {
+ selectedPosition = position;
+
+ int presetNumber = 10;
+ FavoriteStationEntry favoriteStationEntry =
+ favoriteStationsRepository.getFavoriteStationForStationId(
+ listAdapter.getItem(position).getId());
+ if (favoriteStationEntry != null &&
+ favoriteStationEntry.preset != null &&
+ favoriteStationEntry.preset.length() > 0) {
+ presetNumber = Integer.parseInt(favoriteStationEntry.preset) - 1;
+ }
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Pick a preset");
+ builder.setSingleChoiceItems(presets, presetNumber, new
DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ setPreset(item);
+ dialog.dismiss();
+ }
+ });
+
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ return true;
+ }
+ return false;
+ }

private String populateLocalStationParams(Map<String, String> params) {
String query = null;
@@ -376,7 +397,6 @@

return query;
}
-

@Override
public CharSequence getMainTitle() {
@@ -417,6 +437,7 @@
}
return getString(helpId);
}
+
@Override
public void trackNow() {
if (query == null) {
=======================================
--- /Npr/src/org/npr/android/news/StationListAdapter.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/news/StationListAdapter.java Fri Jan 6
11:10:49 2012
@@ -22,11 +22,13 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
-import android.widget.ImageView;
+import android.widget.Button;
import android.widget.TextView;
-
import org.apache.http.client.ClientProtocolException;
+import org.npr.android.util.FavoriteStationEntry;
import org.npr.android.util.FavoriteStationsProvider;
+import org.npr.android.util.FavoriteStationsRepository;
+import org.npr.android.util.StationCache;
import org.npr.api.Client;
import org.npr.api.Station;
import org.npr.api.Station.StationFactory;
@@ -35,20 +37,30 @@

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;

public class StationListAdapter extends ArrayAdapter<Station> {
private static final String LOG_TAG = StationListAdapter.class.getName();
private List<Station> data;
-
- public StationListAdapter(Context context) {
+ protected Context context;
+
+ FavoriteStationsRepository favoriteStationsRepository;
+
+ public StationListAdapter(Context context, boolean showFavorites) {
super(context, R.layout.station_item, R.id.StationItemNameText);
+ this.context = context;
+ if (showFavorites) {
+ favoriteStationsRepository =
+ new FavoriteStationsRepository(context.getContentResolver());
+ } else {
+ favoriteStationsRepository = null;
+ }
}

public int initializeList(String url) {
// If we fail, then the list will be empty.
- data = new LinkedList<Station>();
+ data = new ArrayList<Station>();

Node stations = null;
try {
@@ -70,13 +82,13 @@
Log.d(LOG_TAG, "stations: " + stations.getNodeName());

data = StationFactory.parseStations(stations);
- StationListActivity.addAllToStationCache(data);
+ StationCache.addAll(data);
return 0;
}


public void initializeList(Cursor cursor) {
- data = new LinkedList<Station>();
+ data = new ArrayList<Station>();

while (cursor.moveToNext()) {
Station.StationBuilder builder = new Station.StationBuilder(
@@ -131,16 +143,18 @@
frequency.setText("");
}

-
- ImageView image =
- (ImageView) line.findViewById(R.id.StationItemPlayableImage);
- ImageView placeholder =
- (ImageView) line.findViewById(R.id.StationItemPlaceholderImage);
- boolean hasAudio =
- station.getPodcasts().size() + station.getAudioStreams().size() >
0;
- image.setVisibility(hasAudio ? View.VISIBLE : View.GONE);
- placeholder.setVisibility(hasAudio ? View.GONE : View.VISIBLE);
+ if (favoriteStationsRepository != null) {
+ TextView preset = (TextView)
line.findViewById(R.id.StationPresetView);
+ FavoriteStationEntry favoriteStationEntry =
+
favoriteStationsRepository.getFavoriteStationForStationId(station.getId());
+ if (favoriteStationEntry != null && favoriteStationEntry.preset !=
null) {
+ preset.setText(favoriteStationEntry.preset);
+ preset.setVisibility(View.VISIBLE);
+ } else {
+ preset.setVisibility(View.INVISIBLE);
+ }
+ }
+
return line;
}
-
-}
+}
=======================================
--- /Npr/src/org/npr/android/news/TitleActivity.java Sat Jul 2 19:50:10
2011
+++ /Npr/src/org/npr/android/news/TitleActivity.java Fri Jan 6 11:10:49
2012
@@ -14,17 +14,20 @@

package org.npr.android.news;

+import android.content.Intent;
import android.os.Bundle;
+import android.util.Log;
import android.view.ViewGroup;
import android.widget.TextView;

public abstract class TitleActivity extends RootActivity {
private static final String LOG_TAG = TitleActivity.class.getName();
+ public static final String APP_RESUME = "org.npr.android.APP_RESUME";

/**
* Implementing classes must override this method to provide a title that
* will be shown in the title bar.
- *
+ * <p/>
* Note that the title is read in the onCreate so subclasses must be
* able to generate the title in this method entirely from data available
* at the time it is called. This could mean that subclasses' onCreate
@@ -61,4 +64,9 @@

}

-}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ sendBroadcast(new Intent(APP_RESUME));
+ }
+}
=======================================
--- /Npr/src/org/npr/android/util/FavoriteStationsProvider.java Sun May 22
12:54:47 2011
+++ /Npr/src/org/npr/android/util/FavoriteStationsProvider.java Fri Jan 6
11:10:49 2012
@@ -19,6 +19,7 @@
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
@@ -32,7 +33,7 @@
*
* Author: Jeremy Wadsack
*/
-public class FavoriteStationsProvider extends ContentProvider{
+public class FavoriteStationsProvider extends ContentProvider {
private static final String LOG_TAG =
FavoriteStationsProvider.class.getName();

@@ -40,7 +41,7 @@
.parse("content://org.npr.android.util.FavoriteStations");

private static final String DATABASE_NAME = "favorite_stations.db";
- private static final int DATABASE_VERSION = 1;
+ private static final int DATABASE_VERSION = 2;
private static final String TABLE_NAME = "items";

private FavoriteStationsHelper helper;
@@ -53,7 +54,7 @@

@Override
public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
+ String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = helper.getWritableDatabase();
String realSelection = getSelectionFromId(uri, selection);

@@ -111,10 +112,11 @@
public static final String FREQUENCY = "frequency";
public static final String BAND = "band";
public static final String STATION_ID = "story_id";
- public static final String[] COLUMNS = { NAME, MARKET, FREQUENCY, BAND,
- STATION_ID };
- public static final String[] ALL_COLUMNS = { BaseColumns._ID, NAME,
MARKET,
- FREQUENCY, BAND, STATION_ID };
+ public static final String PRESET = "preset";
+ public static final String[] COLUMNS = {NAME, MARKET, FREQUENCY, BAND,
+ STATION_ID, PRESET};
+ public static final String[] ALL_COLUMNS = {BaseColumns._ID, NAME,
MARKET,
+ FREQUENCY, BAND, STATION_ID, PRESET};

// This class cannot be instantiated
private Items() {
@@ -133,7 +135,7 @@
db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + Items._ID
+ " INTEGER PRIMARY KEY," + Items.NAME + " TEXT," + Items.MARKET
+ " TEXT," + Items.FREQUENCY + " TEXT," + Items.BAND
- + " TEXT," + Items.STATION_ID + " TEXT"
+ + " TEXT," + Items.STATION_ID + " TEXT," + Items.PRESET + " TEXT"
+ ");");
}

@@ -142,6 +144,21 @@
Log.w(FavoriteStationsHelper.class.getName(),
"Upgrading database from version " + oldVersion +
" to " + newVersion);
+
+ // Check if this new column exists, and add if it doesn't
+ try {
+ db.query(TABLE_NAME, new String[]{Items.PRESET}, null, null, null,
+ null, null);
+ } catch (SQLException e) {
+ // Column doesn't exist - so add it
+ Log.w(LOG_TAG, "Database update - adding Preset Number", e);
+ try {
+ db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN "
+ + Items.PRESET + " TEXT DEFAULT NULL;");
+ } catch (SQLException ex) {
+ Log.e(LOG_TAG, "", ex);
+ }
+ }
}
}
}
=======================================
--- /Npr/src/org/npr/api/ApiConstants.java Tue Oct 25 08:27:38 2011
+++ /Npr/src/org/npr/api/ApiConstants.java Fri Jan 6 11:10:49 2012
@@ -47,6 +47,7 @@
public static final String PARAM_SORT = "sort";
public static final String PARAM_DATE = "date";
public static final String PARAM_REQUIRED_ASSETS = "requiredAssets";
+ public static final String PARAM_RANDOMIZE_STATIONS = "randomize";

public static final String STORY_FIELDS
= "titles,teasers,storyDate,byline,text,audio,textWithHtml,image,organization,parent";
private final String apiKey;
=======================================
--- /Npr/src/org/npr/api/Program.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/api/Program.java Fri Jan 6 11:10:49 2012
@@ -74,7 +74,7 @@
@Override
public List<Program> downloadStoryGroupings(int count) {
// Get NPRML list
- List<Program> list = super.downloadStoryGroupings(-1);
+ List<Program> list = super.downloadStoryGroupings(-1);

// Create a hashtable cache for quick look-up by topic id
Hashtable<String, Program> lookup = new Hashtable<String, Program>();
=======================================
--- /Npr/src/org/npr/api/Station.java Sun May 22 12:54:47 2011
+++ /Npr/src/org/npr/api/Station.java Fri Jan 6 11:10:49 2012
@@ -32,6 +32,8 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;

public class Station {
private static final String LOG_TAG = Station.class.getName();
@@ -44,6 +46,9 @@
private final List<Podcast> podcasts;
private final String tagline;
private final String image;
+ private final HashMap<String, String> identiferAudioUrlByType;
+
+ public static final String[] stationListBySize = new
String[]{"WNYC","KPCC","KUSD","KPCV","KQED","WAMU","WBUR","WBEZ","WBEQ","WBEW","KNOW","KSJN","KCMP","KMSE","KBPN","KCCD","KLNI","KNBJ","KNCM","KNGA","KNSE","KNSR","KNSW","KNTN","KNWF","KZSE","WGGL","WIRN","WLSN","WSCN","KCCM","KCMF","KLSE","KRSD","KRSW","KSJR","WIRR","WMLS","WSCD","WHYY","KCRW","KCRI","KCRU","KUOW","KOAC","KOAB","KOAP","KOBK","KOPB","KRBM","KTMK","KTVR","KMHD","WABE","WEVN","WSUF","WUOM","WFUM","WVGR","KJZZ","KBAQ","WKWM","WLRN","KPLU","KPLI","KVIX","KERA","KUHF","KXOT","WUSF","KPBS","KQVO","WUNC","WBUX","WRQM","WUND","WURI","WAMC","WAMK","WAMQ","WCAN","WOSR","WRUN","KCFR","KCFC","KCFP","KKPC","KPRE","KPRH","KPRN","KPRU","KPYR","KVOV","WGBH","WCAI","WNAN","WNCK","WZAI","WMEH","WMED","WMEF","WMEM","WMEP","WMEW","WSUI","KDWI","KOWI","KSUI","KTPR","KUNZ","KWOI","WOI","KUT","KUTX","KWMU","WVPS","WBTN","WNCH","WOXR","WRVT","WVPA","WVPR","WVTI","WVTQ","WWPV","KXJZ","KXPR","KKTO","KQNC","KUOP","KXJS","KXSR","WSHU","WFAE","WFHE","WLTR","WEPR","WHMC","WJWJ","WLJK","WNSC","WRJA","WSCI","WFCR","WNPR","WEDW","WPKT","WRLI","KQEI","WCPN","WYPR","WYPF","KCUR","WERN","WHND","WHRM","WHSA","WLSU","WPNE","WSSW","WUEC","WVSS","KUWS","WHA","WHBM","WHDI","WHHI","WHID","WHLA","WHWC","WLBL","WRFW","WRST","WMFE","KUNC","KRNC","KDMR","KDUB","KRNI","KUNY","WHRS","WPLN","WTML","WDUQ","WEVC","WEVH","WEVJ","WEVO","WEVS","WYPM","WITF","WHRV","WHRO","WFFC","WISE","WVTF","WVTR","WVTW","WVTU","WWVT","WCVE","WCNV","WMVE","WVXU","WMUB","WKRJ","WKSU","WKRW","WKSV","WNRK","WDET","WFYI","KHPR","KANO","KIPO","KKUA","KWSU","KRFA","KFAE","KLWS","KMWS","KNWO","KNWP","KNWR","KNWV","KNWY","KQWS","KWWS","KZAZ","KSTX","KTXI","KNPR","KLNR","KSGU","KTPH","KWPR","KUER","WRVO","WRCU","WRVD","WRVJ","WRVN","WSUC","WGCU","WMKO","WJCT","WFSU","WFSL","WFSQ","WFSW","WKAR","WJSP","WSVH","WUGA","WABR","WACG","WATY","WGPB","WJWV","WMUM","WNGH","WNGU","WPPR","WUNV","WUWG","WWET","WWIO","WXVS","WVPN","WAUA","WVEP","WVNP","WVPB","WVPG","WVPM","WVPW","WVWV","KUAZ","WPBI","WMPN","WMAB","WMAE","WMAH","WMAO","WMAU","WMAV","WMAW","WUWM","WUOT","WXXI","WRUR","WXXY","WSKG","WSQA","WSQC","WSQE","WSQG","WSQX","WBFO","WOLN","WUBJ","WFDD","WBHM","WFPL","KPBX","KIBX","KSFC","WOSU","WOSB","WOSE","WOSP","WOSV","KLCC","KLBR","KLCO","KLFO","KLFR","KMPQ","WCQS","WFQS","WYQS","WCBE","KNAU","KGHR","KNAA","KNAD","KNAG","KNAQ","KPUB","KUNM","KANU","KANH","KANV","KUWR","KBUW","KDUW","KSUW","KUWA","KUWC","KUWD","KUWG","KUWJ","KUWL","KUWN","KUWP","KUWT","KUWX","KUWY","KUWZ","WOUB","WOUC","WOUH","WOUL","WOUZ","WQCS","WKNO","WKNP","KUAR","KLRE","WCMU","WCMB","WCML","WCMW","WCMZ","WUCX","WWCM","KWGS","KWTU","KBSW","KBSJ","KBSQ","KBSX","KBSY","KBSK","KBSS","KBSU","WVIA","WVYA","KJZA","KXLC","WHAD","WMEA","KUCV","KCNE","KHNE","KLNE","KMNE","KPNE","KRNE","KTNE","KXNE","KVPR","KPRX","WFIU","WUFT","WJUF","KSOR","KAGI","KLDD","KLMF","KNCA","KNHT","KNSQ","KNYR","KSBA","KSKF","KSMF","KSRG","KSRS","KRCC","KECC","WVPE","WUWF","KGOU","KROU","WWNO","KTLN","WNIJ","WNIE","WNIQ","WNIW","WNIU","KUAF","KMUW","WBLQ","WRNI","WRKF","WSLU","WSLJ","WSLL","WSLO","WXLB","WXLG","WXLH","WXLQ","WXLU","KVCR","WLRH","KCLU","KALW","WUTC","KIOS","WMRA","WMRL","WMRY","WILL","WSCL","WSDL","WGTE","WGBE","WGDE","WGLE","KCHO","KFPR","KUNR","KNCC","KDAQ","KBSA","KLDN","KLSA","WPSU","WPSX","WHQR","WUKY","WIAA","WIAB","WICA","WICV","WYSO","WUAL","WAPR","WQPR","WYSU","KEMC","KYPR","KBMC","KPRQ","WBAA","WBNI","WBOI","WCKZ","WEKU","WEKF","WEKH","KSMU","KSMS","KSMW","KBHE","KCSD","KDSD","KESD","KPSD","KQSD","KTSD","KZSD","KCBX","KSBX","KUFM","KAPC","KUFN","KUHM","KUKL","KRWG","WETS","WTEB","WBJD","WKNS","WZNB","WKYU","WDCL","WKPB","WKUE","KBIA","WQLN","KCPW","KHCC","KHCD","KHCT","WAER","WGVU","WGVS","WSIU","WUSI","WVSI","WUIS","WIPA","WTSU","WRWA","WTJB","WMUK","KUSP","KBDH","KHSU","WGLT","WANC","WCEL","WXPN","WCBU","KCND","KDPR","KMPR","KPPR","KPRJ","WBST","WBSB","WBSH","WBSJ","WBSW","WKMS","WNIN","KRPS","KEDT","KVRT","WHIL","WEMU","WIUM","WIUW","KAZU","WNJT","WNJB","WNJM","WNJN","WNJP","WNJS","WNJZ","KCCU","KLCU","KMCU","KOCU","KYCU","KZCU","WVIK","KAMU","WDIY","WXLV","WNED","KAWC","KANZ","KJJP","KTOT","KTXP","KZAN","KZNA","WNMU","KWIT","KOJI","KRVS","WNKU","KOHM","KOSU","KOSN","KXCV","KRNW","KMBH","KHID","WFUV","WETA","WGMS","KEDM","KUSU","KUSR","KWBU","WXPR","WXPW","KASU","WRTI","WJAZ","WRTQ","WRTX","WRTY","KVLU","WKGC","WMKY","WOCS","KENW","KMTH","KSUT","KUTE","WFIT","WBLV","WBLU","KZYX","KZYZ","KAJX","KCJX","KPCW","KCUA","KANW","KNLK","WBGO","KRCU","KSEF","KPRG","KTBG","WQUB","WFSS","WYEP","WUCF","KAXE","WNCW","WQED","WQEJ","WDAV","WWFM","WWNJ","WWCJ","WWPJ","WBJB","KRCB","WCLK","WRVS","WTMD","WMNF","WESM","WMHT","WEXT","WRHV","WEAA","WCNY","WJNY","WUNY","WJAB","KETR","WBPR","WFPB","WNEF","WUMB","KUNV","WHDD","WLPR","WYPO","WPRL","WICN","KBYU","WNCU","WVAS","WURC","KCSM","KWSO","WNYE","WSNC","WMOT","KCIE","KEXP","KGPR","KTEP","KUSC","WCPE","WEMC","WPPB","WVUB","WZPE","KDSC","KMST","KPSC","KQSC","KUAC","WCWP","WJSU","WSIE"};

public static class Listenable {
private final String url;
@@ -77,7 +82,7 @@

private Station(String id, String name, String city, String frequency,
String band, List<AudioStream> audioStreams, List<Podcast> podcasts,
- String tagline, String image) {
+ String tagline, String image, HashMap<String, String>
audioIdentifiersByType) {
this.id = id;
this.name = name;
this.marketCity = city;
@@ -87,6 +92,7 @@
this.podcasts = podcasts;
this.tagline = tagline;
this.image = image;
+ this.identiferAudioUrlByType = audioIdentifiersByType;
}

public String getTagline() {
@@ -124,7 +130,21 @@
public String getImage() {
return image;
}
-
+
+ public HashMap<String, String> getIdentiferAudioUrlByType() {
+ return identiferAudioUrlByType;
+ }
+
+ public String getCallLettersInName() {
+ // try to find call letters from title
+ Pattern pattern = Pattern.compile("[KW][A-Z][A-Z][A-Z]");
+ Matcher matcher = pattern.matcher(getName());
+ if (matcher.find())
+ return matcher.group();
+
+ return getName();
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(name);
@@ -147,7 +167,8 @@
private List<Podcast> podcasts = new LinkedList<Podcast>();
private String tagline;
private String image;
-
+ private HashMap<String, String> audioIdentifiersByType =
+ new HashMap<String, String>();

public StationBuilder(String id) {
this.id = id;
@@ -197,10 +218,16 @@
this.image = image;
return this;
}
+
+ public StationBuilder withAudioIdentifierByType(
+ HashMap<String, String> audioIdentifiers) {
+ this.audioIdentifiersByType = audioIdentifiers;
+ return this;
+ }

public Station build() {
return new Station(id, name, marketCity, frequency, band,
audioStreams,
- podcasts, tagline, image);
+ podcasts, tagline, image, audioIdentifiersByType);
}
}

@@ -308,6 +335,7 @@
);
List<AudioStream> streams = new LinkedList<AudioStream>();
List<Podcast> podcasts = new LinkedList<Podcast>();
+ HashMap<String, String> identifierAudioUrlByType = new
HashMap<String, String>();
for (Node n : new IterableNodeList(node.getChildNodes())) {
String nodeName = n.getNodeName();
Node nodeChild = n.getChildNodes().item(0);
@@ -341,10 +369,16 @@
podcasts.add(new Podcast(url, title));
}
}
+ } else if (nodeName.equals("identifierAudioUrl")) {
+ Attr typeAttr = (Attr) n.getAttributes().getNamedItem("type");
+ if (typeAttr != null) {
+ identifierAudioUrlByType.put(typeAttr.getValue(),
NodeUtils.getTextContent(n));
+ }
}
}
sb.withAudioStreams(streams);
sb.withPodcasts(podcasts);
+ sb.withAudioIdentifierByType(identifierAudioUrlByType);
return sb.build();
}

=======================================
--- /Npr/src/org/npr/api/Story.java Mon Jul 25 21:36:24 2011
+++ /Npr/src/org/npr/api/Story.java Fri Jan 6 11:10:49 2012
@@ -62,11 +62,12 @@
private final List<PullQuote> pullQuotes;
private final Text text;
private final TextWithHtml textWithHtml;
- private final List<Parent> parentTopics;
+ private final List<Parent> parents;

public static class Thumbnail {
@SuppressWarnings("unused")
private final String medium;
+
public Thumbnail(String medium) {
this.medium = medium;
}
@@ -79,6 +80,7 @@
public static class Toenail {
@SuppressWarnings("unused")
private final String medium;
+
public Toenail(String medium) {
this.medium = medium;
}
@@ -88,17 +90,21 @@
private final String id;
private final String name;
private final String website;
+
public Organization(String id, String name, String website) {
this.id = id;
this.name = name;
this.website = website;
}
+
public String getId() {
return id;
}
+
public String getName() {
return name;
}
+
public String getWebsite() {
return website;
}
@@ -110,21 +116,26 @@
private final String type;
private final String duration;
private final List<Format> formats;
+
public static class Format {
private final String mp3;
private final String wm;
private final String rm;
+
public Format(String mp3, String wm, String rm) {
this.mp3 = mp3;
this.wm = wm;
this.rm = rm;
}
+
public String getMp3() {
return mp3;
}
+
public String getWm() {
return wm;
}
+
public String getRm() {
return rm;
}
@@ -136,15 +147,19 @@
this.duration = duration;
this.formats = formats;
}
+
public List<Format> getFormats() {
return formats;
}
+
public String getId() {
return id;
}
+
public String getType() {
return type;
}
+
public String getDuration() {
return duration;
}
@@ -168,9 +183,10 @@
private final String provider;
@SuppressWarnings("unused")
private final String copyright;
+
public Image(String id, String type, String width, String src,
- String hasBorder, String linkUrl, String producer, String provider,
- String copyright) {
+ String hasBorder, String linkUrl, String producer, String
provider,
+ String copyright) {
this.id = id;
this.type = type;
this.width = width;
@@ -181,6 +197,7 @@
this.provider = provider;
this.copyright = copyright;
}
+
public String getSrc() {
return src;
}
@@ -195,6 +212,7 @@
private final String caption;
@SuppressWarnings("unused")
private final String link;
+
public RelatedLink(String id, String type, String caption, String
link) {
this.id = id;
this.type = type;
@@ -208,6 +226,7 @@
private final String person;
@SuppressWarnings("unused")
private final String date;
+
public PullQuote(String person, String date) {
this.person = person;
this.date = date;
@@ -264,17 +283,18 @@

public static class Parent {
private final String id;
+ private final String type;
private final boolean isPrimary;
@SuppressWarnings("unused")
private final String title;
@SuppressWarnings("unused")
private final String htmlLink;
- @SuppressWarnings("unused")
private final String apiLink;

- public Parent(String id, boolean isPrimary, String title, String
htmlLink,
- String apiLink) {
+ public Parent(String id, String type, boolean isPrimary, String title,
String htmlLink,
+ String apiLink) {
this.id = id;
+ this.type = type;
this.isPrimary = isPrimary;
this.title = title;
this.htmlLink = htmlLink;
@@ -284,6 +304,10 @@
public String getId() {
return id;
}
+
+ public String getType() {
+ return type;
+ }

public boolean isPrimary() {
return isPrimary;
@@ -292,17 +316,21 @@
public String getTitle() {
return title;
}
+
+ public String getApiLink() {
+ return apiLink;
+ }
}

public Story(String id, String link, String shortLink, String title,
String subtitle,
- String shortTitle, String teaser, String miniTeaser, String slug,
- String storyDate, String pubDate, String lastModifiedDate,
- String keywords, String priorityKeywords, List<Byline> bylines,
- List<Thumbnail> thumbnails, List<Toenail> toenails,
- List<Organization> organizations, List<Audio> audios, List<Image>
images,
- List<RelatedLink> relatedLinks, List<PullQuote> pullQuotes, Text
text,
- TextWithHtml textWithHtml, List<Parent> parentTopics) {
+ String shortTitle, String teaser, String miniTeaser, String
slug,
+ String storyDate, String pubDate, String lastModifiedDate,
+ String keywords, String priorityKeywords, List<Byline>
bylines,
+ List<Thumbnail> thumbnails, List<Toenail> toenails,
+ List<Organization> organizations, List<Audio> audios,
List<Image> images,
+ List<RelatedLink> relatedLinks, List<PullQuote> pullQuotes,
Text text,
+ TextWithHtml textWithHtml, List<Parent> parents) {
super(id);
this.link = link;
this.shortLink = shortLink;
@@ -327,7 +355,7 @@
this.pullQuotes = pullQuotes;
this.text = text;
this.textWithHtml = textWithHtml;
- this.parentTopics = parentTopics;
+ this.parents = parents;
}

public String getLink() {
@@ -436,8 +464,8 @@
return title;
}

- public List<Parent> getParentTopics() {
- return parentTopics;
+ public List<Parent> getParents() {
+ return parents;
}

public static class StoryBuilder {
@@ -457,19 +485,20 @@
private String priorityKeywords;
private final List<Byline> bylines = new LinkedList<Byline>();
private final List<Thumbnail> thumbnails = new LinkedList<Thumbnail>();
- private final List<Toenail> toenails = new LinkedList<Toenail>();
- private final List<Organization> organizations = new
LinkedList<Organization>();
+ private final List<Toenail> toenails = new LinkedList<Toenail>();
+ private final List<Organization> organizations = new
LinkedList<Organization>();
private final List<Audio> audios = new LinkedList<Audio>();
private final List<Image> images = new LinkedList<Image>();
private final List<RelatedLink> relatedLinks = new
LinkedList<RelatedLink>();
private final List<PullQuote> pullQuotes = new LinkedList<PullQuote>();
private Text text;
private TextWithHtml textWithHtml;
- private final List<Parent> parentTopics = new LinkedList<Parent>();
+ private final List<Parent> parents = new LinkedList<Parent>();

public StoryBuilder(String id) {
this.id = id;
}
+
public StoryBuilder withLink(String link, String type) {
if (type.equals("html")) {
this.link = link;
@@ -586,17 +615,17 @@
}

public StoryBuilder withParent(Parent parent) {
- this.parentTopics.add(parent);
+ this.parents.add(parent);
return this;
}

public Story build() {
return new Story(id, link, shortLink, title, subtitle, shortTitle,
- teaser,
- miniTeaser, slug, storyDate, pubDate, lastModifiedDate, keywords,
- priorityKeywords, bylines, thumbnails, toenails, organizations,
- audios, images, relatedLinks, pullQuotes, text, textWithHtml,
- parentTopics);
+ teaser,
+ miniTeaser, slug, storyDate, pubDate, lastModifiedDate, keywords,
+ priorityKeywords, bylines, thumbnails, toenails, organizations,
+ audios, images, relatedLinks, pullQuotes, text, textWithHtml,
+ parents);
}

}
@@ -652,7 +681,7 @@

StoryBuilder sb = new StoryBuilder(
// Create an ID because podcast items don't have any
- Long.toHexString(new Date().getTime() * 1000 +
(long)(Math.random()
+ Long.toHexString(new Date().getTime() * 1000 + (long)
(Math.random()
* 1000))
);

@@ -685,9 +714,9 @@
!node.hasChildNodes()) {
return null;
}
-
+
StoryBuilder sb = new StoryBuilder(node.getAttributes().getNamedItem(
- "id").getNodeValue());
+ "id").getNodeValue());
try {
Log.d(LOG_TAG, "parsing story " + sb.id);
for (Node n : new IterableNodeList(node.getChildNodes())) {
@@ -735,24 +764,32 @@
}

private static Parent parseParent(Node node) {
- String id = null, title = null;
+ String id = null, type = null, title = null, apiLink = null;
boolean isPrimary = false;
Attr idAttr = (Attr) node.getAttributes().getNamedItem("id");
if (idAttr != null) {
id = idAttr.getValue();
}
Attr typeAttr = (Attr) node.getAttributes().getNamedItem("type");
- if (typeAttr != null && typeAttr.getValue().equals("primaryTopic")) {
- isPrimary = true;
+ if (typeAttr != null) {
+ type = typeAttr.getValue();
+ if (typeAttr.equals("primaryTopic")) {
+ isPrimary = true;
+ }
}

for (Node n : new IterableNodeList(node.getChildNodes())) {
String nodeName = n.getNodeName();
if (nodeName.equals("title")) {
title = NodeUtils.getTextContent(n);
+ } else if (nodeName.equals("link")) {
+ Attr childTypeAttr = (Attr)
n.getAttributes().getNamedItem("type");
+ if (childTypeAttr != null &&
childTypeAttr.getValue().equals("api")) {
+ apiLink = NodeUtils.getTextContent(n);
+ }
}
}
- return new Parent(id, isPrimary, title, null, null);
+ return new Parent(id, type, isPrimary, title, null, apiLink);
}

private static Organization parseOrganization(Node node) {
@@ -778,8 +815,7 @@
String nodeName = n.getNodeName();
if (nodeName.equals("name")) {
name = NodeUtils.getTextContent(n);
- }
- else if (nodeName.equals("link")) {
+ } else if (nodeName.equals("link")) {
Attr typeAttr = (Attr) n.getAttributes().getNamedItem("type");
if (typeAttr != null) {
String type = typeAttr.getValue();
@@ -803,9 +839,32 @@
if (idAttr != null) {
id = idAttr.getValue();
}
- Attr srcAttr = (Attr) node.getAttributes().getNamedItem("src");
- if (srcAttr != null) {
- src = srcAttr.getValue();
+
+ for (Node n : new IterableNodeList(node.getChildNodes())) {
+ if (n.getNodeName().equals("crop")) {
+ Attr typeAttr = (Attr) n.getAttributes().getNamedItem("type");
+ if (typeAttr != null && typeAttr.getValue().equals("square")) {
+ Attr srcAttr = (Attr) n.getAttributes().getNamedItem("src");
+ if (srcAttr != null) {
+ src = srcAttr.getValue();
+ break;
+ }
+ }
+ }
+ }
+
+ if (src == null) {
+ Attr srcAttr = (Attr) node.getAttributes().getNamedItem("src");
+ if (srcAttr != null) {
+ src = srcAttr.getValue();
+ }
+ }
+
+ try {
+ src = src.replaceAll("&s=[0-9]+", "");
+ src += "&s=13";
+ } catch (NullPointerException e) {
+ Log.e(LOG_TAG, "Error replacing size in story image parsing");
}

return new Image(id, type, width, src, hasBorder, linkUrl, producer,
@@ -829,7 +888,7 @@
}
}
}
- for (Entry<Integer, String> e: paragraphMap.entrySet()) {
+ for (Entry<Integer, String> e : paragraphMap.entrySet()) {
paragraphs.add(e.getValue());
}
return paragraphs;
=======================================
--- /Npr_Test/Npr_Test.iml Sun May 22 12:54:47 2011
+++ /Npr_Test/Npr_Test.iml Fri Jan 6 11:10:49 2012
@@ -1,15 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="android" name="Android">
+ <configuration>
+ <option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/gen" />
+ <option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/gen" />
+ <option name="MANIFEST_FILE_RELATIVE_PATH"
value="/AndroidManifest.xml" />
+ <option name="RES_FOLDER_RELATIVE_PATH" value="/res" />
+ <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/assets" />
+ <option name="LIBS_FOLDER_RELATIVE_PATH" value="/libs" />
+ <option name="REGENERATE_R_JAVA" value="true" />
+ <option name="REGENERATE_JAVA_BY_AIDL" value="true" />
+ <option name="USE_CUSTOM_APK_RESOURCE_FOLDER" value="false" />
+ <option name="CUSTOM_APK_RESOURCE_FOLDER" value="" />
+ <option name="USE_CUSTOM_COMPILER_MANIFEST" value="false" />
+ <option name="CUSTOM_COMPILER_MANIFEST" value="" />
+ <option name="APK_PATH" value="" />
+ <option name="LIBRARY_PROJECT" value="false" />
+ <option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="true" />
+ <option name="GENERATE_UNSIGNED_APK" value="false" />
+ </configuration>
+ </facet>
+ </component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<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="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Npr" />
- <orderEntry type="jdk" jdkName="Android 1.6 Platform" jdkType="Android
SDK" />
- <orderEntry type="library" scope="PROVIDED" name="Android 1.6
Platform" level="application" />
</component>
</module>

=======================================
--- /Npr_Test/src/org/npr/android/news/StationDetailsActivityTest.java Mon
Sep 27 10:08:53 2010
+++ /Npr_Test/src/org/npr/android/news/StationDetailsActivityTest.java Fri
Jan 6 11:10:49 2012
@@ -17,6 +17,7 @@
import android.content.Intent;
import android.test.ActivityInstrumentationTestCase2;

+import org.npr.android.util.StationCache;
import org.npr.api.Station;
import org.npr.api.Station.AudioStream;
import org.npr.api.Station.Podcast;
@@ -44,7 +45,7 @@
.build();
ArrayList<Station> list = new ArrayList<Station>();
list.add(station);
- StationListActivity.addAllToStationCache(list);
+ StationCache.addAll(list);
Intent intent = new Intent().putExtra(Constants.EXTRA_STATION_ID, "0");
setActivityIntent(intent);
Activity activity = getActivity();
@@ -69,7 +70,7 @@

ArrayList<Station> list = new ArrayList<Station>();
list.add(station);
- StationListActivity.addAllToStationCache(list);
+ StationCache.addAll(list);
Intent intent = new Intent().putExtra(Constants.EXTRA_STATION_ID, "0");
setActivityIntent(intent);
Activity activity = getActivity();
@@ -94,7 +95,7 @@

ArrayList<Station> list = new ArrayList<Station>();
list.add(station);
- StationListActivity.addAllToStationCache(list);
+ StationCache.addAll(list);
Intent intent = new Intent().putExtra(Constants.EXTRA_STATION_ID, "0");
setActivityIntent(intent);
Activity activity = getActivity();
Reply all
Reply to author
Forward
0 new messages