PremoFM 1.2 – Introducing Pinning, OPML support, iTunes links, and more Material

web_hi_res_512

PremoFM, Android’s freshest podcast app, just leveled up with PremoFM 1.2 with a lot more awesome.  It’s available in Google Play now!

What’s new?

Pinning allows you save episodes to your device without subscribing to the podcast.  Want to check out Tim Ferriss Podcast w/ Jamie Foxx, but don’t want to subscribe?  Pin that episode to your device and you can listen to it, download it, add it to a collection or playlist.  Remove it when you’re done with it.

OPML Support allows you to import all of your podcasts from another podcast player, into PremoFM.  You can also export all of your PremoFM podcast subscriptions to an OPML file.  Access both features in settings.

iTunes Podcast Link opening allow you to click on an iTunes podcast link and open it in PremoFM, to that specific podcast.  Most podcasts list their iTunes link directly on their landing page.  This allows you to subscribe to podcast from the web on your Android phone more seamless.

Notifications have been improved.  Not only are they more readable, but now you’ll be able to check out show notes directly from your lock screen when you received a new episode notification.

Not familiar with PremoFM?  Check out the video below, then head to Google Play.

.

 

Cursors, RecyclerViews, and ItemAnimators

RecyclerView is an Android support library class that was introduced with the launch of Android “Lollipop” (compatible all the way back to Android 2.1).  It has a ton of new capabilities and built on a new architecture that promises flexibility and scalability for Android apps.

It’s ListView predecessor didn’t age too well and became cumbersome to use and extend.  ListView had a plethora of built in adapters (classes for binding data to the ListView), including an adapter for binding a ListView to a Cursor.  RecyclerView is great, but it’s missing a cursor adapter.  Because RecyclerView is so extensible, developers can easily write one, like the one written by Jason Yu:

Well, great, now I can use CursorLoaders, Cursors, and RecyclerViews in my Android app.  There’s one problem.  Backing up a bit, RecyclerViews introduced ItemAnimators.  They provide an easy way to run animations when items in the data backing a RecyclerView are inserted, removed, or changed.  If you’ve done any type of animations with ListView, you’d appreciate the ease of ItemAnimators.  RecyclerView determines which animation to run using RecyclerView.Adapter functions called in an implementing adapter:

  • notifyItemChanged
  • notifyItemRangedChanged
  • notifyItemInserted
  • notifyItemRangeInserted
  • notifyItemRemoved
  • notifyItemRangeRemoved
  • notifyItemMoved

Each of the mentioned functions requires an index, or range of indexes indicating which items were inserted, removed, or changed.

Going back to the problem, using a CursorLoader and Cursors in an Android app.  A dataset change (new record, deleted record, changed record) causes CursorLoader to initiate the allocation of a new Cursor that points to the new dataset.  This is great, we can pass that new Cursor to our CursorRecyclerViewAdapter seen above.  This is where our problem rears it’s head.  Our default implementation only knows that the dataset has changed, but doesn’t know the particulars (which records are new, removed, or changed)…so it calls notifyDataSetChanged, which causes the entire RecyclerView to be invalidated and redrawn.  It looks bad and is inefficient (see explanation at the end).

I needed a way to determine which records were inserted, which were removed, and which were changed.  I had a cursor to the old dataset and a cursor to the new dataset.  My only options were to diff them, building a list of records that were added, removed, and changed.  This allowed me to run the appropriate notifyItem* function and gain the corresponding animation that improved the user experience.  This is the RecyclerView adapter I use in my podcast app, PremoFM.

In this Gist, I’ve added several things to make this diff possible, built on the great work of Jason Yu.

First, my dataset contains a column I use to record a timestamp when that record is updated.  I pass the name of the column to the CursorRecyclerViewAdapter.  This becomes my comparison column (mComparisonColumn).  This saves me from having to compare each value, in each row in a record from one cursor, to each value in each row in a record in another cursor.

Second, I’ve added several functions that compare a new cursor and an old cursor:

  1. getChangeOrInsertRecords – iterates over the incoming cursor, then the outgoing cursor in an inner loop.  Matches each record on a row ID.  If a match is found, it then compares the comparison column.  If the values in that column don’t match, this record was edited.  If a record in the incoming cursor is not found in the outgoing cursor, then it’s a new record.  If the incoming cursor is empty, then all the records were deleted.  If the outgoing cursor is empty, then all the records in the incoming cursor can be assumed as inserted.
  2. getDeletedRecords – iterates over the outgoing cursor, then the incoming cursor in an inner loop.  It matches each record on a row ID.  If a record in the outgoing cursor is not found in the incoming cursor, then we can assume that the record has been deleted.

The result of running these two functions is a list that contains the index and that indexes difference (insert, remove, change).  I use this data in swapCursor to run the corresponding notifyItem* function and I gain animations when items change.

What does all of this get me?  Look at the user interface differences below.

notify_dataset  notify_item

Left: notifyDataSetChanged / Right: notifyItem*

Downsides?  The diff is a O(n^2) operation.  If you have a ton of records, this operation could take a while.  A possible optimization is to only diff the records that are visible in the RecyclerView and not the entire dataset behind each cursor.

Let me know if you have any questions by hitting me up on Twitter.  Also download my podcast app, PremoFM, in the Google Play Store now by going to premo.fm/app.

Why is using notifyDataSetChanged with RecyclerView bad and inefficient?  Calling notifyItemChanged causes RecyclerView to make a requestLayout for the RecyclerView.  So even if the text was updated in the first item in my RecyclerView, all the items will be redrawn.  The function notifyDataSetChanged triggers a call to RecyclerView.AdapterDataObservable.notifyChange.  In this function, each RecyclerViewDataObserver.onChanged function is called, which eventually triggers a requestLayout call.

Evidence is freely available for in the Android source code.

Tom Brady, ESPN, Media, and Barack Obama

600x600bb

Bill Simmons and guest, Chuck Klosterman, cover a ton of ground in the latest Bill Simmons Podcast. Particularly insightful conversation as they discuss how cord cutting affects ESPN’s bottom line, Bills Simmons recent interview of President Obama, and Chuck’s interview with Tom Brady. For example, ESPN loses out on $70/year in revenue for every cable subscriber that cuts the cord (cancels their cable subscription)!? Cord cutting is getting worse (I cut the cord 2 years ago) so I’d imagine they’re losing a ton of money.

As an side, I really just got into The Bill Simmons Podcast….it’s a good one.  Check it out.

You can find podcasts like The Bill Simmons Podcast on PremoFM, in the Google Play Store now.

Discover new passions with PremoFM

Screenshot from 2015-11-10 08:05:44

 Google Play | Website | Twitter


 

I have a wide variety of interests and passions that matter to me, gadgets & technology, programming and UI/UX design, Android, indie rap & hip hop, dope Volkswagens, people of color in technology, New York Jets football, NC State Wolfpack athletics, travel and so many more.  I don’t have a crazy amount of time to sit and read things around my interests, but I have tons of time to listen.  Podcasts fill the majority of my free listening time.

As you’d imagine, I listen to a ton of podcasts. A few of my favorites, His & Hers with Jemele Hill & Michael Smith, The Vergecast, Tomorrow, The Accidental Tech Podcast, and Greenbench Podcast.  I built a podcast app for Android because I wanted to find and listen to more podcasts around my interests and passions and, perhaps, discover new interests and passions.  Now, you can too.  PremoFM 1.0 launches in the Google Play Store today, for free.  I’ve been building it in my spare time for almost a year and using it for about 10 months.  I’ve had beta testers using it for 6 months and now I want everyone to use it.  I have a vision for a podcast app that can help you discover new interests and passions. PremoFM 1.0 is the first step towards that vision.

I expect to move very fast, adding highly sought after features like public podcast collections, Chromecast support, variable speed playback and audio enhancements, better podcast discover-ability, and more

However for today, happy listening.

So, I could probably work on this forever.

screenshots

I’ve been building a podcast app for Android for the last year in my spare time. (Side note: I’ve been building an app for Android for the last 18 months. Fun fact, Premo started off as a contextual messaging app). In the last six months I’ve been working with a small team of beta testers to assist me in creating a great experience for Android podcast fans. It’s time to widen the audience.

Taking a few steps back, I love listening to podcasts. I’m a HUGE podcast fan. That might be an understatement. When I’m not listening to my wife, daughter, friends, or coworkers, I’m listening to podcasts. I started listening to podcasts in the Google Reader days (who remembers Google Listen?). Since then I have tried and purchased many podcast apps on Android including BeyondPod, Podkicker Pro, PlayerFM, and Pocket Casts. They’re all great apps.

But I decided to build a podcast app because I think there’s still room to do something different.

  • I should be able to sample and listen to new podcasts, without needing to subscribe to them
  • I should be able to share my collections of podcasts and episodes with other listeners, easily
  • I should be able to see episode show notes without digging too deeply
  • I should be able to easily organize and navigate my podcasts
  • I should be able to use a beautiful app

I decided to build a podcast app I would love to use. Now I’m making a bet that others will too. Today I’m opening up the beta to anyone running Android 5.0 “Lollipop” or higher. To sign up, visit Premo.FM and click “Join the Beta”.

The beta is free, which given all of the work I’ve put into this app, is crazy. The app may go paid (via an in-app purchase) or become listener supported (donations like public radio). In the meantime, just try it out and send me any feedback on Twitter, @PremoFM. I’m looking forward to it.

Happy listening 🙂