This chapter takes a look at how Android stores data and provides content to an application, using what the Android 7.1.1 (and earlier) OS “jargon” calls a content provider. This chapter covers how to share the provided content, as well as how to access, modify, update, and delete the data that these content providers provide. You will also take a look at some of the content providers for contact management that come installed as a part of Android 7.1.1 OS itself.
The topics have become significantly more advanced as you have progressed from one chapter to the next over the course of this book, and this chapter is no different. Data structure access is significantly more complex than event handling, multimedia content, or even UI design. This is because it involves SQLite database design, and therefore, you need to know how any given database is designed, in order to be able to access its database structure correctly.
For this reason, I am going to provide you with the foundational basics of database design during this chapter, as I have often done for other core topics for which deep knowledge is needed for Android 7.x development, such as user interface design, digital imaging, 2D animation, digital video, digital audio, services, threads, and processes.
Content provider (database) usage in Android 7.1.1 also involves requesting security permissions for the application, for different types of content provider (database) access, such as “read” and “write” access. As you’re probably surmising by now, since you have been paying close attention during this book, this is accomplished using your Android Manifest XML application definition file, by adding in the appropriate Android <permission> child tags.
This chapter begins with a high-level overview of exactly what Android content providers are, as well as what they can do for your Android 7.1.1 applications and your end users. After that, you will learn some foundational information regarding database theory, and learn about a SQLite database API used in Android OS. Then you will take a look at the various database structures provided with the Android OS, which you can use with your contact management and new media endeavors, since they have already been created and installed in the Android OS. After that, you will create a ContactManager Activity subclass, which will allow you to learn the basics of how data is accessed.
Overview of Android Content Providers: Sharable Data
The term “content provider” is unique to Android OS development. It means nothing more than a datastore of data values, and is primarily found in the form of SQLite databases, which are an integral part of the Android OS. You can use the content provider SQLite databases that are provided as an integrated part of the Android OS, or you can create your own content provider databases for your application if you want, although that topic is too advanced for this Absolute Beginner’s book. If you want to look for books that are dedicated solely to the topic of Android SQLite database design, go to www.apress.com and enter “Android SQLite” in the search field at the top.
An Android content provider provides you with access to sharable data structures , commonly called databases, which the Android OS has chosen to use for their sharable data structures because databases have the most high-level features, which unfortunately also makes them much more complicated, hence this final (advanced) chapter. The general high-level procedure for utilizing a database management system (DBMS ) is as follows:
Get permission to read from a given database. If you wish to modify your database’s content, you will also need to get permission to write to your database.
Query (search for and find) the data in the database management system (SQLite) using the “key,” which, in Android, uses the _ID data field. You will be learning about fields very soon.
Access (read into memory) the data in the database management system (SQLite) once you have located it using the query and _ID key. The SQL and SQLite DBMS is open source (free).
Modify (over-write, append to, or delete) the data in your database management system (SQLite) once you have located it, read it, and ascertained that your data needs updating.
When accessing data, you might read the data, write to the data (change the values of the existing data), append (add) new data onto the database structure, or delete existing data, based on the type of permission and level of security permission that has been established for your application in the Android manifest XML file. Data can be in Android internal (operating system) memory; in an internal (Android API) SQLite database; or in an external Android device storage location, such as an SD card, or even on an external database server, which would be remote to Android OS as well as being remote to the Android device hardware (and require network connection).
Database Fundamentals: Concepts and Terms
A database management system, or DBMS, is a data storage system that I like to call an “engine” as it is actually a collection of algorithms at its core, which can store data over long periods of time (depending on the storage hardware medium) so that it can be accessed, read, and updated. This capability is quite desirable for a software development platform like Android. If you have never been exposed to database technology, this section covers the database fundamentals. Popular database software packages that you may be familiar with include Oracle, Microsoft Access or Claris FileMaker Pro. The company that is currently developing FileMaker is known as FileMaker, Inc.
As you know, there is a complete open source DBMS API inside of Android OS called SQLite, which is actually something called a Relational DBMS, or an RDBMS. An RDBMS is based on relationships that can be drawn between data that is arranged using tables. These data tables support rows of data and columns of data. This is similar to a spreadsheet program like Excel, except that data in a relational database is not usually visible all at the same time, like data in a spreadsheet. It is important to note that you can generate reports using a database that can achieve the same result, if you want to (once you learn about all of the programming that is involved).
Each database table column contains a similar type and classification of data within any given database record structure, and this column is generally called a database field. This means that, conversely, each row in your database table represents one entire database record. Generally, when you are writing your database records, you will write one entire row or record of data when you first add that record, usually using a form in the front-end (user interface) application allowing users to interact with the database engine itself. On the other hand, when you search a database for information, you will generally be looking through just one of the data table’s columns, a collection of one type or classification of data field, for a specific piece of data or information.
Database columns (fields) can contain many different data types, such as numbers, text, or even references to data stored somewhere else, outside of the database structure itself, such as an image file on a hard disk drive. It is important to note that each data field needs to contain the same exact data type as the other data fields in that same column, as you can see in Figure 14-1. Each row is a database record, and a database record will usually contain all sorts of different data types across the different data table columns.
Figure 14-1. Basic overview of an RDBMS database (SQLite)
The classifications of data fields that Android SQLite data records contain usually spans names (text), numbers (int), and references (addresses) to things such as e-mails, websites, social media profiles, passwords, and so on.
Caution
Once the record structure and data fields that define your DBMS record structure have been set up, make sure not to change this record structure later on, if you are designing your own database. This is because currently loaded records (the data field organization) may not fit into the new database structure definition correctly. It is best to design your database structures up-front. The database design process is especially critical to the success of your DBMS project over time.
One of the most popular database programming languages in the world is called SQL, which stands for Structured Query Language. You will be learning about SQL, as used in SQLite API, later on in the chapter.
The structured part comes from the structured tabular format of a relational database, and the query part comes from the fact that these tables of data are designed to be efficiently searched through, using a specific data value. The language part comes from the fact that SQL has evolved over time into a database programming language, which is quite complex and involved. In fact, I would speculate that there are almost as many books regarding SQL, DBMS, and RDBMS topics as there are on Java 8 programming for the Android OS.
If you have a massive amount of data fields (columns) in the database table, you will probably want to optimize your database using more than one table of data. In real-world database design, the theory of which is largely beyond the scope of this introductory book, you will want to have more than one database table for search and access performance, as well as for organizational reasons. In fact, the Android OS uses more than one database table for its end-user information storage and access, as you will soon see, later in the chapter when you get into the Contacts database table structure. This is quite complex, spanning quite a large number of database tables.
The way to create multiple database tables that act together as one, massive, unified database is to have a unique key (unique index) for each record in each of the tables. That way, information for a single data record can span more than one database table, by using this unique key. In Android OS, this key is called an ID and is always designated using the system database constant _ID in Android’s SQLite databases. For instance, if the key, or _ID value, is 154, your e-mail information and phone information could be contained in two different data tables, but stored under that same key (index) value, and therefore will always be accurately associated with the correct Android user account even though the associated data will most likely span across multiple DBMS tables.
SQLite : An Open Source Database Engine
As you know, the SQL in SQLite stands for Structured Query Language. The “Lite” part denotes that this is a lightweight version of an RDBMS, intended for embedded use in consumer electronics devices, and not a full-blown DBMS, as would be used on an advanced computer system such as a LAMP (Linux, Apache, MySQL, PHP) database server. It’s also interesting to note that SQLite is included in WebKit HTML5 browsers. All you really need to know about SQLite API is that it is part of the Android OS, and that you can use it for data storage.
Note
If you want to research SQLite a bit more on your own, which would be a great idea, if your Android application needs to leverage SQLite databases extensively, SQLite has its own website! This website is kept up to date on a regular basis. You can check it out at: http://www.SQLite.org
There’s a full SQLite API package included with the Android OS that contains all of the DBMS functions needed to work with SQLite. These are contained in a series of classes and methods in the android.database.sqlite package. All that you have to do is learn how to use them properly, which is not at all a simple task, given that this SQLite database structure complexity has evolved during over the 25 versions of Android (7.1.1 API Level 25). I’ll introduce you to the basics during this chapter, so this book provides coverage of all the key Android component types.
SQLite is designed specifically for embedded systems use (similar to JavaME’s memory footprint), and as such, it has only a quarter megabyte (256KB) of total memory footprint. This memory space is utilized to host relational database engine implementation. SQLite supports a minimum (standard) set of relational database functions and features, including the most common SQL syntax keywords; basic database operations like read, write, and append; and prepared statements. These features are enough to provide robust Android OS database support.
SQLite supports three different data types: TEXT, which is known as the String value in Java, INTEGER , which is known as the long value in Java, and REAL, which is known as the double value in Java. When working in SQLite, all other programming data types must be converted (also referred to as “cast” in Java 8) into one of these SQL compatible data types, before entering them into any database data field.
It is important to note that SQLite doesn’t validate any of the data types that may be written into its data fields (table columns) as being one of the required data types. This means that you can write an INTEGER value into a TEXT (String defined) data column, and vice versa, so you will always need to pay close attention to exactly what you are doing with SQLite for this reason. If you don’t validate what you are doing using your Java code, you may get a wrong data type result written into one of your SQLite database fields.
To use SQLite in Android, you construct your SQLite statements for creating and (or) updating your database, which will then be managed by Android OS. When your app creates a database, the database structure will be kept in a specialized Android directory, which will always utilize the following Android OS database path address:
DATA/data/YOUR_APPLICATION_NAME_HERE/databases/YOUR_DATABASE_FILE_NAME_HERE
Next, you will take a look at the many different types of predefined content providers that come standard with the Android OS. You will also be looking at how these are accessed within the Android 7 operating system and its android.content package. You will also be looking at the content provider and content resolver classes and methods used in Android to access its internal database structures that are actually a part of the Android OS.
There are a plethora of Android database structures for all of the different functional areas in the OS. This is why you are getting up to speed on this in the next section, because as you learned in the first part of the chapter, the first step in using any SQLite DBMS is familiarizing yourself with, and completely understanding, its database structure.
Android’s Built-In SQLite DBMS Content Providers
A significant number of SQLite-based database structures are “hard-coded” into the Android OS, so that users of Android devices can handle things that they expect from a phone, iTV set, e-reader, smartwatch, or tablet. These include contact directories, address books, calendars, camera picture storage, digital video storage, music albums (digital audio storage), phone books, and so forth.
The most extensive of the SQLite database structures is the Contacts database, which contains many different tables (essentially acting as sub-databases, or sister databases, if you like) containing personal information, such as contact names, phone numbers, e-mails, preferences, social media settings, and so forth. These structures are very complex, and since this book is focused on programming for Absolute Beginners, and not database theory, you will be working with the primary contact name database, to keep it more about Java programming and Android content providers, rather than about database structure and theory.
The base-level interfaces of the android.provider package allow you to access those data structures that define the setup and personalization of each user’s Android device hardware. Obviously, the data in each of these data structures will be completely different for each user’s smartphone, smartwatch, tablet, phablet, e-reader, iTV Set, or automobile dashboard.
Android 1.5 Contacts Database Contact Provider
Table 14-1 lists the now deprecated Contacts database interfaces for Android 1.5, 1.6, or 2.0, which can be found on the Android Developer site. Deprecated, in this case, means that this Contacts database has been replaced with a more modern ContactsContract database structure. However, the Contacts database structure is still valid, and will work just fine for those users who are still using Android OS versions 1.5, 1.6, or 2.0. The Contacts DBMS structure, shown in Table 14-1, was redone from scratch, starting in Android OS Version 2.1.
Table 14-1. Original Android Contacts database and its data table interfaces to be used for Android 1.5, 1.6, or 2.0 support
Database.Table | Content Description of what is Held in this Database Table Structure |
---|---|
Contacts.OrganizationColumns | Organization |
Contacts.GroupsColumns | Groups |
Contacts.PeopleColumns | People |
Contacts.PhonesColumns | Phone numbers |
Contacts.PhotosColumns | Contact photographs |
Contacts.PresenceColumns | IM presences |
Contacts.SettingsColumns | Phone settings |
Contacts.ContactMethodsColumns | Contact methods |
Contacts.ExtensionsColumns | Phone extensions |
As mentioned, if you browse the current Android Developer website documentation, you will find that these interfaces listed in Table 14-1 are all described as being deprecated. The reason that these are called interfaces is because they define how and where you are going to interface with the data, using the format database.table, so a table that has people in it is referenced using Contacts.PeopleColumns, as you can see in Table 14-1, row 3. You will be taking a look at how these structures have increased in complexity later in the chapter.
Deprecated Database Structures: Software Upgrades
Deprecated is a programming term that means that classes, methods, constants, interfaces, and even database structures have been replaced by other more modern programming or data structures. This usually happens during the release of newer versions of a programming language (such as Java 8) or a new Android API version such as the recent Android 7.1.1.
These newer structures replace the older structures, and are usually more robust (fewer bugs), or more complex (more features), but sometimes they will only differ in how they are implemented. In the case of a database, they sometimes differ in regards to how the data fields are distributed amongst the database tables that contain them.
This deprecation is exactly what has happened with the Contacts database interfaces between Android versions 1.x (1.0, 1.1, 1.5, and 1.6) and 2.0, and Android versions 2.1, 3.x, 4.x, 5.x, 6.0, and 7.x. So database interfaces that work on Android 1.x and 2.0 phones are different than the ones that work in Android 2.1 through 7.1.1 phones. The newer versions use more advanced, feature-rich database structures. If you’re going to support 1.x or 2.0 phones, you’ll use database interfaces listed in Table 14-1. This book uses the Android suggested application support default settings of API Level 15 (Android 4) through API Level 25 (Android 7.1.1), so you need to use a more advanced database structure that replaces the original database structure used prior to Android 2.1 (Level 7).
The good news is that deprecated does not mean disabled. In this case, it more accurately means, “not suggested for general use, unless you need to support pre-2.1 OS versions for your Android users.” So, if you need to support Android 1.5, 1.6, and 2.0 phones, you can use the interfaces listed in Table 14-1. Note that inside Android Studio, deprecated structures and method calls are lined out in the Java code, to show the developer that they are deprecated. As you know, this can be a bit unnerving, since most devices these days are 2.3.7 through 7.1-compatible, so I suggest you take Android’s “advice” and develop for API Levels 15 through 25, or later. This is suggested in the New Android Application Project series of dialogs, which you have already encountered several times over the course of this book.
You will not be able to access data from newer database tables until you add support for the 2.1 through 7.1.1 SQL DBMS structures in your code. You can do this by detecting which OS your user is using, and having code sections that deal with each (1.x through 2.0, versus Android 2.1 through 7.1.1) database access structure differently, using different ContentProvider and ContentResolver Java code structures.
Note
If you want to be able to access every new feature, you can always have your Java code detect which version of Android a device is using, and then use custom code that delivers your optimal application functionality for each specific Android OS version.
Deprecation is a common programming situation that developers need to get used to. Hence, I am covering it during this book as needed, so that as an Absolute Beginner, you can learn all about deprecation now, and not be blind-sided by this advanced programming and application development concept later on down the line.
With Android 7.x OS, deprecation is especially prevalent, as different OS versions will feature different support for the hardware features that manufacturers frequently add to their new smartphones, iTV sets, smartwatches, e-book readers, tablets, game consoles, automobile dashboards, and the like. These usually require new APIs, or changes to the existing Android APIs, in order to support these new hardware features.
For instance, Android 1.5 was initially designed for use on smartphones. Android added touchscreen gestures in Android version 1.6, and camera support in version 2.0. Next, tablets and e-readers came along, and Android 3.0 added feature support for large screen consumer electronics devices such as computers, tablets, or iTV sets.
Later, iTV sets came out in huge volumes, and so Android version 4.0 added more iTV set support, and the TVDPI constant for 1280 by 720p resolutions was added to the API. Next, Android game consoles, such as the nVidia Shield, came out, and faster screen refresh (60 FPS) was added to Android 4.1. Likewise, faster touchscreen refresh (60 FPS) was added to Android 4.2, which focused on enhancing its i3D gaming capabilities.
Recently, smartwatches and smartglasses have become popular, and so the faster Bluetooth 4.0 standard support was added into Android 4.3 and 4.4. Android 5.0 featured new health API additions that allow physical fitness hardware to be utilized with the Android OS, as well as Bluetooth 4.1. Android 6.0 featured new Android TV and Android Auto API additions, as well as Bluetooth 4.2, with Bluetooth 4.3 in Android 7.0, along with the Vulkan i3D rendering engine, and Java 8 support. And so the version enhancements will go on and on, driven by the Android hardware manufacturers, and end users’ demands for increased performance. Manufacturers number in the hundreds internationally, because Android is an “open” operating system platform.
Note
Over time Android version functionality gets more and more difficult to keep track of. Indeed Android already has over two dozen different OS versions (API Levels) that your code should work across. Keeping track of all these current programming constructs, database structures, and logic mazes is enough of a challenge for most, without another layer on top, that involves remembering which Java constructs and interfaces work, or do not work, with any given OS version. This is one of the primary reasons that Android application programmers are so well-compensated financially.
Table 14-2 lists some of the content providers that are compatible with the new Android versions (2.1 through 7.1.1) and that are used for manipulating contact information. A vastly different content provider database structure approach solidified in API Level 8 and beyond may well be the primary reason that the defaults in the New Android Application Project dialog suggests (that is, defaults to) API Level 15 through 25 support.
Table 14-2. ContactsContract database tables in the Android provider package, along with the types of data they contain
Database.Table Interface | Database Table Contents |
---|---|
ContactsContract.BaseSyncColumns | Generic columns used by sync adapters |
ContactsContract.CommonDataKinds.BaseTypes | All type of datatypes supported |
ContactsContract.CommonDataKinds.CommonColumns | Common columns across specific types |
ContactsContract.ContactNameColumns | Contact name and contact name metadata columns in the RawContacts database |
ContactsContract.ContactOptionsColumns | Columns of ContactsContract.Contacts that track the user preference for, or interaction with, the contact |
ContactsContract.ContactsColumns | Columns of ContactsContract.Contacts refer to intrinsic contact properties |
ContactsContract.ContactStatusColumns | Data used for contact’s status info |
ContactsContract.DataColumns | Columns (joined) from the data table |
ContactsContract.DataColumnsWithJoins | Combines all Join Columns returned by ContactsContract.Data table queries |
ContactsContract.DataUsageStatColumns | Columns in the Data_Usage_Stat table |
ContactsContract.DeletedContactsColumns | Deleted Contacts Data |
ContactsContract.DisplayNameSources | DataType used to produce display name |
ContactsContract.FullNameStyle | Constant for combining into full name |
ContactsContract.GroupsColumns | Data used for contact’s grouping info |
ContactsContract.PhoneLookupColumns | Data used for contact’s phone lookups |
ContactsContract.PhoneticNameStyle | Constants for pronunciation of a name |
ContactsContract.PresenceColumns | Additional datalink back to _ID entry |
ContactsContract.RawContactsColumns | Data used for the RawContact database |
ContactsContract.SettingsColumns | Data used for contact’s OS settings |
ContactsContract.StatusColumns | Data used for social status updates |
ContactsContract.SyncColumns | Sync Information across accounts |
All of these Contact related database tables replace the deprecated versions listed in Table 14-1. If you want to look into these data tables in greater detail, detailed descriptions of these are available from the Android developer site at this link:
https:// developer.android.com /reference/android/ provider / package-summary .html
As you can see in Table 14-2, the ContactsContract database table structure is an order of magnitude more complex than the simple Contacts database table structure that was used prior to Android 2.1. With this complexity comes power and flexibility, but at the cost of more complex Java code needed to implement these databases and their features inside your Android applications. This is a complex topic for the Absolute Beginner.
Next let’s take a look at the MediaStore and CalendarContract databases and their tables, and then you will get into how to use the Uri object you learned about earlier in the book, using your content:// content provider URI.
The Android MediaStore Content Providers
The other collections of content providers that you may find important for new media content within the Android OS are the MediaStore content providers. These are listed in Table 14-3.
Table 14-3. The Android MediaStore Content Providers
Database.Table Interface | Database Table Contents |
---|---|
MediaStore.Audio.AlbumColumns | Album information |
MediaStore.Audio.ArtistColumns | Artist information |
MediaStore.Audio.AudioColumns | Audio information |
MediaStore.Audio.GenresColumns | Audio genre information |
MediaStore.Audio.PlaylistsColumns | Audio playlist information |
MediaStore.Files.FileColumns | Fields for master table for media files |
MediaStore.Images.ImageColumns | Digital images |
MediaStore.Video.VideoColumns | Digital video |
MediaStore.MediaColumns | Generic media storage |
Later in this chapter, you will look at how to declare content providers for use, access them, read them, modify them, and append to them. First, let’s take a look at one more often-used Android OS database, the CalendarContract database, and then you will look at how to use Uri objects to reference Android content providers.
The Android CalendarContract Content Providers
The CalendarContract databases include eleven calendar-related databases, each supporting various calendar functions, including events, attendees, alerts, reminders, and other similar calendar-related data support functions.
The reason that the Android operating system provides pre-built support, via its android.provider package, for your Android calendar database access is because it would be logical for applications that access these calendar features to be able to add customized, new capabilities to the existing Android calendar feature set.
Table 14-4 shows the CalendarContract content provider interfaces, as well as the different types of calendar functional data they access, and which they will allow you to reference directly using a content provider.
Table 14-4. CalendarContract databases in the Android provider package, and the type of data that they contain
Database.Table Interface | Database Table Contents |
---|---|
CalendarContract.AttendeesColumns | Columns (joined) from attendees database |
CalendarContract.CalendarAlertsColumns | Data used for calendar alerts function |
CalendarContract.CalendarCacheColumns | Data used for calendar cache function |
CalendarContract.CalendarColumns | Calendar columns that other URIs can query |
CalendarContract.CalendarSyncColumns | Generic columns for use by sync adapters |
CalendarContract.ColorsColumns | Data used for calendar colors function |
CalendarContract.EventDaysColumns | Data used for calendar event day function |
CalendarContract.EventsColumns | Columns (joined) from the events database |
CalendarContract.ExtendedPropertiesColumns | Data Used in Calendar Extended Properties |
CalendarContract.RemindersColumns | Data used for calendar reminders function |
CalendarContract.SyncColumns | Sync info columns used by other databases |
Next, you will take a look at how the content:// area in Android OS is used to access these database structures using a content provider URI. Fortunately, you are already comfortable with Uri objects, so you have a head start. After we take a look at how content:// URIs are used with SQLite databases in Android OS, we will create yet another pure Android design pattern Activity in Android Studio 2.3 to get you more experience with the bootstrap apps that Android Studio will code for you and then get into how to code access and updates to basic contact data records such as those contained in the ContactContracts SQL DBMS structure held in the android.provider package that we learned the basics about during this section of the chapter.
Referencing the Content Provider: Using a Content URI
If you want to be able to tell the Android OS what content provider you want to access, it is important that you understand the concept of the Content URI. You have used Uri objects before, so you are very familiar with the function they play in accurately referencing data (content) pathways in Android apps. Content providers have a specialized path format. Just like the Internet’s HyperText Transfer Protocol has a special format, HTTP://, Android content also has a special format that is very similar (and thus easy to remember), which is: content://.
The complete URI for an Android content provider contained in your URI object will follow this data path format:
content://Authority/Path/ID
Consider in the following (hypothetical) ContactManager Apress Contact database content URI:
content://com.example.user.contactmanager/apress/androidapps/12345
In this imaginary URI, com.example.user.contactmanager is the Data Authority, apress/androidapps/ represents the Data Path, and finally, the 12345 represents the _ID key for the actual Data Record that is being accessed by the URI path (using an Android Uri object).
A Content URI will always contain four necessary parts: The schema to use, in this case, content:// as well as a data authority, an (optional) data path to the data, and the _ID of the data record that you want to access. The schema for content providers is always the word content . A colon and a double forward slash (:// ) always appear in the front of your URI reference, and separates the data schema from the data authority.
The next part of the URI is known as the data authority for the content provider. As you might have expected, the authority for each content provider must be unique. An authority naming convention usually follows your Java package naming convention. Most organizations choose to use the backward dot-com domain name of their organization, plus a data qualifier for each content provider. Thus, the previous example would assume that you own the example.com domain name, which, of course, you do not, as it is owned by IANA.
Since the Android developer documentation recommends that you utilize the fully qualified class name of your ContentProvider subclass, you might then name your ContentProvider subclass ContactManager.java if you were following this example Content URI. I am going to use the ContactManager.java Activity subclass name in the next section, to follow the Java class naming convention used throughout this book.
The third part of the URI standard is the data pathto the data. Although it is optional, it is a fairly standard practice for organizational purposes. You would not usually put your data in the root folder of a server where it would get lost; instead, you would place it in an Apress folder, using subfolders for each of the literary database tables. In this example, one subfolder would be a table named androidapps.
The content provider for the Android MediaStore (which you looked at in the previous section of the chapter) database, for example, will utilize different path names to make sure that the audio, image, and video files are kept in separate data type (and data table) locations. By using different path names, one single content provider can accommodate many different types of data that are in some way related, such as the different new media content types, for example, kept in the MediaStore content provider in the different data tables. For unrelated data types, it is standard programming practice that you would want to utilize a different content provider subclass, as well as a different data authority (and data path, for that matter) for each database.
The last URI reference specification component is the ID, which, as you may have surmised, needs to be unique and numeric. This ID, or _ID in Android, is utilized whenever you want to access one single database record.
So, as you can see, the URI reference specification progresses from the most general or high-level (content://) specification, through the authority (server name), down through the pathway (folder hierarchy) to the database (directory path), and ultimately, to the data record itself (_ID).
Since you are using the default OS support range suggested in the New Android Application Project of API Level 15 (4.0) through API Level 25 (7.1.1), you will use the more modern (that is, not deprecated) content provider for this Android content provider example, which you will be creating during the rest of this chapter.
Let’s get started by creating your new Basic Activity subclass of AppCompatActivity (this will be named MainActivity.java by Android Studio 2.3), since we have already explored the Empty Activity, Navigation Drawer Activity, Fullscreen Activity and Scrolling Activity during other chapters. We’ll call this Basic Activity project the SQLiteProvider project. For your UI design layout, we will take a closer look at Android’s RelativeLayout container class, since it is one of the most popular UI layout containers prior to the CoordinatorLayout introduced with the new Visual Design Editor (and can be used inside a CoordinatorLayout, as you will soon see), and this is a great fit for use with SQLite database tables.
Creating a Basic Activity: The SQLiteProvider Project
Let’s create a new Android Studio project using an Android design pattern (Basic Activity) that we have not used thus far, called SQLiteProvider, so that we can take a close look at at least half of these primary app Activity patterns (notice that some are for helper-activity use, such as login, settings, ads, and maps) during this book.
Go into your DigitalAudioSequencer project, and use the File ➤ Close Project menu sequence, and close the current project, which will open the Android Studio 2.3 start (launch) menu.
Select the Start a new Android Studio project option. Notice on the left that you already have created four different apps, using four of the most popular pure Android design patterns.
In the Application Name field name the Application (and Project) SQLiteProvider, as is shown in the left panel in Figure 14-1, and leave the other options at their default (or automatic) settings, then click the Next button, and proceed to the Select the form factors your app will run on dialog.
Select the default Phone and Tablet option, shown in the middle of Figure 14-2, to create the standard Android application type (rather than using the Wear, Auto, iTV, or Glass APIs), and then hit the Next button. Notice I scrolled the drop-down menu, to show that it does not even offer Pre-API 8 options, so everyone will most likely be using the API 7 and later DBMS structures, which is what my examples will use during the rest of this chapter. That said, I’ll show you in a future section how to manually set minimum and target API levels in the Android Manifest XML definition, in case you ever wanted to develop for Android 1.5 through 2.2.
Figure 14-2. Configure your SQLiteProvider project for API 15 through 25 by using the New Android Project series of dialogs
Select the Basic ActivityAndroid design pattern seen on the left side of Figure 14-3, and click Next. We’ve now covered five of seven Android design patterns that Android Studio will create for you!
Figure 14-3. Select the Basic Activity Android design pattern, and click the Next button, to advance to the Customize dialog
Name the Title SQLiteProvider, and leave the rest of the default Android naming conventions the same, as shown on the far right panel in Figure 14-2. Click the Finish button to have Android Studio 2.3 create your bootstrap project, which we will look at in the next section of this chapter.
Once you become an advanced Android developer, be sure and check out the other two (Tabbed and Master-Detail Flow) design patterns, seen in Figure 14-3, which utilize a more advanced Fragments UI design approach. Fragments are a bit too advanced for an Absolute Beginner’s title.
Examining and Testing Your SQLiteProvider Bootstrap
Let’s take a quick look at how this Android application is set up before we start modifying it, as the best approach for an Absolute Beginner is to make incremental modifications to an already working Android application. Click on the MainActivity.java tab, shown selected in Figure 14-4, and open the import section, by clicking the plus (+) icon on the left. As you can see, we are using the Bundle, View, and AppCompatActivity now used in most all Android 7.x and later appluications, as well as several user interface design classes we have learned about during the book, including the Snackbar, Toolbar, FloatingActionButton, Menu, and MenuItem classes. Since the database code I am about to embark on in this chapter is complex, I wanted to use an Android design pattern which would reinforce what you’ve learned thus far in the book, and then build upon that knowledge with new Android provider classes.
Figure 14-4. Click on the MainActivity.java tab , and examine the classes used (imported), and how the methods are set up
You should be familiar with all of the Java code in Figure 14-4, since we have used and gone over it in previous chapters, which is why I chose this Basic Activity design pattern, so that we could cover other subjects and classes in this chapter. Next, let’s right-click the activity_main.xml file in the /app/res/layout folder, and use Jump to Source to open it in a tab, as seen in Figure 14-5. This top-level UI design should also be familiar, as it is almost identical to the one you learned about back in Chapter 8 (see Figure 8-11), except it references the content_main layout.
Figure 14-5. Open the activity_main.xml tab and examine the top-level CoordinatorLayout and AppBarLayout UI structures
Next, click on the content_main.xml tab, the contents of which can be seen in Figure 14-6, and take a look at the basic Hello World TextView content, inside a RelativeLayout user interface layout container. The RelativeLayout is one of the oldest layout containers in Android, and one of the most popular, along with the FrameLayout, GridLayout and the LinearLayout containers.
Figure 14-6. Click the content_main.xml tab , and examine the top-level RelativeLayout, and its UI configuration parameters
Since RelativeLayout is so popular, I thought we’d take a deeper look at it during this chapter, along with a deeper configuration of the Android manifest XML definition file, while we learned about Android SQLite database management systems and content providers. I’m trying to pack as much basic information about the voluminous Android OS into this Absolute Beginner’s title as possible.
Let’s start with the easy <TextView> child tag which uses wrap_content layout configuration constants and uses the hard coded android:text="Hello World" parameter to configure TextView content. Get some practice using Android constants now, and add a Hello World constant to your strings.xml file and then reference it using @string/hw.
The parent <RelativeLayout> layout container tag is far more complex in its parameter configuration, as you can see at the top of Figure 14-6. It uses a content_main ID, so it can be referenced from the activity_main.xml file’s <include> child tag. It uses match_parent layout configuration parameters, so it fills the parent layout container, and uses four padding parameters to reference the /app/res/values/dimens XML definitions for app dimensions. It defines a scrolling_view_behavior using the app:layout_behavior parameter, defines Context as the MainActivity class, and specifies that it will be shown in the activity_main layout container definition.
We will be adding to this RelativeLayout UI design during the chapter to add more TextView UI elements as well as other UI elements that will allow us to create a front end for this Activity that will allow us to interface with the ContactsContract SQLite DBMS that is part of the Android 7.1.1 OS.
Before we start modifying the Android manifest XML definition for this project in the next section of the chapter, to add SDK support specifications and SQLite DBMS permissions specifications, let’s test the bootstrap code that Android Studio 2.3 created for us to make sure it works, before we start transforming it into a DBMS application.
Use the Run ➤ Run ‘app’ menu sequence to start the Nexus 5 AVD, and launch the SQLiteProvider application, which can be seen in the left-hand side of Figure 14-7, and is thus working well enough to launch in the emulator. Therefore all we have to do now is to test the code concerning the user interface elements (the OptionsMenu and the FloatingActionButton on the upper right and lower right, respectively).
Figure 14-7. Use the Run ➤ Run ‘app’ menu sequence to test all of the features of the bootstrap app’s user interface design
Click the OptionsMenu (three vertical dots) and make sure the Settings option appears, as can be seen in the right side of Figure 14-7. Then click the FloatingActionButton and make sure that the SnackBar appears at the bottom of the screen. This is also shown on the right side of Figure 14-7 (I consolidated screenshots into one Figure). So now you again have a functional (empty) application and user interface ready to use to create your SQLite ContactsContract database management application. Now all we have to do is configure your Android manifest XML definition with database access permissions, and use your AVD’s Contacts app to create dummy test data to use to make sure our database code we will be writing after that is actually working properly. This is a complex topic; there are a lot of steps (and classes) to cover, meaning this will be a long (final) chapter.
Next, let’s take a look at how to use the <uses-sdk> and <uses-permission> child tags inside of the <manifest> tag.
Configuring the Manifest : Uses SDK and Permissions
Open the /app/manifests/ folder, and right-click on the AndroidManifest.xml file, and choose Jump to Source to open it in a new tab. Add a line of markup under the parent <manifest> opening tag and type <uses to get the pop-up helper, and select the uses-sdk option, as is shown in Figure 14-8. Once you double-click on this option, Android Studio 2.3 will add this child <uses-sdk> tag into your parent <manifest> container, and you can then hit the space bar to get your next pop-up helper parameter configuration drop-down menu, where you can select minimum or target API level configuration parameters. This allows you to explicitly define Android OS API level (device software) support.
Figure 14-8. Enter a line of markup after the opening parent <manifest> tag, and type <uses and double-click on uses-sdk
Select, and double-click on, the android:minSdkVersion parameter in the pop-up helper drop-down menu, shown in Figure 14-9, to insert a Minimum SDK Version parameter. Set it to a value of 15 (Android 4), and then follow the same work process to set the Target SDK Version to a value of 24 (Android 7.0) inside of this <uses-sdk> tag.
Figure 14-9. Select the android:minSdkVersion parameter from your drop-down helper menu, and double-click it to insert it
Now your <uses-sdk> manifest configuration is in place, as shown highlighted in yellow in the top in Figure 14-10 and we are ready to do the same work process, only with the <uses-permission> child tag. Type <uses and select uses-permission and hit the space bar and then type android:name (or select it from the helper) and then in the parameter value helper drop-down menu find the android.permission.READ_CONTACTS constant from the Manifest.Permission nested class. This is also shown in Figure 14-10. The documentation containing all of these constants can be found on the Android Developer website, if you are interested, by following this URL:
Figure 14-10. Use the parameter constant value drop-down helper to insert the READ_CONTACT permission in the manifest
https://developer.android.com/reference/android/Manifest.permission.html
Now that you have your permission to READ the CONTACTS database, follow the same exact work process and add the permission to WRITE to the CONTACTS database. This is done by adding a second <uses-permission> child tag to the <manifest> section of your AndroidManifest.xml definition file, as is shown in Figure 14-11.
Figure 14-11. Use the parameter constant value drop-down helper to insert a WRITE_CONTACT permission in the manifest
Notice in Figures 14-10 and 14-11 that you do not have to have the developer information page for the Android Manifest.permission nested class (see previous link) open to ascertain what all of these permission constants do, although reviewing these is a great way to see what you will be allowed to do in your Android applications. This is because the pop-up permissions constants helper has a secondary (pale yellow) pop-up helper that you can use to see what each of these constants are, and what they are used for, as well as how dangerous it is considered for an Android developer to utilize it.
As you can see in Figure 14-12, you have now added child tags (objects) to your manifest that define what device support your Android application will provide, which Google Play Store will use to define which device users will be able to see your application in the store. The uses-permissions (child) objects will be used to define what SQLite operations the application will be able to perform on the ContactsContract Android database.
Figure 14-12. The finished <manifest> child <uses-sdk> and <uses-permission> tags configure how your app can be utilized
Next, we need to create some dummy test data , to use with this app. We can use your Nexus 5 AVD to do this.
Creating Your Dummy Contact Database Using an AVD
When your AVD launches , you may have noticed that it appears to be a fully functional Android device, and for the most part, it really is! Touch the circle icon at the bottom of the AVD emulator to switch from your app test (seen in Figure 14-7) to get the main device screen, shown on the left side of Figure 14-13. Click the apps icon, shown circled in red, and then launch the Contacts app, shown circled in red in the center of Figure 14-13. This will launch the Contacts app, shown on the far right in Figure 14-13, where you can click the Add Contact icon in the lower-right corner (a plus + next to a person). Do that now, as you need to add a few contacts to work with after we start writing code that will display, add to, edit and remove these contacts from the SQLite database management system (API) in Android OS.
Figure 14-13. The Nexus 5 AVD simulates a real-world Android Smartphone, including all OS UI functions and standard apps
The first screen that you will see the first time that you try and add a new contact is the “Your new contact won’t be backed up. Add an account that backs up contacts on-line?” screen, which can be seen on the far left in Figure 14-14. Select the KEEP LOCAL option, which is the Button user interface element on the left, shown circled in red. This will write the Contacts database to your AVD emulator, so that it can be accessed by the app you are going to code during this chapter.
Figure 14-14. Keep contacts local to your AVD, and start creating the first contact by adding the contact name Mister Spock
The next screen, seen in the middle of Figure 14-14, is the Add new contact screen. Notice that it indicates that it is “Saving to: Phone-only (unsynced contact)” on the top left of the form, shown circled in red. Click in the Name field and enter a faux (dummy) name data value.
As you can see in the far right side of Figure 14-14, I decided to use some popular Star Trek characters, including Mister Spock, Captain James Tiberius Kirk, and Nyota Uhura. I entered Mister Spock in the Name field, which uses a symbol that looks like a person (head and shoulders) and the Android onscreen keyboard also appeared, just as it would on a real-world smartphone.
When I was finished, I clicked on the seafoam green add contact icon, shown circled in red at the bottom-right corner of the third screen, seen in the far right pane in Figure 14-14.
This entered the Name of the first Contact record, and I was then ready to add a phone number and an e-mail address, which we will proceed to do next.
The next data field we can add dummy data in is the Phone Number field, which uses a phone handset symbol. Enter a fake phone number, I used 1 234-567-8910, and click the seafoam green enter (done, proceed to next field) button, shown circled in red in the lower right-hand corner of the left-hand pane in Figure 14-15.
Figure 14-15. Enter the phone number and e-mail address data fields, and turn off your on-screen virtual keyboard function
To remove the onscreen keyboard and see the rest of the data field inputs, I clicked on the keyboard icon shown circled in red in the left-hand pane in Figure 14-15, underneath the enter and proceed to next data field entry button. This should toggle off (remove) the onscreen (virtual) keyboard, so we can see the lower data fields.
As you can see in the middle pane of Figure 14-15, the first time that you toggle an AVD virtual keyboard off, you will get the Change keyboard dialog. This dialog provides you with a slider switch, which if you drag it to the left position, will allow you to choose to use your physical workstation keyboard with the emulator, instead of the current setting, which sets a virtual keyboard function to “keep it on the screen while physical keyboard is active.”
After you toggle the virtual keyboard left (off) you will be able to see the rest of the form, which can be seen in the right hand pane in Figure 14-15. Enter an e-mail address in the E-mail Address field, shown using an envelope symbol. I used the made-up [email protected] e-mail address, as you can see at the bottom of Figure 14-15.
Once you have entered the Contact Name, Phone Number and E-mail Address data, as can be seen in the right hand pane of Figure 14-15, you will have enough data to work with during this chapter. Use Check Mark (Done) icon at the top right of the screen, shown circled in red, to enter this data record, and to advance to the next step.
Once you click on the Check Mark button to enter the data record into the ContactsContract database you are creating, you will get a series of screens prompting you to set a series of permissions for the ContactsContract SQLite database structure you are about to create by entering its first data record. This will only happen the first time you add a data record to a new SQLite database structure.
The first screen, shown in the left pane in Figure 14-16, asks “Allow Contacts to access this device’s location?” As this is an AVD and there is most likely no GPS hardware on the motherboard, I selected the DENY button option, since we are focusing just on the ContactsContract SQLite database and its data during this chapter.
Figure 14-16. Select the DENY option for the three Allow Contacts Screens that would actually come up on real smartphones
The second screen, shown in the middle pane in Figure 14-16, asks “Allow Contacts to access your calendar?” As we are not dealing with the CalendarContract database (see Table 14-4) during the remainder of the chapter, I selected the DENY button option , since we are focusing primarily on the ContactsContract SQLite database and its data during the remainder of the chapter.
The third screen, shown in the right pane in Figure 14-16, asks “Allow Contacts to send and view SMS messages?” As this is an AVD and there is most likely no 4G LTE cellular network hardware on the motherboard, I selected the DENY button option, since we are focusing only on the ContactsContract SQLite database and its data during this chapter, and not on SMS messaging APIs, which are too advanced for an Absolute Beginners title. After you finish with these three screens, you should see your newly created Mister Spock contact in your AVD smartphone emulator.
Figure 14-17 shows the completed (first) contact data entry, in the far left pane in red, as it would look on a smartphone when it was actually in use, such as when you were making a phone call to that contact. Now you need to add a couple more data sample contacts, Jim Kirk and Nyota Uhura, and we’ll be ready to start coding.
Figure 14-17. After a completed contact record is displayed, do an Add Contact work process twice more to create 3 records
To do this you need to retrace the nine primary steps that are shown in Figures 14-13 through 14-15. The way to get back to this position in the emulator is to use the circle icon at the bottom of the Android OS UI, shown circled in red in Figure 14-17 in the bottom of the left pane.
This will allow you to again access the apps icon and then click on the Contacts icon. When you do this you will get the Contacts application start screen, shown in the middle pane in Figure 14-17, where you will now see the Mister Spock contact you have added listed inside of the application.
Use the Add Contact icon at the bottom-right corner of the application as you did before, and bring up the screens that you used to enter contact data in Figure 14-14 and 14-15. Enter a Jim Tiberius Kirk record using a false phone number and e-mail, and a Nyota Uhura record using a false phone number and e-mail.
Once you are finished the Contacts entry screen will have all three contacts, which is shown on the far-right pane in Figure 14-17.
Next, you’re going to learn about Android’s RelativeLayout class, so you can create the SQLiteProvider UI design.
RelativeLayout: Create Morphing User Interface Design
The Android RelativeLayout class is a good layout container ViewGroup subclass to learn about in this chapter, as a database data entry UI uses a data entry formUI layout, and that’s what the RelativeLayout class is optimized to provide. The RelativeLayout class is most likely going to be used for applications that need to create user interface layouts that can morph between different screen sizes and shapes. As such, it is a logical fit for use with databases, and a great UI layout container class to learn about in this chapter, as it is frequently used, and covered in all of my Android books.
The RelativeLayout class is subclassed from the ViewGroup class, which you have already read about, so you will use the layout container to create a data entry form and user interface buttons to trigger the database access functions. You will use Button UI elements as UI widgets inside of the relative UI layout container, so that users can click on these Button UI elements to be able to invoke changes to the database based upon the UI elements.
The RelativeLayout class is a public class that extends ViewGroup, so its Java class hierarchy is as follows:
java.lang.Object
> android.view.View
> android.view.ViewGroup
> android.widget.RelativeLayout
A RelativeLayout is a fantastic layout container (class) to use in designing a user interface because it can optimize memory usage by eliminating the need to nest multiple UI layout objects (ViewGroup subclasses). This will keep your layout hierarchy “flat,” with no deep nesting of objects, which improves memory usage and processing performance. If you find yourself using nested LinearLayout containers to create a user interface design, you’ll be able to replace them with one RelativeLayout container, after you learn the material in this section of the chapter.
A RelativeLayout is a ViewGroup subclass that displays child View subclass UI widgets using relative position algorithms. The position of each widget contained inside the parent <RelativeLayout> is specified using a relative positioning algorithm (parameter) specifying sibling elements in the parent layout container or the parent layout container itself. The nested LayoutParams class contains constants that implement algorithms that will scale and position widgets into positions relative to the parent RelativeLayout container (such as aligned to the bottom, left, right, or center) or to neighboring widgets as the size and shape of the Android device display changes. This allows the same UI design to be used on a smartphone, iTV set, auto dashboard, e-book reader, or tablet.
RelativeLayout lets child widgets specify their position relative to the parent latout container or to each other by specifying the ID of the widget (or layout) that they wish to position relatively to. So you can align two elements by right border, or make one below another, have both centered in the screen, or have one centered to the left of the other, and so forth. By default, all child views are drawn at the top left of the layout, so you must define the position of each view using the layout parameters available from the RelativeLayout.LayoutParams nested class. If you want to investigate this class on your own, you can see it at the following Android Developer website URL:
https:// developer.android.com /reference/android/widget/ RelativeLayout.LayoutParams .html
Creating Your RelativeLayout UI for MainActivity
Right-click the strings.xml file in the /app/res/values/ folder and Jump to Source. First, let’s add three <string> constants to serve as Button UI element labels. I added these after the app_name and before the action_settings constants, as shown in Figure 14-18. These will give us buttons to add, edit, and display your Contact database.
Figure 14-18. Add three <string> constants to strings.xml for use in labeling database read and write operation buttons
Change the “Hello World” text value to be “Contact Name Operations” to label your database access buttons. Add a line of code after the TextView and type a left-chevron, as is shown in Figure 14-19, and select the Button widget to add that user interface design element to your design underneath the text title for your user interface.
Figure 14-19. Add a line of code after the TextView, type a left-chevron, and select the Button widget from the helper menu
As you can see in Figure 14-20, this will code a Button child tag, along with the required layout width and height parameters, which you’ll be prompted to select constant values for. Buttons usually use wrap_content, so I used this constant for both. I also added an android:id parameter set to @+id/addContact to allow me to access the Button in Java code and an android:text parameter set to @string/add_name, to add the text label to the Button.
Figure 14-20. Use the Graphical Layout Editor tab to preview the TableLayout filled with Button UI elements
Add an android:id="@+id/screenTitle" parameter to the TextView so that you can position UI elements relative to it. To implement your RelativeLayout positioning between the first Button element and the TextView element (title), type in android:layout_below and select the @id/screenTitle option, seen selected in the menu in Figure 14-21.
Figure 14-21. Wire the Button UI element’ s relative position to the TextView using android:layout_below=“@id/screenTitle”
You now have your UI screen title <TextView> and Add Contact Name <Button> UI elements in place and referencing each other using the Layout_Below algorithm, as shown in the completed code in Figure 14-22. This means if you right align your title, the button will align under it on the right. If you center the screen title, which we will be doing later on, the UI button, as well as anything aligned to it, will follow it. As you will see, RelativeLayout algorithm chains (connections) can be powerful, but will take some getting used to (practice using parameters). I added an android:layout_centerVertical=”true” to center the button. Notice the layout_below will take priority.
Figure 14-22. Referencing a TextView UI element’s master position for a Button UI element subordinate (below) positioning
In this chapter I decided to code the XML markup by hand, to be more advanced than in Chapter 6, where you used the Visual Design Editor to create your markup. You can use the Design Editor tab, shown selected at the bottom of Figure 14-23, to preview what your XML markup is going to be doing. This is especially useful when you are developing RelativeLayout UI container designs, to see what any given chain of inter-connected positioning algorithms referencing your UI widget’s relative positioning is going to produce visually on the screen.
Figure 14-23. Use the Design tab to access the Visual Design Editor to preview your hand coded XML
Copy and paste the Button element underneath itself to create a second Button element as seen in Figure 14-24.
Figure 14-24. Create a second Button from the first, and position it relative to the first with layout_below via addContact ID
Change your second Button ID to editContact, change android:layout_below to reference @id/addContact, and change android:text to reference @string/edit_name. Copy and paste the second UI Button underneath itself, and create a third Button element, as seen in Figure 14-25. Change your third Button ID parameter to be listContact, change your android:layout_below parameter to reference @id/editContact and change your android:text parameter to reference @string/list_names.
Figure 14-25. Create a third Button from the second, and position it relatively, using layout_below referencing editContact
Again, click on the Design tab, at the bottom of the content_main.xml editing pane, and preview the relative positioning results, which can be seen in Figure 14-26. As you can see, each UI element lines up below each other, as specified in each UI widget’s XML markup parameters. The blueprint view shows your entire hierarchy, as well as the classes used, assigned names, widget rendering locations, and pixel dimensions and boundaries.
Figure 14-26. Use the Design tab to access the Android Studio Visual Design Editor, and check your relative UI positioning
To demonstrate the power and flexibility of your RelativeLayout positioning hierarchy, let’s add an android:layout_centerInParent parameter in the TextView UI element, and set its value to true. This will center the title in the Activity screen, and RelativeLayout algorithms in the Button tags will then align the buttons underneath it. As you’ll see in Figure 14-27, I had Android Studio write this markup for me. Notice the algorithms are named (lower case then camel case) the same way that Java methods are named, telling you that these positioning algorithms are actually implemented as (and eventually calling) custom relative positioning algorithm Java methods.
Figure 14-27. Add an android:layout_centerInParent parameter into the TextView UI element, and set its data value to true
To make the screen title more prominent, I also added the android:textAllCaps parameter, using the drop-down helper menu in Android Studio 2.3, as seen in Figure 14-28, and set the data value of that parameter equal to true.
Figure 14-28. Add the android:textAllCaps parameter into the TextView UI element, and set its Boolean data value to “true”
Finally, to center the Buttons in the middle of the screen, underneath the now prominent title, change the android:layout_centerVertical=“true” parameter inside each of the Button UI elements to instead be an android:layout_centerHorizontal=“true” parameter, as shown highlighted in cornstarch blue in Figure 14-29.
Figure 14-29. Change android:layout_centerVertical =“true” Button parameters to android:layout_centerHorizontal=“true”
The final XML markup for the contents of your bootstrap content_main.xmlRelativeLayout should look like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_main"
android:layout_width="match_parent" android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.example.user.sqliteprovider.MainActivity"
tools:showIn="@layout/activity_main" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:text="Contact Name Operations"
android:id="@+id/screenTitle" android:textAllCaps="true" />
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:id="@+id/addContact" android:text="@string/add_name"
android:layout_below="@id/screenTitle" android:layout_centerHorizontal="true" />
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:id="@+id/editContact" android:text="@string/edit_name"
android:layout_below="@id/addContact" android:layout_centerHorizontal="true" />
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:id="@+id/listContact" android:text="@string/list_names"
android:layout_below="@id/editContact" android:layout_centerHorizontal="true" />
</RelativeLayout>
Use the Design tab at the bottom of the XML editor pane to switch to Visual Design Editor mode, and preview the revised design, which now centers nicely, and uses the bottom half of your Activity screen, seen in Figure 14-30.
Figure 14-30. Use the Design tab to access the Android Studio Visual Design Editor, and preview your revised user interface
Let’s use the Run ➤ Run ‘app’ menu sequence, and make sure our UI design looks the same in the AVD emulator as it does in the Visual Design Editor. As you can see in Figure 14-31, the UI design looks good, and I also included a screenshot of the next step (on the right-hand side) where we’ll remove the FloatingActionButton, and use its Java code for the first Button user interface element, which we will then replicate twice more. After that, we will place the database code in the event listener and handler constructs so we can access the database.
Figure 14-31. Use the Run ➤ Run ‘app’ menu sequence and test the RelativeLayout user interface design in the Nexus 5 AVD
Next, let’s remove the FloatingActionButton from the bottom right of the user interface. We will be using the Java code for the FAB to create one of our Button objects, and then copy and pasting it twice more to create the other two Button event handling structures, adding database access code to the Snackbar code.
Remove the Floating Action Button widget from the bottom of the CoordinatorLayout (Figure 14-5), as is shown in Figure 14-32, by selecting that tag’s block of markup, and then hitting the delete key on your keyboard.
Figure 14-32. Remove the <FloatingActionButton> child tag and its parameters, by block selecting it and using a delete key
Next, change the FloatingActionButton code to instead reference the Button class, change fab to add, and reference the addContact Button ID, as shown highlighted in Figure 14-33. Delete the import statement for the FloatingActionButton class, and use Alt+Enter to have Android Studio write a Button class import for you, or add an import android.widget.Button; statement at the bottom of your import statements block, as shown at the top of Figure 14-33 highlighted in light blue. The reason this is highlighted is because I clicked on one of the Button class usage instances in the instantiation statement in order to track the class usage, from import through instantiation through implementation.
Figure 14-33. Replace the FloatingActionButton fab code with Button add code referencing an addContact Button definition
Don’t forget to call your .onClickListener()off of this new add Button object. Your new code should look like this:
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button add = (Button) findViewById(R.id.addContact);
add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
Now you have enough infrastructure in place to start writing the Java logic that will access your SQLite database.
Transform the MainActivity Class for Database Access
As you know by now, the first step in the process of transforming the MainActivity.java Activity subclass is to declare and instantiate your UI Button objects. These are contained inside of a parent RelativeLayout container, which you have already defined using XML markup inside the content_main.xml file in the /app/res/layout folder. Copy the add Button structure underneath itself twice and change add to edit and list as shown in the following Java code, which is also shown in Figure 14-34:
Figure 14-34. Copy and paste your add Button instantiation and event listening /handling structure underneath itself (twice)
Button add = (Button) findViewById(R.id.addContact);
add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Adding to Contact Database", Snackbar.LENGTH_LONG)
.setAction("Action", null).show(); }
});
Button edit = (Button) findViewById(R.id.editContact);
edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Editing the Contact Database", Snackbar.LENGTH_LONG)
.setAction("Action", null).show(); }
});
Button list = (Button) findViewById(R.id.listContact);
list.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Listing the Contact Database", Snackbar.LENGTH_LONG)
.setAction("Action", null).show(); }
});
Now it is time to get into the most difficult sections of this chapter—the Java code necessary to implement the ContentProvider and ContentResolver objects, which are needed to access database structures in Android OS.
Creating Your Custom .listContacts() Database Access Method
Since resolving a content provider in Android takes more than one or two lines of code, in fact it, will involve a do while type of loop to read through the database records one by one, you should add a method call in the list Button object’s event handler to a listContacts() method , which we will be coding next. Let’s add the method call inside of the list Button object’s onClick() event handler, right after the Snackbar “Listing the Contact Database” message creation method chain Java code, as can be seen in Figure 14-35. Once you type in the listContacts(); line of code, Android Studio will give you a lightbulb with an exclamation point in it on the left margin for that line of code. Use the drop-down menu arrow to open the options and select the Create method ‘listContacts’ option, and when the second Choose Target Class drop-down menu appears, choose the MainActivity (com.example.user.sqliteprovider) option, since you want the method coded in the MainActivity.
Figure 14-35. Add a listContacts( ) method call, and select the Create method ‘listContacts’ and MainActivity menu options
This drop-down menu sequence will instruct Android Studio to code an empty Java method body for you to use to code your listContacts() database access method. Once you do this the red code error highlighting will disappear, because a method can now be called. As you can see in Figure 14-36, Android Studio writes the private void listContacts(){...} empty method bootstrap programming structure for you, at the end of the onCreate() method. You’ll be adding the database access Java code to the body of this method to list contacts.
Figure 14-36. A bootstrap private void listContacts( ) method created by Android Studio inserted after the onCreate method
The first step will be to declare and instantiate a Cursor object named nameCursor and load it with the database content you are going to list by using the getContentResolver().query() method call chain. A Cursor object is used to traverse database records looking for the data that you are trying to locate. To create and configure this Cursor database searching engine, use the following line of Java code, which is shown in Figure 14-37, along with a series of several pop-up helper dialogs that contain methods and URIs that you will require:
Figure 14-37. Declare Cursor object named nameCursor and instantiate it using getContentResolver( ).query( ) method chain
Cursor nameCursor =
getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
As you can see in Figure 14-37, once you type the ContactsContract.Contacts (database.table) reference in the .query() method parameter list, and then press the period key, a list of possible URIs will then appear. Find the CONTENT_URI option, and select it, by double-clicking it to insert that URI into your method call parameter. This will create the first ContactsContract.Contacts.CONTENT_URI parameter for your method parameter list.
Remember that you will need to mouse-over the error highlighting for the Cursor object, and trigger the code to Import ‘Cursor’ (android.database) to have Android Studio 2.3 code the import for the Cursor class to use, or simply use Alt+Enter. After the Cursor object is declared and imported, add a space and name it nameCursor, and then use the equals operator to load the Cursor object with the results of the getContentResolver().query() operation. Type “getCon” and select and double-click on the getContentResolver( ) ContentResolver class option, as shown in the Android Studio pop-up Java coding helper, seen in blue on the right side of Figure 14-38.
Figure 14-38. Use the ContentResolver class’s getContentResolver( ) method to load the Cursor object with your query data
Next, type a period after the Cursor nameCursor = getContentResolver portion of the statement, and select the query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) method (shown as being from the Cursor class), as is shown, selected in blue, on the right side of Figure 14-39.
Figure 14-39. Type a period and select query (Uri, String[] projection, String selection, String[] selectionArg, String sortOrder)
Notice that the four parameter options for the getContentResolver().query() method call have been specified for you in the drop-down helper menu, on the left portion, and the class (Cursor) that the method is contained in, on the right portion. The parameter list with object or variable types and suggested name is provided to show you what valid data values go in each of these positions in order for the method call to be valid, even if this is a “null” value, which is used to serve as an unused parameter indicator (that is, no data value is provided, or “none”).
Since this chapter does not cover advanced SQL database concepts, such as projection, selection arguments, sorting order and selection, you are going to be using null values in these optional database query parameters, as can be seen in the completed and error-free line of code, which can seen in Figure 14-42, if you look ahead.
Inside of the query() method parameter area type “Contac” and select the ContactsContract (android.provider) option, from the Android Studio pop-up helper drop-down menu, as is shown selected in blue in Figure 14-40.
Figure 14-40. Type in “Contac” and select the ContactsContract (android.provider) option from the drop-down helper menu
Next, hit the period key and select Contacts from the drop-down helper menu, and then hit the period again and select the CONTENT_URI constant from the drop-down helper menu, as is shown in blue in Figure 14-41. This finishes the first Uri uri parameter requirement and all you have to do is to add the null, null, null, null parameters.
Figure 14-41. Hit a period key and select Contacts from the drop-down, then hit the period again and select CONTENT_URI
Next, add a comma and four null parameters (no parameter specified), and you will have your completed Cursor declaration, instantiation and configuration Java statement, as is shown error free in Figure 14-42.
Figure 14-42. Add a comma and four null parameters (no parameter specied), and you have a completed Cursor statement
It is now time to create a do-while loop construct, which will read through the Cursor object loaded using the ContentResolver object. In the Android OS, a do-while loop begins with the keyword while and then specifies something to evaluate to determine how long to process the statements inside of the do-while loop. In this case, that will be whether your Cursor object, which is used to traverse or read through the database content, has reached the end of the database. This happens when the Cursor object reaches the last (final) record in the database, and cannot read another record, much like reaching the EOF (End Of File) character when reading a file. In pseudo-code, the do-while loop structure , which you’re going to write next, equates to the following logic:
While (there's another record to moveToNext to, and therefore to be able to read, perform this) {
Create String object to hold Contact Name Data; place the name data from a database Column into it;
Use Android Toast and a makeText() method to write this value to the display using a long duration;
}
Add a line of code after the Cursor code and type a Java while keyword and opening parenthesis. Start to type the nameCursor object, and select, and double-click, the nameCursor Cursor object option seen in Figure 14-43.
Figure 14-43. Type while and parenthesis , then type the nameCursor object, and double-click the nameCursor Cursor option
Next, type a period and select the Boolean moveToNext() method from the drop-down helper menu, as shown in Figure 14-44. This will complete the nameCursor.moveToNext() evaluation statement in the while loop condition.
Figure 14-44. Type a period character , and then select the Boolean moveToNext( ) method from the drop-down helper menu
Inside of the while() loop, we will create a String object named contactName, and set it equal to the result of the nameCursor.getString() method call. This will load the contactName String object using the nameCursor Cursor object, which will be accessed using dot notation and the .getString() method. The getString() parameter area will contain a nested Java statement construct that will call the .getColumnIndex() method off of the nameCursor Cursor object. Inside of the .getColumnIndex() method parameter area will be your reference to the DISPLAY_NAME_PRIMARY constant using the ContactsContract.Contact reference path. The full Java statement we will be creating would therefore look like the following, and will be constructed using the Android Studio code helper (coding assistant) work process, shown in Figures 14-45 through 14-47:
Figure 14-45. Create a String object named contactName; set it equal to the result of the nameCursor.getString(int) method
String contactName =
nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contact.DISPLAY_NAME_PRIMARY));
Inside the .getString() parameter area, enter the nameCursor Cursor object, and hit the period key. In the pop-up helper menu that appears, select the getGolumnIndex(String s) integer method as shown in blue in Figure 14-46.
Figure 14-46. Inside the .getString( ) parameter area, enter nameCursor, and hit the period key, and select getColumnIndex( )
Inside the getColumnIndex() parameter area select the ContactsContract.Contacts.DISPLAY_NAME_PRIMARY.
Figure 14-47. Inside the getColumnIndex( ) parameter area select the ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
The second statement will use the Android Toast class, similar to the SnackBar class, to broadcast each name as it is read to the screen. Type the letters “Toa” and select the Toast (Create a new Toast) option in the pop-up helper menu, as is shown in blue in Figure 14-48. This will create the entire Toast.makeToast().show() method chain, instead of a single Toast object, which you would get if you select the other Toast (android.widget) option.
Figure 14-48. Type the letters “Toa” and then select the Toast (Create a new Toast) option seen in the pop-up helper menu
Once Android Studio writes the Toast Java code structure for you, as shown highlighted in Figure 14-49, you will need to add the Context object (this) as the first parameter, the String (contactName) as the second parameter, a display time length as the third parameter using the Toast.LENGTH_SHORT constant to the .makeText() method called off of the Toast object. Then you chain a .show() method call to the end of the .makeText() method call, to complete the complex Java statement structure. This is also shown completed, in Figure 14-50.
Figure 14-49. The Toast messaging construct takes a Context object, a String object, and a Toast.LENGTH_SHORT constant
Figure 14-50. The final Java code structure for the private void listContacts( ) method
Toast.makeText(this, contactName, Toast.LENGTH_SHORT).show();
If you want to research the Toast class (object) further, visit the following Android 7.1.1 developer website URL:
https:// developer.android.com /reference/android/widget/ Toast .html
The final code for the listContacts() method can be seen in Figure 14-50.
Next, let’s take a look at how to do SQLite DBMS write operations, by using the Android ContentValues class.
Writing to a Database: Using the ContentValues Object
Now you are ready to go to the next level of database access and write new data values into your Contacts database.
This is more complex, as well as more advanced, because the WRITE operation can change the database, and is thus termed a destructive database operation in the database industry. Conversely, a database READ operation is inherently non-destructive, as the data is only read, and the SQLite database will not be changed.
In this section of the chapter, you are going to create another custom method for your MainActivity Activity subclass, this time to write new Star Trek officers to the Contacts database, which you are accessing here as an example of how to work with Android ContentProvider, ContentResolver and ContentValues objects.
This chapter is getting a bit long, so I am not going to include a summary of these classes here, however, if this SQLite database management is an area of interest to you, you can get the foundational information regarding these core Android SQLite database management classes by using these Android developer website URLs :
https:// developer.android.com /reference/android/content/ ContentProvider .html
https:// developer.android.com /reference/android/content/ ContentResolver .html
https:// developer.android.com /reference/android/content/ ContentValues .html
Just like you did in the previous section, add an .addContact() method call in the add Button’s onClick() event handler method structure inside of your add.setOnClickListener() event listener Java code construct.
This will force Android Studio to create your bootstrap private void addContact() method structure for you, if you use the Alt+Enter keyboard shortcut, that is.
As shown in Figure 14-51, after you add the addContact(); line of Java code, you will select the "Create method ‘addContact’ option in the red exclamation point (error) drop-down in the left margin by the code statement, and then select the second MainActivity (com.example.user.sqliteprovider) class and package to specify at what level you want this method structure to be coded for you, which would be at the same level as all of the other methods which are currently coded in your MainActivity class in your SQLiteProvider application. As you’ll see in Figure 14-52, Android Studio again codes your new method structure for you, right after the onCreate() method.
Figure 14-51. Add an addContact( ) method call in the add Button OnClickListener( ) and have Android Studio code it for you
Figure 14-52. Declare a ContentValues object named newContact, instantiate it using new ContentValues( ); use Alt+Enter
In the addContact() method, declare a ContentValues object named newContact, add an equals operator, and then use the Java new keyword to instantiate the object, using the ContentValues() constructor method, as can be seen highlighted in Figure 14-52. To get rid of that red ContentValues error highlighting indicating no import statement, use the Alt+Enter keystroke combination to import android.content.ContentValues seen in the pop-up.
Add a second line of code in the addContact() method and type the newContact ContentValues object and then a period character, which will open a pop-up helper where you can select the put(String key, String value) void option, as shown selected in blue, at the bottom of Figure 14-53. This calls a .put() method off the newContact ContentValues object, allowing you to write a data value in the ContactsContract.RawContacts database table.
Figure 14-53. Call a void .put(String key, String value) method off of the newContact ContentValues object, to load a DBMS
For the String key parameter, type in the ContactsContract.RawContacts database table we are using, and type a period character, which will open the pop-up helper, where you can select the ACCOUNT_NAME data field, as is seen in Figure 14-54 highlighted in blue.
Figure 14-54. Add the ContactsContract.RawContacts database table, then hit a period and select an ACCOUNT_NAME field
Add a String object named newName to the addContact() method parameter list, shown highlighted in blue in Figure 14-55, and then reference this newName String object in the .put() method call’s second String value parameter. This will put a new name passed into the addContact() method into the RawContacts database table, by referencing it inside of your newContact.put(ContactsContract.RawContacts.ACCOUNT_NAME, newName); ContentValues.put() database data loading (WRITE) Java statement, as shown highlighted in Figure 14-55.
Figure 14-55. Add a newName String to .put( ) call and addContact( ) parameter String; add “Leonard McCoy” to method call
Since you added an newName String parameter to the addContact method, to pass the new name into the RawContacts database table ACCOUNT_NAME data field, let’s also add a new name (using quotes) into your addContact(String newName) method call, as is shown at the top of Figure 14-55 in quotes, using a green color.
I decided to use addContact("Leonard McCoy"); for my addContact() method call. Leonard McCoy was the Chief Medical Officer on the Starship Enterprise, and was commonly known as “Bones” to the crew and to Star Trek fans. Now we are ready to add some more advanced Java statements involving Uris, ContentUris, and ContentResolvers, which will populate other RawContacts data fields, and then utilize these Uri, ContentUri and ContentResolver objects to move data around between this web of database tables and data fields. You are now beginning to see why working with complex database structures is an advanced endeavor in and of itself, Java coding aside. Your understanding of the DBMS structure must be as advanced as your Java programming skills!
Copy the .put() method call that loads the ContactsContract.RawContacts.ACCOUNT_NAME table with the newName String passed into the method, and paste this underneath itself. Change ACCOUNT_NAME to ACCOUNT_TYPE in order to put the name into this data field. You could also classify your Account (Contact) Name with a classification using this field if you wanted to classify contacts in some fashion.
Next, create a Uri object, name it newUri, and load it using a getContentResolver().insert() method chain, as is shown in Figure 14-56, highlighted in pale yellow. Insert a newContact ContentValues object into the ContactsContract.RawContacts database table using the CONTENT_URI data field constant.
Figure 14-56. Instantiate a Uri object named newUri and then use the getContentResolver( ).insert( ) method chain to load it
The Java statement for this insert operation should look like the following code:
Uri newUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, newContact);
Now we can use this Uri object to create a ContentUri object to write the Contact name date into the data table!
What you just accomplished, in a nutshell, was to create a newContent ContentValues object to hold your content values, loading that object with your passed in newName name String data, using the .put() method calls, and loading these content values into the ContactsContract database RawContacts table by using the getContentResolver().insert() method call to place this value into the newUri Uri object. You are about to convert this Uri object data representation into a different type of (long) data value using the ContentUris class .parseId(Uri) method call. Seems like a lot of matriculation to go through to write a value to a data field, doesn’t it? I can’t say I disagree, but this is how this is done using Android SQLite API classes, methods, and constants.
Now that you have a URI loaded with Uri data for the ContactsContract.RawContacts.CONTENT_URI regarding the newContact ContentValues object, declare a long variable named rawContactsId, and load it with the result (using the equals evaluator) of the ContentUris class’s .parseId() method call, which contains the newUri Uri object as its sole parameter.
Finally, clear the newContact ContentValues object, by calling the . clear() method off of it. This will empty all of the data that you just loaded it with in the previous (first three lines of Java) code, and will thereby clear it for its next use (during the next four lines of Java code). Your Java code should look like the following structure, which can be seen error free in Figure 14-57:
Figure 14-57. Create a long variable named rawContactsId, and set it equal to the result of ContentUris.parseId(newUri)
protected void addContact(String newName) {
ContentValues newContact = new ContentValues();
newContact.put(ContactsContract.RawContacts.ACCOUNT_NAME, newName);
newContact.put(ContactsContract.RawContacts.ACCOUNT_TYPE, newName);
Uri newUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, newContact);
long rawContactsId = ContentUris.parseId(newUri);
newContact.clear();
}
Next, you are going to use the rawContactsId long data value to put this new name data into the ContactsContract.RawContacts database Data table using the RAW_CONTACT_ID key (_ID) constant. You will again use the ContentValues.put() method, called off of your newContact ContentValues object, as shown in Figure 14-58. Now the rawContactsId for the new contact name you passed into the addContact() method is loaded in the ContentValues object. Next, we need to add the MIMETYPE into the ContentValues object as well.
Figure 14-58. Call the .clear( ) method and then the .put( ) method off of a newContact object to clear and load it with the ID
Use another .put() method call to load the ContactsContract.RawContacts.Data.MIMETYPE data field with the ContactsContract.CommonDataKinds.StructureName.CONTENT_ITEM_TYPE MIME type, as is shown in Figure 14-59, using a pale yellow highlight. Adding these classes and constants using the Android Studio pop-up helper drop-down menus will import all of the necessary classes and packages for use with your application.
Figure 14-59. Add another .put( ) method call loading a CONTENT_ITEM_TYPE data constant into the MIMETYPE Data table
Next, you will use a third .put() method call again to place the DISPLAY_NAME data field information for the new contact name into the ContactsContract.CommonDataKinds.StructureName database tables. You select the DISPLAY_NAME constant, as shown in blue in Figure 14-60, after you type a period after the StructuredName table in the Android Studio pop-up helper drop-down menu selector.
Figure 14-60. Add a .put( ) method call to write the newName String to the StructuredName.DISPLAY_NAME database field
The Java code is highlighted in Figure 14-61.
Figure 14-61. Track the newName String object usage from method parameter to three of the ContentValues.put( ) methods
Once the (same) newContact ContentValues object has once again been fully loaded for use, you will again use the getContentResolver().insert() method call chain to insert this newContact ContentValues object into the ContractsContract.Data database table using the CONTENT_URI data field using the following Java statement:
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, newContact);
The final Java code for the entire addContact(String newName) method should look like the following:
protected void addContact(String newName) {
ContentValues newContact = new ContentValues();
newContact.put(ContactsContract.RawContacts.ACCOUNT_NAME, newName);
newContact.put(ContactsContract.RawContacts.ACCOUNT_TYPE, newName);
Uri newUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, newContact);
long rawContactsId = ContentUris.parseId(newUri);
newContact.clear();
newContact.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactsId);
newContact.put(ContactsContract.RawContacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
newContact.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, newName);
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, newContact);
}
The code is shown in Figure 14-62, error free, and is ready to test in the Nexus 5 AVD emulator. You have written a database WRITE method using less than a dozen lines of Java code! Congratulations!
Figure 14-62. Finalize the work process by using getContentResolver( ).insert( ) method chain to insert the new Contact name
Now use the Run ➤ Run ‘app’ menu sequence and launch the Nexus 5 AVD and test the application.
Summary
In this chapter, you learned about Android content provider databases as well as about database concepts, principles, processes, and optimization. You learned about ContentProvider, ContentResolver, Uri, ContentUris and ContentValues objects, and how to use the getContentResolver(),.query(), and .insert() method calls.
You learned all about different types of databases that come with Android, including Contacts, ContactsContract, CalendarContract, and MediaStore, among others. You learned about CONTENT_URI and about what makes up a URI data path reference.
You created your own database access Activity subclass called MainActivity to read from and write to the ContactsContract database in Android. You learned about the RelativeLayout container class and how easy it is to use to make resizable layouts using just a few lines of XML markup.
You created the infrastructure for your MainActivity class and created your listContacts() database READ method and addContacts() database WRITE method. You used these methods inside of your Button object event listener and handler structures to read and write to the ContactsContract database table structures which come with the Android OS. I hope you have enjoyed your journey from Absolute Beginner to Android Developer!