Saturday, January 12, 2013

Got my Kickstarter BERO remote controlled robot in

Last year I funded one of the BERO robots via Kickstarter. And last week the matte finish black Basshead arrived! This is wat it finally looks like:

Quite a sturdy little robot, remote controllable via an Android and iOS app. Biggest challenge was to switch it on! In the supplied instructions it said to turn it on and off, but I just couldn't find the on/off switch! Almost sent out a question for this to the support, but then I found it! Below a picture, should you come across the same problem.
And some Android app screenshots with which you can control the Bero: Splash screen
Home screen
Control screen to let Bero react (dance) to sound/music
Eight programmable modules for its movement/lights/sound
So, quite funky!

Wednesday, October 24, 2012

How to remote debug PL/SQL with Oracle SQL Developer

There are quite a few posts (see the References section at the bottom) on how to debug PL/SQL remotely. That is when you want to step through another session that is running some PL/SQL procedure. Still I didn't find it obvious to get it working. Mainly because none of the blog posts have a diagram on how all components are connected and which component tries to connect with the other component(s). Therefore this post, including..... a diagram! :)

Setup
- on a Windows 7 desktop PC running SQL developer with IP xxx.yyy.19.84
- remote database running in VM with IP xxx.yyy.19.104. This one actually is running on the above desktop machine, but that should not change much if it would be on a separate machine, except there might be some firewall(s) in between.

In short this is what we are trying to achieve:


  1. Start up the remote debug listener on port 4000 on the desktop machine
  2. Start up SQl*Plus connecting to the remote database
  3. Connect to the remote debug listener from the database. This might be the tricky part for some: SQL*Plus is connected to the remote database, so the DBMS_DEBUG_JDWP.CONNECT_TCP call is executed from that database! So that machine (.104) needs to be able to get to the machine *.84) with the remote debug listener listening on port 4000
  4. Execute the stored procedure from the package you want to debug and SQL Developer will stop at the set breakpoints

Detailed steps
  1. As mentioned in all the referenced blog posts, right-click on the connection and select Remote Debug....
  2. In that popup enter:

    If done correctly, you should see something like:

    If you get a message saying "The debugger is not able to listen for JPDA using the specified parameters." then there is something wrong with the parameters you entered. Yes that's all I could figure out too... Potentially you entered the wrong Local address or maybe there's already something listening on the specified port.
    Note that the Local address is the IP of the machine SQL Developer is running on. Unless your database is running on that same machine, you can't use 127.0.0.1 (localhost). You have to use the full IP address.
  3. Since the database (which is on xxx.yyy.19.104) needs to connect to the SQL Developer remote debugger listener, it must be able to get to that machine (xxx.yyy.19.84). You can test this by trying to telnet to it and the port the listener is listening on from the .104 machine:
    
    
    $ telnet xxx.yyy.19.84 4000
    Trying xxx.yyy.19.84...
    Connected to MyPC (xxx.yyy.19.84).
    Escape character is '^]'.
    JDWP-Handshake
    ^]quit
    
    

    Look for the JDWP-Handshake string. If you get that, you know it can get to the remote debug listener on the .84 machine. Also note that when you quit the telnet session, the listener is stopped, so make sure to start it again!
  4. Start up an SQL*Plus session from the .84 machine to the remote database. For example:
    
    
    sqlplus user/passwd@xxx.yyy.19.104/oracle
    
    

  5. Connect to the remote lister on the .84 machine:
    
    
    SQL> set serveroutput on;
    SQL> execute DBMS_DEBUG_JDWP.CONNECT_TCP('xxx.yyy.19.84',4000);
    
    

    Note it is the .84 machine and of course port 4000 where the remote debugger is listening on.
  6. In SQL Developer, set a breakpoint in a procedure you want to debug, e.g test_me. Tip: as a first try, set it at the first line of code in the method test_me, then you know for sure that if it gets called, it should stop there definitely.
  7. Now call that procedure from the SQL*Plus commandline, e.g:
    
    
    DECLARE
      P_ANR VARCHAR2(25);
      P_RESULT VARCHAR2(4000);
    BEGIN
      P_ANR := 666;
    
      SOME_PCK.test_me(
        P_ANR => P_ANR,
        P_RESULT => P_RESULT
      );
      DBMS_OUTPUT.PUT_LINE('P_RESULT = ' || P_RESULT);
    END;
    /
    
    

    Note the test_me method has an IN and an OUT parameter.
  8. Your SQL Developer should now stop in the breakpoint you've set, see below SQL Developer screenshot of the Remote Debug session log window:


Remarks
I did not seem to need to run:


GRANT DEBUG CONNECT SESSION TO some_user;
GRANT DEBUG ANY PROCEDURE TO some_user;
/



You might still need to execute these commands if you get the permission errors you see in Step 5 here.

If you have a firewall on the .84 machine or between the two machines, you might need it to allow port 4000 to be accessed from the (other) DB machine...

References
Using SQL Developer to debug your anonymous PL/SQL blocks
Remote debugging with SQL Developer revisited
Application Express Community - Remote debugging
Sue's Blog... again... - Remote debugging with SQL Developer
Barry McGillin - Remote debugging with SQL Developer
Debug ApEx App with SQL Developer
Use Oracle SQL Developer to aid Oracle Application Express development
OTN Discussion Forums - Remote Debugging

Wednesday, October 17, 2012

Lessons learned Seam 2.2 project

Quick post with some bullet points of lessons learned during my last (and first) Seam project:

  • Seam 2.2
  • Hibernate 3.3.1
  • JEE 5
  • EJB 3.0
  • JSF 1.2
  • Richfaces 3.3
  • JBoss 5.1
  • Drools 5
  • SQLServer 2008
  • MySql 5.x
  • TestNG
  • Hudson
Seam
- To have a navigation for a method with a parameter like exportSelected(String value): see Seam navigation based on a function with parameters
- If your Seam app keeps re-deploying (restarting) when using JBoss within Eclipse: delete all files that end with 'dia' in your WEB-INF dir. See: Seam keeps redeploying For example, I modified pages.xml, which apparently created a pagesdia (or similar) file... after removing that one, it worked fine again.

Hibernate
- To see which validator fails on a persist():


    try {
        entityManager.persist(verbinding);
    } catch (InvalidStateException e) {     
        for (InvalidValue invalidValue : e.getInvalidValues()) {         
            System.err.println("--------------> create(): exception Instance " +
               "of bean class: " + 
               invalidValue.getBeanClass().getSimpleName() +                  
               " has an invalid property: " + invalidValue.getPropertyName() +
               " with message: " + invalidValue.getMessage());     
        } 
	throw new RuntimeException(e);
    }




MySql
- To see data in better format in MySQL: show engine innodb status\G (so add the \G to a query)

Hibernate/JPA
- NamedQueries are loaded and parsed at startup time, so that's an advantage above em.createQuery() which are only parsed and evaluated at runtime. So with NamedQueries you get the errors sooner.

Drools
To solve this error in Drools decision table .XLS spreadsheet:


   [testng] DEBUG [test.service.drools.DroolsHandling] getKnowledgeBase: getting path info from settings service
   [testng] DEBUG [test.service.drools.DroolsHandling] getting test.service.drools.DroolsHandling ruletable file: HandlingModelTest.xls
   [testng] DEBUG [test.service.drools.DroolsHandling] getKnowledgeBase: Trying to open a File
   [testng] WARN  [jxl.read.biff.NameRecord] Cannot read name ranges for Excel_BuiltIn__FilterDatabase_1 - setting to empty
   [testng] WARN  [test.service.drools.DroolsHandling] There are errors in the decision table file
   [testng] WARN  [test.service.drools.DroolsHandling] Decision table error: message=[ERR 102] Line 9:16 mismatched input '"DroolsHandling6"' expecting ']' in rule "DroolsData_12" in pattern stringDataset
   [testng] WARN  [test.service.drools.DroolsHandling]     line: 9
   [testng] WARN  [test.service.drools.DroolsHandling] Decision table error: message=[ERR 102] Line 9:35 mismatched input '"Prefab"' expecting ']' in rule "DroolsData_12" in pattern stringDataset
   [testng] WARN  [test.service.drools.DroolsHandling]     line: 9
   [testng] WARN  [test.service.drools.DroolsHandling] Decision table error: message=[ERR 102] Line 22:16 mismatched input '"DroolsHandling6"' expecting ']' in rule "DroolsData_12" in rule "DroolsData_13" in pattern stringDataset
   [testng] WARN  [test.service.drools.DroolsHandling]     line: 22



Make sure your definition has all the cells merged that contain a condition! For example, check the red arrow pointing at the border of the cells between G9 and H9.
The last cell for the condition Prefab is not merged (i.e one cell) with all the other conditions. Here's now how it should be: see again the red arrow, there's no cell separation anymore, all condition columns are now merged into 1 cell.


- A space in a condition in a decision spreadsheet can cause a non-match! E.g GATE VALVE is not matched. Fixed it by always replacing a ' ' with an '_'.

Sunday, May 20, 2012

BlackBerry Playbook Tablet for Android porting

Houston, we got it in the mail: a free RIM Blackberry Playbook Tablet because I submitted on time an Android app to the BlackBerry App World. The Android app runs within the Playbook Android emulator (yes so the app is native Android! Pretty cool I'd say!

Booting
Below a few bootup screenshots:

The bootup time for the device is quite long, definitely longer than the Samsung Galaxy Tab 10.1 for example.

Home-screen and taskmanager
This is what the home-screen looks like:
This is what the taskmanager looks like:
The way to go back to the home-screen or switch to another app is by swiping from the button of the screen up.

The simpel app running
And the app runs fine on the real device too :) Those Android figures are also checking it out as you can see...
Those four buddies are called: Don Pablo Calaveroid, Longevity, Fortune and Blessing. The last three were released because of the chinese Year of the Dragon. Too bad they sent one with still the USA power plug attached to it. Luckily I had a converter (as the globetrotter I am ;)...
This is how the tablet 7" screen compares to the Samsung 10.1 (note both screens are very clear, the picture is only trying to show the difference in size of 10" vs 7"):
Tetris just runs fine on it (but that's a native app), the screen is very clear:


Some generic feedback:
  • The tablet smells rubbery because it has rubber on the back. Probably done as anti-slip, but even your fingers start to smell like it.
  • A bit annoying was that you have to watch the Basics tutorials when doing the setup.
  • Like the virtual keyboard, feels like lot less typos than similar sized touch screens


Porting
During converting the app to the PlayBook Android 2.3.3 or higher runtime, I ran into a few issues:
  • For emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, emailBody) statements needed to add .toString() to emailBody because otherwise you get an exception telling that EXTRA_TEXT is not a String.
  • After each re-install of the app on the emulator, nothing seems to be remembered of the preferences. E.g had to accept the EULA each time after a new deploy, while this does not happen on the "regular" Android emulator. Also works fine on the real Playbook device.
  • In my app the user can swipe left to right and vice versa to browse through tips. On the Playbook emulator the text on bottom of the screen was not always correctly updated. It was lagging behind.
  • When trying to open the browser with an Intent and URL in the simulator, it says 'No network connection'. Strange, the pre-installed browser on the home screen works just fine... Seems similar to this reported issue.

Thursday, April 12, 2012

JBoss exception Transaction is not active: javax.transaction.RollbackException: The transaction is not active!

Sometimes you get this exception when using JBoss (5.1.0 and Seam 2.2 in my case):



14:08:17,312 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.TransactionReaper_18] - TransactionReaper::check timeout for TX -3f57fd63:6ae:4f5df31e:b7in state RUN
14:08:17,312 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.BasicAction_58] - Abort of action id -3f57fd63:6ae:4f5df31e:b7 invoked while multiple threads active within it.
14:08:17,312 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.CheckedAction_2] - CheckedAction::check - atomic action -3f57fd63:6ae:4f5df31e:b7 aborting with 1 threads active!
14:08:17,343 WARN [JDBCExceptionReporter] SQL Error: 0, SQLState: null
14:08:17,343 ERROR [JDBCExceptionReporter] Transaction is not active: tx=TransactionImple < ac, BasicAction: -3f57fd63:6ae:4f5df31e:b7
status: ActionStatus.ABORTING >; - nested throwable: (javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: -3f57fd63:6ae:4f5df31e:b7 status: ActionStatus.ABORTING >)
14:08:17,359 ERROR [TxPolicy] javax.ejb.EJBTransactionRolledbackException: org.hibernate.exception.GenericJDBCException: Cannot open connection
14:08:17,375 ERROR [TxPolicy] javax.ejb.EJBTransactionRolledbackException: org.hibernate.exception.GenericJDBCException: Cannot open connection
14:08:17,453 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.TransactionReaper_7] - TransactionReaper::doCancellations worker Thread[Thread-10,5,jboss] successfully canceled TX -3f57fd63:6ae:4f5df31e:b7
14:08:17,500 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.BasicAction_40] - Abort called on already aborted atomic action -3f57fd63:6ae:4f5df31e:b7
14:08:17,515 ERROR [AsynchronousExceptionHandler] Exception thrown whilst executing asynchronous call javax.ejb.EJBTransactionRolledbackException: Transaction rolled back at org.jboss.ejb3.tx.Ejb3TxPolicy.handleEndTransactionException(Ejb3TxPolicy.java:54)
at org.jboss.aspects.tx.TxPolicy.endTransaction(TxPolicy.java:175)
at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:87)
at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:190)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.security.RoleBasedAuthorizationInterceptorv2.invoke(RoleBasedAuthorizationInterceptorv2.java:201)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:186)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.session.SessionSpecContainer.invoke(SessionSpecContainer.java:176)
at org.jboss.ejb3.session.SessionSpecContainer.invoke(SessionSpecContainer.java:216)
at
org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:207)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.jboss.seam.util.Reflections.invoke(Reflections.java:22)
at org.jboss.seam.intercept.RootInvocationContext.proceed(RootInvocationContext.java:32)
at org.jboss.seam.intercept.ClientSideInterceptor$1.proceed(ClientSideInterceptor.java:76)
at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:56)
at org.jboss.seam.async.AsynchronousInterceptor.aroundInvoke(AsynchronousInterceptor.java:52)
at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:107)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.jboss.seam.util.Reflections.invoke(Reflections.java:22)
at org.jboss.seam.util.Reflections.invokeAndWrap(Reflections.java:144)
at org.jboss.seam.async.AsynchronousInvocation$1.process(AsynchronousInvocation.java:62)
at org.jboss.seam.async.Asynchronous$ContextualAsynchronousRequest.run(Asynchronous.java:80)
at org.jboss.seam.async.AsynchronousInvocation.execute(AsynchronousInvocation.java:44)
at org.jboss.seam.async.QuartzDispatcher$QuartzJob.execute(QuartzDispatcher.java:243)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:529)
Caused by: javax.transaction.RollbackException: [com.arjuna.ats.internal.jta.transaction.arjunacore.inactive]
[com.arjuna.ats.internal.jta.transaction.arjunacore.inactive] The transaction is not active! at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1414)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:137)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
at org.jboss.aspects.tx.TxPolicy.endTransaction(TxPolicy.java:170)
... 47 more



For 99% sure this is caused by a timeout. Unfortunately you can't immediately see that from the exception.

The fix
Increase the timeout in transaction-jboss-beans.xml in your JBoss deploy dir. For example if you have JBoss installed in:

C:\jboss-5.1.0.GA\server\default\deploy


then open transaction-jboss-beans.xml and set the field transactionTimeout much higher. E.g at 3000 (default is 300).
In older JBoss version this setting used to be in default/conf/jboss-server.xml, see here.
The official documentation can be found here.

Saturday, April 7, 2012

Why cancel-button now also first on Android?

Just wondering about this big time...

Why is Android switching the standard buttons order for Android 4.0 and higher? See for example this screenshot, the cancel button is now the first button!
I was always taught that what the user most likely wants to do should be first, and so the dismissive button last.
And Cancel is usually not what to user wants to do!

Apple is doing it also in their iOS, is Android now just plainly copying it?
Definitely prefer Cancel at the end, (western) reading occurs from left to right, so now one always has to read/skip the dismissive button!

From the expert the conclusion is just to be consistent. That means at least within the Android platform it is not consistent between pre 4.0 and 4.0+... I think an unneeded and unnecessary change.

Thursday, February 16, 2012

Summary of presentation with tips on creating visually appealing Android applications that scale to various screen sizes

Interesting points from a presentation in which Eric Burke shares tips on creating visually appealing Android applications that scale to various screen sizes.
The session focuses on custom views, rounded corners, scalable drawables, gradients, shine, holograms etc.
Eric is the St. Louis Engineering Manager and Android Tech Lead with Square.

06:00 Use alpha compositing with anti-aliasing, not canvas.clipPath() because you get jagged pixelated corners

17:25 triangular shine

29:00 Check the imeOptions for EditTexts/keyboard input. E.g imeOptions="actionNext" for example. Or "actionDone". NOTE: does not work on HTC Sense UI! So "actionNext" seems safest (might work, might not, but no harm done). For "actionDone" you still need a button for it on your screen anyway.
31:00 SafeViewFlipper code because the standard one crashes your app eventually (guaranteed) on pre-HoneyComb!
35:00 Holograms! Using the accelerator it acts as if it's shiny. Cool!
39:00 demo of cards sliding in/out and hologram on emulator

Wednesday, February 1, 2012

Android Tablet project lessons learned

A little while ago I had the opportunity to work on a tablet-only app, running Android 3.1 or higher.
The app consists of only one screen, on which everything can be done.Therefore the app basically consists of one Activity and several Fragments (about 4 to be precise).
The Activity controls the Fragments for potential re-use of the Fragments, as recommended. As a basis the official Honeycomb Gallery project was used. Quite unique to this app was though that it has to have 3 columns instead of the usual 2...

To get a feeling for the app, here are some screenshots:


The very first time the user has to log in, after which a sync with the server is done. Note the progress bars showing in the background.


Synchronisation in progress... Notice that the title is indented for some reason. Needs some investigation why it shows here, and not in the Login DialogFragment...


After the data has been loaded, the user can filter all data by selecting from the dropdowns, and selecting items in the lists. The rightmost column then shows a list of questions, dependent on the selected filters. Many types of questions and actions had to be supported; beside the ones you see, also built were dropdowns, multiple radiobuttons, view images and PDFs, etc.

Lessons learned

  • Prevent EditText in a ListView from losing focus/text disappearing:

    android:windowSoftInputMode="adjustPan"



  • Sometimes Eclipse/DDMS loses the connection with the emulator, it just can't "see it" anymore. For example when you want to run an app in the emulator, the start dialog does not show your running emulator. To fix that run 'adb kill-server', that restarts the server. After this the emulator should be detected again.

  • You can't disable a RadioGroup such that it disables it contained radiobuttons. You have to disable each radiobutton within that radiogroup seperately.

  • Note that all Activities in your app might have had onDestroy() called, but the Application instance might still be around! So watch this when you have static variables, like a reference to the database... it might still be open when your Activity's onCreate() is called again!

  • The 4.0 emulator was taking ages to start up (gave up after about 30mins). Then I compared the settings of my 3.1 AVD with the new 4.0 AVD. It turns out that the hardware property 'Abstract LCD density' setting of the 4.0 AVD is set by default to 240, which means high density. That implies a lot of pixels to draw. Read somewhere that that's one of the issues of a slow starting AVD. So I changed that property to be the same as of the 3.1 AVD, so made it 160. After that, the 4.0 AVD started about as fast as the 3.1 AVD (several minutes).
    I also reduced the 'Device ram size' setting from 512 to 256, but don't think that was the one that fixed it. See also here.

  • The monkeyrunner recorder script records really slow on a 160 density Android 3.1 AVD. Playback is a lot faster. Here's a bunch of instructions, including 2 scripts for recording/playback which you can also find here and here.

  • Had a problem trying to show the progress indicator in a ListFragment with the correct top margin. Tried adding addHeaderView() on the fragment to show some text above a fragment list. That works fine, except: when showing the progress indicator, it does not take into account any (top margin) LayoutParams being set! The app needs to set that to stay below the Action Bar which is in "overlay" mode. As soon as the adapter is set, the margin is taken into account correctly. I found the following here: in onCreateView() there is also a similar problem, they set the layoutparams explicitly again:


    // For some reason, if we omit this, NoSaveStateFrameLayout thinks we are
    // FILL_PARENT / WRAP_CONTENT, making the progress bar stick to the top of the activity.
    ... code where LayoutParams are set ...

    Tried several combinations of setting the LayoutParams, but those didn't work either. As a workaround now I have added a FrameLayout above the <fragment> in the main Activity xml. It is a <fragment> with a FrameLayout with a LinearLayout with some TextViews in it. For that one the top margin is set, and since the ListFragment is below it, it is pushed down correctly, even when the progress indicator is shown. Maybe the FrameLayout is causing the problems? Maybe a custom list fragment element would fix it.

  • How to open a PDF from an app's local private storage:


    // Assuming you have stored a file like this (note the mode):
    FileOutputStream f = context.openFileOutput("theFilenameYouStoredThePdfAs", Context.MODE_WORLD_READABLE);

    ... write bytes to f, do other stuff...

    // Then you can get a reference for it for an external app via:
    File file = activity.getFileStreamPath("theFilenameYouStoredThePdfAs");
    if (file.exists()) {
    Toast.makeText(activity, activity.getString(R.string.openingFile), Toast.LENGTH_SHORT).show();
    Uri path = Uri.fromFile(file);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(path, "application/pdf");
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    activity.startActivity(intent);
    }


  • Definitely take a look at the latest Google I/O app, that's always a good reference for figuring out how to do specific things; I consider it a best-practices app (I've also seen it being referred to in blog posts/presentations from the Android team, can't find them now :).

Android invasion of the Mini Collectible - Series 02!


They are 16 in total, but sort of left out the doubles: Bernard, Hexcode, Cupcake, GD-927, Iceberg, Greeneon, ???, Blackbeard, Racer, Bluebot, Hexcode, Rupture, Noogler.

Monday, October 31, 2011

Android UI Development: Tips, Tricks Techniques from Devoxx 2010 Summary

Quick summary of the most important/new tips from the Parleys talk Android UI Development: Tips, Tricks Techniques at Devoxx 2010. Now free for viewing.

13:40: tool for tracking allocations in Android via Debug.setAllocationLimit(int)

25:15: example showing the use of DDMS, Heap Analysis Tool, hprof, adb shell dumpsys meminfo

36:14: don't over-invalidate when redrawing. Only redraw what you have to. Demo shows use of traceview to detect too much being re-drawn.

44:20: (new) hierarchy viewer example. You can use Annotations to export your own values, i.e. the ones you want to check in the viewer.

48:09: you can use the ViewStub to not create your Views too early. Kindof lazy-instantiation. Uses android:inflatedId.

51:15: better way of using an ImageView (e.g icon) with a TextView inside a LinearLayout: android:drawableLeft(Top/Bottom/Right). Much more efficient because not using 3 Views.

51:45: shows use of the static analysis tool 'layoutopt'. It needs an layout XML file and it tells you what can be improved.

53:45: the one you should know by now :) Don't inflate() views in ListView.getView(), re-use. Shows a graph of performance differences if you do vs if you don't.

54:24: tip on View.setDrawingCacheEnabled(true), and explains quicky how Android uses it when you flick a ListView.

Monday, September 26, 2011

Segmented Button Control in Android

For an Android project I did a little while back I needed a pair of so-called segmented buttons, as they are named in iOS (see for example the official iOS API for it).

The idea is that when you select one of the buttons, it usually shows just about the same related data but in a different way (at least that's my translation of what it should do).
The Android version of it which I provide below looks currently most like the third and fourth example in the above image. But of course you can change the code to style it like the others too.

Android does not have this control out of the box. Luckily, I found a reference to the source of the Foursquared app.
In there, a segmented button control is created! Big thanks for the creators of Foursquared for sharing the code and thus of the segmented button control :)

Since the Foursquared app is quite big, I extracted the segmented buttons code from it and made it an Eclipse project, for your and my convenience. It's now just one Activity and the widget.
You can get it from here and use it anyway you like, following the Foursquared source Apache License 2.0 rules of course.
Here's two screenshots of what it will show:


Above shows at startup by default the Recent button is selected


Above shows after clicking on the Nearby button

Please not that I kept it as basic as possible; for example you'll keep seeing the progress indicator from layout/empty_loading.xml, which of course should be replaced with actual data. I also did not go through all the drawables etc to see which ones are needed and which are not, so you'll still have to clean those up.
Code has been tested on 2.1-update1.

Saturday, August 27, 2011

Using dependency injection and annotations in Android for cleaner code

Interesting overview of possibilities for cleaner code using dependency injection and annotations by using the RoboGuice and/or


Android Annotations


frameworks:

Clean code in Android applications

Definitely interesting possibilities, but these frameworks are quite young. And is it really worth the effort, since Activities and Intents can also be used quite effectively to keep the app loosely-coupled? Also the libraries will increase the app's size.

Not convinced yet :)

Friday, August 19, 2011

Houston we got them: Android Mini Collectible Special Edition Summer 2011



1st: "Greentooth is a master of mind control technology."
"One shake of his hand and you are under his command."

2nd: "The Hidden Task waits in the shadows, always prepared to set things right."
"The Hidden Task makes no sounds and leaves no traces."

3rd: "El Poderoso fights for justice, equality.. and fancy belts."
"El Poderoso is ready to wrestle the worst villans and pin down the biggest kingpings."

4th: "Cycle-On is a power-hungry Android who will stop at nothing."
"Cycle-On's tough metal casing makes it a formidable opponent."

Thursday, August 18, 2011

Android project lessons learned

Quick post with some bullet points of lessons learned during my last Android project:

  • To be able to keep strings in strings.xml apart, prefix them with the name of the Activity they belong to. So listOrdersOrderNr instead of just orderNr.

  • If you inflate() a view without the parent, it won't take into account the parent's attributes like padding! Sadly apparently that trick does not seem to work in the getView() method. For that you'd have to compute the padding in dp and then set the padding of the view. E.g:

    
    
    float scale = getContext().getResources().getDisplayMetrics().density;
    float dpSize = 10 * scale; // So 10 would be the number of dp you want
    buttonRow.setPadding((int) dpSize, (int) dpSize, (int) dpSize, (int) dpSize);



  • When using a RelativeLayout, make sure you refer to other elements using "@id/someId", and not "@+id/someId". If you use the latter, Android will just create a new id named "someId" and will place the layout starting from the top of your Activity. Pretty obvious this one (the docs say the "+" creates a new id) but still an easy one to miss... So don't do:

    
    
    android:layout_below="@+id/feedbackEmailLogsSeparator"


    But use:

    
    
    android:layout_below="@id/feedbackEmailLogsSeparator"



Monday, July 4, 2011

Android pro tips for advanced expert Android developers

Short summary of the Google I/O 2011 session "Android Protips: Advanced Topics for Expert Android App Developers". Full presentation can be found here.

6:35: the parallel Activity pattern: based on SDK version, start another "parallel" code line. Easy check to see what your SDK version is so you can distinguish use of more advanced APIs as for example available since HoneyComb.
8:20: use interfaces for backward compatibility
9:09: get real user feedback before launching. Note you can also of course use Analytics to log an exception!
10:38: example of doing A/B testing
11:35: use Android Market for beta testing. It seems there's no special facility for this in the market, you just have to control it yourself... e.g password protect, obscure the name.
12:31: But don't forget to protect your package name before someone else takes it! So upload and save it but don't publish it.
13:43: don't assume the default (natural) orientation on a device is portrait, e.g most tablets start in landscape. The orientation sensor's default is also 90degrees "off"
15:00: Don't track devices to detect unique installations. Give your app a unique id using a UUID, store it in the prefs.
20:00: the app's data has to be always fresh if possible, the user does not have to wait. See also this recent post: http://android-developers.blogspot.com/2011/06/deep-dive-into-location.html
25:51: how to get fresher data
32:00: make your services intelligent like do everything async, let it die asap
33:13: make your app psychic: leverage what the phone already knows, remembers what the user has told it. Like the Account Manager. Preferences should follow the user across devices and resets (which means storing them offline). Give users the ability to delete their preferences, using the Backup Manager to backup shared preferences.
38:21: intercept links using an Intent Receiver
39:25: make your apps adaptive. E.g: behave as expected, optimize for different user experiences. You can specify the IME keyboard behavior by setting the appropriate action (Go, Search, Send, Done etc). Use android:imeOptions for that
43:20: handle volume and playback buttons icw requesting audio focus for playback
47:10: provide translations for your app and title description to have it better found in people's native language
48:00: make your app smooth: make everything async. Loader and CursorLoader are available since HoneyComb.

Wednesday, June 29, 2011

Recommended Android Market screenshot format

In the Android Developer Console you can upload the screenshots of your app (max 8 currently) in several sizes, like 320x480, 480x800, 480x854 and 1280x800.

But it turns out that sometimes (always?) if you upload in the higher resolutions (like 480x800), the images get cropped in a weird way in the market on your phone (at least the Nexus One). The images will look fine in the online Android market, but not on a physical device. Actually, when you look closely, the dev-console gives a thumbnail of the screenshots and there you can already see that the images are cropped strangely sometimes; like the left 20-50 pixels just cut off. And that's actually how they also appear in the market on devices!
After some experimenting, it turns out this does not happen when you upload in 320x480 format.
Thus the recommended resolution to upload screenshots seems to be 320x480! Doesn't matter if you use a device (like the Galaxy S2) or emulator to take the screenshots.

Note I didnt check if the browser market image quality decreased when going from 480x800 to 320x480. Though clicking on an image in the webmarket shows the 320x480 uploaded image quite fine.

Android Bluetooth bugs on Galaxy S2

Just a quick post regarding Bluetooth on the Samsung Galaxy S II.

Recently I've been creating an app that uses Bluetooth for sending data between devices (not FTP just strings). A pretty good introduction can be found here, though some things are missing from it. Like an example how you can register a BroadcastReceiver for the ACTION_SCAN_MODE_CHANGE. Just in case you are looking for it, here it is:



// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

...

// Creates a BroadcastReceiver for ACTION_SCAN_MODE_CHANGED for BT discovery
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {
if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
BluetoothAdapter.ERROR);
String strMode = "";
switch(mode){
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
strMode = "mode changed: SCAN_MODE_CONNECTABLE_DISCOVERABLE";
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
break;
case BluetoothAdapter.SCAN_MODE_NONE:
strMode = "mode changed: SCAN_MODE_NONE";
break;
default:
strMode = "mode changed: unknown mode " + mode;
break;
}
Log.d(TAG, strMode);

}


Basically a device can be made discoverable by others, or a device can be set to find other devices (discover). Being discoverable is configurable up to 300 seconds max, finding other devices is not configurable and about 12 seconds.

Making a device discoverable entails the following code:


Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
startActivityForResult(discoverableIntent, REQUEST_MAKE_BT_DISCOVERABLE);


That should enable Bluetooth if it isn't already and make the device discoverable for 120 seconds, and the result of the intent comes in at onActivityResult(). For receiving Bluetooth state changes you can register a BroadcastReceiver. This all works fine on a Nexus One.
But not on all devices, their Bluetooth stack contains bugs and behaves differently:

Samsung Galaxy S II
  • When you make the device discoverable, its state should become SCAN_MODE_CONNECTABLE_DISCOVERABLE, i.e value 23, meaning this device is both discoverable and connectable from remote Bluetooth devices.
    On the Nexus One I see the device.getScanMode() always return 23, as it should be. But on the Galaxy S2 it sometimes just changes to scanmode SCAN_MODE_CONNECTABLE (21), making it not discoverable anymore! And indeed, other devices don't see it anymore when discovering. In the S2 phone's settings I also see in that case that the discoverable checkbox is not checked.
    Workaround: the only workaround that always works seems to be to always first turn on the Bluetooth, and after that send the above discoverableIntent. Thus first do a


    Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableIntent, REQUEST_ENABLE_BT);


    then the


    Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
    startActivityForResult(discoverableIntent, REQUEST_MAKE_BT_DISCOVERABLE);



  • When you start the discoverable intent with any number different from 120, say 300, the popup that asks for user confirmation always shows 120, instead of the different number, say 300. The discoverability actually stays on for the specified amount (300 seconds), so functionally it works (phew), but the display is just incorrect and you can't change it. Confusing for the user.



Other bugs
I also found a few other more generic bugs reported by others and found by myself. Maybe they were SDK or device related (my app runs on Android 2.0+, tested on N1, Wildfire and Galaxy S2):

Sunday, April 17, 2011

Horizontally sliding grid of images in Android

For my Android app I wanted a swipeable and horizontally slideable 3x3 grid of images. If there were more than 9 images, a swipe should show the next 3x3 grid. Another requirement was that all should be in a ScrollView because there would be more on the screen above the 3x3 grid.
The first solutions that directly came to my mind were of course the GalleryView or a GridView or a horizontal ListView.
But the final solution was a ViewFlipper with TableLayouts as its children!
Below you can find my "journey" figuring out, experimenting and getting to the solution.

List View

Luckily for the horizontal ListView there was already a good example here. The result:


It really does work as a horizontal ListView, but it doesn't support the paging I was looking for; it is just a continuous list.
Sadly the code wasn't downloadable in one block (update: seems I just missed it at the top!), and it also contained typos (the layout tags were all lowercase), so it took me a while to get it all fixed and useable in an Eclipse project.
For people looking for the complete source code, you can find it here.
Note that the getView() has not been optimized yet, it creates a new view each time!
Also weird in the original code was the TextView within the ImageView, I changed the code to have them below each eachother.
An advantage of this implementation is that there is no "center locking" of the image anymore.

Gallery View

So the horizontal ListView didn't cut it, let's try a GalleryView (g1 in the code) with per item a standard GridView. That worked reasonably, except that the swiping didn't work anymore! Keypad scrolling still worked, kind of:


Luckily I found a fix for that, implementing a custom GridView that passes the fling on to the parent gallery (g2 in the code).



But then I still had to put it in a ScrollView... And then the trouble started, look at the vertical scrollbars that suddenly appear:


Bummer!

I ended up with quite some trial code which you can find here. Disclaimer: this is quite messy trial code so you are warned and I'm not supporting this code in any way; you are on your own here :)

Grid View

Making a GridView scroll horizontally in sets of 3x3 images just gave me headaches from the beginning. For example you can't set the number of rows, so it might sometimes show more rows than 3. And a GridView inside a ScrollView is also strongly warned against. Which btw also applies to ListView within a ScrollView. In general it even seems: a scrolling View inside another scrolling View is not a good idea and even strongly discouraged!
Furthermore, on a xlarge screen (WXGA) the gridview had almost its own will for laying out, making it look very different than on a HVGA screen.

The solution

After some more thinking I came across the solution: put Table Layouts of 3x3 images inside a ViewFlipper! Per table you can control how many images should be shown and it's quite easy to add swiping! Paging is then done per table. You can swipe left and right.
Page 1:


Page 2 after swiping to the left:


The source code of the final solution you can find here.
Note the top left icon in the grid changes when you swipe to the left the first time (so you can tell that the 2nd page is showing).
Note also the use of the FixedViewFlipper to handle rotation correctly.
A future improvement: keep track of the "page" you're on so with the example of 2 tables, don't allow the swipe/fling to "go round", i.e swiping to the left on the 2nd table shows the first table again...

PS: of course you could also create your custom component, but I want to avoid that as much as possible to minimize (custom) maintenance.

Misc

These links I also stumbled upon but didn't do anything with:

Hope this helps somebody some time :)

Monday, February 14, 2011

Upgrade to iTunes 10.1.2.17 iOS 4.2.1 on Windows XP 64bit

Over a year ago I managed to get iTunes 9.x.y working on my Windows XP 64 bit machine.
I had to try so many stuff, that I didn't really keep track of what I did :( The only things I wrote down were:


But now (february 2011) I wanted to upgrade my iTunes 9 to iTunes 10 because my OS on my iPod Touch was still at 3.x, and more and more apps are not running on 3.x anymore, they require 4.x and higher.
I knew it was going to be a nightmare, remembering the iTunes 9 installation. But this time I decided to keep track of it as detailed as possible. Hopefully it helps somebody...
I'll write down the steps as how I did them, so read through them all before trying yourself, you might be able to skip certain steps because they didn't work... WARNING: it is definitely not a clean walk-through!

  1. Goal is to follow the steps from http://skunkwerks.wordpress.com/2008/04/13/getting-itunes-to-work-with-windows-xp-x64

  2. Downloaded the 64b Vista version iTunes64Setup.exe from the Apple site

  3. First thought I could just try to run the .exe, since it's just an upgrade. But after some thought, thought I needed to modify the .msi file(s), since they do a check at startup. And, since all I need is an updated iTunes I don't think I even need to run all .msi files, only the modified iTunes64.msi (with the edits from the first post, NOT the deletion of entries as in this post).

  4. Installer starts successful, see screenshot (sorry for the Dutch):


  5. Let it install where the 9.x.y was already installed: C:\Program Files (x86)\iTunes

  6. A popup showed saying itunes+quicktime needs sp2 or higher. See screenshot:



    Just hit OK, install continues afterwards and it said successfully installed.

  7. And iTunes starts fine! Note that I still manually have to start the Mobile Apple service. I do that before starting iTunes, then plugin the iPod (with popup see screenshot), Then start iTunes.



    I also still do always get the popup about not being able to burn CDs. No problem.

  8. But now i get popup saying "itunes requires a newer version of Apple Mobile Support. Please uninstall both Apple Mobile Device Support and iTunes, then install iTunes again.". See screenshot:



    So it seems I do need to run AppleMobileDeviceSupport64.msi.
    But first edit it also. And instead of deleting entries, also here I just change 600 to 501.
    Starting AppleMobileDeviceSupport64.msi gives again needs SP2 or higher.
    So I also changed (VersionNT=501 And ServicePackLevel>=2) Or VersionNT>=600
    to (VersionNT=501 And ServicePackLevel>=2) Or VersionNT>=501.
    Yup .msi starts now, installs update in C:\Program Files (x86)\Common Files\Apple\Mobile Device Support
    Got popup saying it didn't pass windows logo testing. Continue anyway.
    Again for USB driver.
    And again. ipod goes on/off. Shows popup again that tries to open 2 programs when inserting ipod.
    Successful!! Note that it didn't require a restart and that I did not re-install iTunes.
    Started iTunes
    Yeah: ipod lights up, sync in progress

  9. It now asks for 4.2 upgrade of iPod software! yeah! See screenshot:


  10. Hit Download and update, then it says automatically checking for updates is disabled. If you hit cancel, nothing happens. So you have to select "Check this time":


    Then iTunes says my iPod isn't synced, are you sure to contiue? Canceled and did that sync first, making sure all my apps etc are in iTunes too.

    Selected Upgrade again, yup started downloading 4.2.1, see screenshot:


  11. Crap, got error 1603/1604, while popup Find new hardware wizard was suddenly showing up!

    Apple support link with some more info: http://support.apple.com/kb/TS1275
    Crap, can't find a driver (usbaapl). Seems it didn't get installed?
    So did this order of .msi running:
    1) AppleApplicationSupport.msi
    2) AppleMobileDeviceSupport64.msi
    3) iTunes64.msi
    Selected "recover" each time.
    Still didn't work, got 1604 error or something.
    Note that iTunes does see that the phone needs to be recovered, but it just fails after a long time with a 1603 or 1604 error. That indicates something is wrong with the USB connection.

  12. Then I manually got the driver from AppleMobileDeviceSupport64.msi as described here (using lessmsi*.exe): http://discussions.apple.com/thread.jspa?messageID=5619588
    Yeah but which one do I need, the 64b or 32b? Guess the 64b, that's named usbaapl64.
    Note that you need to do Have disk as in http://support.apple.com/kb/TS1538
    Ah now got 1604 error (device was sometimes recognised, then windows says new device found again etc)
    Did what is says here for error 1604 (turn if off etc):
    Showing Preparing iPod for restore... for a long time... minutes... Again 1604 error.
    So the USB driver is just not set up correctly.
    You hear when you try the restore (if the device is recognised in iTunes), the USB ping "rings" twice, meaning something is happening twice or something.
    Don't think that should happen. I also see the ipod flash shortly, then it's just dark.

  13. I did read somewhere that in this case you're kindof lost, so you need to restore the iPod OS via another PC/Mac for which everything is working. After that's on the iPod, you can restore from the 64b iTunes...
    Luckily I have such a PC around: XP 32bit. Downloaded the latest iTunes 10, installed, and plugged in the iPod. iTunes detected it needed restoring so it put 4.2.1 on it! Note that I didn't give my device a name at the end of the restoring (thinking that should come from the XP 64b iTunes anyway...).

  14. So after this, plugged it back in on the XP 64b. But it was not seen/recognised by iTunes! Though in the Windows Device Manager it said Apple iPod!

  15. Then I did update the driver as in here in the part Troubleshooting iTunes <-> iPhone Link/Sync
    After that, the Device Manager says Apple Mobile Device USB driver! See screenshot:


  16. And (since) iTunes was already started, it started immediately restoring apps! Yeah!! See screenshot progress:


    Darn! An error occurred restoring this ipod (-402620395):


  17. Before hitting OK, I removed any provisioning files I had on the ipod as said here in the post "bkynaston post: Posted: Jun 23, 2010 6:45 PM"

    Hit Ok. After this it tries to sync again.

    Yes! It says: iPod sync is complete. OK to disconnect.

  18. And yes, finally, finally, after about 1 day of working on this, my iPod is ok again! Even the language is back to English!
    Finally! Hope I never have to do this again...



Misc
Here's some other knowledgebase tip(s) that I looked at and tried