name: inverse layout: true class: center, middle, inverse --- # The Whole Toolkit Part II Lauren Bricker CSE 340 Spring 2021 --- layout:false [//]: # (Outline Slide) # Today's goals - Cleanup of slide decks (PPS, Whole Toolkit Part I) - Clarifications - Continuing The "whole toolkit" - Recount how we get input from the user (part 1) and produce output for the user (part 2) - How to store application specific data Administrivia - ColorPicker Midpoint (video) Due Thursday 10pm --- # Clarification 1 There were some constructive criticisms to address from the the midquarter evaluation. 1. Learning objectives not clearly laid out - Learning objectives for each day are on the Schedule page - Outline gives finer grained details on what will be in the class. 2. Creative aspects to assignments (like 154) - We try to have a creative/design piece for each assignment - Goal for next year: having a creative final project in this class 3. Time management - - This is something readily admit I am working on and will try to be better about 4. Include activity instructions on a slide - Great suggestion! We'll try to remember this. --- # Clarification 2 From last time: Which of the following does NOT generate an event? A. Pressing the CTRL key
B. Pressing the A key
C. Moving your finger on the phone screen
D. Clicking a mouse button (on a computer) A subtlety this question does not capture: >Each key press is described by a sequence of key events. A key press starts with a key event with ACTION_DOWN. If the key is held sufficiently long that it repeats, then the initial down is followed additional key events with ACTION_DOWN and a non-zero value for getRepeatCount(). The last key event is a ACTION_UP for the key up. .footnote[From the Android [KeyEvent](https://developer.android.com/reference/android/view/KeyEvent) page] --- # The Whole Toolkit Architecture - Input - Input models (events) - Event dispatch - Event handling (state machine) - Callbacks to application - Output - Interactor Hierarchy design & use - Drawing models (`onDraw()`) - Layout (`onLayout()` or `XML`) - **Damage and redraw process** - Storage - Bundles - Shared Preferences --- # Interactor Hierarchy .left-column40[
graph TD A[0: Drawing Canvas] --> B(1: CircleA, Green) A --> C(2: LineB, Orange) B --> D(3: LineA, Black) C --> E(4: PointA, Blue) C --> F(5: PointB, Black)
 ] --- .left-column-half[ # Core Toolkit Architecture `damage` is a bit kept on each view. If state changes, `damage` becomes true If there is `damage` do - *layout* (may change) - position - size - *redraw* ] .right-column40[
graph TD A[0: Drawing Canvas] --> B(1: CircleA, Green) A --> C(2: LineB, Orange) B --> D(3: LineA, Black) C --> E(4: PointA, Blue) C --> F(5: PointB, Black) classDef dirty fill:#ff9999,color:#ffffff; class A,B,D dirty
 ] --- .left-column-half[ # Core Toolkit Architecture `damage` is a bit kept on each view. If state changes, `damage` becomes true If there is `damage` do - *layout* (may change) - position - size - *redraw* ] .right-column-half[ # Component Developer - May need to implement `onMeasure()` and `onLayout()` (if a container) - Will always implement `onDraw()` but *never call it* (call `invalidate()` instead) ] --- # View Update: .red[Damage/Redraw] How does the toolkit know what to redraw? What causes damage? ??? concrete example on next slide --- # View Update: .red[Damage/Redraw] What should be redrawn?  --- # View Update: .red[Damage/Redraw] What should be redrawn?  --- # View Update: .red[Damage/Redraw] What should be redrawn?  --- # View Update: .red[Damage/Redraw] How does the toolkit know what to redraw? What causes damage? -- - Window hidden and re-exposed - Resizing - Redrawing - Reacting to system or other events - others??? --- ## View Update: .red[Damage/Redraw] Naive approach to redraw  -- count: false - Can be slow (redrawing everything unecessary) - Can cause flickering - double buffering is better, - hence the 'Canvas' abstraction or equivalent - can then switch which FB is displayed (very fast) ??? TODO ADD pic like this using divs? --- ## View Update: .red[Damage/Redraw] Naive approach to redraw - redraw the entire screen  More efficient: calculate only the region necessary  --- ## View Update: .red[Damage/Redraw] Naive approach to redraw - redraw the entire screen  More efficient: calculate only the region necessary  **Never** just draw: Why not? -- - Update *state* - Report *damage* (by calling 'repaint()) - Wait for *toolkit to request redraw* (also works if damage comes from elsewhere) - How does it generalize to any cause of damage (always need state!!) --- ## View Update: .red[Damage/Redraw] How does the toolkit know what to redraw? - Let the component report: Damage/Redraw invoked by `invalidate()` or equivalent --- ## View Update: .red[Damage/Redraw] How does the toolkit know what to redraw? - Let the component report (`invalidate()`) **NOTE** we are *not* calling *onDraw()* directly (important for your assignment) - Aggregate - Usually calculate bounding box --- ## View Update: .red[Draw/Redraw] Virtual device abstraction provided by windowing system Component abstraction provided by toolkit - Enforced using clipping - Supported using coordinate system transformations Drawing is recursive - Makes it possible for parent to *decorate* kids - Parent responsible for making kids think they are the center of the universe (translate) - Clipping: intersect parent and child, also handled by parent ??? Allows each program to (mostly) pretend that it has the screen (frame buffer) to itself Allows each component to (mostly) pretend that it has the screen to itself --- # Core Toolkit Architecture If damage do - *layout* (may change) - position - size - do *redraw* - Union of damage (any of those can cause it) used to trigger redraw of anything inside that union - Drawing + clipping – standard drawing order, but only for things damaged; clipped to damage region - Clear damage --- # The Whole Toolkit Architecture - Input - Input models (events) - Event dispatch - Event handling (state machine) - Callbacks to application - Output - Interactor Hierarchy design & use - Drawing models (`onDraw()`) - Layout (`onLayout()` or `XML`) - Damage and redraw process - **Storage** - Bundles - Shared Preferences --- # So you got lots of apps.. And they all want to use _tons_ of memory, but they don't **need** that memory all the time. - Android: You get no/minimal memory when you're not actively being used. - Apps: But then how do we remember stuff when we are being used if we can't save it in memory - Android: Use this `bundle` _(In truth the app could write/read its state to disk whenever it is being closed/opened but that is time consuming and would delay the OS launching new things)_ --- # What is a Bundle? - First what happens when the user closes the application? Does it die? - **No!** --- # What is a Bundle? - First what happens when the user closes the application? Does it die? **No** - Where does it go then? - Think about it as **hibernation** - All of it's memory is cleared, so all of your variables are _GONE_ 😲. - **But** Android lets you save some variables to a `bundle` right before your memory is cleared, and gives you your `bundle` back when you get the memory space back. [Bundle](https://developer.android.com/reference/android/os/Bundle) documentation --- # What is a Bundle? - First what happens when the user closes the application? Does it die? **No** - Where does it go then? **Stored by Android OS** - What's in the bundle? - You decide, entirely up to the application developer (you). - Values are mapped to unique KEYS (for set/retrieve) - Bundle is destroyed if the user **force quits** the app/phone is turned off. --- # Bundles & Code - Bundle management is occuring at the `activity` layer, not to be confused with any individual `view`. - When the user closes the app, right before it closes and its memory is cleared, Android invokes `onSaveInstanceState(Bundle outState)` on the activity, providing a reference to the activity for the app to save values into. - Then when the user opens the app again, Android invokes `onRestoreInstanceState(Bundle savedInstanceState)` returning the same bundle from earlier. - You can think of the `bundle` as a `Map
` that only supports certain types of objects (they must be `Serializable` which includes all primitives and `String`) _(Read more [here](https://developer.android.com/topic/libraries/architecture/saving-states))_ --- # Bundles: Code Example ```java public abstract class MainActivity extends AppCompatActivity { @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("OUR_KEY", "bundles? bundles!!!"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); String whatWeSaved = savedInstanceState.getString("OUR_KEY"); // whatWeSaved would contain "bundles? bundles!!!" } } ``` --- # Bundle in Accessibility Bundles can be used to comunicate when switching activities. Example in Ask For Help - in `ChooseRequestsActivity` ```java // Instance variable declaration private Bundle mMessageInfo; // Instance variable initialization (in setVariables called by onCreate) mMessageInfo = new Bundle(); // Setting key/value pairs mMessageInfo.putString("last_button_pressed", "none"); // Switching activities Intent intent = new Intent(this, ChooseContactGroupsActivity.class); mMessageInfo.remove("last_button_pressed"); String request = ((TextView)view).getText().toString(); mMessageInfo.putString("last_button_pressed", request); // Attaching the bundle to the intent intent.putExtras(mMessageInfo); startActivity(intent); ``` --- # Bundle in Accessibility Bundles can be used to comunicate when switching activities. Example in Ask For Help - in `ChooseContactsActivity` ```java // Instance variable declaration private Bundle mMessageInfo; // Instance variable initialization (in setVariables called by onCreate) mMessageInfo = getIntent().getExtras(); // Add things to the bundle in addNumbersToBundle and textPeople mMessageInfo.putString("contact" + (ii + 1), contactsAsString); // Get information from the bundle String textBody = mMessageInfo.getString("last_button_pressed"); String numbers = "smsto:" + mMessageInfo.getString("contact" + contactID); ``` --- # Shared Preferences - Bundle data is transient - it will go away if the app is completely unloaded from memory. - Typically small amounts of data. - Used to share information between activities. - Shared preferences are saved in internal storage space on the phone. - Also stored in Key Value pairs [SharedPreferences](https://developer.android.com/reference/android/content/SharedPreferences) documentation --- # Shared Preferences To see what is in your shared preferences 1. start the Device File Explorer (View->Tool windows->Device File Explorer) 2. Use the toggles to open up the following folders ```` data data
for your app, such as cse340.askforhelp shared_prefs
.PREFERENCES.xml ``` --- # Shared Preferences Creating the Shared Preferences File ```java protected SharedPreferences getPrefs() { if ( mSharedPreferences == null ) { try { Context context = getApplicationContext(); mSharedPreferences = context.getSharedPreferences(context.getPackageName() + ".PREFERENCES", Context.MODE_PRIVATE); } catch (Exception e) { //failed to edit shared preferences file showToast(R.string.shared_pref_error); } } return mSharedPreferences; } ``` --- # Shared Preferences Saving to/restoring from the Shared Preferences file. ```java // setting data SharedPreferences.Editor editor = getPrefs().edit(); editor.putBoolean("mLocationOn", true); editor.putInt("group_selected", mWhichGroup); editor.putString("contactGroup" + (ii + 1), contactGroup); editor.putStringSet("contact_numbers" + (ii + 1), mNumbers.get(ii)); editor.apply(); // have to force the write. // getting data, the last parameter is the default. mLocationOn = getPrefs().getBoolean("mLocationOn", true); mWhichGroup = getPrefs().getInt("group_selected", 1); String contactGroupName = getPrefs().getString("contactGroup" + (ii + 1), ""); Set
ids = getPrefs().getStringSet("contact_ids" + (ii + 1), ``` --- # END OF DECK