Android and Linux

Sunday, August 28, 2011

Part 2: Finding the current song playing in Android, still sorta.

I made a post about getting the filename from the mediaserver at the Tasker discussion group and others found that the lsof utility only comes on Cyanogen so many people don't have it. I found an alternative in "ls -l /proc/$(pidof mediaserver)/fd" and I'll show some ways to use it below.

Another poster, Matthieu, also figured out a way to get the mp3 tags from the files. The ID3v1 tags are pretty simple, but ID3v2 is a pain. Here is a post I made to that thread incorporating the lsof alternative, Matthieu's method of extracting ID3v1 from that thread, and the start of an ugly method to get ID3v2 tags. More on that in a moment.

Here's an lsof alternative. I think it could do without the grep command but it's safer with it in:
ls -l /proc/$(pidof mediaserver)/fd | sort -g -k 9 | grep mp3 | tail -n1
This will give you the long filename like /sdcard/foo.mp3:
ls -l /proc/$(pidof mediaserver)/fd | sort -g -k 9 | grep mp3 | tail -n1 | awk '{print substr($0, index($0,$11))}'
This gives the short filename like foo.mp3:
basename "$(ls -l /proc/$(pidof mediaserver)/fd | sort -g -k 9 | grep mp3 | tail -n1 | awk '{print substr($0, index($0,$11))}')" .mp3
This gives the id3v1 tag:
tail -c 125 $(ls -l /proc/$(pidof mediaserver)/fd  | sort -g -k 9 | grep mp3 | tail -n1 | awk '{print substr($0, index($0,$11))}') | head - c30
This gives the id3v2 tag but needs tweaking:
grep -A 20 TIT2 "$(ls -l /proc/$(pidof mediaserver)/fd  | sort -g -k 9 | grep mp3 | tail -n1 | awk '{print substr($0, index($0,$11))}')" | grep -m 1 [a-z]
That last one is ugly and unfinished. ID3v2 tags are variable length and the length of the tag is supposed to be encoded in the file itself but, for the life of me, I can't find a file on my system that follows that guideline. I'm obviously missing something because they display correctly when played, but most of them also have ID3v1 tags on the end as well, so maybe that's why they display properly.

I would spend time trying to find a better solution but Pent said he is going to add mp3 tag support to Tasker soon and that should work better than anything I could come up with. Plus, I'm really not interested in the tags. When something interests me, I usually get about an hour late at night to work on it, and I only want the file name.

As ugly as it is, the last one still kinda works. All the tags on my system start with TIT2, but are followed by a lot of hex gibberish before the plain text tag begins. This greps TIT2 then greps the first line below it that contains a lower case letter. On my files, it has a high success rate. One big problem it has is that the tag is often followed by another field such as TALB, so the output can be "Song TitleTALB." Someone could figure out all the possible fields in an ID3v2 tag then just use sed to chomp them off. Or someone cou otu a more reliable method, or we can just wait for Tasker to do it for us.

Saturday, August 20, 2011

Finding the current song playing in Android, sorta.

I've seen people wanting to use Tasker to get the title of the currently playing song before, but it's not really possible because Android doesn't have an API for it. There is a semi-workable solution using the Locale Execute Plugin. We can find which files the media server has open with the lsof command. Assuming the only files you're interested in are mp3s, we can just grep the mp3s from the list and print the filename:
lsof -p $(pidof mediaserver) | grep -m 1 mp3 | awk '{print substr($0, index($0,$9))}'
As luck would have it, the current song seems to always be at the top of the list, so using grep -m 1 we can just get the first line of output.

Output: /mnt/sdcard/download/AloeBlacc-YouMakeMeSmile.mp3

It can be cleaned up a little with the basename command:
basename "$(lsof -p $(pidof mediaserver) | grep -m 1 mp3 | awk '{print substr($0, index($0,$9))}')" .mp3
Output: AloeBlacc-YouMakeMeSmile

If you're interested in files other than mp3, you can add them to the grep command:
grep -E -m 1 'mp3|wav|other1|other2'
The downside is that this just gets the file name and doesn't display any metadata or anything like that. If your file is Track10.mp3, it may show information about the artist and song in the media player, but with this trick it will only show Track10.

This trick also works to display the file being played in BeyondPod, the podcatcher app, but podcasts often don't follow naming schemes meant for humans so the output for the Jordan Jesse Go! podcast ends up being jjgo110814_ep187. If your goal was to display the file in Tasker or Zoom, you could set up replacement titles for your podcasts, like if the title matches jjgo*, display "Jordan Jesse Go!"

Aug 23: I edited the awk command to handle filenames with spaces, then I realized the awk command isn't needed. There's no need to pick out the field with the filename with awk. You can use the entire line, the basename command will only output whatever is to the right of the last "/" anyway. I'll leave the original up since it's been there for a couple of days and people have already seen it, but this simpler command would be better to use:
basename "$(lsof -p $(pidof mediaserver) | grep -m 1 mp3)" .mp3
Use that one instead, and I'll try to notice the obvious sooner next time.

Wednesday, August 3, 2011

Free Nexus S

If you go to google.com, there is a line of text that reads "Nexus S, a pure Google experience. Get it FREE today only"

It requires a 2 year contract or an extension of your existing contract. I wasn't impressed with the Nexus S when it came out but my Nexus One is seriously aging and I'm contemplating getting a Nexus S. I can't make up my mind because I want to wait for something better too. Oh well, in case you missed it, go to google.com and click the link.

Wednesday, July 20, 2011

New Tasker plugin

If you read this blog, you're probably interested in the new Secure Settings Tasker plugin. It has a lot of features which seem good. Unfortunately, I don't have a use for any of them except the shell command runner and I'm happy enough with my own methods that I probably won't change. But the plugin does allow you to store stdout and stderr in a Tasker variable and I'm sure will be a hit.

The downside is that you have to set up a "variable changed" profile to get the output (see the second post in that thread and the later reply). It's more or less the same as my command runner profile except it can display multiple lines of output and my profile can't because Tasker still can't read entire files. (I got it on the todo list, but it's been sitting there for nearly a year).

Plus, it has other features. If you're interested in those, it's probably a win-win.

Thursday, July 14, 2011

Notify My Android down

Wouldn't you know it, as soon as I start using and posting about Notify My Android, it stops working. According to an NMA Twitter post:
Google C2DM SSL Server certificate expired and they didn't update. It should be normalized later this morning. Sorry for that.
That was 16 hours ago. Hopefully they get it running soon.

Monday, July 11, 2011

Pulling data from Notify My Android notifications

Notify My Android (NMA) doesn't have Tasker integration, but we can bridge the gap... sorta.

Data for the app is stored in a database so we can pull out the ever handy sqlite3 and grab it.

sqlite3 /data/data/com.usk.app.notifymyandroid/databases/nma "SELECT * from notifications;"

The data is stored in pipe delimited fields with the newest record on top. Here is what it looks like when I send the output of the date command as both the event and description field.

44|141395|Home|Mon Jul 11 20:18:43 EDT 2011|Mon Jul 11 20:18:43 EDT 2011|1310430099|0|1

Assuming we want the 5th field, just tell Tasker to read the line, split the variable then flash %VAR5.

But here's the catch, Tasker can perform an action when the notification comes in but the data doesn't go into the file until a few seconds after the app is opened, after it syncs with it's home server.

The most elegant solution doesn't work. That would be a Tasker context watching for the file to be modified, then running the command to get the new notification. Unfortunately, watching for file modification doesn't seem to work in /data/data.

You could write a script to watch the file for new contents then put something in a file on the sdcard that Tasker could see, but it would be necessary to keep the script looping and if you lost the network connection or something, it could loop indefinitely. You could kill it once the app exits, but this is getting too complicated.

The easy answer is to just add a wait. You'll need a profile watching for the app to open, but disable that profile. Then have a profile watching for the notification. Once the notification comes in, have it enable the "App Open" profile. The App Open profile can carry out a task to

1 wait 5 seconds (adjust if your network connection takes longer)
2 execute: @! sqlite3 /data/data/com.usk.app.notifymyandroid/databases/nma "SELECT * from notifications;" > /mnt/sdcard/Tasker/notified
3 read line 1 of Tasker/notified to var %NOTIFIED
4 Variable Split %NOTIFIED splitter: |
5 flash %NOTIFIED5
6 Profile Status set Notified off

Remember, the NMA app must be opened for Tasker to grab the notification data, but with these profiles, when there isn't a notification, it can be opened without triggering the task.

%NOTIFIED5 will contain the main body of the notification. You can select from 3, 4 or 5. They are the application, event and description. All three are necessary to send a notification through NMA and can contain 256, 1000 and 10000 characters respectively. Only the application and as much of the event as possible show in the Android Notification area. The entire event and the description are shown in the app.

Pent has put the ability to read any file system-wide on Tasker's todo list. Until then, I hope someone gets some use out of this.

Sunday, July 10, 2011

Prowl-like custom Android push notifications

Oh boy, I've been wanting this forever. When I used the iphone a year and a half ago, there was an app called Prowl which allowed you to send any push notification to your phone. You could, for example, have your computer send a notification when it booted and the notifications could say anything up to a 1024 character limit.

I've really wanted that for Android and hoped to see it when Google came out with the Cloud to Device Messaging service but I didn't find it until now.

Notify My Android will allow you to send custom push notifications to your phone and here's how to use it from the Linux command line.

You first get the app from the Market then create an account on their website. Once you log in to the website, go to "my account" and generate an API key. Now it's as simple as crafting a cURL or wget command to post the data.

curl -k https://nma.usk.bz/publicapi/notify -F apikey="YOURKEY" -F application="Testing" -F event="test event" -F description="hello world"

wget -q -O - --post-data "apikey=YOURKEY&application=APPNAME&event=EVENT&description=this is the description area where the actual notification text is supposed to go" https://nma.usk.bz/publicapi/notify

The fields can be found on the website on the API page. cURL is easier to understand here because you just put a -F then the name of the field to post to and the text to post. -F apikey="YOURKEY" is pretty easy to decipher, but wget is actually doing the same thing, just differently.

One trick I always thought was useful was to set it up so you can pipe output to the phone. You can pipe output to this short script and it will show up on your phone. I include both wget and cURL. cURL is simpler to use on a computer but wget can be used on your phone, however, I've found that wgen on the phone doesn't allow secute https. You can still use regular unsecure http if you think the threat is low enough.

#! /bin/sh
out=$(< /dev/stdin)
wget -q -O - --post-data "apikey=YOURKEY&application=APPNAME&event=EVENT&description=${out}" https://nma.usk.bz/publicapi/notify

#! /bin/sh
out=$(< /dev/stdin)
curl -k https://nma.usk.bz/publicapi/notify -F apikey="YOURKEY" -F application="Testing" -F event="test event" -F description="${out}"

Followers