Jon Rumsey

An online markdown blog and knowledge repository.


Project maintained by nojronatron Hosted on GitHub Pages — Theme by mattgraham

Week Eight Discussions Log

Tuesday 5-July

Monday was a holiday.

What full-stack things we've done in class so far:

Tech Interview Scoring Advice

Technical Interview scoring:

Interpreting the Question:

Solved the Technical Problem:

Analyze the Proposed Solution:

Communicated Effectively Throughout:

Keep In Mind:

Deploying Your App To GooglePlay

Inclue images. Don't have to be exactly right but get the idea behind how the app operates.

Screenshots: Include screenshots of your app "in action".

Be aware that your app will get rated by users, bots, etc.

Updated On: Be realistic but keep it regular. A stale app might not look too good to some users.

Starting an App as "free" cannot easily be changed to "paid" later. Consider starting the app as "Paid" to make transition to "Free" easier.

Android App Bundle: AAB file extension. app-debug.aab: Drag this file into the App Bundles upload screen.

MUST create a signing key and sign the bundle in order for GooglePlay to allow publishing to the store.

Generate a key in the "Generate Signed Bundle or APK" modal.

Multiple AAB files can be uploaded to the Release workflow.

Release Name will get updated when you upload your signed AAB.

Release Notes should be updated though.

Always update documentation when content in the App changes.

There is a Set Up Your App workflow that you should follow:

Main Store Listing:

Testing:

In Summary:

  1. Set aside several hours to get this all organized.
  2. Set up your app must be completed first.
  3. Set up the App as free, or later on a Payments Profile can be added to monetize the App.
  4. Release your app.

Google Play Store will tell you when things are wrong or not ready for publication.

Using Deletes with GraphQL and Amplify

CUD: Create, Update, Delete. Part of CRUD. Query: The READ part of CRUD.

Delete Process:

  1. Send instance details using Intent Extras.
  2. Get the ID of the item you want to delete.
  3. Query the DB to get the instance by ID.
  4. Within the response lambda: LOG, then Amplify.API.mutate() and see code below for inner detail.
// setup a listener, create an Amplify API Query, get the data, then handle delete within a nested .response() lambda
deleteButton.setOnClickListener(view -> {
  Amplify.API.query(
    ModelQuery.get(TaskModel.class, finalTaskId),
    response -> {
      Log.i(TAG, "message");
      Amplify.API.mutate(
        ModelMutation.delete(response.getData()),
        success -> Log.i(TAG, "message"),
        error -> Log.e(TAG, "message")
      );
    },
    error -> Log.e(TAG, "message")
  );
  finish();
})

Lab Goals Today

  1. Register for GooglePlay Developer account.
  2. Ensure the TaskMaster App can read & write to Amplify w/ GraphQL.

Code Challenge Class 34

  1. Find a peer and work through the whiteboard.
  2. Work through the rubrik.

Career Coaching Workshop: Presentation Prep

  1. Begin working through the presentation workshop slides.
  2. 7 slides + Title + Final side.

Career Accellerator Update

Next Saturday will feature assignments that should be completed if interested in joining CAP.

Post-Interview Debrief

Areas where I need to improve:

Remember Big(O) in time:

Trees:

Weds 6-July

Authentication Considerations In General

Going to need the following:

Great functionality to have:

Amplify and Authentication

OAuth is only supported in JS (for "social sign-in"), not for AndroidStudio.

Amazon Cognito

Identity Pools are free of charge: Sign-up, login, logout, and verification.

IAM Authenticates Developers that manage data, processes, etc.

Cognito Authenticates END USERS.

Cognito can also be used for Authorization.

User Pools:

Cognito will be another AWS "Category: API".

Check out my getting started with Cognito reading notes

Steps

  1. amplify add auth
  2. Use Email to signing.
  3. Advanced settings? No (for now).
  4. amplify push: Builds local backend resources and provisions in the cloud.
  5. amplify publish: Builds local backedn and frontend resources and prvosions.
  6. Implement Amplify addplugin statements to your code (entrypoint? HomeActivity?) onCreate() to get ref on AWSCognito.
  7. Implement Cognito registration statement(s) to your code (right after the previous step code).

Remember: Intents are used to move user to a new Activity (see my notes on Component Activation).

Note: Amplify.Auth has a bunch of built-in methods. EXPLORE THESE, as some of them allow 3rd party auth methods like facebook, etc.

Remember: When dealing with Amplify you must use a builder e.g. AuthSignUpOptions.builder(), and builders use chained-methods to configure e.g. AuthUserAttributeKey.email(). End with .build(), a comma, then success -> {log}, failure -> {log} sub-block.

Cognito AWS Web UI

Go here to see the users that have registered.

Displays details about registered users.

Password details, hashes, etc, is not easily available.

Login experience can be configured here as well.

How To Get Nickname To Display

Use Fetch User Attributes.

Can add this to onResume lifecycle method.

public void fetchUserDetails() {
  // remember: this is running in a single thread so UI update requires special handling
  String nickname = ""; // this could be a global variable
  Amplify.Auth.fetchUserAttributes( // note: .fetchAuthSession could be used in similar way but not for this
    success -> {
      // log result of fetching user attribs
      for (AuthUserAttribute authAttrib: success) {
        if (authAttrib.getKey().getKeyString().equals("nickname")) {
          String userNickname = authAttrib.getValue();
            runOnUiThread(() -> {
              ((TextView)findViewById(R.id.id_of_view)).setText(userNickname); // casting required if not grabbed earlier
            });
        }
      }
    },
    failure -> { // log this failure state with info }
  )
}

Functionality: If email address is 'verified' is a good place to implement authorization within the App.

Implementing Login and Logout Buttons

  1. Create method called something like handleSignIn.
  2. Implement Amplify.AUTH code in it.
  3. Call handleSignIn from onCreate.

Handle Conditional Rendering

  1. Create a new method called setupLoginLogoutButtons or something similar.
  2. Consider whether to call setupLoginLogoutButtons at onCreate and/or onResume lifecycle method(s).
  3. Instantiate a new authUser using Amplify.Auth.getCurrentUser();
  4. If authUser is null get a ref to the Login Button and set its visibility to visible e.g. View.VISIBLE.
  5. Same for the logout button BUT View.INVISIBLE.
  6. If authUser is NOT null, get a ref to the Login Button and make it View.INVISIBLE.
  7. Same for the logout button BUT VIew.VISIBLE.

Conditional Rendering Activities

  1. Perhaps implement a specific Registration and Login Activity(ies).
  2. Do an authentication check before executing setContentView(R.layout.activity_name).

Testing

Espresso Tests are optional but encouraged.

Unittests are not required, but encouraged.

Do manual testing though, to verify authentication is operational.

Logging

Check System logging files in /var/log!

App logs will probably be in the installation directory of the App itself.

Advice:

Thursday 7July Notes

S3 Storage

Simple Storage Service.

Buckets of snakes (objects representing data).

DynamoDB: AWS's noSQL DB with relational capabilities overlayed.

S3 By Comparison:

Three out of Four steps to show an image are ASYNCHRONOUS:

  1. Select an image.
  2. Upload to S3.
  3. Link to Data.
  4. Display Image.

JS uses Promises and async/await.

Java uses CompletableFuture which is a more complete solution that Promises.

Uses .complete() a CompletableFuture otherwise an async call will remain incomplete in the Stack.

There is a lot of boilerplate code to write to get this working so modularize a lot.

LL and Tree Traversals Review

Linked List

Single LL Traversal:

Trees

Binary Tree vs. Binary Search Tree

BST:

Technical Interviewing Advice

It is okay to talk about an iteration / traversal without addressing the problem domain logic when depicting how the solution will work.

However, the logic that occurs inside the looping structure ("under the while" in the depiction model) must also be visualized.

It is okay to ask the interviewer if you can use a library object to utilize Queue or Stack. If they ask what you need then tell them the specific methods and functionality you are looking for.

If unable to use a Library-based Queue or Stack, then use recursion.

Ask the interviewer how the data is coming in to the algorithm to determine whether balancing is necessary.

Consider initialize the value of Root/Head as the comparison value.

Find a root by thinking about Preorder, Inorder, and Postorder: Beginning, Middle, or End of an array (in the end).

DFS vs BFS

DFS: Depth First Search

Preorder: Logic FIRST, children LAST.

Inorder: Left child FIRST, Logic SECOND, Right child LAST.

Postorder: Children FIRST, Logic LAST.

Recusion

Types:

While whiteboarding, whenever a recursive function calls itself, put the calling method on the Stack (represent with a value if possible).

Android Image Picking Actvity

Intents can be used to do more than just navigate between Activities.

An ImageSelector can be launched using an Intent.

Android ImagePicker has an onTap functionality.

Develop an impagePickerResultLauncher that is triggered by onActivityResult event handler lambda.

OnActivityResult should:

uploadInputStreamToS3(InputStream) should:

  1. Upload the image to S3 AND capture the S3 Bucket Key.
  2. Store the S3 Bucket Key by calling another custom method saveProductKey(String s3Key).

saveProductKey(String) should:

  1. Store key to DynamoDB.

Flow Summary:

PRoject Goal: Grab the image, stream it to S3, then display it on the local device.

Android Intents

When using Intents to select an item and launch a new Activity:

  1. Get the KEY of the item that was selected. This could require a completable future call to a distant DB.
  2. Set up the Activity to be called to collect and use the Key via Intent Extra. A CompletableFuture call might be necessary to call a distant DB to hydrate the object instance using the Key.
  3. Just pass-in the Key as an Intent Extra when setting up and calling the Intent to load the next Activity.

Use Intent.ACTION_GET_CONTENT: ??

Using ActivityResultLauncher<Intent>:

Using Intent.EXTRA_MIME_TYPES:

Activity Result:

A Quick Note About GraphQL Schema To Support S3

For the Labs a change will be made that will associate an S3 Bucket storage file with a Task. In order to support this in the TaskMaster app, a GraphQL Schema change is required.

In my case, I added imagepath: String to the Task Entity.

This requires an update to the GraphQL schema on the back-end:

amplify push
# continue? Yes
# update code for your updated GraphQL API? Yes
# Generate GraphQL statements (Queries, Mutations, Subscriptions) based on schema types (this will overwrite existing)? Yes

Note: Schema changes might not be immediately visible in the console and could require adding a new item (or edit an existing one) before the column shows.

Applying S3 Storage to Android Apps

amplify status

amplify add storage

Respond:

amplify push

Insert S3 dependencies ABOVE AuthCongnito in App:build.gradle.

Sync Gradle files.

Add the AWSS3StoragePlugin() into your Entrypoint Activity.

Upload A File To Bucket

Create a new method to perform a file upload.

Utilize:

Init a test filename using String filename and File Type.

Try-Catch: Buffered Writer to append strings to a new file and be sure to .close() the buffer.

S3 expects a key aka the file reference:

Implement Amplify.Storage:

Amplify.Storage.uploadFile arguments are: String key, File local, Consumer<StorageUploadFileResult> onSuccess, Consumer<StorageException> onError

Amplify.Storage.uploadFile(
// no NULLs are allowed
  testFileS3Key,
  testFile,
  success -> Log.i(TAG, SUCCESS_MESSAGE),
  failure -> Log.e(TAG. FAILURE_MESSAGE)
);

S3 Amplify Security

Amplify will be allowed to do the things that are configured when implementing it in your project.

This means the "Amplify App" has S3 access, whether or not Cognito has authenticated a user of your App.

It is up to the developer to implement user-properties and access logic along with IAM and the S3 File Key to enforce access permissions / authorization.

Download A File From Bucket

Note: Lab37 preview and work will be on Monday the 11th (appended below).

Mondy 11 July Continuation Notes

Overview of methods:

  1. Activity Result Launcher opens local file as an InputStream, calls UploadInputStream method.
  2. UploadInputStreamToS3 using InputStream filedata then calls SaveProduct using String S3Key.
  3. SaveProduct using String S3Key.

In Alex's demo, method 1 called method 2, method 2 called method 3.

Remember: Android Activity Lifecycle must be utilize properly to get S3 interoperation working properly. For example, OnResume is most commonly used, but OnCreate and OnClear? are also used to initialize/set and clean-up before and after using the App.

CompletableFuture must be:

ImagePicker:

Allow/Deny Lists:

ActivityResultLauncher:

Intents:

Success:

S3 Storage Key:

Whenever calling on AWS Storage Services:

File Access Advice Within a Success Callback Function:

Image Viewer:

If your Amplify Key expires (these steps need validation, I probably wrote them down incorrectly):

Saving an item:

  1. Setup a collection of objects to be stored.
  2. Grab the item(s) from the UI Element that has the Field data.
  3. Implement a try-catch upon a CompletableFuture of a List of the Class that needs to be stored.
  4. In the try block call the UI element to get the value(s).
  5. In the catch block(s) track for InterruptedException and Exceution Exception.
  6. Build an item to save and utilize the output of completableFUture.get() and .stream().filter(item -> item.getField().equals(comparison_item)).findAny().orElseThrow(RuntimeException::new);
  7. ...more steps...wifi issues dropped my knowledge stream here.

InterruptedExecption: Ensure the Thread STOPS execution if this is thrown: Thread.currentThread().interrupt(). Be sure to log this.

ExecutionException: This is simply to report that a Thread was interrupted while trying to execute, so log it also.

Amplify Download File:

  1. Create a variable to capture result of calling Amplify.Storage.downloadFile()
  2. Inside parens param 1: s3ImageKey
  3. Inside parens param 2: new File(getApplication().getFilesDir(), s3IimageKey)
  4. Inside parens param 3 & 4: success and failure callback functions with logging.
  5. Success callback: Need to find ImageViewer Element by ID, then use BitmapFactory.decodeFile() to get file path.

Android UI Workflow Logic Considerations:

Friday Notes

Storage Access Framework Reading Notes

Open files using storage access framework android developers documentation

SAF (Storage Access Framework) introduced in Android 4.4 (API Level 19).

Browse and open documents, images, other file types, across storage providers.

Standardized file browsing UI.

Implement 'DocumentsProvider' to gain storage access functionality.

SAF includes:

SAF Features:

Within DocumentsProvider files paths follow traditional file hierarchy.

COLUMN_ROOT_ID: Points to a doc/directory representing contents under that root.

Root of path is a single document, which can point to N other documents, in a cascading 1:N relationship hierarchy.

COLUMN_DOCUMENT_ID: Must be unique. This ID represents a single file or directory. This is used as part of the persistent URI "grant".

Document Properties: Openable with specific MIME type; Traversable (directory, MIME_TYPE_DIR) with additional documents list.

Document Capabilities: Described by COLUMN_FLAGS:

COLUMN_DOCUMENT_ID can be included in multiple directories.

Control Flow:

App => OPEN_DOC || CREATE_DOC => System UI (picker) => DriveDocProvider || UsbDocProvider || CloudDocProvider

System Picker: Asks registered providers for matching content roots when an Intent fires that includes filters e.g. "openable files of MIME type 'image'".

System Picker: Provides a standard UI regardless of which register provider (or providers) and responding to the Intent, including Cloud, USB, or local storage types.

Storage Access Framework Write a Client App

Invoke an Intent to retreive a file from another app:

Android 4.3 and earlier: ACTION_PICK, ACTION_GET_CONTENT

Android 4.4 API 19 and up: ACTION_OPEN_DOCUMENT, and the above Intents.

Android 5.0 API 21 and up: ACTION_OPEN_DOCUMENT_TREE, and the above Intents.

ACTION_GET_CONTENT: Read or import data, such as a single image file.

ACTION_OPEN_DOCUMENT: Long-term, persistent access, such as a photo-editing app.

Checkout android/storage-samples Github for code samples.

TODOs

Return to Root README