Retail Therapy

Motivation

The motivation for this app is for all of the times that I go to the store and wonder, "Do I have enough [enter grocery]?". This app is supposed to answer that for you. Simply enter what you buy, when you buy it, and the next time you're at the store open the app. You'll be faced with a list of what items you need and when.


Furthermore, this will notify you if you will soon run out of an item. For example, if you buy trash bags only every few months, you may not realize you are out until you go to replace the liner and there are no remaining trash bags.


Finally, for especially regularly bought items, this makes it exceedingly easy to realize you can set up a subscription. In particular, there are services such as Amazon that offer a discount if you sign up for a subscription service, such as auto-buy food every week.

Views

There are four views here, listed below. Each is a Fragment, stitched together by a navigation pane on the bottom.

  • Shopping List

  • Item Entry

  • Map

  • Settings

Shopping List

This is the view that can be used for viewing items you've bought, when you bought them, and when you'll need more. The list highlights items that you are likely out of, and it is readily viewable when certain items will need to be replaced.

Item Entry

This is where most of the complexity lies. Here, you can enter items that you've bought. This form has several features. For the below, I used suggestion adapters that I wrote for each of three form fields that autocomplete, and I have two text watchers for three items that I validate data in (one is used for two different ones).

Entry Validation

This validates everything you've entered before you're allowed to enter. This includes text watchers on a few of the fields, including the price, which allows you to enter digits and it will automatically format it for you with a dollar sign and two decimal places. The way to type in here is digits typed will push everything left, maintaining everything to its right exactly as it was. This way, starting at $0.00, typing on the right a '4' leads to $0.04. Putting the cursor to the left of the decimal place and typing a '4' will instead make this $4.00.

In this list, you can click on an item. This will display the last time it was purchased and for how much. You can also "quickadd". For example, you may be at the store and bought four more oranges. Simply go to the list, find oranges and quickadd 4. The list will update and re-sort.

Autocomplete

For the fields which may have recurring values, like the store it was bought from, the brand and item name, there is autocomplete. For the item and brand name, the suggestions are simply all items slurped up from the database. For the store, I use the database but I also use the Google Places API to supplement these suggestions with locations nearby. I monitor the current location of the user and set a rectangle nearby to bias the results.

One sorta cool thing is if you autocomplete an item, and the item had a brand (visible in the list), then clicking it will also fill in the brand. I wanted to additionally incorporate barcode scanning, which would be easy to pre-populate everything associated with the barcode. This was another nice-to-have that I ultimately didn't end up doing.

Extrapolation

On entering a new item, I punt the new item to a service I created, the Extrapolator. This takes all items and, on each modification to the list of purchases, recomputes when it will need to be replenished. I set a notification for when you are three days out from needing to replenish something.

Maps

The maps view is underwhelming. Here, I just have a plain map and, upon clicking on a location "Point of Interest", a dialog appears with the place's hours and phone number. The phone number can be clicked to call the place. For this, I use the Google Places API. I wanted to do more with this, because Google exposes business types. I could use that to effectively say "I know you bought XYZ from this store, which according to Google is grocery. That means this other store which is also grocery might have XYZ". Turns out this is somewhat involved, and I didn't end up doing it.

Settings

I ended up saving settings for last because I wasn't sure how many settings there were, and aimed not to "future-proof" -- waste time on features that may or may not be useful. In the end, the only setting that made sense was "how long before you need something do you want a notification?". Because I ran out of time, I didn't implement this.

APIs

Google Maps API

This uses the Google Maps API to view the map.

Google Places API

This uses the Google Places API to get information about places. The two main use cases for this are within the map, to view details about points of interest, and in item entry to autocomplete suggestions of stores upon typing.

Services

Location Manager

I use the location manager to center location on the map as well as to tailor autocomplete suggestions in the item entry phase.

Notification Manager

The notification manager is used to push a notification whenever it's nearing time to replenish something.

Alarm Manager

The alarm manager is used to set a timer for myself for three days before the next item to be replenished comes. If replenishing that item occurs first, then I set a new alarm for the next item to be replaced.

SQLite

Everything that I store that persists is stored in a SQL database. There are five tables: brands, items, purchases, skus and stores. These together track what has been bought and when, and are used to figure out when they will need to be bought again.

For this, I wrote my own wrapper that does a lot of the heavy lifting. This is found in the DbHandler class.

Extrapolator

This is a service I wrote myself. It sits idly in the background waiting for a new purchase item. When that item is purchased, I ship it off to this service, which stores it and then re-computes when the next time you will have to buy that is. If this was also the next item needing to be bought, this computes what item needs to be bought next and sets notification timers accordingly.

General

There are a few common items that I have in a common directory. One is a Constants class, which just serves as a centralized place for constants. I have a utilities class which ended up only implementing permissions checks.

The most interesting thing here is my GenericData class which I use as a container to pass around data in a genericized form. This made inheritance easier and defining then implementing abstract classes ant interfaces.

File Structure

Retail Therapy

  • common - package with common/miscellaneous items

    • Constants.java - constants

    • GenericData.java - implements a generic data class so I can objectify data types. I did this before realizing I could have used Long, Integer, etc to do the same thing.

    • Utilities.java - this mainly just has permissions checks

  • services - services I wrote along with a database helper

    • db - my database wrapper

      • DbHandler.java - the database wrapper that implements various operations done on different tables in the database

      • DbTableBase.java - base class for database tables. Each table is its own class which allowed me to generalize some common functions

      • LastPurchaseInfo.java - retrieves from the database the last purchase. This is what drives the dialog in the shopping list view

      • tables - package with tables in it, that all inherit from DbTableBase

        • Brand.java - brands of items

        • Item.java - an item. Several same items can come from different brands

        • Purchase.java - defines a purchase (store, item, brand, price, etc)

        • SKU.java - this was going to be used with the barcode scanner. A SKU is a very specific item, down to number of units in each package

        • Store.java - a store. This contains mostly results from Google Places, but also manual entries (e.g. Google Places doesn't have anything for online retailers)

    • extrapolate - package with extrapolation service

      • ExtrapolatorService.java - the service used for extrapolating next purchase

      • ReplenishmentInfo.java - basically just a struct that holds the various items for knowing when something will need to be bought again

      • ReplenishmentNotification.java - a convenience class that handles notifications

    • ui - package with all the fragments and UI in it

      • item_entry - package with items used in item entry fragment

        • adapters - package with all the adapters used in it

          • BrandSuggestionAdapter.java - fills autocomplete for brands text view

          • BrandSuggestionItem.java - an item that goes into the brands text view autocomplete list

          • ItemSuggestionAdapter.java - fills autocomplete for item text view, and autofills brand if there's an associated brand on click

          • ItemSuggestionItem.java - an item that goes into the list and is used by the adapter

          • StoreSuggestionAdapter.java - fills autocomplet for store text view. This also interfaces with Google Places for injecting Google's suggestions also

          • StoreSuggestionItem.java - an item that goes into the store suggestions list

        • text_watchers - package with text watchers

          • PriceTextWatcher.java - maintains $#.## format on price

          • QuantityTextWatcher.java - validates quantities and makes sure that the number of items bought and the number of items per pack are not zero

        • ItemEntryFragment.java - the main class that controls the item entry fragment

      • map - package with map fragment and associated items

        • MapFragment.java - has the map and the logic for showing point of interest details

      • settings - package that was supposed to have settings

        • SettingsFragment.java - the main settings fragment class

        • SettingsViewModel.java - populates the only view with "There are no settings"

      • shopping_list - package that contains items in the shopping list view

        • adapters - package with adapters in it for shopping list

          • ReplenishmentItemAdapter.java - the adapter used to populate the list of items that need to be bought soon

        • ShoppingListFragment.java - the logic for the shopping list view

      • MainActivity.java - the main activity. this file is almost completely empty because most logic is spread over the fragments

The next items show using the app. Here I enter the same item twice to show autocomplete.

Here I simply close the app and reopen to demonstrate that data is saved.

Here I add an item and go to the list. Notice the item is red because we still need some. I open a dialog to show previous purchase, and then quickadd more.

Here you see the map, clicking on a store you see the hours. You can click on the phone number and it will call.

Challenges

The main challenges were getting everything to work.

Google APIs don't work on my phone, so I had to get the emulator to work. They also kept failing and I spent 6-8 hours figuring out that the solution was to wipe the phone. I've never made an adapter before and it was especially challenging for 2-line items. Getting the fragments to all talk to each other was challenging. The SQL stuff was also hard because I didn't know what I was doing half the time, and I ran into a hair situation where I was accidentally deleting ID's without the changes cascading, so I was getting a lot of null dereferences.