Git Product home page Git Product logo

gdcs's Introduction

Google Developers Challenge Scholarship

Table of Contents

Stage 1
Stage 2
Stage 3
Stage 4
Stage 5
Stage 6
Stage 7
Stage 8
Stage 9
Stage 10
Stage 11
Stage 12

So why not just set the minSDK to 1 and support everyone? Generally, you’ll want to target as many users as you can, but there's a cost associated with supporting older versions - things like creating different execution paths around deprecated or updated APIs, or presenting a different UX to devices with different features. You need to balance the opportunity of expanding your audience, with the cost of supporting those users.

Also remember that each release introduced new APIs and hardware support, so it may not make sense to make your app available to devices that don’t support your minimum feature set. Here are some examples of hardware support and features, tied to releases.

  1. Home screen widgets (Cupcake)
  2. Multiple finger tracking (Froyo)
  3. Tablet (Honeycomb)
  4. Android Beam (Jellybean)
  5. Android TV, Auto, Wear (Lollipop)
  6. Pro Audio (Marshmallow)

Setting targetSDK By comparison, the targetSDK is NOT a high pass filter -- it’s used only to declare which platform version you've tested your app on. An app targeted to a certain API or Android version will continue to be forward compatible on future releases -- the platform uses the target SDK values in case a future release makes a significant change to expected behavior, ensuring your app doesn’t break when a user’s phone gets upgraded.
Android Studio by-default targets the latest release. If you’re developing a new app, there’s really no reason to target anything but the latest Android version, and once your app has been released, make it a point to update your target SDK and test as soon as possible when new platform releases roll out, so your app can take advantage of every new platform optimization and improvement.
*

Your app runs within its own instance of the run time using the classes and services provided here in the application framework.

**The first thing that happens when you hit RUN is your code gets compiled into byte code that can be run in the run time on the device. In Android Studio is done using Gradle, a build tool kit manages dependencies and allows you to define custom build logic. For now, not that we start with your project, whic Gradle builds and then packages the byte code along with your applications resources, externalized images, UI, XML, into an Android Application Package file. An APK whic is especially formatted ZIP file. Once you got a APK ready to go Android Studio signs it through JAR Signer and then pushes it to the device using the Androdi Debug Bridge or ADB

On Windows and Mac, the x86 emulator relies on a special kernel driver called HAXM for hardware virtualization

A gradle task represents a single, atomic piece of work for a build. To see a list of tasks, you can open the tasks window in Android Studio by clicking on the gradle button on the far right. Clicking on the name of the task runs that task.

Run gradle build task from the command line ./gradlew tasks

For example, to start your android app from the command line, you could type:

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

What is an Android Application A collection of components that work with each other, and with the Android Framework. There are four types of components that make up apps

  1. Activity
  2. Services
  3. Broadcast Receiver
  4. Content Providers

Android knows about each of these components because they are registered in Android Manifest
The Android Manifest is used to register components with the Android framework.

Activity
Single focused thing that the user can do.
Activities are responsible for creating the window that your application uses to draw and receive events from the system such as touch event from the system

Type of Views in Android

  1. UI Components

  2. TextView

  3. EditText

  4. ImageView

  5. Button

  6. Chronometer

  7. Container View

  8. LinearLayout

  9. RelativeLayout

  10. Framework

  11. ScrollView

  12. ConstraintLayout

A word about id @+id/tv_toy_names The @ sign tells the tools not to treat the stuff inside the quotes as a string literal. And to instead, look for the contents inside of the Android resources. While the plus sign tells the tool to create the id if it doesn't yet exist. The id preceding the slash, lets the tools know that we're creating an id rather than a reference to something like a style, string or image drawable

Well the R class is autogenerated by the Android tool chain. But because Java doesn't allow for slashes in variable names the tools replace the slash after the id with a period

Stage 1 Completed!

Message logging display levels

  1. Error
  2. Warn
  3. INFO
  4. Debug
  5. Verbose

In term of increasing verboseness in severity Error, Warn and Info level messages are always preserved in release versions During development you can also use DEBUG and VERBOSE log messages

Android has another very special logging level that is more severe than ERROR
It is called WTF for What a Terrible Failure. The WTF level is for errors that should never, ever, ever happen and most developers should never ever, ever use it. On a debug build of a device, a WTF error may force the device to halt and output a debug report. Because the behaviour of WTF isn't clearly defined, it might be best to only use the knowledge of this sepecial log level to impress your friends ;)

R is a static class that is generated by aapt (Android Asset Packing Tool) for us to reference resources in Java Code

We also allow applications to specify menu items as actions. If there's enough horizontal space, those actions appear on the action bar as buttons. To do this in our menu XML, we add the show as action attribute. We're doing this in the app namespace because we're using AppCompat to make our app compatible with API Level 10 Gingerbread devices.

Toast
Provides simple feedback about an operation in a small popup

use the showAsAction XML attribute. You can actually add a button to the Toolbar layout, but that's making a custom toolbar rather than making a menu item show up as a button.

We can take advantage of the Android URI framework class. It allows us to create a well-formed URI without having to worry about the particular of URI components. For example, adding ampersands between query parameters and encoding invalid character with a percent followed by the character code

UTF-8
Is used by JSON and JavaScript
UTF-16
Is the format used by android

While declaring a permission is required for using the camera, directly dialing the phone, and directly accessing the contact database - you can do each of these things by using a system app as an intermediator. Because the camera app, dialer, and contacts app will be used to provide the data you request - users have the chance to cancel the action you initiated, giving them runtime ability to refuse your app access.

Your app can only access the user’s location if it explicitly declares a uses-permission.

NetworkOnMainThreadException only occur if your app is running in device over Gingerbread version of android if the version is Gingerbread or lower it would work fine but its a bad practice to do network stuff on Main Thread

To run at an ideal 60 frames per second We need to make sure that all the computations between draws takes less than 17 milliseconds

After five second of ignoring user input, Android would actually prompt the user to close your app.

AsyncTask
Allows you to run a task on a background thread, while publishing results to the UI thread

AsyncTask is a generic class. Meaning it takes parameterized types in its constructor. Each one of these generic parameters is to find as a Java variable argument with the three dots, which means that it is technically passed as an array in Java world.

The three types used by an AsyncTask are the following

  1. Params -> Parameter type sent to the task upon execution
  2. Progress -> Type published to update progress dueing the background computation
  3. Result -> The type of the result of the background computation

There is a list of menthods and which runs on UI thread of background thread, got it varun!!!

  1. execute() -> UI thread
  2. onPreExecute() -> UI thread
  3. doInBackground() -> Background thread
  4. publishProgress() -> Background thread (called from inside the doInBackground())
  5. onProgressUpdate() -> UI thread
  6. onPostExecute() -> UI thread

JSON
Primary method for data exchange on the web because it's format is syntactically identical to the code for creating JavaScript objects, this means that JavaScript programs can use standard JavaScript functions to read JSON data since it's essentially native JavaScript

Why JSON

  1. Human readable
  2. More compact
  3. Easier to write
  4. Allows for declaration of arrays

Stage 2 Completed!

The first thing to note is that almost every part of recycler view is completely modular. The RecyclerView has an Adapter that is used to provide the RecyclerView with new views when needed. This Adapter is also used to bind data from some Data Source to the views. The adapter sends the views to a RecyclerView in an object called a ViewHolder. The ViewHolder contains a reference to the root view object for the item. And you're expected to use it to cache the view objects represented in the layout, to make it less costly to update the views with new data. This way findViewById gets called only for each data view whenever that new item is created and not each time you want to populate the views in the item with data. The LayoutManager then tells the RecyclerView how to lay out all those views. It could be a vertically scrolling list like we use in SunShine, or Horizontally scrolling lists, or even a staggered list or a grid. You get some awesome animations for item insertion and removal for free. Because it's based on a data source, it's easy for app using RecyclerView to allow for filtering a list items

It's important that you use the view holder to cache the view objects that you're going to be populating with data or images. When the RecyclerView is first being populated. You call findViewById for each view that will be showing data from the adapter. This search can be expensive, especially if you have a hierarchy of views in your list item. So it's best to do it once and cache those views in a view holder. This way you can access those views later without having to look them up repeatedly. If you don't use a view holder calling findViewById for each view can get out of hand pretty quickly

Let’s say that each item in your RecyclerView list contains four individual data views, and you don't cache these views in a ViewHolder. If eight items fit on screen, approximately how many extra findViewById() calls will be made if you scroll through 30 items? In addition to the eight items that fit on screen, assume that two extra items are needed for smooth scrolling.

Note:-
We use SP and DP when we've reffered to dimensions in Android layouts. These are both not acctual pixel sizes but are automatically scaled to be the same approximately physical size, regardless of the density of the pixels on the screen. The first Android phones were 160 dpi and i dp would actually equal to 1 pixel on these devices. The conversion of dp units to screen pixels is simple, px = dp * (dpi/160). Many modern devices have 480dpi or more. Sp work like DP but are also scaled based upon user preferences, typically the text size preference.

One of the most important things a ViewHolder does is cace these references to the views that will be modified in the adapter since findViewById can get expensive

The adapter is called by the RecyclerView to create new items in the form of ViewHolders, like the one we just created. It also populates, or binds, these items with data. And return information about the data, such as how many items there are in given data source. This data could come from an ArrayList, the JSON result of a network request, or any other data source you can model. The Adapter requires us to override three functions. The first is onCreateViewHolder, which is called when the RecyclerView instantiaes a new ViewHolder instance. The second is onBindViewHolder, which is called when RecyclerView wants to populate the view with data from our model, So that the user can see it, effectively binding it to the data source. And third is getIteCount, which returns the number of item in our data source.

What is the Adapter responsible for?

  1. Creating a ViewHolder for each RecyclerView item.
  2. Returning the numbers of items in the data source
  3. Binding data from the data source to each item.
  4. Inflating each item view that will be displayed.

The adapter does create ViewHolder objects and inflates item views in its onCreateViewHolder function, it also returns the number of items in the data source, and binds data from the data source to each item (even if we pass the responsibility to the ViewHolder). It doesn't cache views associated with each item (that's the job of the ViewHolder class) nor does it recycle them; that’s what our RecyclerView does.

Whereas a ViewHolder determines how an individual entry is displayed, the LayoutManager determines how the collection of items is displayed. LayoutManager is a key part of the way recycling works in RecyclerView since it determines when to recycler items views that are no longer visible to the user.

Note:-
Assigning setHasFixedSize(true) to true allows RecyclerView to do some optimization on our UI, namely, allowing it to avoid invalidating the whole layout when adapter contents change

Android comes with three LayoutManager

  1. LinearLayoutManager
  2. GridLayoutManager
  3. StaggeredGridLayoutManager

Stage 3 Completed!

How do we start one activity from another?
Well, instead of having an activity call each other directly, Android facilitates communiction using messaging objects called Intents. Intents let an app request that an action take place. That can be anything from starting a new activity to picking or displaying a photo from your phone gallery, or make a phone call

In an android application, activities can be started and stopped at any time, So the context gives us a way of doing certain things that might effect an app as a whole or might outlive the lifetime of a single activity

Most implicit intents include two things, an action and a data. The action says what you're trying to do and the data is what you're passing onto the action

query parameter limit from 8000 to 190,000 characters, depending on the browser used.

A URI is like an address that points to the data you're plannning to pass through to the intended action

A URI or Uniform Resource Identifier is a string of characters that identifies a resource. One of the ways you use URIs every day is when you navigate to web addresses like https://www.udacity.com The formal name of a web address is a URL or Uniform Resource Locator. A URL is a URI that identifies a web or network resource. But as we saw in the Android documentation for opening a map, a URI like geo can describe a physical location.

scheme:[//[user:password@host[:port]][/]path[?query][#fragment]

Here's the full form of a URI, |^| with all the optional parts shown in brackets. It begins with a scheme. The scheme describe what type of resource we're pointing to. Popular schemes on the web are HTTP and HTTPS, mailto, FTP, file, and geo, but there are many more. Depending on the particular scheme, it might be followed by two slashes and an authority part. The authority indicates an optional username and password to log in, a host name which could be a domain name or an IP address, and an optional port. For HTTP request, unless otherwise stated, browsers assume port 80.

mailto is one of the schemes that doesn't require the authority part and can worrk with just a path

It's important to note that if there is a query paramters for our street address or business, the lat long path must be 0,0

geo:0,0?q=Antwerp,Belgium&z=10

Here is a |^| geo URI that shows the city of Antwerp in Belgium with a zoom factor of 10

When you want to share data there's a lot more you need to think about, than if you're just starting a new activity. Depending on where you want to send the data you have to think about what type of data it is, the number of files and other things. To save you from all those concerns Android had a special helper class called ShareCompat. ShareCompat and it's inner class IntentBuilder abstract away all of these decision so you can chain together the bits that you need and ignore those that you don't

Every type of content that can be transmitted on the internet has two part identifier and that's called media type. You might see the more archaic name sometimes, MIME type, which stands for Multipurpose Internet Mail Extensions. When they were initially defined for RFC 2046, media types allowed you to have multi-part emails with different types of attachments. Media types are a reason why someone can send a single email with images, video and other file types as attachments, and your email client knows exactly how to interpret each file. A media type string consists of a type, a subtype, and optional paramters.*

Media Type String top-level name / subtype name [; paramters]

Here's an example media type that describes most of the web pages on the internet text/html; charset=UTF-8

The type of data is text specially HTML text, and it has a character set encoding of UTF-8

More Examples

  1. text/plain
  2. text/rtf
  3. image/png
  4. video/mp4

Whenever you want ot share between apps, you'll have to specify it's media type so that Android can determine how and if it can fulfill the request

*Opening a web link would be an implicit intent because you aren’t specifying a specific browser to use, the user gets to choose.

Opening an activity uses an explicit intent because you know exactly where to go.

Sharing content to Twitter is a bit of a curveball. We’ve taught you the best way to do it using an implicit intent. It is possible as an explicit intent but not recommended.*

Stage 4 Completed!

Activity Lifecycle Diagram

Activity Lifecycle Diagram

The Active Lifecycle is when your activity is in the foreground and has focus. Here it's actively receiving input from user events. And no other activities are obscuring it. onPause is call, and the active lifetime ends as soon as your activity is partially obscured, like when you have another activity trying to fulfill an implicit intent and user needs to make a selection. So to make efficient use of limited resources, you'll want to use these signals to adjust your app's resource burden. So most updates to your UI can be paused when this life time ends, which is announced by onPause. But as you see, the app is still visible So, you shouldn't pause any processes that are drawing your UI. The visible lifetime on the other hand, continues whenever the app is all visible and ends as soon as it's completely obscured by another app. At that point our app is moved to the background. So when you see onStop you know the user can't see your app at all. So, while onCreate and onDestroy will be called at most once each time your app is run. These handler (See the picture below) are likely to be called many times while the app is running.

Android App Lifecycle Methods

Rotating the device causes the Activity to be destroyed and recreated, so our lifecycle starts at onPause and ends at onResume. Note that we don't see onRestart, which only happens if the activity is stopped (but not destroyed) and then restarted.

OnSaveInstanceState takes a bundle as its parameter. This bundle is a key value storage mechanism that we use to store the data we want to be saved. Now it can't quite store any data. Bundles need to passed between processes or serialized to a file. So they support a limited set of types. That being said, you can add complex types to a bundle by having them implement the parcelable interface. The parcelable effectively contains the instructions for how to output an Object to a stream of data, and then recreate the object from that stream.

onSaveInstanceState is called after the onPause but before onStop, onDestroy

onPause and onStop are singnals that our app may be killed imminently. So we need to clean up any resources that need an orderly tear down such as closing an open connections or sockets.

When we destroy and recreate an activity the application continues to run. That means that all of the threads that were running continue to merrily process away. In the case of our GitHub query app it delivers the result of the query to a zombie activity that has gone away and the activity is left empty. But it's even worse in the current version of sunshine. In sunshine, we create our AsyncTask in the onCreate method of our activity. It starts a thread which begins a background task. If we rotate the device or do something else which causes the activity to be restarted, the new activity will create another async task to do the background operation. There will be extra network usage as both threads run in parallel and it will take a longer time for the user to see the result of the load. Even worse because those background thread ultimately deliver their result to a callback that's part of the activity, those async tasks actually keep all of those old zombie activities around as long as the threads are running casuing extra memory pressure. Enter loaders to solve this problem. They've been part of Android since Hineycomb and were added to the support library so they are available on any useful Android release. Loaders provide a framework to perform asynchronous loading of data. They're registered by ID, with a component called the LoaderManager, which allows them to live beyond the life cycle of the activity they are associated with, preventing duplicate loads from happening in parallel. If we want to load data on a background thread, we can use an implementation of a loader pattern called AsyncTaskLoader. The AsyncTaskLoader implements the same functionality as AsyncTask, but because its a loader its lifecycle is different. With an AsyncTaskLoader, once we rotate the device, the loader manager will make sure that the running loader is connected to the AsyncTaskLoader equivalent of onPostExecute the onLoadFinished function. The loader thread keeps running in the loadInBackground function. And once it finishes, the activity gets notified through onLoadFinished.

AsyncTaskLoader is a better choice for Activity-bound thread management, because it handles lifecycle changes correctly, delivering the result to the current active activity, preventing duplication of background threads, and helping to eliminate duplication of zombie activities.

Creating loader is actually pretty simple. We start off by creating an integer constant for a loader ID. Then, we implement the loader call backs. Finally, we initialize the loader with the loaderManager

restartLoader will create loader if it doesn't yet exist

Loaders are tied to the application lifecycle. They automatically handle changes in configuration, such as rotation. They are designed to reload if the user navigates away from the activity and then return. We can avoid that extra load if we don't find it desirable by caching and redelivering our existing result.

If you want to cache your result and put it back if you don't need to ftech the data again save the result and override deliverResult().

Stage 5 Completed!

Data Persistence

Persistence Option | Type of data saved | Length of time saved
--- |--- | --- |---
onSaveInstanceState | key/value(complex value by using parcelable interface) | While app is open
SharedPreferences | key/value(primitive values) | Between app and phone restarts
SQLite Database | Organized, more complicated text/numeric/boolean data | Between app and phone restarts
Internal/External storage | Multimedia or larger data | Between app and phone restarts
Server (ex. Firebase) | Data that multiple phones will access | Between app and phone restarts, deleting the app, using a different phone, etc

PreferenceFragment
Because SharedPreferences are usually used for app settings, they work hand-in-hand with another part of the Android Framework. Which was meant for creating user interface for settings activities. This framework class is called PreferenceFragment.

Fragment
A class that represents a modular and reusable piece of an Activity

The PrefernceFragment subclass is specially built for displaying prefenrences

There's also a class called PreferenceActivity which I don't want you to confuse things with. Since Honeycomb it's been deprecated in favor of the more flexible fragment version.

PreferenceFragments populate themselves with preferences defined in XML. This is much like how our activity layouts are also created in XML. The XML is used to generate UI widgets in the fragment. When the user changes these values in the widgets, this automagically updates associated key value pairs in the SharedPreferences file.Then when you need to actually know the value of the preference, you can then read from the SharedPreferences file in your app

android:launchMode="singleTop" *This |^| this make sure that when you navigate back from SettingsActivity it doesn't remake the VisualizerActivity

  1. Astro
  2. Bender

Any preference XML's outer tag is always a PreferenceScreen tag. Within that tag you can nest other preferences tags of different types. YOu can even nest other PrefefrenScreen tags if you want. This will create a nesrted hierarchy of preferences. What do I mean by a nested hierarchy of preferences? Well here, I'm in the phones settings screen. And if I click on the display it would actually show me another settings screen

Once you've decided which get method to use, you pass in the string whcich is the key of the preference and then you pass in the default value, which is basically the result of this call if that preference wa not found in the file. Note that of course the default value always matches the type of the return of that call. Also note that there's no type checking so if you tell SharedPreferences to get you a long when there's actually a string stored in there which cannot be converted to a long, the app will crash.

SharedPreferences.Editor editor = new sharedPreferences.edit();
editor.putBoolean("show_bass", true);
editor.apply();
editor.commit();//use apply insted of commit because apply perform the update off the main thread

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!

The SharedPreferences object allows you to register an object that implements an OnSharedPrefernceChangeListener. This object then gets called whenever a value in the SharedPreferences file changes. In this way, you can actually be tiggered to update the UI if and only if a preference is actually changed

Setting PreferenceChangeListener take 4 steps

  1. Determine Activity
  2. Activity implement OnSharedPrefernceChangeListener
  3. Link Listener to Shared Preference File using .registerOnSharedPreferenceChangeListener
  4. Unregister the listener using .unregisterOnSharedPreferenceChangeListener

Setting an acceptable range
To limit the acceptable values between 0 (non inclusive) and 3 (inclusive) we opted to use a PreferenceChangeListener - this is not the same as a SharedPreferenceChangeListener. The differences are:

SharedPreferenceChangeListener is triggered after any value is saved to the SharedPreferences file. PreferenceChangeListener is triggered before a value is saved to the SharedPreferences file. Because of this, it can prevent an invalid update to a preference. PreferenceChangeListeners are also attached to a single preference.

Generally the flow goes like this:

  1. User updates a preference.
  2. PreferenceChangeListener triggered for that preference.
  3. The new value is saved to the SharedPreference file.
  4. onSharedPreferenceChanged listeners are triggered.

Should it be a Setting
Here's a handy flowchart to help you decide if something is worth being a settings or should you just decide for the user a default value for:

Should it be a Setting Flow Chart

Stage 6 Completed!

On older android devices, this shared storage was actually on an external memory card.Today most android devices only emulate this card, so that there is the shared external storage app need available on the device. Some android devices have emulated shared storage and secondary external storage. Android 4.4 KitKat added an API to allow developers to access this secondary external storage

Contract in android is a class that defines the tables and the columns for each table that are included in the database. A good way to organize the contract class, is to put the definitions that relate generally to the whole database directly in the contract class. And then create an inner class for each table, and then includes the columns for each of those tables inside that inner class

You should never need to create an instance of the contact class, because the contract just a class filled, with DB realted constants that are all static.

Also note that inner class inside the contract class needd to implement the BaseColumns interface. The base columns interface, automatically includes a constant representing the primary key field called _ID. Some android adapters will expect your contact to have this constant defined. It's not always required to implement BaseColumns interface, but that can help your database fit in better with the android framework.

Unit tests are designed to verify that a small, individual part of the code, works as expected.

For creating database, we need to create a new class known as DBHelper. Because we will be using SQLite as our database, we'll extend the Android class SQLiteOpenHelper to create our DBHelper class. SQLiteOpenHelper exits mainly to take care of creating the database for the first time and upgrading it when the schema changes. The DBHelper also provides other classes with a reference to the database, giving them a way to access it's information through queries. SQLiteOpenHelper provides us with two important methods, onCreate and onUpgrade. onCreate is responsible for creating the actual database for the first time, while onUpgrade is responsible for making sure the database schema is up to date.

Inside the DBHelper class create a final string to store the name of the database and a final integer to store the current database version number. This should always start from one and any time you decide to modify the data base schema in your code, you should increment it. This will force user to upgrade their databases whenever you release a new version of the app. Also, don't forget to change the contract if you ever do change the database schema.

onUpgrade() method will only called when the version number we defined earlier becomes larger than the version number of the database on the device. It's meant to upgrade the database schema without losing any user data, so ideally this should include a schema update statement.

If we weren't inserting any data but just interested in reading data, we could use a getReadableDatabase() instead of getWritableDatabase().

A cursor is what any SQL query result will be stored in. The cursor class allow you to iterate, over the query result in a very easy and simple way

A result of any selection query is formed as a table

You can think of the cursor as if it's pointing to a particlar row in that result. To extract information from a particular row, you can move the cursor to point there, and then extract the specific goal information. For example, to get the guest name in the fourth row, move the cursor to the fourth position, and then call getString, passing in the index of that column.

mCoursor.moveToPosition(position)
|^| this will return false if there's no data there, or if it's out of the bounds of the cursor so, make sure you add a check

To insert data into the database, we need to use a ContentValues object that maps coulmn names as the key to the desired values. see the example below

contentValuesObject.put(colmnsName, value);

Now that we have our ContentValues object ready, we can call insert passing in the table name and the ContentValues object. This will insert a new row in the table with the value specified in CV. we call the insert method on SQLiteDatabse object who is getting the refference from our DBHelper class OBJECT.getReadableDatabase() or OBJECT.getWritableDatabase() and our DBHelper class is extending the SQLiteOpenHelper class that's why we get the writeable and readable methods

If you change the database schema, you must increment the database version or the onUpgrade method will not be called.

RecyclerView offers something called a tag object it's meant to store any data that doesn't need to be displayed

SQLite swag
"UNIQUE (" + WeatherEntry.COLUMN_DATE + ") ON CONFLICT REPLACE);";

Stage 7 Completed!

Now usually data sources like a SQLite database are private to the app which created them. This is done for security reasons. So how do two totally different apps easily read and write from the same source? This is where ContentProviders come in. In short, a ContentProvider is a class that sits between an application and its Data Source. And it's job is to provide easily managed access to the underlying data source

ContentProvider Advantages

  1. Easily change underlying data source.
  2. Leverage functionality of Android Classes. ex Loaders and CursorAdapters.
  3. Allow many apps to access, use and modify a single data source securely.

Big Picture of How ContentProvider and ContentResolver works together

ContentProviderAndContentResolver

General Steps for Using a ContentProvider
You will take the following steps:

  1. Get permission to use the ContentProvider.
  2. Get the ContentResolver.
  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.

Steps for using a ContentProvider

Complete each of the following

  1. Get permission to use the ContentProvider
    <uses-permission android:name="com.example.udacity.droidtermexample.TERM_READ"/> Why ask for permission -> It's a security feature of ContentProvider, that they're protected by the same permissioning system that will pop-up these dialogues which inform the user of what the app actually does. Since you must request permissions to use ContentProviders, this mean that an evil developer can not just get access to any data on the user's phone that they want. For example, they can't use the Contacts ContentProvider to say steal all of the user's friends emails. Or at least they wouldn't be able to do it without a dialogue popping up requiring the user's consent.

  2. Get the ContentProvider ContentResolver resolver = getContentResolver(); Cursor cursor = resolver.query(DroidTermExampleContract.CONTENT_URI, null, null, null, null); This above code accesses the correct content provider and grabs some data from it. Or you can say getting us a refference to the system's ContentResolver

So what's the purpose of having ContentResolver class sit between your app and direct access to the ContentProvider?

If you think about it, there are multiple ContentProvider on your phone, and you add more ContentProviders when you download apps, that store local data, which use ContentProviders. Besides the DroidTermsExample ContentProvider, you have a ContentProvider for contacts, your device has one for user files of the device, one that keeps track of user alarms, the calendar provider, and some others. Also, your app is not the only app running on the device. There are other apps that might also be using ContentProvider in parallel. Managing what ContentProvider are talking to what apps, and keeping all the data in sync could turn into a huge traffic jam. That's where the ContentResovler comes in. The ContentResolver acts as an intermediary between each app and the ContentProvider, or provides, it want to access. It hanles inter-process communication and keeps everything in sync and running smoothly. Even if you have five processes accesasing two ContentProviders.

  1. Pick one of four basic action: query, insert, update, delete

    1. Read from the data -> query()
    2. Add a row or rows to the data -> insert()
    3. Update the data -> update()
    4. Delete a row or rows from the data -> delete() *So basically, you specify what you want to do, by calling ContentResolver. and then one of those methods. Then the ContentResolver tells your ContentProvider to performm that action.
  2. Use a URI to identify the data you are reading or manipulating content://com.example.udacity.droidtermexample/terms Content Provider Prefix://Content Authority or Which ContentProvider to use / Specefic Data or which table in the specified ContentProvider The all above jargons means -> I'm a URI for ContentProvider on Android. And I'm accessing this specific droidtermexample provider and I'm only interested in the list of terms stored in droidtermexample data The URI defines the exact data you're trying to read or manipulate.

  3. In the case of reading from the ContentProvider, display the information in the UI

Important Note

Now you might be scuffing right now and going, but lyla, how would I know what the structure of these URIs is even supposed to look like? I'm not a mind reader, and yes that is right. You are not a mind reader. Which is why if the designer of the ContentProvider followed conventions, which they hopefull did, they should have created a Contract class

Always make your database operation calls off the main thread

Cursors are iterators that provide read/write access to data of a ContentProvider. The data in a tabular format

  1. Projection - filters columns
  2. Selection - statement for how to filter rows
  3. Selection arguments - what to filter
  4. Sort order - how to sort rows

When you first get your cursor back from the query method, it's position at row negative one, which is nothing

moveToNext()
Moves to next row
returns true/false

moveToFirst()
Moves to first row

getColumnIndex(String heading)
Given a heading of a column, return index

get(int ColumnIndex)
Return the value at the column index
can be string, int, long, etc.

getCount()
Returns the number of rows in cursor

close()
Always close your cursor to prevent memory leaks

Stage 8 Completed!

Multiple rows of data, are generally called a directory. And a single row of data is often called an item.

Steps to create a ContentProvider

  1. (a) Create a class that extends from the content provider and (b) implement the onCreate() function. OnCreate() is called to initialize the ContentProvider.
  2. Register you ContentProvider in the Manifest. -> ContentProviders need to be registered similar to activities. So that your app knows that the Provider exits and knows how to refer to it, by name and authority.
  3. Define URI's that identify the ContentProvider that different data types that it can return. These are needed so that later on a ContentResolver can find the provider and the specific data you want to acdess just based on a given URI.
  4. Add these URI's and String constants to the Contract class. That will help you refer to the most commoly used URIs.
  5. Build URIMatcher to match URI pattens to integers. This is a clas that helps a ContentProvider recognize and respond correctly to different types of URIs. For example, it's often useful to use this matcher to distinguish between URIs that point to a large dataset, like multiple rows of task data. And URIs that point to a subset of that data, like data for an individual task.
  6. Implement the required CRUD methods like query, insert, update and delete in ContentProvider.

In general, onCreate is where you should initialize anything you'll need to setup access your underlying data source.

android:exported="true OR false"
|^| the above atrribute is for provider tag in manifest. This tells the system whether or not ContentProvider can be accessed by other applications.

In general URIs tell ContentResolver two things.

  1. Identify your provider (Which provider they want to talk to)
  2. Identify different type of data (what specific data is being requested.)

Wild card Character for number # in URI

URIMatchers have matching capabilities similar to regex pattern matching, but it's a quite a bit simpler. Regular expressions match a lot of special character, but a URIMatcher recognizes only two wildcard characters, an asterisk and that hash symbol that you just saw. The hash symbol can stand for an integer of any length. The asterisk stands in for a string of any length.

Examples of URI's with wildcard characters

  1. "path"-> matches "path" exactly
  2. "path/#" -> matches "path" followed by a number
    "#" matches a string of numeric characters
  3. "path/" -> matches "path" followed by a string
    "
    "" matches a string of string of any length
  4. "path/*/other/#" -> matches "path" followed by a string followed by "other" followed by a number

Contract is designed to keep track of constants that will help you access data in a given database.

BaseContentUri = Scheme + ContentAuthority
BASE_CONTENT_URI -> this is an unchanging URI that will be the start of all the URI's we'll be using to access our provider.

Scheme = content://
Content authority = reference to the provider
(com.example.android.todolist)
Base content = URI scheme + authority
Pat = to specific data
Content URI = base content URI + path

UriMatcher
Determines what kind of URI the provider receives and match it to an integer constant. So, that you can easily make a switch statement

UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|^| pass the constant UriMatcher.NO_MATCH for building a empty uriMatcher

Resolver to Database Flow
UI -> Resolver(URI) -> Provider -> URIMatcher -> SQL Code -> Database
ResolverToDatabaseFlow

Overview of Providers Functions

  1. onCreate() -> initializes tte provider
  2. insert(Uri uri, ContentValues cv) -> to let users of your app create new data, you need to code the content provider's insert method. This will take in a content Uri which tells the correct directory to insert data into, and a ContentValues object that contains the new data to insert. After the data is inserted, this returns a newly created content URI that tells you the location of the inserted data.
  3. query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrdder) -> To read data and display it in your UI, you'll write the query method which asks for data from your content provider. This return a cursor that contains a row, or rows, of data that the query has asked for.
  4. update(Uri uri, ContentValues values, String selection, String[] selectionArgs)-> This takes in the same parametes as insert, so it knows where to update data by the Uri, and with what ContentValues. And this will return an integer value for the number of rows that were updated.
    5.delete(Uri uri, String selection, String[] selectionArgs) -> And then to delete, you'll implement the delete method, which needs to know the Uri that points to the row, or rows, to delete. And this should return the number of rows deleted.
  5. String getType(Uri uri); -> this will returns the MIME type of the content being returned. And a mime type is just a way of identifying what format the content is in, in a similar way to file types.

SQLiteDatabse.insert() return an integer ID. If the insert wasn't successful, this ID will be -1. But if the insert is successful, we want the provider's insert method to take that unique row ID and create and return a URI for the newly inserted data. So, if the ID is valid means not 0 or -1, then the URI we construct will be our main Content URI, which has the authority and tasks path, with the id appended to it. And we can do that using `ContentUris.withAppendedId(TaskContract.TaskEntry.CONTENT_URI, id);

One last thing, right before this return statement, you'll also notify the resolver that a change has occured at this particular URI. You'll do this using the notify change function. This is so that resolver knows that something has changed, and can update the database and any associated UI accordingly.

finish() tells system that this activity is over and so we should return to the MainActivity after an insert is complete.

setNotificationUri() in query method
But this time it tells the cursor what content URI it was created for. This way, if anything changes in the URI, the cursor will know.

We can grab the ID that is passed into the URI by using a call to getPathSegments().get(1); by passing the 1 as the parameter if we pass 0 we get the path which is the name of our table in that URI.

if you plan to provide your app's data to other applications, you should implement all of the operations that you want other developers to be able to use.

getType() -> only becomes useful when MIME types are used to organize data or to validate data.

/* getType() handles requests for the MIME type of data
We are working with two types of data:
1) a directory and 2) a single row of data.
This method will not be used in our app, but gives a way to standardize the data formats
that your provider accesses, and this can be useful for data organization.
For now, this method will not be used but will be provided for completeness.
 */
@Override
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);
    }
}

bulkInsert() ContentProvider's method
bulkInsert() tries to inset an array of ContentValues, not just one set, into a database

SQLiteDatabse Transaction
A transaction is a way to mark the start and end of a large data transfer
db.beginTransaction();
db.setTransactionSuccessful();
db.endTransaction();

/*

  • If we pass null as the selection to SQLiteDatabase#delete, our entire table will be
  • deleted. However, if we do pass null and delete all of the rows in the table, we won't
  • know how many rows were deleted. According to the documentation for SQLiteDatabase,
  • passing "1" for the selection will delete all rows and return the number of rows
  • deleted, which is what the caller of this method expects. */

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

A CursorLoader is a subclass of AsyncTaskLoader that queries a ContentProvider, via a ContentResolver and specific URI, and returns a Cursor of desired data. This loader runs its query on a background thread so that it doesn’t block the UI. When a CursorLoader is active, it is tied to a URI, and you can choose to have it monitor this URI for any changes in data; this means that the CursorLoader can deliver new results whenever the contents of our weather database change, and we can automatically update the UI to reflect any weather change!

Stage 9 Completed!

One of the most powerful features of Android has always been the ability for apps to run in background. As is usually the case though, with great power comes great responsibility. So if you're building an app that has significant background components, you need to be very conscious of how it's consuming resources. A poorly behaved background app can have a seriously negative impact on the overall user experience.

Services
Services are Android Framework Components meant for running background tasks that don't need a visual component. Because of this services unlike activity, do not provide a user interface. An activity can start a service, which will then continue to run even after the activity is shut down. Therefore, services are great for things like loading and processing of data in the background even when none of these activity are open

Activity
The android component that is tried to the User Interface of your application

In fact it doesn't make sense to do network transaction that talk to a database inside of an any activity

Services are one of the four primary Android components and needs to be registered in the Android manifest. Including activities, ContentProvider and Broadcast receivers. All connected using Intents.

Loader vs Services

Where use Loader

  1. When task tied to the Activity Lifecycle
  2. Easy to make user interface changes and communication with Activity

for example decoding an image that was going to be used in an ImageView. Or querying a database that is going to be used to populate a recycler view adapter. Some network transaction even fall into this use case. If your app is inherently real time, you might want to just fetch data as you need it in the UI rather than cache the data in a database.

Where to use Services

  1. When the task that you are doing is Decoupled from the user interface. An example would be updating a database in the background. While the user interface needs to know that this is happening, the operation should continue, even if the application doesn't have an active window.
  2. Exists even when there is no user interface.

In short, if you're loading or processing data that will be used in the UI, use a loader. If you need to process upload or download in a way where end result will not directly affect the UI such as caching data in database, do it in a servie.

Services

Services hang out in the background, processing, downloading, or uploading data while the phone is locaked or the user is using the unrealted apps.

How to start a service
There are three ways to start a service

  1. Start -> The most straightforward way to start a service is to call the startService() method from a Context, such as an activity. The service will execute, but will not communicate back to the component which started it. For example EmailService
  2. Schedule -> If you want to have a service execute as some point in the future, or when some condition are met, you can create a special type of service called JobService. You create your JobService, and then you define when the job should run using a scheduler, like JobScheduler or FirebaseJobDispatcher. They allow you to define complex run schedules for your service, like have a service run every 24 hours when on Wi-Fi. Then the scheduler schedules the job for you.
  3. Bind -> A bindService() offers a client-server like interface. With your service being the server and the various components binding to the service being the clients. Components bind to the service using the bindService() method. Unlike started services, which you tend to start and then forget about. Bound services can easily communicate back to the components that are bound to them. An example of when you migh use a bound service is a media player application. You might have a bound service which plays audio and an activity which controls the UI. It's important to use a bound service as oppose to a started service because you probably want to update the UI to represent where in the audio you are. If you press the pause button in the activity, you might also want to send additional commands to the service from the activity. Note that a service being bound or started is not mutually exclusive. A service can be both Started & Bound

There is one fact though that confuses a lot of developers. All android core components start on the main thread. This means that Activites, ContentProviders, Broadcast Receivers, and yes even Services, start their life on the main application thread. To be clearer, the base Service class is started on the main thread. And it will block your UI if you run a long running task. To get around this, you'll need to create a background thread, or AsyncTask, from within your service.

How to do this right?

From a high level, services like activities, have a lifecycle. Their lifecycle is different from activity's though. For a simple started service, the service is created when a context, such as an activity, calls startService(), passing in an intent. This is very similar to how you start an activity using an intent using startActivity(). What this does is trigger the service's onCreate() method. Just as with an activity, onCreate is responsible for any setup. After that, onStartCommand is called. And inside the onStartCommand method is where you should actually do whatever it is that your service does (Start the AsyncTask here). When your service is done, you can signal this by calling stopSelf() (The service is stopped by itself or a client. Note that if you're using jsut the service class, you, as a developer, will need to call this method. calling stopSelf() will then trigger onDestroy() and the service will be destroyed.

Now running time consuming taska from a service in a background thread is fairly common thing to do. So, the Android framework actually contains a helpgul subclass of service called IntenService

Intent Service
Unlike the service class we just talked about, IntentService is a service that actually runs in a seperate background thread altogether

Starting an Activity

Intent myIntent = new Intent(this, MyActivity.class);
startActivity(myIntent);

Starting an IntentService

Intent myIntent = new Intent(this, MyIntentService.class);
startService(myIntent);

How to create a IntentService class
First you create a class that extends from IntentService. Inside that class, you will specify what your IntentService should do in the background by overridding the onHandleIntent() method. Then you simply create an Intent and pass in that service class. Finally, to start it, you just call startService() as opposed to startActivity(). You pass in the Intent you just created which inside has your IntentService. Yup, that's an IntentService inside an Intent, an Intent-ception. Once on startService() is called it launches an IntentService, runs the code inside the onHandleIntent() on a background thread, and then stops itself when it's done

Note that the intent object passed into the onHandleIntent() method is the same intent you created when using the startService(). Which means you could attach the extra data when creating it. And then you can access that data inside onHandleIntent() and use it to say decide what action to perform using that intent. It's important to note that all IntentService requests are handled in a single background thread, and they are issued in order. Each IntentService will take as long as necessary and will not block the application's main thread. But only one call to the IntentService's onHandleIntent() method will be processed at a time. This makes IntentService great for tasks that need to occur in order such as updating the counter in our case.

<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>
String formattedChargingReminders = getResources().getQuantityString(R.plurals.charge_notification_count, chargingReminders, chargingReminders);

The first usage of chargingReminder is the quantity number. It determines which version of the pluralized string to use (you must pass in a number). The second usage of chargingReminder is the number that’s actually inserted into the formatted string.

Services like every other android component needs to be registered in the android manifest file. Go to AndroidManifest.xml file and create a new service tag. We'll do that within our application's tag itself. We'll set the android:name pointing to our intent service class and we'll set the android:exported="false". This works the same way as an exported tag for ContentProviders. It controls whether other applications can access your service. This is important because we only want to our app to be able to call our service.

Notification
Though notifications started out as a convenient way to notify users of background updates, they've grown to become a powerful standardized shortcut to interact directly with apps in a lightweight way.

Starting in jellybean, rich notification were able to include actions. Which let you define two or three specific actions to performs on the data included in the notification.

Lollipop added new template types for notifications along with heads-up notifications.

Nougat changes the layout of notifications to always show the app posting them. The new APIs allows for inline replies to notifications, similar to Android Wear, as well as specifying custom layouts for heads-up and big notifications.

The best part is that Android support library wraps almost all of these platform differneces with an easy to use API.

Note:- But don't overdo it with notifications, there's a fine line between useful and spammy And it's only a couple of clicks for a user to disable your apps notifications permanently.

As a standardized UI designed for specifically conveying timely information in limited time space, they're an obvious mechanisms to use when Android expanded into wearables with Android Wear. Suddenly, the rich notifications you designed for phone and tablet make your app wearable compatible for free

To launch the app when the notification clicked, we have first need to learn about something called PendingIntents. You've already seen and used intents a couple of times before. Intents allows your application to launch activities within the same app ,or launch other applications altogether, either explicitly or implicitly using URIs. Launching other apps or services from your own application requires the manifest to include the appropriate permisssions. But what if we want another applications to launch an activity in your own applications. Like in the case with notifications. Any notifications in Android is displayed by a system service called NotificationManager. A system service is a service that starts by the Android system itself, and is not part of your application processes at all, which means, If you want the NotificationManager to be able to launch an ativity in your application, you needed to have the permissions to do so. And since you can't modify the system's service permissions, pending intents come to play. A PendingIntent is a wrapper around a regular intent, that is designed to be used by another application. The PendingIntent gives that application the ability to perform the included actions as if it was your application, with all the permissions your application has been granted. It can launch services, private activities, and Broadcast protected intents, even if the your applications is no longer running. This works great for the notification service, because we want to able to launch the hydration activity from the notification pop-up even if the app was closed. To create a PendingIntent instance, you can use one of those static methods in the PendingIntent class. Either getActivity or getService or getBroadcast, depending on what intent you want to wrap in the PendingIntent. Note that these all methods return the actual instance of PendingIntent that you will pass to another app, which in our case will be the NotificationManager. And then you can expect the NotificationManager to lauch our activity when the user click on the Notification

Since the launch of Android Jelly Bean, notification can take up to three action buttons, which appear in the notification itself. Each action button can launch a different pending intent and perform different actions on your app. Even though actions are optional, you should at least set the notification to allow the users to lauch the app so that they can look at the event that caused this notification to show up in the first place.

A special type of service called a foreground service
One common example of a foreground service is a music player that's able to play music long after you've navigate away from the actual audio player activity. Another example is when you get direction via Google Maps after you've closed the app. If you've navigates somewhere while google maps wasn't active, you've used the Google map foreground service.

What is foreground Service?
And what makes these two examples foreground services as opposed to plain old background services? A foreground service is a service that the user is actively aware of because Android requires that service post a non-dismissible ongoing notification. They are typically used to show the user the real-time progress of a long-running operation. Because these notifications exist, the user can do some rudimentary interactions with your service, such as pausing the music, or stopping the service entirely. In addition, because the service is doing something very visible and likely important for the user, Android will prioritize not shutting these services down even if the system is memory constrained.

Application Priority
App priority is divided into four general buckets critical, high, medium, and low. Critical apps are those which are active. They're in the foreground interacting with users. That includes activities in the foreground and apps running a foreground service. An activity in the foreground is slightly more important than a foreground service only on recent Android releases. But both are considered critical. So if you're running a very heavy weight app in the foreground or your foreground service process is very heavy iteself, Android may kill your other foreground services. Visible activities are high priority, while less impactful than killing a foreground app or foreground service, destroying visible activities is still quite undesirable, because users will most likely notice it. Services are important but they can be killed much of the time without impacting the user directly. That being said the system tries to keep them alive and will even try to restart sticky services, services process comes in medium bucket. Apps that running in the background are either completely empty with no active Android component or apps with a stopped component, but not yet destroyed. They're the red shirted instance of the app priority landing party. Android keeps both of these around as much as possible, killing empty apps first, because keeping allocated memory around is cheap, while restarting apps can be relatively expensive. They'll be killed as needed in a last seen, first killed order to support the higher priority apps.

Three laws of Android resource management

  1. Android will keep all apps that interact with the user running smoothly.
  2. Android will keep all apps with visible activities followed by services running, unless doing so violates the first law.
  3. Android will keep all apps in the background running, unless this violates the first or second law.

Consider these four apps. Which priority order would the system rank them in?

  1. Gmail doing a background mail sync.
  2. Google Music playing a song in the background.
  3. The Camera app being used to take a photo.
  4. Google Maps in the background

Answer -> C,B,A,D

*That's right -- Maps isn't visible or running any services, so it's the most likely to be killed --- if we were navigating, it would be completely different.

Gmail is running a service of some sort but not directly interacting with the user, while Google Music and the Camera app are.

Of those two, the music app is still only a foreground service, so it's got a slightly lower priority -- though neither will be killed unless memory pressure is exceptional. Which, since the camera app is pretty heavy-weight, it could be. *

JobScheduler
In android L JobScheduler was introduced.

FirebaseJobScheduler
*It's a more compatible version of JobScheduler. Since JobScheduler was introduced in Android L, it inly provides compatibility back to API level 21, whereas FirebaseJobScheduler provides compatibility back to API level 9 Gingerbread

What is Google Play Services

You might be wondering what's up with the GooglePlayDriver. FirebaseJobDispatcher has a dependency on Google Play Services, which is why you need a GooglePlayDriver. So what is Google Play Services? Google Play Services is app that Google maintains which comes pre-installed on and runs in the background on many, many phones. It is essentially a collection of Services that your app can use to leverage the power of Google products. If the user has the Google Play Services apk installed (and many do) you can use Google Play Services Libraries to easily do things like use the Places API to know where your user is or integrate Google sign in. FirebaseJobDispatcher is one of the many services you can take advantage of via Google Play Services. Google choose to distribute these services as an installable apk so that updates to the services are not dependent on carrier or OEM system image updates. In general, devices running Android 2.3 (API level 9) or later and have the Google Play services app installed receive updates within a few days.

JobService need to tell the system when the job is actually done by calling jobFinished(). The job is done when the AsyncTask is Done. And AsyncTask signals that it's done by calling onPostExecute. So, call the jobFinished() inside the onPostExecute() method of AsyncTask and pass the jobparamters and false. The jobParamters are are a bundle of key value arguments that are passed in when the job starts. And a boolen parameter, and here we pass false, because false here represent whether or not the job needs to be rescheduled. The job was successful. So, there's no need to reschedule, which is why we're passing false here.

onStopJob(JobParameters jobParamters) Well, it gets called if the requirements of your job are longer met. So for example, let's say tat you maybe specify a job that wants to upload large video fiels. And because you want to be kind to your users data plan, you say that you're going to do this over wifi. In this case, onStopJob would be called if for example you started this job when the user was on WiFi. And then in the middle of uploading this huge file, the wifi connection get shut off. So in our case what we're going to do here, is we're going to see if our AsyncTask is null. And if it's not null, we're going to go ahead and and cancel that backgroundTask. And return true from the onStopJob() method. And what returning true means, is that as soon as the condition are re-met, the job should be retried again.

System Broadcast Intent
A special intent sent by the system when events occur on the phone

Broadcast Receiver
Core android component that enbles applications to receive intents that are broadcast by the system or by other applications. A broadcast Receiver can be triggered even when the app is not running.

IntentFilter Expression that says what intent should trigger your component. It should be noted that intent filters are not specific to broadcast receivers. For example, the first activity in your app always has an intent filtet. This intent filter uses the action Main, and the category LAUNCHER, as seen here. This signals that this is the main entry point for your app. And when the launcher starts an intent to launch the app, this is the activity that should be triggered by that intent.

Two ways to create a Broadcast Receiver

  1. Statically
  2. Dynamically

The difference is that static is broadcast receivers are triggered whenever the broadcast intent occurs. The receiver will be triggered even if the app is offline. This is opposed to dynamic broadcast receivers, which are tied to the app's life cycle. Generally, if possible, it's better to create a dynamic broadcast receiver, or to use job scgeduling, if you can. Relying on static broadcast receiver can be problematic. It involes registering the receiver in the manifest, and using the XML intent-filter tags and this will trigger the a broadcast receiver java class that you mentioned in the xml code. It would be a class that extends BroadcastReceiver and implements the onReceive method, onReceive takes a context and the broadcast intent that triggered that broadcast. Because this is a static BroadcastReceiver that was defined in the manifest, this BroadcastReceiver would be triggered whenever a photo was taken, even if your app isn't running. Now, this is very powerful, but it's also very easy to abuse. Just think of what would happend if the user had downloaded ten apps that were all using the new picture taken broadcast. Whenever a new picture was taken, suddenly a bunch of apps would be triggered, and the device would slow to a crawl. Because of this, some broadcast intents won't even let you statically define a broadcast receiver. Specifically, intents that have the FLAG_RECEIVER_REGISTERED_ONLY. So if you need to execute code when your app isn't running, Schedule a job if possible. Jobs have constraints for the most common situations, as we mentioned, such as wifi and charging. And jobs are set up to be very battery friendly. That said, in some cases, you will need to register a static broadcast receiver. These are usually cases where you need to catch certain specific intents on older devices when the app is not running. But if you need to listen for system broadcasts when the app is running, dynamic broadcast receivers are your best option.

Helpful adb Commands

To simulate the phone being unplugged from usb charging you can use:

adb shell dumpsys battery set usb 0

or if you're on a device Android 6.0 or higher you can use:

adb shell dumpsys battery unplug

To "plug" the phone back in, just reset it's charging status using:

adb shell dumpsys battery reset

Getting the Current Battery State
As mentioned, our code currently contains a bug. Our app adds and removes the dynamic broadcast receiver in onResume and onPause. When the app is not visible, the plug's image will not update. This can lead to the plug sometimes having the incorrect image when the app starts.

Now we could move the code to dynamically add and remove the broadcast receiver in different lifecycle methods, for example onCreate and onDestroy, but this would cause us to waste cycles swapping around an image which isn't even on screen. A better approach is to check what the current battery state is when the app resumes and update the image accordingly.There are two ways to do this, depending on whether you're on API level 23+ or before.

Getting Charging State on API level 23+

To get the current state of the battery on API level 23+, simply use the battery manager system service:

BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
boolean isCharging = batteryManager.isCharging();

Getting Charging State with a Sticky Intent

Prior to Android 23+ you needed to use a sticky intent to get battery state. As we've seen, a normal, broadcasted intent will be broadcasted, possibly caught by an intent filter, and then disspear after it is processed. A sticky intent is a broadcast intent that sticks around, allowing your app to access it at any point and get information from the broadcasted intent. In Android, a sticky intent is where the current battery state is saved.

You don't need a broadcast receiver for a sticky intent, but you use similar looking code to registering a receiver:

IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);

Notice how registerReceiver is used, but instead of passing in a broadcast receiver, null is passed. The intent filter here is the intent filter for the sticky intent Intent.ACTION_BATTERY_CHANGED. The registerReceiver method will return an intent, and it is that intent which has all of the battery information, which you can use:

Stage 10 Completed!

View
In order to build UI in Android, we use Views essentially, a view handles drawing and event handling

ViewGroups
A ViewGroup is a container for children views and is the base class for all layouts in android. It's worth mentioning here that a view group enhanced all the layouts extends from the base view class, which means that any layout is a view itself. That means you can nest view groups and layouts within each other, opening up tons of possibilities of designing any layouts you can imagine but be careful. Nesting too many layouts in one another, may effect the overall performance when rendered.

ConstraintLayout
ConstraintLayout allows you to create a complex layout without having to nest ViewGroups inside each other. The underlying structure of a ConstraintLayout is much simpler. And hence, it usually outperforms other layout types. The ConstraintLayout is built as a separate support library that works on all Android versions back to the Android 2.3

To define a view's position in a ConstraintLayout, you must add two or more constraints for each view. each constraints represents a connection or alignment to another view or to the parent layout

Vector images like this SVG file are the best to you in your Android application. Because they can resize easily to work with any phone size and resolution without affecting the quality of your image

A general rule of thumb is to have a maximum of 80 views in a single layout, and never more then ten nested view groups.

Android offers a really cool library called the Data Binding Library. This can help us link any UI with actual data without having to call the findViewById for every view item.
How to use the DataBinding Library

  1. Enable data binding in build.gradle
  2. Add as the root tag to the UI
    1. Android automagically generates a binding class for any layout that includes this tag as its root.
  3. Create a binding instance
  4. Set the content view using DatabindingUtil
  5. Bind each attribute in the views to the corresponding data

Note:- Android Studio 2.1 and above support DataBinding and to API level 7+

Accessibility
Accessibility refers to the design of products, devices, services, or environments for people who experience disabilities. Android provides accessibility features like Accessible means your app can be utilized by users have visula or physical limitations, that prevent them from fully seen or using a touch screen. Along with users hearing loss who may not be able to perceive audible information and alerts

Internationalization Localization (also known as Internationalization) is the adaptation of a product or service to meet the needs of a particular language, culture or desired population's "look-and-feel". Internationally friendly, means that your app can be translated to other languages without effecting the integrity and layout design. Specifically, for a right to left languages.

ContentDescription is just one of many things you need to consider when building your app for accessibility, others include:

  1. Enable focus-based navigation which makes sure users can navigate your screen layouts using external hardware like bluetooth keyboards.
  2. No audio-only feedback which guarantees any audio feedback to always have a secondary feedback mechanism to support users who are deaf or hard of hearing

*TextViews typically don't need any content description as the screen reader can simply read out the text, specially when the text view is a label describing something itself!

Input EditTexts are identified by the screen-reader and hence do not need any extra contentDescription.

Text-based Buttons typically don't need any content description as the screen reader can simply read out the text inside the button.

Text labels, text-based Buttons and EditTexts don't really need a content description as the screen reader can easily read the text inside.

However Images and image-based Buttons do need some explanation to help those who cannot see them well.*

*Sometimes however, you would still want to use the strings recourses for strings that you don't intend to translate, this includes strings representing identifiers for views or variable names os string formats etc.

For those strings, there's an attribute called translatable that can be set to false to indicate that this string recourse should not be translated.

hh:mm a *

Keep in mind that these Start and End attributes are relatively new, so to support older devices (prior to 4.1) you should still backup the Start and End margins with the outdated Left and Right ones with the same values, and if your app ends up running on a more recent device the Left Right margins are ignored and the Start End ones are used instead.

Responsive Design
A responsive UI is simply a UI that reacts to the amount of available space in the screen

Use to include pieces of xml chomuus and make a big chomu layout

Note:- Cahe id xml code m asse hi kyu na ho like mera_naam par DataBinding library iss ko asse bana de ga meraNaam samjha ghochu, java code m access karne k liye

agar tu kisi xml layout k andar include tag k through koi chota xml layout inject karta hai toh tere ko DataBinding m uss layout id ko nest kar ke fhir un view ki id ko nest karna hoga jo small layout xml file m the agar tu ne DataBinding instance ko reference big xml file ka diya hai jis m small xml file include tag k through inject ho rhi hai, samjah chhottu

layout-land values-port

Stage 11 Completed!

Users judge the quality of your app within the first 30 seconds. It may not be fair, but, a disproportionate amount of that judgement will be based not on the functionality, but on the visual aesthetics. Does it look polished? Does it look professional? How easy is it to use?

Mockup
A model of an app, used for design evaluations.

keyline
Lines that specify the size and spacing of app components.

Material Design
A set of principles for creating useful and beautiful visuals.

Primary Color
The Primary color is the main color base for your app. Views and components like the menu bar will generally include this color.

Accent Color
The accent color is typically brighter, and serves to draw attention to key elements like buttons in an app.

The default text for Android is a font called Roboto, which is designed to work across a range of differently sized platforms. And it comes in a variety of font families.

Font Families
Font-families are ggroups pf fonts that share similar design characterstivs like serif or sans-serif

Scale-independent Pixels
Scale-independent pixels will stay the same physical size accross different resolution screens. Text size is measured in units of sp.

Style
A style is an xml resource file, separate from the layouts, where you can set all these properties in one place. Then later, we can apply that style to any view we want

Theme
And a theme is created in the same way, and is just a style that's applied to an entire Activity or application and not just one view

Style Inheritance
Styles and themes use inheritance similar how classes use inheritance. If a style has a parent, it will inherit all the properties that the parent style include, whether that's color or padding or more. And then it can define only the properties that you want to add to that style or override in the parent class.

Responsive Design
Responsive design is the act of creating designs that respond to and work well on a variety of screen sizes, shapes and orientation

Android categorizes the device screens using two general properties, size and pixel density.

Density Density is the number of pixels in the physical area of the screen, and it's often measured in dots per inch or "dpi."

Density-Independent Pixels
Density Independent Pixels, or dips[dps] for short, are the same physical size on each device.

All externalized Android resources, everything from strings to layouts to drawables and animations, are all stored within your project's res folder.

Android allows you to create alternative versions of every resource by placing them into folder with different qualifiers. We separate each of those using a hypen. And we can add those qualifiers based on anything, from language and/or dialect to whether the device is docked, the type of touch screen, the pixel density of the display, the orientation of the screen. And most importantly, for a responsive design in particular, the samllest available screen width which you can support with that layout. At runtime, android will check the current device configuration, its language, its screen size, pixel density, everything, and then load the right layouts, strings, and drawables accordingly. And you can even chain these qualifiers togeter. For example, to create a different layout for German language users.

And when it comes to providing alternative layouts, Android has gone through a few alternative models, starting in the early days with this screen buckets idea of small, normal, large, and extra large. But since Android3.2, the new smallest width qualifier has given us much more fine-grained control over our layouts.

res/
    values/
        strings.xml 
    layout/
        main_activity.xml
    values-fr/
    values-fr-rCA/
    layout-desk/
    layout-stylus/
    drawable-xhdpi/
    layout-land/
    layout-sw720dp/

Say your app is on a Nexus 5, Which has dimensions 360 by 640dps. Android knows that the smallest width, no matter te orientation of this device, is 360dp.

You can also use the smallest width qualifier to create alternate values resource folders.

600dp is the usual cutoff for 7 inch tablet screens

<style name="ActionBar.Solid.Sunshine.NoTitle" parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
        <item name="displayOptions">useLogo|showHome</item>
        <item name="logo">@drawable/ic_logo</item>
</style>

Remember the selected states are as follows: android:state_pressed, android:state_activated, and android:state_selected.

Concepts from this stage

  1. Colors and fonts
  2. Styles and Themes
  3. Touch Selector
  4. Resource qualifier

Stage 12 Completed!

The End

gdcs's People

Watchers

James Cloos avatar Swati Singh avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.