My program is a “sticky notes” application, but so-as to not get sued by 3M, I’m calling it TackyNotes. It’s essentially a wall on which you can place 3-d looking notes. Currently they are all yellow, but it could interface with a color picker quite readily. The background color is otherwise dynamically set by coloring a mask and applying a shading image over it.
TackyNotes has a menu with two sets of options. You can always create a New Note, which will be created in the center of the screen and immediately zoomed in for editing. Or you can Delete All to clear the wall.
When you are done editing a note, you can dismiss it with a simple pinch gesture on the note. It will zoom back out and take it’s place on the wall with its friends. You can also drag the notes around to organize them how you like, though it wont let you drag them off the screen (currently, they *can* fall off the screen if you change orientation).
With a TackyNote open, the menu still allows you to create new notes, but it now also allows you to delete the current note.
Open notes stack appropriately (you can create/open multiple notes and dismiss them in reverse order).
If you rotate the screen, currently the positions remain relative to the top/left of the current orientation, so it’s possible to place notes in one spot in one orientation that makes them inaccessible in another.
TackyNotes are automatically saved when dismissed, when the orientation or configuration changes, or when the program is closed. Positions of notes on the wall are saved as soon as you life your finger.
The zoom feature originally combined scale and translation animations in an AnimationSet. Chris Johnson worked out how to do it with a ScaleAnimation alone by calculating the appropriate pivot point for the scaling. Thanks!
Reversing the animation used another trick, a reverse interpolator. Basically, the animation progresses from 0 to 1, but the interpolator takes those values and subtracts them from 1, effectively causing the animation to go in reverse.
The changing menus is done by overriding the onPrepareOptionsMenu method, which is called before every showing of the menu. From there, I can hide/show the appropriate menu items (like showing Delete Note vs Delete All Notes depending on whether any notes are open).
There are still many tweaks and new features I would like to add to this project in the future, but I’m happy with it for now. In order to test the pinch-zoom, I had to run it on a loaner HTC phone that only had API 10, so I had to work around a few issues there as well.
Well, that’s about it. Hope you like it!
]]>My girlfriend suggested something along the lines of a photo journal (a way to take pictures and immediately say sometime about them). Seemed simple enough…
The landing page either includes a welcome message, or it displays the most recent journal entry.
There is also a library to list previous entries:
If you tap on an item, it displays the entry. If you tap and hold, it opens a dialog to confirm that you wish to delete the entry. If deletion is successful, you are returned to the library.
The third page allows you to add new entries. For some reason, the built in camera would not cooperate with Cordova’s camera plugin, so it’s currently pulling from the phone’s gallery. This page features a button with a custom icon (which I had to hack with css to get it to display like this).
Once you’ve selected an image (or taken a photo, in an ideal world. I’ll try this on a Droid phone when I get back to one), it replaces the button with the image. You may then add any textual comments about the photo in the textarea below and click Save.
Data is persisted via a custom Cordova plugin that writes serialized JSON data to files stored on External Storage. The plugin has 3 actions: one to fetch all current journal files, one to remove a journal file by name, and one to save a new journal file.
As I said, I had trouble getting Cordova to work. This is because the deviceready event did not seem to fire. Instead, I setup an interval hook to detect for cordova.exec, which seemed to work nicely.
Hope ya like it!
]]>After a bit of searching, I could find nowhere on the sinfest.net website that linked to an rss feed or web api. Expanding the search to google revealed that there actually was an undocumented rss feed url which was provided by the web app software that handles the site comic administration. The format of the content was not quite how I’d have liked it though. In the process of searching, I had found where someone had set up a “Yahoo! pipe” for this rss feed (though the pipe was long gone) so I decided to check out these so-called pipes. They are extremely cool!
Given a feed, they offer all sorts of options to re-format or filter the data, and exposes a new feed url. It also lets you decide what sort of output you wanted to deal with, so it’s quite handy if for nothing else than translating xml to json (which I did as well).
Armed with this, I was able to extract the comic title, date (in a friendly format), link to the comic page, and link to the comic image. My main activity extended ListActivity and used a SimpleAdapter, which was very handy! I can organize the data in a Map, and I used the a modified version of the simple_list_item_2 layout. Because this layout needs two values, you cannot simply use an ArrayAdapter. Instead, I give the SimpleAdapter two arrays, one containing keys into the map, and one containing resource identifiers into which to put the values from the map. This allowed me to have the comic title in large text, and the date in smaller text beneath it.
Upon a simple click, it would call the default Action.VIEW intent, which in this case was the browser, to display the comic image alone.
You have the option to perform a long-click on the item, and doing so will instead load up the web comic within the website.
Per the instructions, I added an item to the menu to call up the ListActivity showing the links from the twodee.org site.
This list was very similar, though it just used the ArrayAdapter and simple_list_item_1 layout. Clicking a list item merely starts up the browser to display the linked page.
I don’t know how reliable it is, but I discovered that I could call getContent() from the HttpUrlConnection to get an InputStream (or GZIPInputStream if the data was zipped) so I wouldn’t have to worry about whether this was the case. I also used a CharBuffer to read the data at once rather than re-write a loop. Not perfect, but seemed cleaner for this job.
Enjoy!
]]>Hope you like it!
]]>The app makes use of TextView, EditText, GridView, Button, and Spinner.
The app starts up with a relatively blank screen and a Spinner at the top. Scripts are loaded from a resources file as an array of strings. Each string has the script’s title, followed by a pipe character, and then the story script. The spinner is populated with the titles of the scripts found.
Upon selecting a valid script, a form appears with a GridView of EditText views, each with a hint corresponding to a word in the script that needs replacing. Below that are two buttons for resetting the form, or creating the final story.
When [Create Story] is clicked, the values the user entered are gathered up and sent along with the script to another activity. This activity assembles the final story and displays it in a TextView. It also has a button to return the user to the previous activity in order to create another script.
I learned how to use the Spinner control (pretty basic) as well as how to iterate over GridView children. I learned that heirarchial data cannot be stored in the resources xml files (which seems somewhat odd considering that’s precisely what xml was for in the first place), and I had to encode the values to get an extra layer of depth. I learned how to pass ArrayLists of Strings to an intent. Somewhat unrelated to the mobile side of things, but I also learned how to dynamically replace patterns using regular expressions in Java.
]]>By the end of the semester, I would like to have some direction towards writing the following types of apps:
That seems like a good start.
Cheers – Jake
]]>