Android: Saving MMS Photos and Videos and Text Conversations
Until recently, I didn't know anyone who texted much, but just recently I've been corresponding with several people who like to text. Which is fine with me, but comes with one problem: when someone texts me a photo or video, it's tough to see much on a phone screen. I'd much prefer to view it on my big computer screen.
Not to mention that I'd like to be able to archive some of these photos on a platform where I can actually do backups. Yes, I know I'm a dinosaur and modern people are supposed to entrust all our photos, addressbooks, private documents and everything else to Google or Apple. Sorry. I'm happier in the Mesozoic.
Anyway, the point is that I wanted a way to get a message that's sitting in my Android phone's Messages app onto my computer.
As far as I can tell from web searching, there's no way to back up all the texts on an Android phone if it's not rooted. If you want to keep an archive of a conversation ... well, sorry, you can't. (If you are rooted, there's apparently an sqlite db file buried somewhere under /data.)
After much searching, I did discover that you can long-press on an individual photo or video and choose Save attachment.
A dialog will come up offering a filename and a checkbox. The box is unchecked, and the Save button won't enable unless you check the box. What good that does is beyond me, since there's no option of letting you choose a different filename.
There's also no option of seeing where it saved, and Android doesn't
give any clue. But it turns out (after much searching and discovering
that commands like find / -name "Download*" 2> /dev/null
don't work in Android, they just fail silently; I don't know what
you're supposed to use to search for files. Oh, right, you're not
supposed to) to be in /sdcard/Download.
So once you know the filename, you can
adb shell ls /sdcard/Download adb pull /sdcard/Download/whatever.jpg
This isn't so bad as long as you do it before people have sent you 100
images that you want to archive. Fortunately I have less than ten I
need to save.
Saving SMS Conversations
You can save the text of an entire SMS conversation with someone, though it won't save the media associated with the conversation.
Click on the three-dots menu item in the upper right of a conversation, scroll down in the menu and choose Save messages; the filename will be something like sms-20190723122204 (the numbers are the current date and time). Select all messages, or just the ones you want to save. Click your way through a warning screen or two telling you it won't save MMS media or group messages.
When you tap Save. there's a briefly-flashing message telling you where it saved it: if you have a photographic memory, be prepared, otherwise, you might need to save the same conversation several times to read the whole path; on my old Samsung, it turns out they go into /storage/emulated/0/Messaging.
Format of a Saved Conversaion
What you get is an XML file with lots of entries, in reverse chronological order. One entry looks something like:
<message type="SMS"> <address>5059208957</address> <body>We+will+probably+be+here+all+day.+Let+me+know+when+you+could+visit+</body> <date>1563723506834</date> <read>1</read> <type>1</type> <locked>0</locked> </message>
So to make them easily readable, you'd want something that could split out the <body> parts and then HTML decode them to turn those plusses and HTML entities into spaces and readable characters.
Parsing XML is easy enough with Python's BeautifulSoup module -- or is it? Usually with BS4 I use the "lxml" parser, but I hit a snag in this case: see that body tag inside each message tag? It turns out the lxml parser, despite its name, is meant to be an HTML parser, not general XML; it inserts
>>> from bs4 import BeautifulSoup >>> with open('sms-20190723202727.xml') as fp: ... soup = BeautifulSoup(fp) ... for tag in soup.findAll(): ... print(tag.name) ... html body file thread message ...
See how it's added html and body tags that weren't actually in the file? Worse, it also ignores the body tags that actually are in the file -- which is a problem because body is the tag where Android's Messages app chooses to put the actual text of the message.
Instead, use the "xml" parser, which is actually intended for XML.
Once you get past the lxml problems, the rest is pretty easy,
except that you need to know that the dates are in Java Timestamp
format, which means you have to divide by 1000 to get a Unix timestamp
you can pass to datetime. And each SMS text is URL plus-encoded,
so you can unquote it with urllib.parse.unquote_plus
.
from bs4 import BeautifulSoup from datetime import datetime import urllib.parse import sys def parse_sms_convo(filename): with open(filename) as fp: soup = BeautifulSoup(fp, "xml") for msg in soup.findAll('message'): d = datetime.fromtimestamp(int(msg.find('date').text)/1000) body = msg.find('body') addr = msg.find('address') print("%s (from %s):\n %s" % (d.strftime("%Y-%m-%d %H:%M"), addr.text, urllib.parse.unquote_plus(body.text))) print() if __name__ == '__main__': parse_sms_convo(sys.argv[1])
Of course, the best would be to get the entire conversation including images and videos all together. Apparently that's possible with a rooted phone, but I haven't found any way that doesn't require rooting. (It forever amazes me that advanced users who care about things like root access aren't bothered in the least by the fact that rooting almost always requires downloading a binary from a dodgy malware-distribution website, running it and giving it complete access to your phone. Me, I just hope a day eventually comes when there's a phone OS option that gives me the choice of controlling my own phone without resorting to malware.)
[ 13:41 Jul 27, 2019 More tech | permalink to this entry | ]