This is the Spring 2021 final website.

as6: Undo

Last revised: 23 May, 2021
Assigned:
  • 26 May, 2021
Due:
  • Code and Video
    Due 3-Jun, 10:00pm
    Lock 5-Jun, 10:00pm
  • Reflection
    Due 7-Jun, 10:00pm
    Lock 8-Jun, 10:00pm

Android Goals:

  • Be able to understand and modify an existing user interface
  • Learn about floating action buttons
  • Implement core data structure for Undo

HCI Goals:

  • Modify and existing app in a consistent fashion
  • Make your modifications accessible
  • Make your modifications usable

GitGrade links

Overview of assignment

Demo of our solution (with no extra features):

Explanation of Codebase

This is one of the more complex programs we are giving you in terms of code. Moreover the starter code is already has a couple of fully functional features that you will be modifying as part of this assignment.

The initial interactor hierarchy at instantiation is shown below (shown at the side is a legend for the visibility status of different interactors). Hidden means on screen and drawn but hidden behind something else.

The Floating Action Button (FAB) subtrees are the menus at the top of the screen (for undo and redo) and bottom (for color and thickness), made up of one or more floating action buttons. The DrawingView is the place where drawing takes place. Each new stroke is saved as a separate, new StrokeView added to the DrawingView.

graph TD M[ReversibleDrawingActivity] --> D[DrawingView] M --> FUndo[FAB:Undo] M --> FRedo[FAB:Redo] M --> FColor[FAB:Color] M --> FThick[FAB:Thickness] FColor --> Red[Red] FColor --> Green[Green] FColor --> Blue[Blue] FThick --> Thin[Thin] FThick --> Med[Med] FThick --> Thick[Thick] Vis[Visible] --> In[Invisible] In --> Hid[Hidden] classDef normal fill:#e6f3ff,stroke:#333,stroke-width:2px; classDef start fill:#d1e0e0,stroke:#333,stroke-width:4px; class M,D,FColor,FThick,Vis start class Red,Green,Blue,Thin,Med,Thick,Hid normal

When the user draws on screen (by clicking and dragging inside the DrawingView, this adds a new StrokeView to the interface. Notice that the Undo button is now visible instead of invisible because there is an action to undo.

graph TD M[ReversibleDrawingActivity] --> D[DrawingView] D --> Stroke1[StrokeView] M --> FUndo[FAB:Undo] M --> FRedo[FAB:Redo] M --> FColor[FAB:Color] M --> FThick[FAB:Thickness] FColor --> Red[Red] FColor --> Green[Green] FColor --> Blue[Blue] FThick --> Thin[Thin] FThick --> Med[Med] FThick --> Thick[Thick] Vis[Visible] --> In[Invisible] In --> Hid[Hidden] classDef normal fill:#e6f3ff,stroke:#333,stroke-width:2px; classDef start fill:#d1e0e0,stroke:#333,stroke-width:4px; class M,D,Stroke1,FUndo,FColor,FThick,Vis start class Red,Green,Blue,Thin,Med,Thick,Hid normal

The sequence in the interface:

Empty drawing program window Drawing program window with one red stroke and undo button visible

You can play around with the interface to change color and thickness. Each new stroke you add adds another StrokeView to the interface.

Codebase Structure

This is a complete codebase for a drawing program. It is designed to be as modular as possible and includes support for Command Objects which encapsulate changes to the application model.

Actions

Actions are Command Objects, which encapsulate changes to the application model. An AbstractAction has a single method, doAction(view) which, when called, will apply the action to the view. Our provided implementation of AbstractAction#doAction is incomplete, it is overridden it in the concrete subclasses, such as ChangeColorAction, ChangeThicknessAction, and the subclass you will create to support your new feature (see Part 5).

AbstractReversibleAction extends AbstractAction to add undoAction(view) which, when called, reverses the action.

classDiagram AbstractAction <|.. AbstractReversibleAction AbstractReversibleAction <|.. ChangeColorAction AbstractReversibleAction <|.. ChangeThicknessAction AbstractReversibleAction <|.. AbstractReversibleViewAction AbstractReversibleViewAction <|.. StrokeAction class AbstractAction { doAction() } class AbstractReversibleAction { +boolean done +undoAction } class AbstractReversibleViewAction { +invalidate }

As with events, AbstractActions are part of an inheritance hierarchy. AbstractReversibleAction has three subclasses – ChangeThicknessAction, ChangeColorAction and StrokeAction. All of them modify properties of the DrawingView class. ChangeThicknessAction modifies the stroke width, ChangeColorAction will modify current color of the stroke, and the , and StrokeAction.represents the creation of a child view object that encapsulates a painted stroke (a StrokeView that is added to the DrawingView).

Application Code (/app)

We’ve mentioned a DrawingView (which is the main canvas for the drawing application) and StrokeView (which encapsulates a specific stroke for the drawing application).

classDiagram AbstractDrawingActivity <|.. AbstractReversibleDrawingActivity AbstractReversibleDrawingActivity <|.. ReversibleDrawingActivity class AbstractDrawingActivity { DrawingView: "Canvas the user draws on" addMenu() addCollapsableMenu() doAction() } class AbstractReversibleDrawingActivity { +AbstractHistory +doAction() +undo() +redo() } class ReversibleDrawingActivity { +onAction() +onColorMenuSelected() +onThicknessMenuSelected() } class StrokeView { Path onDraw() }
stateDiagram-v2 [*] --> DRAWING: Press/
onDrawStart() DRAWING --> [*]: Cancel/
onDrawCancel() DRAWING --> [*]: Up/
onDrawEnd() DRAWING --> DRAWING: Move/
onDrawMove()

Parts 1-5: Programming requirements

There are five parts for the programming portion of this assignment:

You can also (optionally) work on improving the usability

Part 1: Implement ChangeThicknessAction

In order to familiarize yourself with Action objects and reversible logic, implement ChangeThicknessAction. This will be very similar to ChangeColorAction, so first read and understand that code before implementing the TODO items in ChangeThicknessAction.

Part 2: History

Actions are the objects that are used in the history. An AbstractHistory simply allows an AbstractReversibleAction to be added and supports undo() and redo(). We subclass this with a stack-based history class called StackHistory to support undo() and redo(). You will implement the methods to support these features in this StackHistory class.

A StackHistory has a capacity (a max number of actions that it can store), a undoStack (the history) and a redoStack (actions that have been undone and can be re-applied). It also supports specific capabilities you must implement (see comments in the code for specifically what to do):

classDiagram AbstractHistory <|.. StackHistory class AbstractHistory { addAction(AbstractReversibleAction action) undo() redo() canUndo() canRedo() } class StackHistory { +capacity: "Max stack size" }

Related APIs

Deque

Undo/Redo behavior

Here is an example where the user performs a set of operations in the Undo app, and the state of the Undo Stack, Redo Stack and Interface after each operation. In this scenario we have denoted the user’s operations as:

(1) draw a stroke in the default color/thickness
(2) change the color
(3) change the thickness
(4) draw another stroke in the original thickness and color

with various undos and redos mixed in.

Action Undo Stack Redo Stack Interface state
drawstroke (1) 1   1
change color (2) 1,2   1,2
undo 1 2 1
redo 1, 2   1, 2
change thickness (3) 1, 2, 3   1, 2, 3
undo 1, 2 3 1, 2
undo 1 3, 2 1
drawstroke (4) 1, 4 CLEARED 1, 4
undo 1 4 1

Part 3: Adding a thickness 0 FAB to the thickness menu

There are two main things you will need to do to add a “thickness 0” FAB button to the thickness menu.

First, find the place in thickness_menu.xml to modify. For example, this is the XML in that file for the thickest FAB Action Button:

<android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_thickness_30"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center"
        android:layout_marginBottom="@dimen/fab_label_margin"
        android:alpha="0"
        android:clickable="false"
        android:contentDescription="@string/thickness_desc"
        app:fabSize="mini"
        app:srcCompat="@drawable/ic_thickness_30" />

You must use the @string notation for your android:contentDescription, meaning you’ll need to add a string definition to values/strings.xml.

Next, you will need to update onThicknessMenuSelected to respond when your FAB is pressed. It must change the stroke width to 0.

Finally, you will need to make sure that ReversibleDrawingActivity is updated to account for your new 0 thickness menu item. You will need to look for where the code or comments mention thickness and change it to include this new item.

Part 4: Integrating color picker

In addition to adding a thickness, we want to be able to draw strokes of any color instead of being limited to 3. You may recall that we have already made a custom interactor that allows users to choose any color on the color spectrum in a previous assignment. It is time to wire up a ColorPickerView into this application! To do this, you’ll need to:

  1. Decide which color picker you will use. If you made a MyColorPickerView that has at least 10 colors, you may use it. Otherwise use the CircleColorPickerView.

  2. Copy over the colorpicker files (we have already provided the stub for AbstractColorPickerView for you) from your color picker assignment. Put them in the same directory as your ReversibleDrawingActivity.java file. Also be sure to change the package at the top from cse340.colorpicker to cse340.undo.app in your ColorPickerView.java and CircleColorPickerView.java or MyColorPickerView.java files.

    (note: If the compiler complains that it can’t find ColorInt, replace import androidx.annotation.ColorInt; with import android.support.annotation.ColorInt and import android.support.v4.graphics.ColorUtils; instead of androidx.core.graphics.ColorUtils;.)

  3. Add your color picker to your layout. One way to do this is to add xml for your colorpicker to drawing_activity.xml. Be sure it is neither visible nor focusable until the user clicks on the color FAB. Also be sure to change the package name here too.

  4. Change the behavior of the color FAB so that, when clicked, it opens up the color picking interface instead of opening the collapsible color menu. For reference on how to do this, take a look at ReversibleDrawingActivity.java#onCreate (hint: make sure to take a look at what interfaces ReversibleDrawingActivity.java implements).

  5. Ensure that the location of your colorpicker, when visible, is not impeded by any of the FAB buttons on the screen as those buttons can impact your user’s interaction with the colorpicker. You may choose to solve this problem however you want. One quick solution is to set the Y location of the mColorPickerView in ReversibleDrawingActivity to the R.dimen.color_picker_y_offset value read in from dimens.xml. (Feel free to change this value in dimens.xml as needed). Remember, to get the value you must use getResources().getDimension(R.dimen.color_picker_y_offset).

When users have the color picker interactor open, they must not be able to access any other FABs (undo, redo, thickness change) so be sure to disable them when you show the color picker and visually indicate to the user that they are disabled. This behavior should be very similar to the what happens when you try to change the thickness using the collapsible menu. If a color change is undone or redone, the color picker must reflect the correct color. Once you set up all the listeners to respond correctly, your color picker interactor must allow users to pick any stroke color they want!

NOTE: You do not need to use bundler to change the starting color or to store your model for this assignment.

Related APIs: View#using-views

Part 5: Improve the application

Create an interesting way the user can interact with the application that can be undone and redone. This means that whatever interaction you add must have a custom undo and redo function (it might help to take a look at AbstractReversibleAction#doAction and AbstractReversibleAction#undoAction(DrawingView)).

Whatever you choose to implement, you will be describing how the user interacts with your addition in your reflection and justifying the design of this new feature using Application Design Principles we discussed in class.

In addition, make sure that the action you add is accessible.

Below is a video of an app with the a new Clear Screen and other features.

Other example videos on this playlist

Optional addition: Improving usability

Try to identify at least one usability problem and address it (optional). As with adding a feature, there are several options here. Here are some examples of things we think are usability issues. You may not agree, if you choose to do this, you should focus on something you think is a usability issue.

Whatever problems you address, please briefly describe the problems and solutions in your reflection.

Part 6: Video

To facilitate the online-only nature of this quarter, you will need to make a video of your final solution. To create a video, click on the … at the bottom of the panel to the right of your emulator, then select “screen record” and “start recording” (see images below).

Empty drawing program window Drawing program window with one red stroke and undo button visible

In addition, you should follow these instructions to make sure that clicks are visible:

Your video should have the following sequence which is to demonstrate undoing and redoing actions for changing thickness, color, and your new feature. Please DO NOT try to create a perfect version with no mistakes! You will not lose points for any usability issues or mistakes (unless your code isn’t working, which will not be affected by this video). You will get much better feedback if your video isn’t perfect!

Finish as you will (e.g. demonstrate optional usability improvement(s)).

Part 7: Reflection

For this part, you will submit your reflection on this assignment to Gradescope. Create a MS Word, Google or other type of document and copy the following questions (in italics below) into that document. Add your responses below each question. You can have more than one answer per page, but if you can, please try to avoid page breaks in the middle of a question. Insert page breaks between questions as needed.

  1. Describe the extra action you added to the Undo application.
    • What was the goal of this action?
    • Describe how a user interacts with it and include a screen shots illustrating its use.
    • Justify the design of your action using the Application Design Principles we discussed in class.
    • Describe how you made your new feature accessible.
    • If you made any additional usability improvements, document what you did and why here.
  2. The Undo app as it is written is not completely accessible. Using accessibility tools such as TalkBack and the Accessibility Scanner, find and describe at least two accessibility issues that exist in the Undo App. For each of the problems you listed, detail a possible solution that will make the app more accessible.

  3. How would you add context-aware computing in your Undo drawing app?
    a. Pick one of the types of sensing-based apps (capture and access; adaptive services; novel interaction; behavioral imaging; data collection & response) and discuss how it could enhance the app.
    b. Describe a case where your new context-aware feature could be problematic using the issues we discussed in the Sensing/ML lecture.

  4. Thinking about CSE 340 as a whole:
    • What do you feel was the most valuable thing you learned in CSE 340 that will help you beyond this class, and why?
    • If you could go back and give yourself 2-3 pieces of advice at the beginning of the class, what would you say and why? (Alternatively what 2-3 pieces of advice would you give to future students who take CSE 340 and why?)
  5. Acknowledgements: Cite anything (website or other resource) or anyone that assisted you in creating your solution to this assignment. Remember to include all online resources (other than information learned in lecture or section and android documentation) such as Stack Overflow, other blogs, students in this class, or TAs and instructors who helped you during OH

Turn-in

Submission Instructions

You will turn in the following files via GitGrade. It will accept:

You will turn in your video to Canvas:

You will turn in your reflection on Gradescope

Grading

This HW will be out of 50 points and will roughly (subject to small adjustments) be distributed as:

Implementation (30 pts)