Skip to content

Help for Developers

Susi Felsen edited this page Mar 13, 2018 · 9 revisions

On this page, you can find some background information about how this app is implemented.

Database

The app uses an SQLite database. The package .database.entities.impl contains all entity classes whose objects can be persisted to the database. All of these classes inherit from the common superclass AbstractPersistentObject, which provides an object ID field. Every entity class has a corresponding database table. The create statement for that table can be found in the field TABLE_CREATE. The class DBService provides methods for storing, updating, retrieving and deleting objects of entity classes.
The app has the following entity classes:

  • User: An instance of this class represents a user, i.e. a person that is using the app. A user is characterized by his first and last name, date of birth and gender. However, none of that information is required (fields might be null).
  • DiaryEntry: Instances of this class represent pain diary entries made by a user. A diary entry is identified by its date. There can only be ony entry per day. It holds information entered by the user such as his condition and additional notes. Every diary entry contains a pain description that encapsulates information about the pain the user is experiencing. Furthermore, a set of drug intakes is associated with each diary entry. Each of these objects encapsulates information about a specific drug taken in by the user (i.e. the quantity taken at different times of day). Since users are not required to enter any information, some of the fields might be null.
  • PainDescription: Instances of this class encapsulate information about the pain a user is experiencing, i.e. the intensity, nature (qualities) and times of the pain, as well as the affected body regions. Since users are not required to enter any information, some of the fields might contain empty sets or be null.
    The enum sets bodyRegions, painQualities and timesOfPain are stored in the database as strings in which the values are separated by commas. The class DBService has private methods to convert an enum set to string and vice versa.
  • DrugIntake: Instances of this class store in which quantity a specific drug is taken in by the user at different times of day. Every drug intake is associated with a diary entry.
  • Drug: Instances of this class represent pharmaceutical drugs. A drug is characterized by its name and dose. There should only be one drug for each combination of name and dose in the database.

Multiple enum classes have been created for reasons of type safety. They are:

  • Gender: Represents a user's gender.
  • Condition: Represents a user's condition, i.e. how he feels at the time of making a diary entry. Every condition has a drawable (image) associated with it.
  • BodyRegion: Represents a region on a person's body. The values of this enum can be divided into two categories: body regions on the front of a person's body and body regions on the back of a person's body. Every body region has a drawable (image) associated with it.
  • PainQuality: Represents the quality of the pain experienced by a user, i.e. how he would characterize the nature of his pain. Every pain quality has a resource id (of the corresponding string resource) associated with it.
  • Time: Represents the time of the pain experienced by a user, i.e. when he feels pain. Every time has a resource id (of the corresponding string resource) associated with it.

Activities

Every activity is responsible for performing one logical task (or a group of logically related tasks), controlling one view. All activities that can be accessed from the navigation drawer inherit from the parent activity BaseActivity. The following activities play an important role:

  • TutorialActivity: Responsible for displaying the tutorial. Ensures that the tutorial is only started at the app's first launch. At the end of the tutorial, the UserDetailsActivity is launched.
  • UserDetailsActivity: Lets the user enter user details (name, date of birth, gender). Stores and retrieves the user object from the database.
  • MainActivity: Responsible for displaying the calendar on the app's main page. Days for which a diary entry exists are highlighted in that calendar. Launches the DiaryEntryActivity if the user wants to create a new diary entry. Also let the user view a diary entry by opening an AlertDialog.
  • DiaryEntryActivity: This is the most complex activity. It lets the user create a new diary entry in seven steps or edit existing diary entries. The field edit indicates whether a new entry is being made or an existing one is being updated. Each of the seven steps has a separate view associated with it. The views are contained in a view pager. When the user selects a view, the data on this view (= slide) has to be set. View X is initialized by calling the method setDataOnSlideX().
    The main difficulty when creating a new diary entry was making the body regions clickable. This was achieved by following this tutorial. Every body region was assigned a different colour:

    This image is displayed in an invisible ImageView in the background of the view. When the user selects a body region, the colour of the pixel of the touch event is compared against the colours of the body regions to determine the body region that was touched. The body regions that were selected are displayed in an ImageView in the foreground of the view. When multiple body regions are selected, a single image to display is computed by overlaying all the images of the selected body regions. The class Helper provides methods to overlay the images.
    Since the user should be able to change the orientation of the screen without all the data he previously entered being lost, an instance of the class RetainedFragment is created in the method onSaveInstanceState(...). It is retained across configuration changes (e.g. screen orientation changes) and contains a diary entry object encapsulating all the data that the user has already entered and which has not yet been stored to the database. The activity's fields are then re-populated with the data from the fragment in the onCreate(...) method.
  • ExportPDFActivity: Lets the user export and/or share all diary entries made within a specific period of time (marked by a start date and an end date). Exporting the diary entries requires the permission "WRITE_EXTERNAL_STORAGE" because the pdf file is written to the device's storage. The pdf document is created by the class PdfCreator. It first prints a header containing the user details (name, date of birth, gender) and then prints the diary entries.
  • SettingsActivity: Lets the user set preferences. Lets the user reset the app and decide whether he or she wants the medication last entered to automatically be added to new entries. The user can also enable daily reminders for a specific time or disable them. When daily reminders are enabled, the reminder notification is scheduled using the class NotificationJobService.

Notifications

The daily reminder notifications have been implemented in a way that is not only compatible but optimized for Android Oreo. The class NotificationHelper configures the daily reminder notification and creates a notification channel in case of Android Oreo. The class NotificationJobService is responsible for scheduling and displaying snoozable daily reminder notifications at the reminder time set by the user. When a notification job is scheduled it is set to be persisted across device reboots. For this to work, the app requires the permission "RECEIVE_BOOT_COMPLETED".

Dependencies

  • Material Calendar View: https://github.com/prolificinteractive/material-calendarview
    Calendar View in which specific days can be highlighted (see classes .activities.MainActivity and .helpers.EventDecorator). Used for the calendar which is used as the main view of the app.
  • Glide: https://github.com/bumptech/glide
    Image caching library. Used for loading a Bitmap image inside the class .activities.DiaryEntryActivity. (This is done to prevent a drawing cache error as a result of a too large size.)
Clone this wiki locally