Saturday, August 22, 2015

Content Providers

Looking at the Android API Guide, content providers appears to be the remaining core components that I have not touched upon: http://developer.android.com/guide/topics/providers/content-providers.html.

Turns out, however, that is a specialty case that we are unlikely to use very often.
You don't need to develop your own provider if you don't intend to share your data with other applications.
So, will pass on them for now.

Friday, August 21, 2015

Services and Broadcast Receivers

The Google Cloud Messaging (GCM) implementation was full of services, intents, etc., and while I understood the gist, I felt that it was important to rewind a bit and understand services and working with them a bit more.

The first thing I recall from the GCM implementation is the intent-filter and implicit intents on the services and receivers. So, we learn about these in the context of activities at: http://developer.android.com/training/basics/intents/index.html. The key phrase is:
When your app is installed on a device, the system identifies your intent filters and adds the information to an internal catalog of intents supported by all installed apps.

note: The discussion about services running in the main thread got me sidetracked;  thinking about how Android works under the hood; essentially with a message loop: http://codetheory.in/android-handlers-runnables-loopers-messagequeue-handlerthread/.

Looks like a common pattern is to simply use the IntentService class as it will allow for background processing; separate thread and stopping itself, e.g., a file download etc.

Also, one important thing to note is that services are singletons, i.e., subsequent calls to startService only trigger the onStartCommand method (not the onCreate).

In our example, we will create an IntentService that will run with a delay of five seconds and throw up a status bar notification.


Looking back at the GCM example code then: then: https://github.com/larkintuckerllc/HelloAndroid/commit/3c023cca2c460a81c8f580174c13276b58e10067

we have three exported = false services (i.e., can only be started by components in the application).

RegistrationIntentService

This service has no intent-filters and thus must be started explicitly, i.e., initiated by a call including the class itself.  It extends IntentService; so it runs in its own thread and exits on its own.  It is called from MainActivity's onCreate method (essentially when the application is launched).

Looking at the functionality, it is designed to register the application with GCM (listening to certain topics) and then to pass an identifying token to a backend server (say to allow it to message it individually).

MyGcmListenerService

This service registers itself with the action of (but is can only be initiated by the application itself) com.google.android.c2dm.intent.RECEIVE. It extends GcmListenerService (unclear if it is IntentService under the hood).

Looking at the functionality, it is designed to take action when the GCM message comes in.

note: At first, it was not clear as to why this service had to be started with an implicit intent (likewise with the MyInstanceIDListenerService  service).  But, my general understanding is that it is Google's code that needs to trigger these services and it is not aware of the actual classes (in JavaScript, one would have simply passed call-back functions to Google's code).

MyInstanceIDListenerService

This service registers itself with the action of (but is can only be initiated by the application itself)
com.google.android.gms.iid.InstanceID It extends InstanceIDListenerService (unclear if it is IntentService under the hood).

Looking at the functionality, it is designed to take action (calling the RegistrationIntentService) when something happens to the service that requires it to re-register.

Broadcast Receivers

Just when I was getting a handle on services, it still want clear how the GCM messages received outside the application were triggering the internal only services, e.g., MyGcmListenerService (remember these were not exported).

But, there is another bit of the example that I did not understand, i.e., the receiver in the AndroidManifest.xml and the BroadcastReceiver code in MainActivity's onCreate method.

The answer lies in broadcast receivers: http://developer.android.com/guide/topics/manifest/receiver-element.html.  What makes them a little confusing is that they are somewhat similar to services in that they can respond to intents (e.g, seems very similar to our IntentService).  Seems like the primary difference is that implicit intents and services will only be sent to a single service (OS will limit it to the one that is picked by the user) and intents and broadcast receivers can go to multiple.

com.google.android.gms.gcm.GcmReceiver

This broadcast receiver is registered in AndroidManifest.xml and implemented in Google's code. It is exported with an intent filter looking for:

action: com.google.android.c2dm.intent.RECEIVE
category: com.larkintuckerllc.helloandroid

It also requires the sender to have the permission: com.google.android.c2dm.permission.SEND

This is the missing link that is catching the GCM messages and dispatching them to the locally defined services: MyGcmListenerService and MyInstanceIDListenerService.

mRegistrationBroadcastReceiver

One more broadcast receiver is setup; this one only local (not exported) and is created in the MainActivity's onCreate method (one of those funky anonymous classes) and registered (and deregistered) in the activity's onResume (and onPause) methods.

This is how the MainActivity can be informed when the RegistrationIntentService completes (the broadcast is send at the end).

Security

First one needs to understand permissions: http://developer.android.com/guide/topics/security/permissions.html.

In the GCM example the broadcast receiver registered in the AndroidManifest.xml requires the permission com.google.android.c2dm.permission.SEND on the sender before receiving.  Seems that an application can simply set itself to use this permission to fake send GCM messages (but guess the user would have to allow this to install).

The only other thing that is non-obvious is how does the GCM solution prevent other applications from snooping on the GCM messages (they are broadcast).  Guessing the answer is that the messages are encrypted and unlocked by GcmReceiver using the secret data in the google-services.json file.

Whew!

Wednesday, August 19, 2015

Tools and SDK Versions

Ok, even after reading scores of documents on this topic, the whole business of tools and SDK versions is still confusing.  My best understanding is:

  • Android SDK Tools: Platform independent; use latest version.
  • Android SDK Build-Tools: Platform independent; use latest version.
  • Android SDK Platform-Tools: Platform dependent; backward compatible; use latest version.
  • SDK Platform: Platform dependent; use appropriate version.

note: As before, upgrading to build tools 23.0.0 caused weird errors; staying with 22.0.1.

As for SDK Platform and the appropriate version, there seems to be a lot of discussion on this.

By default, Android Studio will typically use the latest installed SDK Platform.  The argument for this approach is then to allow one to conditionally use the latest features at run-time (checking for version).  In this project, I used the latest version of several libraries, e.g., appcompat, etc, so have to continue to stay with the latest version of SDK Platform.

The opposite argument is to use the lowest possible SDK Platform (above the minSdkVersion) that will compile. This is to catch any incompatibles with hard errors (rather than warnings based on minSdkVersion).



Google Cloud Messaging

In the project that I am working on I had to figure out if I changed the applicationId, would an existing Google Cloud Messaging configuration continue to work.

So, I followed the instructions from Google: https://developers.google.com/cloud-messaging/android/client and set up GCM using the com.larkintuckerllc.helloandroid package name.

It is during the step get configuration file, we provide the package name.  At this point, is not clear if it is the applicationId or the actual package name that is relevant; I used the package name (same as applicationId in one of the applications).  The mysterious file created is:

google-services.json
{
  "project_info": {
    "project_id": "fromandbackagain",
    "project_number": "1009647335975",
    "name":"FromAndBackAgain"},
    "client":[
      {
        "client_info": {
          "client_id": "android:com.larkintuckerllc.helloandroid",
          "client_type":1,
          "android_client_info": {
            "package_name":"com.larkintuckerllc.helloandroid"
          }
        },
        "oauth_client":[],
        "services": {
          "analytics_service":{
            "status":1
          },
          "cloud_messaging_service": {
            "status":2,"apns_config":[]
          },
          "appinvite_service":
          {
            "status":1,"other_platform_oauth_client":[]
          },
          "google_signin_service":{
            "status":1
          },
          "ads_service":{
            "status":1
          }
      }
    }
  ],
  "ARTIFACT_VERSION":"1"
}

Apparently it is the gradle plugin that parses this file and pushes these configuration values into your application.

Going through the documentation, I realized that it got fairly sparse on what appears to be a seemingly overly complicated endeavor.  So, I ended having to download their sample application and copy large chunks of code over to get it working: https://developers.google.com/cloud-messaging/android/start

Purely by accident (left off the plugin step), I figured out the connection between the google-services.json file and my application came in through some generated resources, e.g., R.string.gcm_defaultSenderId. Turns out that the value of this string is the project_number in the json file.

So, my interpretation of what is going on is that all this gobbly-gook with json files and gradle is to have the project_number (identifies clients that are allowed to listen) be automatically be injected as a string resource in the project (thus needing the package name - not applicationId).

The one relatively simple thing was to figure out how to send messages to the application: use a tool, e.g., Chrome application Postman to send HTTP post: https://developers.google.com/cloud-messaging/topic-messaging

Was able to reverse engineer the example code to determine the appropriate to/topics/global

Not sure that I ended up liking the code that I ended up pasting in, e.g,. seemed unnecessarily complicated, e.g., used anonymous classes at one point and there seem to be some extraneous classes.  At some point, I would likely choose to refactor it to my liking.  But are the changes anyway: https://github.com/larkintuckerllc/HelloAndroid/commit/3c023cca2c460a81c8f580174c13276b58e10067

Tuesday, August 18, 2015

Creating a Tablet Only Version

While it is against normal practice, I explored creating a tablet only version of this application and strangely ran into all sorts of issues.

My first attempt was to set the minSdkVersion to 15 (greater than 13) and then use android:requiresSmallestWidthDp="600" to target only large enough screens.  This did not work as expected (a normal size phone seemed to pick up the option to install).

The following article appears to address this: http://developer.android.com/guide/practices/screens-distribution.html

So, I followed their instructions and hope that it is right (would have to find a device that was large screen but was smaller than 600dp on the smaller dimension to see).

Another annoying this is that I was fighting the Designed for phones label in the Play Store.  Tried things like adding a layout for xlarge and ensuring that android:largeScreens="true"  and android:xlargeScreens="true" were set.  After much Googling, it appears that one need to provide screenshots with lots of space being used too.

The result: https://github.com/larkintuckerllc/HelloAndroid/commit/8fbb8c75f34b6848048a251b02ff8928588c68f8

Friday, August 14, 2015

Creating a Library Module

Another way to bring in functionality to our Android application is via a library module: https://developer.android.com/tools/projects/index.html.

A good walk-through: http://hessan.annahid.com/game-development-days/day-9/

An example with changes: https://github.com/larkintuckerllc/HelloAndroid/commit/8a89f92132c759fa5b75f0b0b2144d8c97b4eec7

Maven and jcenter

Ok...  As I continue to scratch my way through the work project I uncover more stuff to learn.  The first is Maven: https://maven.apache.org/.  This software is well embedded in Android Studio (or embedded in Gradle that is embedded).  Either way, it appears to be package management software much like Bower or NPM in the world of web development.

The next piece of the puzzle is jcenter: https://bintray.com/bintray/jcenter. In a nutshell, this is a repository of Maven packages that one can include in their project: http://inthecheesefactory.com/blog/how-to-upload-library-to-jcenter-maven-central-as-dependency/en.

The following are the changes that one makes to use an example package (e.g., joda-time:joda-time):

Changes: https://github.com/larkintuckerllc/HelloAndroid/commit/92e74b50e18908f3e24640e80a014017a410f9b6


Integrating Fabric

First, what is Fabric: http://www.wired.com/2014/10/twitter-fabric-sdk/.

Two, how does one install and use it: http://docs.fabric.io/.  Some tips on getting going with Fabric.

  • Had a hard time signing up for an account with Fabric; maybe you will not experience this.
Ok, bottom line it was easy to setup.  Even tested by creating a crash and saw the results in the monitoring page.


Supporting Multiple Screen Sizes

It is generally good practice to create a single APK that supports multiple screen sizes using alternative layouts: http://developer.android.com/guide/practices/screens_support.html.  At the same time, one can also target different flavors to different screen sizes and accomplish the same thing.

In this example, I am looking to lock the screen in landscape orientation for all activities on xlarge screens and in portrait orientation for all activities on small, normal, and large screens.  As it requires changes to the AndroidManifest.xml document for these changes; this forces us to use the multiple flavors approach.

In my evolving example, I go ahead and create additional flavors:
  • notelephony
  • notelephonytablet (new)
  • telephony
  • telephonytablet (new)
Then update the AndroidManifest.xml files for each with the supports-screens element, e.g.,:

AndroidManifest.xml (notelphony)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.larkintuckerllc.helloandroid" >
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="false"
    />
</manifest>

Using the aapt tool we see that it now reports:
supports-screens: 'small' 'normal' 'large'
This only targets the APK to the proper devices, now to actually force an orientation for the activities:

Now that we getting deeper into merging manifests we need to read up on them more: https://developer.android.com/tools/building/manifest-merge.html.

While the document does refer to a document that can be used to troubleshoot the merge: manifest-merger-<productFlavor>-report.txt, it did not seem to contain the details that I was expecting.  Rather I dug around and found what appears to the the final composite AndroidManifest.xml file under ./app/build/intermediates.

note: While I never found documentation that explains exactly how the merging works (just lots of examples), it is clear that andoid:name has special meaning and the merge process will match blocks up based on it.

So, we add to the notelephony flavor AndroidManifest.xml file the uses-feature for portrait and screenOrientation to each of the activities.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.larkintuckerllc.helloandroid" >
    <uses-feature android:name="android.hardware.screen.portrait" />
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="false"
    />
    <application>
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait">
        </activity>
        <activity
            android:name=".ThingActivity"
            android:screenOrientation="portrait">
        </activity>
    </application>
</manifest

Now this flavor always shows two activites vertically and the aapt tool reports:
uses-feature: name='android.hardware.screen.portrait'
With all this in place, here are the changes to the code to support this configuration: https://github.com/larkintuckerllc/HelloAndroid/commit/09e643979d3e15980382fa31b22dbfdc3cb67cfc




Tuesday, August 11, 2015

Multiple APK Support

Now that we have a better understanding of the build tools, we can dive into the topic of multiple APK support: https://developer.android.com/google/play/publishing/multiple-apks.html. As a demonstration, will be creating two different flavors of the app: one that supports phone calls and one that does not.

note: Not convinced that we have to actually create two flavors, i.e., if the phone feature was used in a limited fashion then seems like one could set the feature to be optional and handle the existence of the feature in the code itself.  For now, however, will plow on by creating two flavors.

For phone calls, the following are important entries in the AndroidManifest.xml.  How these are relevant is discussed in: http://developer.android.com/guide/topics/manifest/uses-feature-element.html.
  • permission: android.permission.CALL_PHONE
  • feature: android.hardware.telephony
Using the aapt tool (on Macintosh look in one's home directory under Library to find), one can dump out the features (and why) that an app requires.  In the case of another project that I am working on I get the following mess (seems overworked).
uses-feature: name='android.hardware.bluetooth'
uses-implied-feature: name='android.hardware.bluetooth' reason='requested android.permission.BLUETOOTH permission, requested android.permission.BLUETOOTH_ADMIN permission, and targetSdkVersion > 4'
uses-feature: name='android.hardware.location'
uses-implied-feature: name='android.hardware.location' reason='requested android.permission.ACCESS_COARSE_LOCATION permission, and requested android.permission.ACCESS_FINE_LOCATION permission'
uses-feature: name='android.hardware.location.gps'
uses-implied-feature: name='android.hardware.location.gps' reason='requested android.permission.ACCESS_FINE_LOCATION permission'
uses-feature: name='android.hardware.location.network'
uses-implied-feature: name='android.hardware.location.network' reason='requested android.permission.ACCESS_COARSE_LOCATION permission'
uses-feature: name='android.hardware.screen.portrait'
uses-implied-feature: name='android.hardware.screen.portrait' reason='one or more activities have specified a portrait orientation'
uses-feature: name='android.hardware.telephony'
uses-implied-feature: name='android.hardware.telephony' reason='requested a telephony permission'
uses-feature: name='android.hardware.touchscreen'
uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
uses-feature: name='android.hardware.wifi'
uses-implied-feature: name='android.hardware.wifi' reason='requested android.permission.ACCESS_WIFI_STATE permission, and requested android.permission.CHANGE_WIFI_STATE permission'
Ok, for our application we startup with a minimal set of feature requirements:
uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
We first try to have the app make a phone call (say on a menu click) with the following:

MainActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_hello) {
        Snackbar
                .make(findViewById(R.id.snackbarPosition), R.string.snackbar_text, Snackbar.LENGTH_LONG)
                .show();
        return true;
    }
    if (id == R.id.action_call) {
        Intent phoneCallIntent = new Intent(Intent.ACTION_CALL);
        phoneCallIntent.setData(Uri.parse("tel:3524748328"));
        startActivity(phoneCallIntent);
    }
    return super.onOptionsItemSelected(item);
}

While the application compiles and even runs, clicking the Call menu item causes a fatal exception (security exception). The simple fix is to add the following to the AndroidManifest.xml file.

AndroidManifest.xml
<uses-permission android:name="android.permission.CALL_PHONE" />

The issue now is that our application now has a feature requirement of telephony and will not be available to devices without this feature (tablets).
uses-feature: name='android.hardware.telephony'uses-implied-feature: name='android.hardware.telephony' reason='requested a telephony permission'uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'

To address this, we will refactor our code to first check for telephony feature ability in the code before trying to make the call.

MainActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
         case R.id.action_hello:
             Snackbar
                     .make(findViewById(R.id.snackbarPosition), R.string.snackbar_text, Snackbar.LENGTH_LONG)
                     .show();
             return true;
         case R.id.action_call:
             if (((TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number()
                     == null) {
                 Snackbar
                         .make(findViewById(R.id.snackbarPosition), R.string.no_telephony, Snackbar.LENGTH_LONG)
                         .show();
                 return true;
             }
             Intent phoneCallIntent = new Intent(Intent.ACTION_CALL);
             phoneCallIntent.setData(Uri.parse("tel:3524748328"));
             startActivity(phoneCallIntent);
             return true;
         default:
             return super.onOptionsItemSelected(item);
     }
}

Then we can change up AndroidManifest.xml to allow the app to be loaded on devices without phones (had to add the READ_PHONE_STATE permission because of the phone check).

AndroidManifest.xml
 <uses-feature android:name="android.hardware.telephony" android:required="false" />
 <uses-permission android:name="android.permission.CALL_PHONE" />
 <uses-permission android:name="android.permission.READ_PHONE_STATE" />

With this in place, we now have the feature requirements as follows:
uses-feature-not-required: name='android.hardware.telephony'
uses-feature: name='android.hardware.touchscreen'
uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps' 
The problem, however, now is that the app still has the call menu item and dynamically changing the menu in code adds a bit more complexity (especially if one has to do it repeatedly).  This is where we use multiple APK support.

Following what we have learned can make two flavors with different requirements:

telephony
uses-feature: name='android.hardware.telephony'uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
notelephony
uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'

We use a single applicationId but two different versionCode entries to distinguish the two flavors.  The version code was selected so that the phone code always have a higher version number (so that devices that meet all the feature sets, i.e., phones, will get the phone code).

With all this done, we go through the publishing process and create two APK files (app-notelephony-release.apk and app-telephony-release.apk): https://developer.android.com/tools/publishing/publishing_overview.html.

The result of all this work can be see in the changes: https://github.com/larkintuckerllc/HelloAndroid/commit/128adfe13bad0f20e794a467e8810e28b9411299.

Saturday, August 8, 2015

Sidebar into Build Tools

In the current project one of the problems that I ran into was that the Google Maps Android API was not working.  The issue ended up being was that I did not have either the Android debug nor the release certificate that was used to authorize the Google Maps Android API in the application (via the Google Maps Android API Key).

While I knew the quick fix was to simply obtain a new key based on my current debug certificate and insert it into the AndroidManifest.xml file.  The problem, however, is that I had no easy way to switch between using keys for debug and release (other than by copying and pasting). But, looking at the result of the Google Maps Android API tutorial I noticed that there is a way to have separate code running when debugging verses when released.  The key is using Gradle: https://developer.android.com/tools/building/configuring-gradle.html

Some tips on going through the material:
  • At first it was not clear that there is a pulldown just above the list of files that allows one to change views of the folder hierarchy, e.g,. Android vs. Project.  In Android view, one only sees the files relevant to the build variant one is working on.
  • In working through the example, I missed the step where one copies demo to full. I mistakenly copied the moved files back in to the main folder hierarchy.  Interestingly, having duplicate xml files seemed to be fine, but having duplicate classes caused compile errors (has to do with how Gradle handles different types of items).
  • While this example walks one through a fairly non-intuitive process of creating flavors, it does not address build types (e.g,. debug vs. release).  Based on what learned, one can interpret the Gradle manual on build variants: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants. In particular, creating a folder as app/src/demo to hold any general debug specific items and app/src/demoDebug for any flavor specific ones.
In this particular case the solution was to create two files:
  • src/debug/res/values/google_maps_api.xml
  • src/main/res/values/google_maps_api.xml
Then we can pull in the string value in AndroidManifest.xml based on whether are running as debug or release.



Thursday, August 6, 2015

Sidebar into Google Maps Android API

While not part of the sample project that I am building, had a need to explore this topic for work.

There not much to say other than it is easy to get going with Google Maps Android API: https://developers.google.com/maps/documentation/android/intro.

A couple of tricks:

  • Google Maps Android API requires Google Play Services which requires API 9 (I was originally setting a minimum of API 7); just need to up the minSdkVersion to 9 in the build.gradle file.
  • The preferred way of getting at location data also uses Google Play Services.  The example provided by Android: https://developer.android.com/training/location/retrieve-current.html is too sparse.  Rather, the following got me there: http://blog.teamtreehouse.com/beginners-guide-location-android
  • Learned that as of today (8/6/15) the system images (emulator) for Android 5.1.1 (API 22) ship with an older version of Google Play Services (6774470) but the Android 5.0.1 (API 21) ships with a newer one 7571470.   So, to use the emulator with the current shipping client (requires 7571000) one needs to use the Android 5.0.1 (API 21) version.

Monday, August 3, 2015

Fun with Fragments

For our simple application (2 activities), we are going to use fragments to allow a tablet to see both the list of things (to the left) and the selected thing (to the right): http://developer.android.com/training/basics/fragments/index.html.

The first step will be to create a fragment for ThingActivity and move its functionality into it.  Using Android Studio's New Fragment > Blank wizard we create ThingFragment.

Will start with ThingFragment as it is fairly easy; only displays the id with no interactivity.  The end result consisted of:

note: Because creating the fragment was pretty much from the tutorial (above), opted to simply summarize the actions here.  Going forward, thinking I will use GIT more effectively to show differences.
  • Creating fragment_thing.xml
    • Changing the provided TextView to have id name.
  • Creating ThingFragment.java
    • Only need to override onCreateView to use the fragment_thing.xml.
    • Implemented a custom method selectThing to set the TextView text.
  • Updating activity_thing.xml; replacing the TextView with the ThingFragment.
  • Updating ThingActivity; replacing setting the TextView text with a call to the custom method selectThing.
Now let us move to MainFragment; this time will share the changes in the commit.  Again, we start with the wizard.  The gist is as follows:
  • Bring layout from activity_main.xml to fragment_main.xml
  • Bring functionality from MainActivity to MainFragment
At this point, we leave out the complexity of the clicking for now.

note: Interestingly, when documenting JavaScript frameworks, I have found that I could include the source code changes in my documentation (as I have done thusfar), but Java / Android code is sufficiently heavy that I have had to change to using GIT commits (and their diffs). 


Ok, now we need to be able to take a click in MainFragment and have MainActivity do something with it; doing it as per the documentation.  At this point, we have gotten the code back to the original functionality.

Changes: https://github.com/larkintuckerllc/HelloAndroid/commit/b7766e8d201172f97e959b0a4e9b002f8c174104

Now, we need make it so that on a tablet we get a single screen with both fragments showing.

Changes: https://github.com/larkintuckerllc/HelloAndroid/commit/3a15f7efc286e79f05639238b63ec9904f078ab5

Sunday, August 2, 2015

Second Activity and Intents

We can use Android Studio's New Android Activity wizard to create an activity called ThingActivity.

Then let us update onClick method of the ThingViewHolder to start this new activity.

MainActivity.java
public void onClick(View view) {
    Intent intent = new Intent(MainActivity.this,ThingActivity.class);
    intent.putExtra("id",  name.getText());
    startActivity(intent);
}

Update the activity's XML and onCreate method.

activity_thing.xml
<TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

ThingActivity.java
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_thing);
    String id = getIntent().getStringExtra("id");
    ((TextView)findViewById(R.id.name)).setText(id);
}

Then there is a bit of work to use the toolbar, e.g., enabling the Toolbar (aka, Action Bar).  There is quite a bit here involving updating activity_thing.xml, ThingActivity.java, and AndroidManifest.xml.  Also got rid of the code for the menu including: menu_thing.xml. Also All done right, one will have a navigation arrow leading back up to the parent activity.  Details on this are at: https://developer.android.com/guide/topics/ui/actionbar.html.