Learning Android

Errata for Learning Android

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released.

The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.

Color Key: Serious Technical Mistake Minor Technical Mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date Submitted
Safari Books Online example 7-8
Note 8

Example 7-8 never sets the text color to yellow and the text never appears as green because the text color is immediately set to red or defaultTextColor in the following lines. textCount.setTextColor(Color.GREEN); if (count < 10) textCount.setTextColor(Color.RED); else textCount.setTextColor(defaultTextColor);

Darryl Gough  Feb 04, 2014 
Printed Page 1

I think it is worth mentioning in the book that eclipse offers a javadoc window which can be used to view details about classes, method and so on. For instance on page 107 of the book START_STICKY is returned as mentioned in another errata entry, information about this can be viewed if you click on it and view the details in the javadoc window!

Anonymous  Sep 06, 2011 
PDF Page 16
2nd paragraph under "Setting Up a PATH to Tools"

Author directs readres to step 2 in the document "Installing Android SDK" for instructions on configuring PATH variable. Actual instructions are presented in the step 5 of the document.

Aleksandar Dragosavljevi&#263;  Jun 30, 2011 
PDF Page 29
Figure 4-1

1.The "Stopped" on right side should be "Paused".(confirmed) 2.The "Stopped" on left side ,the "onCreate" above should be "onRestart"

yizhi  Dec 01, 2011 
Printed Page 60
Adding the jtwitter.jar library

Have been unable to download and install the modified winterwell with success. Printed description does not apply since download file format has changed. There is no accompanying readme file that says what to do. Closest I have come is by using File/Import function which left my with a JTwitterYamba package that has 79 compile warnings. (??) But I still cant get my yamba project to accept/use the import winterwell.jtwitter.Twitter; error is The import winterwell cannot be resolved I dont have any jtwitter.jar file to add in libraries tab, so I tried to add the JTwitterYamba to my project References , but that did not help . Have also loaded the yamba-1 project which does accept the JTwitterYamba, cant find out why, but at the same time it has 6 compile errors e.g. The method afterTextChanged(Editable) of type StatusActivity must override a superclass method.

Steen Hess  Nov 28, 2011 
Printed Page 68
Section 2

Eclipse flags the Twitter.Status status = twitter.updateStatus(statuses[0]); statement with the build error "Twitter.Status cannot be resolved to a type". Explicitly resolving it to winterwell.jtwitter.Status seems to work. http://marakana.com/forums/android/learning_android_book/477.html

Phil Ringsmuth  Sep 29, 2011 
Safari Books Online 81
3rd paragraph

"Hierarchy Viewer allows you to attach to any Android device, emulator, or physical phone and then introspect the structure of the current view. ..... You can introspect not just your screens, but the screens of any application on your device. This is also a good way to see how some other applications are structured." You cannot view structure on a device of production builds of other people's applications. By adding additional code from https://github.com/romainguy/ViewServer you can view the hierarchy structure of your own applications on the device. I was very excited to view the structure of other applications as the paragraph suggests, but the fact hierarchy viewer could not connect to my phone forced me to search on the internet. A quick google search lead to the conclusion that you cannot view the structure of other applications

Leon  Jul 22, 2012 
Safari Books Online 86
United States

extra attribute in the edittextpreference element for password. Where did this come from? The instructions don't mention it. It ends up being an 'unknown xml attribute' in the developers view of prefs.xml android:password="true"

Kevin  Nov 07, 2011 
Printed Page 87
Example 7-2

How do I get around the problem that addPreferencesFromResource(R.xml.prefs); is deprecated?

Carl-Gustav Nyman  Mar 13, 2013 
Printed Page 95
Third Paragraph

The code snippet is meant to demonstrate where the new 'Twitter' singleton is utilized to send a status update. The call is shown within the 'onClick()' method and is executing a blocking call directly to Twitter, but the accompanying text suggests that we are using the 'PostToTwitter' async inner class. The code snippet should show the 'getTwitter().setStatus(..)' being called within the 'doInBackground()' method in this inner class. This is exactly what the downloadable example code shows, so I think it was just a mistake when producing the text... :-)

Nick DiPatri  Jul 16, 2011 
PDF Page 95
first paragraph

The text reads that the onClick method requires updating and shows the updated onClick method. However, the onClick method doesn't set the status. It does this: new PostToTwitter().execute(status); The method that should be updated instead is PostToTwitter.doInBackground() The downloadable sample code shows this. Note from the Author or Editor: That is correct - we did add AsyncTask in Chapter 6. So, we should say: ... So, doInBackground() in PostToTwitter task becomes: protected String doInBackground(String... statuses) { try { Twitter.Status status = getTwitter().updateStatus(statuses[0]); return status.text; } catch (TwitterException e) { Log.e(TAG, e.toString()); e.printStackTrace(); return "Failed to post"; } }

Anonymous  Aug 09, 2013 
PDF Page 98
Last paragraph

The statement, "I usually compare it to finding someone's laptop on the street," seems to be a false analogy. Reading clear text data off of a hard drive is much easier than reading data off of a phone's internal storage, at least if the phone is password-protected.

Anonymous  Nov 01, 2011 
Printed Page 103
Code listing

the use of this in when referring to twitter is not required and serves no purpose.

Anonymous  Sep 03, 2011 
Printed Page 103
Code listing

The class is defined as YambaApplication1 however in other places it is used as just YambaApplication.

Anonymous  Sep 03, 2011 
Printed Page 111

The code does not in anyway control the updater as a singleton. I can see no reason for having multiple updaters. It would be better if the code defined updater as a static on the UpdaterService2 class and provided access to this via getter and setter methods. This way the runFlag is not required and there will only ever be one updater thread. The onStartCommand should check if updater is null and only create the thread if it is.

Anonymous  Sep 06, 2011 
Printed Page 114
application listing

As I mentioned in my errata on page 111 and the use of a static within the updater this could be used by the application class and then the isServiceRunning method could just return UpdaterService2.updaterRunning(). This makes the code so much clear, resolves various threading issues, makes sure a singleton pattern is in place and also removes the need for a flag in the application and the setServiceRunning() method.

Steve Webb  Sep 06, 2011 
PDF Page 121
First paragraph

The text says, "these four items will be the columns in our schema, along with a unique ID for each tweet." But only three columns (created_at, txt, user) are in the list immediately prior. Perhaps the "source" column was supposed to be listed as well?

Anonymous  Nov 02, 2011 
PDF Page 121
3rd full paragraph

"when we need to use SQL" should be "where we need to use SQL"

Anonymous  Nov 02, 2011 
Printed Page 123
code listing

It seems strange that SQLiteOpenHelper is an abstract class and stores the context but doesn't provide a method to be used by a subclass to get it! Is this typical of the way android SDK classes are put together? To me with 15yrs of OO experience I'd view it as a glaring design bug.

Anonymous  Sep 15, 2011 
PDF Page 123
example 9-1

static final String C_TEXT = "txt"; it should be : static final String C_TEXT = "text"; beacuse we defind it as "text" in the database

Tareq Jahhaf  Feb 27, 2012 
PDF Page 126
inside the foreach loop

db.insertOrThrow(...) should be db.insert(...) It is in the "Database Constraints" section that introduces db.insertOrThrow(...) method and its exception handling.

Danny Sun  Jul 26, 2011 
Printed Page 126
timeline loop

As you have a DbHelper class it would strike me as good OO practice for this to be the place to provide a method to insert new status information into the database. As it is you make use of public attributes of the DbHelper class which strikes me as a strange thing to do as it still allows the wrong type of data to be inserted into the table and only to be found when running the app. If a method with the correct signature for the vales was provided the only option would be to supply the correct data types. Also of course this would encapsulate the database logic.

Anonymous  Sep 16, 2011 
PDF Page 126
example 9-2

in function run, inside the for loop values.put(DbHelper1.C_ID, status.id); when it should be 'values.put(DbHelper1.C_ID, status.id.toString()); the function put takes strings : put(String,String)

Tareq Jahhaf  Feb 27, 2012 
PDF Page 126
example 9-2

in the function run, inside the for loop values.put(DbHelper1.C_SOURCE, status.source); there wasn't a source field at that time in the database, maybe we should include "source" in the sql string in function onCreate() in exapmle 9-1.

Tareq Jahhaf  Feb 27, 2012 
PDF Page 127
para "Verify that the database was created"

Examining the database file as suggested is not possible when using real hardware unless it's 'rooted'. adb shell, File Explorer View etc are subject to UNIX file permissions 'other', and /data/data and other related fies and directories are usually mode 0771. [I'm using a Nexus 7 running 4.1.2 -- Android 2.x may not require rooting.] Examining the database file as suggested does work for virtual devices.

Patrick Middleton  Oct 17, 2012 
Printed Page 130
First sentence

It says change insert to insertOrThrow however the code listing it is talking about on page 126 already made use of insertOrThrow not insert.

Anonymous  Sep 22, 2011 
PDF, Other Digital Version Page 130
1st paragraph

The 1st paragraph says you should replace db.insert() with db.insertOrThrow() however the source code on 126 already uses db.insertOrThrow().

Jonathan Bullock  Feb 11, 2012 
PDF Page 130
example 9-3

in the last line public static final String C_TEXT = "txt"; should be public static final String C_TEXT = "text"; like the Database

Tareq Jahhaf  Feb 27, 2012 
Safari Books Online 131
Example 9-3, Note 7

The call to SQLiteDatabase.insertWithOnConflict() in StatusData.insertOrIgnore() is an API Level 8 function. In example 6-4 on Pg. 61 indicates we are using minSdkVersion=4 so running the application on a device with a lower API level throws a NoSuchMethodError at runtime.

damok  Jul 09, 2011 
Printed Page 131
insertOrIgnore method

The dbHelper has a close() method but you actually call db.close in the finally clause. This does not strike me as a very well structure bit of programming. Also it strikes me you should not be using the db attribute outside the dbHelper class. The dbHelper class should be performing all database processing. The insertOrIgnore should be in the DbHelper class not in the StatusData class. Also insertWithOnConflict is used!!!! This isn't available in the version of the SDK we are programming to.

Anonymous  Sep 22, 2011 
Printed Page 132
2nd paragraph

Comment reads: @return Timestamp of the latest status we ahve it the database Should probably read: @return Timestamp of the latest status we have in the database

Anonymous  Jul 14, 2011 
PDF Page 132
the comment of the 2nd block of code

/** * * @return Timestamp of the latest status we ahve it the database */ the typo : we ahve it the database I think you meant we have in the database

Tareq Jahhaf  Feb 27, 2012 
PDF Page 133
example 9-4

I think you should add this line of code to function onCreate() in YambaAppication (example 9-4) this.statusData = new StatusData(this); beacuse without it there is a Null pointer exception.

Tareq Jahhaf  Feb 27, 2012 
Printed Page 134
Code listing

The code refers to Status and no longer Twitter.Status. This implies that the class now statically imports Twitter but this is not mentioned anywhere in the book as far as I can see.

Anonymous  Sep 22, 2011 
PDF Page 134
example 9-4

inside the for loop values.put(StatusData.C_ID, status.getId()); should be values.put(StatusData.C_ID, status.getId().toString()); "put" function takes (String,String)

Tareq Jahhaf  Feb 27, 2012 
Printed Page 134
At the end of example 9-4.

At the end of example 9-4 there is a Log.d which makes the application write a log row 'Got X status updates'. This row is written everytime the service runs the method fetchStatusUpdates() and is then written as 'Got 20 status updates'. That is, the application always considers all 20 fetched statuses to be new even if they are not. My understanding is that this is not the idea with this log row. I would consider it better (more clear) to but that log message as 'Got X potentially updated statuses', or to alter the if-logic to include the IDs to check if the statuses are really updates (or already stored in the local database). The reason for this confusing behavior to me seems to be that the statuses are typically not new, they are old with the same IDs as the ones already stored in the database and as such they will not be written to the database in with the insertOrIgnore method. So, when the SW extracts the latestStatusCreatedAtTime value, it will every time be the same old time value (from the very first database insert, unless a new status has actually been written to the cloud) and therefore the extracted statuses will all be reported as new in comparison even though they are the very same as those already stored locally. Key here is also that 'status.createdAt' is not the time that the status was entered into the cloud service (which to me would be the logical 'created time'), it seems to be the time it is fetched by getFriendsTimeline(). Therefore it is always a recent time although the status as such is same old as last time getFriendsTimeline() was used 60 seconds earlier. I would like to get my understanding confirmed.

Carl Rehnstedt  Dec 16, 2012 
Printed Page 138
2nd para

The book introduces the timeline activity and gives xml and code for a version using a text view, it then basically says this isn't a good way of doing it and never gives a complete implementation so that atleast you can see the code working and listing out the tweets in the text view. This is most annoying if you have just entered all the code and created a new screen. Personally I went ahead and made the refactoring changes mentioned a few pages later to add the menu's so that I could atleast see it working before refactoring to use the adaptor. I think the order should be changed so that the code using the textview can be seen to be working and then introduce the better alternatives when the menu and start/stop service are already available on the timeline activity.

Anonymous  Oct 07, 2011 
PDF Page 138
4th paragraph

the line before the last one typo : and than add that layout into ScrollView. you meant :"and then add .... "

Tareq Jahhaf  Feb 27, 2012 
Printed Page 138, 140
bottom of p. 138, top of p. 140

Example 10.1 at the bottom of page 138 shows a file called res/layout/timeline_basic.xml , but the code at the top of page 140 (in example 10.2) refers to timeline: setContentView(R.layout.timeline); This statement doesn't compile (ADT v22.0.1-685705: "timeline cannot be resolved or is not a field"). Matching the xml file name fixes the error: setContentView(R.layout.timeline_basic);

r. clayton  Jul 18, 2013 
PDF Page 139
Example 10.2 - source code of TimelineActivity.java, version 1

In chapter 9, StatusData class has been created to encapsulate and hide database-specific details from the rest of the application. However, in the mentioned example (and also in rest of chapter 10 talking about ListView, Adapters and Cursors) the DbHelper class and its methods are used explicitly, instead of using the statusData object created in the YambaApplication object. Why is that? In the onResume of TimelineActivity, wouldn't it be nicer to use something like cursor = yambaApp.getStatusData().getStatusUpdates(); instead of cursor = db.query(DbHelper.TABLE, null, null, null, null, null, DbHelper.C_CREATED_AT + " DESC"); ... when the feature of getting all status updates is already available in StatusData class?

Anonymous  Nov 01, 2011 
Printed Page 140
First paragraph - onCreate(), onResume()

The material in chapter 10 (List and Adapters) is inconsistent with the refactoring made at chapter 9, so the examples would not compile, as they use DbHelper directly, rather than using the code refactored to statusData. I assume the reason for this mistake is the writing of the chapters not in order, and an experienced programmer would overcome the errors in no time, but it might be frustrating for a novice one.

Ron Munitz  May 11, 2012 
Printed Page 145

I have got the impression that in chapter 9 we embedded class DBHelper into StatusData. But chapter 10 still uses DBHelper directly instead of taking advantage of the new class StatusData. I've deleted DBHelper.java in chapter 9, so all direct references to it need to be changed. Also instead of using here db.query . . . we have now getStatusUpdates(), which gives you directly what we need to use. Hope this helps. I like the book a lot. Also there is no source code in the zip file for chapters 9, 10, 11, 12.

Jorge Sanguinetti  Apr 22, 2012 
PDF Page 152
Example 10-10

This throws an exception if the preferences have not been created yet, i.e., the first time the application is run before filling in the preferences: if (yamba.getPrefs().getString("username", null) == null) I modified it: if (!yamba.getPrefs().contains("username") || yamba.getPrefs().getString("username", null) == null)

Anonymous  Dec 19, 2011 
PDF Page 154
1st paragraph

onPrepareOptionsMenu() is a much better choice here than onMenuOpened() to dynamically modify the contents

Patrick Middleton  Oct 23, 2012 
Printed Page 154
Example 9-8

When handling R.id.action_tweet in onOptionsItemSelected(), it tries to issue implicit intent (or that's what it looks like: it passes the string "com.marakana.android.yamba.action.tweet"); but this intent isn't hooked up to anything; if you click the "tweet" button, the application crashes. Changing this to an explicit intent works, and allows you to actually use the newly updated StatusFragment (which uses the preferences). Alternately, you could hook up the "yamba.action.tweet" (presumably in the manifest) so that it actually works.

George Dunlap  Apr 09, 2014 
Printed Page 155
Middle of page

Several new menu options have been added without discussion. Also, use of Intent Flags is introduced throughout without discussion as to what they are or how they work. The book overall has been very good with explaining key points as they are introduced, so this looks like an oversight.

Anonymous  Jun 04, 2011 
PDF Page 157
TimelineActivity code

Both fetchStatusUpdates() in YambaApplication.java and setupList() in TimelineActivity.java are using the same instance of StatusData (returned by getStatusData() in YambaApplication). This causes an "Cursor: invalid statement in fillwindow()" error because fetchStatusUpdates() closes the database that the managed cursor in TimelineActivity is using. Reproduce this bug by: 1) Starting the application in the debugger, the TimelineActivity is shown. 2) Selecting 'Update Status' from the menu. 3) Start the 'Status updater service' from the menu. 4) Wait for it to fetch the status updates. 5) Return to the 'Timeline' (TimelineActivity) 6) "Cursor: invalid statement in fillwindow()" error is shown in LogCat Possible solution: Let TimelineActivy use its own StatusData instance instead of obtaining it through yamba.getStatusData()

Marcel H  Jul 30, 2011 
PDF Page 157
latest code

At the end of chapter 10 we still depend on the DbHelper.java file because TimelineActivity and TimelineAdapter are still using the database column name strings from that file. Why not delete that file and replace: static final String[] FROM = { DbHelper.C_CREATED_AT, DbHelper.C_USER, DbHelper.C_TEXT }; with: static final String[] FROM = { StatusData.C_CREATED_AT, StatusData.C_USER, StatusData.C_TEXT }; in TimelineAdapter and TimelineActivity?

Marcel H  Jul 31, 2011 
Printed Page 157

This was previously reported as minor technical mistake on page 152 however the error propagates through the rest of the chapter and shows up again on page 157. The variable 'yamba' is never declared nor initialized and yet many parts of the code have been refactored as if it had. I'm adding this again as it seems the serious errors tend to get more attention from the author to add an explanation (and I believe it is a serious error).

Anonymous  May 21, 2012 
PDF Page 158
Second to last paragraph in the last sentence.

The excerpt "functional concerns among BaseActivity, StatusDate, and TimelineActivity" should read "functional concerns among BaseActivity, StatusData, and TimelineActivity". StatusDate does not exist as a class. However, StatusData does.

Brandon Blincoe  Dec 14, 2012 
PDF Page 162
2nd paragraph

"which in turn will launch our TimelineActivity activity" should read "which in turn will launch our UpdaterService service."

Anonymous  Nov 14, 2011 
Printed Page 164
Listing 11-5

The code makes use of an attribute 'receiver' in the call to registerReceiver(receiver, filter) however no explanation is given as to what receiver is. Obviously it is an attribute assigned a new instance of TimelineReceiver but that has not been shown or described in the book!

Steve Webb  Oct 11, 2011 
PDF, Other Digital Version Page 164
code example: registerReceiver(receiver, filter);

I am getting an error of "receiver cannot be resolved to a variable". We have never declared/instantiated a "receiver" variable.

Sean Harrop  Oct 22, 2011 
PDF Page 164
Example 11-5

registerReceiver(receiver, filter); the two variables are not mentioned in the book, but are seen in the git code as shown below. protected void onCreate(Bundle savedInstanceState) { ... receiver = new TimelineReceiver(); filter = new IntentFilter("com.marakana.yamba.NEW_STATUS"); ... } Suggest to mention it in the book: Example 11-5. TimelineActivity.java with TimelineReceiver

Cullen SUN  Mar 28, 2013 
Printed Page 169
No where!

There is no where in this section of the book that describes how we test the code which checks for data connection lost! It didn't seem to work for me using the emulator and going into airplane mode. Also where are all these filter names coming from and how do we find them? Eclipse doesn't even offer some of them mentioned in the book at possible shortcut values.

Steve Webb  Oct 11, 2011 
Printed Page 177
first code listing

The definition of uri content uses yamba7, this really should be yamba or maybe yamba1. The use of numbers through out the entire book is inconsistent and has causes lots of compilation errors or runtime errors that I have not raised here.

Steve Webb  Oct 12, 2011 
PDF Page 177
last Paragraph

nubber (3) We can than throw a runtime exception is We can then throw a runtime exception

Tareq Jahhaf  Feb 27, 2012 
PDF Page 182
4th full paragraph

Should say "AppWidgetProvider" instead of "AppWidgetProviderInfo".

Anonymous  Nov 16, 2011 
PDF Page 185
example 12-3

inside <LinearLayout> android:background="@color/edit_text_background" I think you should mention that color is user defined. without defining it cause a problem with the widget: "Problem Loading Widget" or "Problem Loading Gadget"

Tareq Jahhaf  Feb 27, 2012 
Printed Page 195
Point 2

It says that the compassrose.jpg is in the drawable folder. I downloaded the source code (I typed everything in prior to this!) and the jpg isn't there, also there doesn't appear to be any code for the compass demo.

Steve Webb  Oct 17, 2011 
PDF Page 210
1st paragraph

entities and entity instead of entries and entry are used in three places as follows: This is the list preference. It shows a list of entities, as represented by android:entities. The value associated with it comes from android:entityValues. entries and entry should be used in the above three places as follows: This is the list preference. It shows a list of entries, as represented by android:entries. The value associated with it comes from android:entryValues.

Emerson Wang  Nov 11, 2011 
Safari Books Online 211
No where!

I downloaded the source code and I've noticed the widget in Yamba-8 does not get updated. The problem is that the manifest does not have the correct filter set for the update, it should be com.marakana.yamba.NEW_STATUS

Steve Webb  Oct 17, 2011 
PDF Page 213

A Notification typically contains "tickerText", so that when the notification shows up, the icon and the tickerText show up briefly, and then the icon remains in the notification area. The tickerText is set to an empty string in onHandleIntent(), but it was never set to something meaningful afterwards. I think the following line should be added. CharSequence notificationSummary = this.getString(R.string.msgNotificationMessage, timelineUpdateCount); this.notification.tickerText = notificationSummary; <-- Add this.

Jaewook  Jan 26, 2012 
Printed Page 221
Remote Client Intro

The AIDL files are needed by the client, I understand this as they are the definition of the interface with the service. However I do not understand how requiring a copy of the message.java class in the client makes good sense! Surely this can lead to all sorts of issues with versioning? Surely the whole point of having a interface definition is that both parties only need an updated definition and do not rely on the other for an actual class implementation, this just wouldn't make sense. You would need to make sure that both parties client/service(server) were updated at the same time. Is there some information about versioning we are missing here, or is there some way of defining the message object using AIDL that generates a java class on both sides. Maybe it's me but AIDL just doesn't seem very well thought out based on the description in the book.

steve Webb  Oct 19, 2011 
Printed Page 231
4th paragraph (the JNI Header file)

The classpath setting in the page is wrong, as the classpath required for the javah is <yourdir>/bin/classes Suggested correction: Instead of "so it is easiest ot just change directory to your project's bin directory" it should say "so it is easiest ot just change directory to your project's bin/classes directory" I would rather simplify by: 1. mkdir jni 2. cd jni 3. javah -classpath ../bin/classes/ com.marakana.FibLib

Ron Munitz  May 12, 2012 
Other Digital Version 2296
1st two paragraph

Hi, I am reading the book on Kindle application on an android device and so it shows me the location instead of page number. The Android SDK Hierarchy Viewer does not work with production phones or tablets. It only works with development emulators. Something you might want to change in next release. Very good book so far. Will come back if I find something else. Cheers. http://code.google.com/p/android/issues/detail?id=13002

naeem khan  Jun 25, 2011