oscarb pad

Guides, resources, notes, docs and knowledge for everything Oscar.

Tags

Android Development Notes

WearOS

Tutorials

Samples

android/wear-os-samples: Multiple samples showing best practices in app and watch face development on Wear OS.

Learn Android

Material 3

https://m3.material.io/

2022

Build your first app  -  Android Basics  -  Android Developers

Android features

Testing

Jetpack Compose

Documentation

Tutorials

Journeys

Accessibility

Animations

Checklists

Flow vs LiveData

Intents

Example from Intents and Intent Filters

// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

Github Actions

https://proandroiddev.com/android-ci-cd-pipeline-with-github-actions-demystifying-github-actions-83258e76a18f

Preserve state when navigating upwards

android:launchMode

When up-navigation is present, use singleTop for the main activity to preserve state when navigating upwards. This will also stop new activites from being started from a notification.

See

Uri

Build URLs using Uri.Builder

compileSdkVersion, minSdkVersion, and targetSdkVersion

Organizing code

Previewing layouts in Android Studio

Use the tools attribute

Frpm Android Studio 3.0, samples are available:

@sample/lorem

Lifecycle

An Activity appears in the foreground
onCreate -> onStart -> onResume

An Activity is sent to the background (app switched)
onPause -> onStop -> onDestroy

From Active to Visible (but not active) (modals)
onPause -> onResume

From Active or Visible to Background
onPause -> onStop -> onRestart -> onStart

Lifecycle

Loaders

Persistance

Five ways to persist data in Android:

Preferences

Reading From SharedPreferences

getDefaultSharedPreferences
Gets a SharedPreferences instance that points to the default file that is used by the preference framework in the given context!
getSharedPreferences
Gets a specific SharedPreferences instance by name in case you have more than one preference in the same context!

Preference Change Listener

Update preference fragment

Guidelines

Should it be a setting?

Make it a setting?

SQLLite Database

Create a database

Content provider

Using a ContentProvider

  1. Get permission to use the ContentProvider.

    <uses-permission android:name="com.example.appname.TERMS_READ" />
    
  2. Get the ContentResolver

    ContentResolver resolver = getContentResolver();
    Cursor cursor = resolver.query(Contract.CONTENT_URI, null, null, null, null);
    
  3. Pick one of four basic actions on the data: query, insert, update, delete
  4. Identify the data you are reading or manipulating to create a URI
  5. In the case of reading from the ContentProvider, display the information in the UI

Creating a content provider

  1. Extend the ContentProvider class and implement the required methods
  2. Register it in the Manifest
  3. Define URI’s
  4. Add URI’s to the contract class
  5. Build a URIMatcher to match URI patterns

URI Example:

content://com.example.appname/specificData

CONTENT_URI

Cursor methods arguments

Match integer: #
Match string: *

Example of getType:

public String getType(@NonNull Uri uri) {
    int match = sUriMatcher.match(uri);

    switch (match) {
        case TASKS:
            // directory
            return "vnd.android.cursor.dir" + "/" + TaskContract.AUTHORITY + "/" + TaskContract.PATH_TASKS;
        case TASK_WITH_ID:
            // single item type
            return "vnd.android.cursor.item" + "/" + TaskContract.AUTHORITY + "/" + TaskContract.PATH_TASKS;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }
}

The best way to asynchronously load data from any ContentProvider is with a CursorLoader

Comparing S09.05-Exercise-MoreDetails…S09.05-Solution-MoreDetails · udacity/ud851-Sunshine

BulkInsert
Comparing S09.02-Exercise-ContentProviderBulkInsert…S09.02-Solution-ContentProviderBulkInsert · udacity/ud851-Sunshine

Code Quality

Services

Background task

“More background” than Loader (more front-end)
Loaders outlasts configuration changes
Services outlasts activites

Start a service

Lifecycle:

IntentService

Automatically runs on the background thread in order, perfect for one off tasks that need to be handled in the background in order.

Runs on a worker thread (compared to the main thread for a Service).

Stops itself after requests have been handled unlike a Service which needs to be stopped with stopSelf() or stopService()

“Fire and forget”

Comparing T10.01-Exercise-IntentServices…T10.01-Solution-IntentServices · udacity/ud851-Exercises

Android 8.0

Many apps that rely on IntentService do not work properly when targeting Android 8.0 or higher. For this reason, Android Support Library 26.0.0 introduces a new JobIntentService class, which provides the same functionality as IntentService but uses jobs instead of services when running on Android 8.0 or higher.

https://developer.android.com/reference/android/support/v4/app/JobIntentService.html

JobService

The base class that handles asynchronous requests that were previously scheduled in a JobScheduler.

Because it runs on the main thread any task needs to be set up on another thread/handler/AsyncTask to avoid blocking future callbacks from the JobManager.

Adding a JobService - YouTube
Exercise: Schedule with FirebaseJobDispatcher - YouTube

It’s important that we verify that we’ve imported jobdispatcher.JobService rather than the Android framework’s JobService, because if you do, you’ll definitely have some headaches. Double and triple check that please.

Android Developers Blog: Moar Power in Android 9 Pie and the future

ForegroundService

Service required to have a non-dismissible ongoing notification. Less likely to be destroyed when memory gets low.

JobScheduler

FirebaseJobDispatcher

Example

Driver driver = new GooglePlayDriver(context);
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(driver);

Job myJob = dispatcher.newJobBuilder()
    // the JobService that will be called
    .setService(MyJobService.class)
    // uniquely identifies the job
    .setTag("complex-job")
    // one-off job
    .setRecurring(false)
    // don't persist past a device reboot
    .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
    // start between 0 and 15 minutes (900 seconds)     
    .setTrigger(Trigger.executionWindow(0, 900))
    // overwrite an existing job with the same tag
    .setReplaceCurrent(true)
    // retry with exponential backoff 
    .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
    // constraints that need to be satisfied for the job to run
    .setConstraints(
        // only run on an unmetered network
        Constraint.ON_UNMETERED_NETWORK,
        // only run when the device is charging
        Constraint.DEVICE_CHARGING
    )
    .build();

Scheduling Jobs - YouTube
The Firebase JobDispatcher

Comparing T10.04-Exercise-PeriodicSyncWithJobDispatcher…T10.04-Solution-PeriodicSyncWithJobDispatcher · udacity/ud851-Exercises

String resources

Pluralization

XML

<plurals name="charge_notification_count">
   <item quantity="zero">Hydrate while charging reminder sent %d times</item>
   <item quantity="one">Hydrate while charging reminder sent %d time</item>
   <item quantity="other">Hydrate while charging reminder sent %d times</item>
</plurals>

Java

String formattedChargingReminders = getResources().getQuantityString(R.plurals.charge_notification_count, chargingReminders, chargingReminders);

Documentation

String format

XML

<string name="format_art_url" translatable="false">https://raw.githubusercontent.com/udacity/Sunshine-Version-2/sunshine_master/app/src/main/res/drawable-xxhdpi/art_<xliff:g id="description">%s</xliff:g>.png</string>

Java

context.getString(R.string.format_art_url, "clear");

PendingIntent

Intent in your app designed to be started by another app/service.

This allows a notification (which is handled by the NotificationManager (a system service)) to start your App.

Notifications

Use a PendingIntent to open your App from a notification.

Notifications - Patterns - Material design guidelines

Notifications - Android Developers
Using Big View Styles - Android Developers
Notification.BigPictureStyle - Android Developers

Comparing T10.02-Exercise-CreateNotification…T10.02-Solution-CreateNotification · udacity/ud851-Exercises

Add actions to notifications

Engage with rich notifications

Check for Google Play Services both in onCreate() and onResume()

For Android versions >= HONEYCOMB, notifcation image sizes can be found with

int largeIconWidth = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? 
	? resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_width)
	: resources.getDimensionPixelSize(R.dimen.notification_large_icon_default);

Where R.dimen.notification_large_icon_default is set to 48dp

Set the color for notifications from Firebase in the manifest with:

<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
     See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
     notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorAccent" />

If the device doesn’t have a compatible version of Google Play services, your app can call GoogleApiAvailability.makeGooglePlayServicesAvailable() to allow users to download Google Play services from the Play Store.

Must use channels once target O is targeted.

Memory prioritization

Broadcast Receiver

System Broadcast Intents - triggers for various device changes.

Creating a broadcast receiever:

To handle broadcasts when app isn’t started: JobDispatcher or in some cases a static broadcast receiver
To handle broadcasts when app is started: dynamic broadcast receiver

Register in onResume() and unregister in onPause if it only is needed for foreground stuff.

Comparing T10.05-Exercise-ChargingBroadcastReceiver…T10.05-Solution-ChargingBroadcastReceiver · udacity/ud851-Exercises

Android Debug Bridge

Start app

adb shell am start -n com.package.name/com.package.name.ActivityName

Simulate unplugging from USB

adb shell dumpsys battery set usb 0

Above for Android 6.0+

adb shell dumpsys battery unplug

Simulate plugging into power

adb shell dumpsys battery reset

Android Debug Bridge - Android Studio

View and View Groups

Avoid nesting view and viewgroups too deep.

General rule of thumb:

Views & View Groups - YouTube

Correct the ImageView’s adjustViewBounds behaviour on API Level 17 and below with AdjustableImageView :: The Cheese Factory

ViewGroups

FrameLayout

LinearLayout

RelativeLayout

GridLayout

ConstraintLayout

Center vertically by putting a top-constraint to the bottom of the other view, and a bottom-constraint to the top of the other view

TextAppearance

Captions

@style/TextAppearance.AppCompat.Captiom

Headline

@style/TextAppearance.AppCompat.Display1

Hierarchy viewer

Tools > Device monitor > Perspective > Hierarcy viewer

Accessibility

Accessibility Developer Checklist - Android Developers

Localization

Localization Checklist - Android Developers
ISO 639-2 Language Code List - Codes for the representation of names of languages (Library of Congress)
Localization checklist - Android Developers

RecyclerView

Different views in a RecyclerView
Comparing S11.02-Exercise-TodayListItem…S11.02-Solution-TodayListItem · udacity/ud851-Sunshine

Provide height and width or face the consequences

DesignSupportLibrary

Parcel

http://blog.bradcampbell.nz/a-comparison-of-parcelable-boilerplate-libraries/

Style vs Theme

A theme is a style applied to an activity

For lists, use dimensions such as: @dimen/list_item_icon_margin_right

Layout

Use the smallest width qualifier

?attr/listPreferredItemHeight

Touch selectors

  1. Create drawable list_item_selector.xml
  2. Root element should be selector
    0- Add items for different states
    • state_pressed
    • state_activated
    • state_selected
    • default
      Note: each item can have a color as the drawable
  3. Set background of item to be selected to the newly created touch selector

Publishing an app

Material Design

Typography

Display 4 corresponds to

android:textAppearance="@style/TextAppearance.AppCompat.Display4"

and so on…

Libraries

Look for support, documentation and functionality

Testing

Android Testing Codelab

Material design

Toolbar

Default toolbar size height: `?attr/actionBarSize”

Style with

   android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"   

Card

cardPreventCornerOverlap="false"

Refs

Aliases as resources

refs.xml

<resources>
  <item type="layout" name="refName">@layout/some_layout</item>
</resources>

RecyclerView

Item choices

https://github.com/udacity/Advanced_Android_Development/compare/6.17_Improving_our_RecyclerView…6.18_Bonus_RecyclerView_Code
Using RecyclerView Part 3 - YouTube

Implementing a modal selection helper for RecyclerView

Parallell scrolling

  1. Attach scrollListener
    • Change translation in y of background by half in relative to the y scroll
    • Clear onScrollListeners in onDestroy

Scrolling in Parallel - YouTube
Scrolling in Parallel - YouTube

Support higher screen ratios

In order to support screen ratios such as 18.5:9 (like that on the Samsung Galaxy S8), add the following to the manifest, in the application element:

_Where ratio_float is the maximum aspect ratio your app can support, expressed as (longer dimension / shorter dimension) in decimal form. _

https://android-developers.googleblog.com/2017/03/update-your-app-to-take-advantage-of.html

Transitions

Exclude navigation bar and status bar with @android:id/statusBarBackground and @android:id/navigationBarBackground respectively.

<item name="android:windowContentTransitions">true</item>
<item name="android:windowEnterTransition">@transition/enter_transition</item>
<item name="android:windowReturnTransition">@transition/return_transition</item>
ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(this);
ActivityCompat.startActivity(this, intent, activityOptions.toBundle());

Animation Resources - Android Developers
The Transitions Framework - Android Developers
Animating Views Using Scenes and Transitions - Android Developers
Applying a Transition - Android Developers

Shared elements

Shared Element Transitions (solution) - YouTube

Android Developers Blog: Continuous Shared Element Transitions: RecyclerView to ViewPager

Places API

Comparing 8.00_Places_API_Start…8.07_Adding_Attributions · udacity/Advanced_Android_Development

Place Picker  -  Google Places API for Android  -  Google Developers

Widgets

Creating the Widget’s view - YouTube
7.01_Sunshines_First_Widget · udacity/Advanced_Android_Development

SyncAdapter

minResizeWidth can be set to smaller than default size minWidth

Collection widget: YouTube

AppWidgetProviderInfo (XML)

Required attributes

Size = 70dp * n - 30dp
= 40 dp for n = 1
= 110 dp    = n = 2

AppWidgetProvider (Java)

public class TodayWidgetProvider extends AppWidgetProvider {
  @Override 
  public void onUpdate(Context context, AppWidgetManager manager, int[} appWidgetIds) {
    for (int appWidgetId : appWidgetIds) {
      RemoteViews = views = new RemoteViews(context.getPackageName(), R.layot.widget_today_small);
      
      // Clickable widget
      Intent intent = new Intent(context, MainActivity.class);
      PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
      views.setOnClickPendingIntent(R.id.widget, pendingIntent);
      
      manager.updateAppWidget(appWidgetId, views);
      
    }  
  }
}

Performance

“Users will complain about performance 3x than any other issue”

Performance Tips - Android Developers

Overdraw

Test overdraw

Memory Monitor

Manage Your App’s Memory - Android Developers

Night Mode

https://developer.android.com/preview/features/darktheme

Publishing

Fonts

MVI - Model View Intent

Debugging

Fragments

Getting context

Fragment#requireContext, for example, returns a non-null Context and throws an IllegalStateException if called when a Context would be null. This way, you can treat the resulting Context as non-null without the need for safe-call operators or workarounds.

UX

Check for unessecary added permissions from libraries

How Libraries can silently add permissions to your Android App

Android Architecture

Android Developers Blog: Android and Architecture

Guide to App Architecture - Android Developers

WebView

Enable Google Safe Browsing API

Same as in Chrome

<manifest>
	<meta-data
    	android:name="android.webkit.WebView.EnableSafeBrowsing"
        android:value="true" />
    <!-- ... --->
</manifest>

Adaptive icons

Date, time and calendars

Android O

Location

VectorDrawable

Launcer Icon Sizes

size identifier size
ldpi 36x36
mdpi 48x48
hdpi 72x72
xhdpi 96x96
xxhdpi 144x144
xxxhdpi 192x192
Google Play 512x512

Google Play Referrer API

See http://android-developers.googleblog.com/2017/11/google-play-referrer-api-track-and.html

Auto-backup for apps

Android Developers Blog: Discover tools for Android data migration and improve your app retention

Resources

Checklist

Udacity Android Developer Nanodegree - Core App Quality Guidelines

        Visual Design and User Interaction

              Standard Design

                    App does not redefine the expected function of a system icon (such as the Back button).

                    App does not redefine or misuse Android UI patterns, such that icons or behaviors could be misleading or confusing to users.

              Navigation

                    App supports standard system Back button navigation and does not make use of any custom, on-screen "Back button" prompts.

                    All dialogs are dismissible using the Back button.

                    Pressing the Home button at any point navigates to the Home screen of the device.

              Functionality

                    Permissions

                          App does not redefine or misuse Android UI patterns, such that icons or behaviors could be misleading or confusing to users.

                          App does not request permissions to access sensitive data or services that can cost the user money, unless related to a core capability of the app.

                    User/App State

                          App correctly preserves and restores user or app state, that is , student uses a bundle to save app state and restores it via onSaveInstanceState/onRestoreInstanceState. For example,

                                When a list item is selected, it remains selected on rotation.

                                When an activity is displayed, the same activity appears on rotation.

                                User text input is preserved on rotation.

                                Maintains list items positions on device rotation.

                          When the app is resumed after the device wakes from sleep (locked) state, the app returns the user to the exact state in which it was last used.

                          When the app is relaunched from Home or All Apps, the app restores the app state as closely as possible to the previous state.

              Performance and Stability

                    Stability

                          App does not crash, force close, freeze, or otherwise function abnormally on any targeted device.

              Google Play

                    Content Policies

                          All content is safe for work content.

                          App adheres to the Google Play Store App policies.

                          App’s code follows standard Java/Android Style Guidelines.
                          

Android TV Development Notes

Best practices

Features

Adoption

80 % Android 7+ (Source:

Recommendations

Recommendations channels are only available in Android 8.0 (API level 26) and later. You must use them to supply recommendations for apps running in Android 8.0 (API level 26) and later. To supply recommendations for apps running on earlier versions of Android, your app must use the recommendations row instead.

Recommend content on the home screen

Resources:

Samples