Tuesday 18 December 2012

Tiklbox


I'm pleased to announce - finally! - the release of Tiklbox.

You can get it here from iTunes for iOS:

https://itunes.apple.com/app/id541582548

There is more background on the Intermorphic website:

http://www.intermorphic.com/tiklbox/index.html

We worked very hard at the design of Tiklbox, to make it very easy to use, and as relaxing as possible. We also made it from the beginning for international users; Tiklbox is available from first release for many languages (English, Chinese, Dutch, French, German, Indonesian, Italian, Japanese, Korean, Portuguese, Russian, Spanish)...

The Android version is also written, but not fully debugged; we're planning to release that version in January (Android 2.3+).

Wednesday 26 September 2012

XVim - XCode plug-in for Vim!

What a great thing to have discovered - a Vim plug-in for Xcode. :)


https://github.com/JugglerShu/XVim

That is, quite simply, tremendous. :)

Thursday 19 July 2012

Android NDK debugging - on the Mac

I've been very busy the past couple of days, figuring-out how to track-down a bug in some JNI code.
I've installed a new Google "NDK Plugins" tool for Eclipse... that works rather well, and is well worth investigating if you need to debug Android JNI code from Mac, Windows or Linux.

http://tools.android.com/recent/usingthendkplugin

I had to install Eclipse 3.7 (I had been using 3.6...), and made sure to select the NDK Plugins option when installing the Android tools.

I also had to:
- turn-off all C++ building and code analysis from the Eclipse side
- set android:debuggable="true" in the AndroidManifest.xml
- right click project, select Debug -> Android Native...
- The breakpoints aren't hit; but I DO get the debugger fire-in when the code goes wrong!

Pete

Friday 18 May 2012

Noatikl 2 now on the Mac App Store

Noatikl 2 has now arrived on the Mac App Store, which is great news of course! You can find it here..

http://itunes.apple.com/us/app/id469613273?mt=12&u1=webmac&affId=1860684

We now have four products on the Mac App Store - Noatikl, Mixtikl, Mixtikl Free and Liptikl - thanks to Apple for providing this distribution channel!

Tuesday 15 May 2012

XCode and static libraries - auto-archiving the .a files for iOS and Mac

I was scratching my head figuring-out how to copy-out the .a files from an XCode static library build for iOS, so that I could distribute the static library variants easily to my customers.

For example, I had builds both for iOS and Simulator, and they were both going somewhere like this:

/Users/me/Library/Developer/Xcode/DerivedData/mylib_blahblahblahblah/Build/Intermediates/...
...ArchiveIntermediates/mylib/IntermediateBuildFilesPath/UninstalledProducts/libmylib.a

... which is a bit tricky to deal with!

The solution for me was to create a Run Script entry in the Build Phases for my Target under the XCode project/target settings...:

mkdir -p /Users/me/myfolder/$CONFIGURATION-$PLATFORM_NAME
cp -p $TARGET_BUILD_DIR/libmylib.a /Users/me/myfolder/$CONFIGURATION-$PLATFORM_NAME
if [ $CONFIGURATION eq Release ]
then
  /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip -S \
  /Users/me/myfolder/$CONFIGURATION-$PLATFORM_NAME/libmylib.a
fi

Note the strip command, which is essential as the script is always run before XCode's normal strip behaviour!

Note also that to generate a library for Release, for both iOS and simulator, you need to Build for Profile.

Here is the command-line I use to build it all automatically!

xcodebuild -project mylib.xcodeproj Release -sdk iphonesimulator clean
xcodebuild -project mylib.xcodeproj Release -sdk iphoneos clean
xcodebuild -project mylib.xcodeproj Release -sdk iphonesimulator
xcodebuild -project mylib.xcodeproj Release -sdk iphoneos

To configure an app to link-in the appropriate version of the (copied!) static library to match your app's Release/Debug configuration and target (iOS/Simulator), simply put this in the Other Linker Flags section of your app...:

/Users/me/myfolder/$CONFIGURATION-$PLATFORM_NAME/libmylib.a

Sunday 13 May 2012

Noatikl 2 launched!

Noatikl 2 - we've finally launched it! What a huge chunk of work this has been.

http://www.intermorphic.com/tools/noatikl/index.html

I'm pretty pleased with the way that the network editor works, together with the pop-up Synth and Effects editors. The cross-integration of our code base is working well!

Next step: the mobile versions for both iOS and Android. That'll take quite a lot more work, but the GUI changes already in place in Noatikl 2 have taken us a long way down that road.

Tuesday 24 April 2012

Noatikl 2 - getting close!

The initial version of Noatikl 2 is now very close to release!

We have just got a couple of things to tweak, and some documentation to check.

Hopefully Noatikl 2 will "go live" in just a few weeks...!

Monday 12 March 2012

Debugging JNI/C++ code under Android/Eclipse is now easy!

At long last: debugging JNI/C++ code under Android/Eclipse is now easy!

All I had to do was to get this amazing software for Eclipse - thank you ARM ! ...:

http://www.arm.com/products/tools/software-tools/ds-5/community-edition/ds-5-community-edition-debug.php

Because I'm using a Mac, I had to install under a Linux VM on my Mac...

The only obscure thing I found when following the install/configure instructions, was that I in order for the new DB-5 Debugger Debug Configuration to find my device, I first to do this:

sudo ln -s /usr/bin/adb

Brilliant!!

Pete

Wednesday 29 February 2012

Android - handling rotation/orientation in an Activity - the easy way!

As I've been asked a few times about the easy way to handle rotation in an Android Activity, I thought I'd outline what you need to do here. It is really easy when you know how!

Firstly, you need to ensure that you create your layouts such that they resize easily. See this post for more details:
http://sseyod.blogspot.com/2010/09/art-of-scalable-android-layouts.html

Secondly, if you find that you need a different layout for your Activity (say in Landscape mode), create it in a parallel folder to your portait layout like this...:

res/layout/fred.xml
res/layout-land/fred.xml


In your mainfest xml activity... block, make sure you put this:

android:configChanges="orientation|keyboardHidden"


In your Activity's onCreate() method implementation, pretty much all you need to do is call this method:

  myConfigureUI();


Create the following override method in your Activity (this is called when the orientation changes as your Activity is rotated):

@Override
public void onConfigurationChanged(Configuration newConfig) {

  // Copy current item values from all your UI gadgets...!
  String myvalue = myitem.toString();
  // etc...

  // Call the method that re-orients and re-creates the gadget bindings
  myConfigureUI();

  // Restore the gadget values to the post-rotation UI:
  myitem.setText(myvalue);
}


Finally, you can create your new method, which makes your Activity use the correct layout to suit your orientation, and which looks-up all the gadgets, prepares their data and sets-up their event handlers (remember: this is called both at creation time, and whenever a rotation event occurs!):


private void myConfigureUI() {

  /*
  // Can check the orientation of the screen...
  if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
    setContentView(R.layout.main_landscape);
  } else {
    setContentView(R.layout.main);
  }
  */

  // This will automatically pick-up the landscape layout or portrait layout...
  setContentView(R.layout.main);

  // Look-up new items etc. ... mySpinner is an instance variable in this example...
  mySpinner = (Spinner) findViewById(R.id.spinnerx);

  mySpinner .setOnItemSelectedListener(new OnItemSelectedListener() { ...
  ...}
  // etc. ...
}

Monday 23 January 2012

Noatikl 2

We've been working very hard on Noatikl 2, and it is going to be a huge improvement on Noatikl 1.x!

Noatikl 2 will include built-in support for the Partikl synth that has already been featured in Mixtikl (in addition to all of Noatikl's traditional MIDI I/O options).

When Noatikl 2 launches later this year, it will be available directly from the Mac App Store in standalone form (Windows versions and Mac Plug-in variants will be on the Intermorphic web store as usual). We're also planning to bring it to both iOS and Android at some point this year. :)

Pete

Saturday 24 December 2011

Mixtikl on Kindle Fire!

I got hold of a Kindle Fire via a re-seller on eBay UK, so we could make sure that Mixtikl worked fine on there.

This being Android, Mixtikl worked first time! I installed Mixtikl using "adb install", having first configured adb to recognise the Kindle Fire.

I changed just a couple of things to make it "Kindle Fire friendly". Those being:
- I had to increase the audio latency a bit to prevent audio break-up; that took just a few minutes...
- I had to account for Kindle Fire's taking-up the bottom 20 pixels to display the soft menu bar, which was also very easy...

I must say that Mixtikl works *really* well on the Kindle Fire. The Mixtikl icon doesn't display at the moment; but I have read that is because the icon is downloaded only when you purchase the app from the Amazon Kindle App Store.

I should say that I'm really very impressed with the Kindle Fire! It is a really nice size, fabulous for reading books (thanks to the touch screen, which isn't on the older Kindle of course). It seems very fast, with a lovely clear display and it is very responsive. Will I like it more than my iPad 1? Well, I prefer the size of the Kindle Fire, and like the stereo speakers; and I like that it was so easy to get our app running on it. :) The iPad has the advantage of having lots of good quality games for it... but it is *very* much more expensive ... if the Kindle Fire had more storage space, it'd make quite a nice MP3 player.

It's a pain that the Amazon App store is US-only at the moment, but I'm sure they'll roll that out worldwide soon enough. I think they're going to sell huge numbers of Kindle Fires!

I also want to mention that so far, the Amazon developer support team have been *incredibly* responsive to the questions I've had; I'm genuinely impressed!

Pete

Monday 19 December 2011

Mixtikl on Android - reprise

The port of Mixtikl to Android is now complete.

I thought I'd share how I ended-up porting Mixtikl to Android, as this has been a long road!

When I first started looking at porting Mixtikl to Android, there were a couple of blocking issues;
- no support for C++ template library (though eventually STLPort arrived!)
- the original Mixtikl made heavy use of Modal code for dialog handling
Both of those two items made a port with Android a non-starter.

However, when Airplay (now renamed to Marmalade) added Android support, we were able to attempt a port of Mixtikl; as Airplay contained both C++ template library support, and supported modal code.
Ultimately however, the audio had too many problems (very, very high latency, mono only etc.) and this made the Airplay-based port too low quality to allow us to release.

Time moves on however, and a while back, Google added full support for the C++ standard template library to the Android NDK. And, critically, they added support for both OpenSL (to Android 2.3), and 32-bit ARM code generation (via armeabi-v7a). Given those changes, we spend a month or so removing all the modal code from Mixtikl (not an easy undertaking: you have to bear in mind that Mixtikl is > 500K lines of C++ code!).

So, given those changes in both the Android NDK and Mixtikl's own internal architecture, we were able to start and complete a full native port of Mixtikl to Android.

The bulk of Mixtikl remains in C++; all the graphics are drawn via the same C++-based graphics engine we use on all platforms. The interaction with Java is just a thin JNI layer that looks after drawing primitives and some native dialog interfacing. The OpenSL support is just a thin adaptor layer between our audio engine and OpenSL.

Mixtikl performs brilliantly on Android in my opinion; though as noted elsewhere the Android OpenSL implementation is missing a big trick in regards latency management; and we have to configure Mixtikl to run with slightly higher latency that we would like in order to avoid any potential audio break-up.

I should note that debugging C++ code on Android remains a pain, you have to rely on trace statements, though it is at least possible now to use gdb and the bt command to trap crashes when they happen and see where the code died in the call stack.  The good news is that Mixtikl is very solid as 99% of the code is cross-platform and very well tested, so I've hardly had to touch gdb!

Tuesday 6 December 2011

Android - how to reduce the audio latency

I been approached a few times recently about how to write low-latency audio latency apps in Android, and I point such queries to OpenSL and say "look how much better it is that it used to be" (provided you use C++ of course)...

But the bottom line is that the Android audio APIs - and that includes OpenSL - still miss the basic trick, which is to be able to offer a programmatic contract that is as simple as this:

App : what sets of audio rates and formats do you support?
(API responds, maybe only 22Khz stereo, for sake of simplicity here)
App asks: how  many audio blocks do I need to keep prepared and primed in the queue to guarantee no break-up, at this rate/format, with minimal latency?
(API responds, maybe 2 blocks of 512 sample frames each).
App prepares the first 2 blocks, submits, and gets "block delivery" callbacks; it runs a separate thread to keep internal queues topped-up ready to deliver to the callbacks.

With that sort of contract, every app can run at minimal latency dictated by the underlying audio device (through the driver layer), on any device.  Without that sort of contract, every app developer is kept guessing, and has to assume a worst case that works on all devices available for testing. :)

I implemented this scheme nearly a decade ago in the intent Sound System (I had the luxury of designing and implementing a complete audio architecture for mobile devices from scratch!). It is a piece of cake to do, and IMO audio developers for Android are screaming for it. Intent was focused on ultra-low audio latency for games / multimedia and musical instruments...

I should note, Apple have also missed the same trick - they've been able to get away with it however, as the offer a fixed range of hardware....

Sunday 4 December 2011

Android - high performance audio - how to do it

I've just been through a very interesting period of work, sorting-out a high-performance audio interface for the Android port of Mixtikl. I've learned quite a few things - here are the highlights.

Firstly, target Android 2.3 or later. This allows you to use OpenSL ES, which is the only realistic approach for low-latency audio on Android. The audio allows you to delivery audio with pretty low latency, and totally avoids any problems of garbage collection blocking you. This of course assumes that all your audio code is written in C++!

As you're using OpenSL ES, and assuming you have some very heavy audio DSP going-on (like in Mixtikl!), you'll need to use a separate Posix thread to keep your audio callbacks pumped-up. Basically, if your OpenSL ES audio block callbacks take much time at all, then your audio will break-up. So, use a worker thread to keep sufficient audio pre-processed and ready to be picked-up and delivered via your callbacks!

Finally, and believe me this is important (!): make sure you target armeabi-v7a, and that you use the right compiler flags to generate 32-bit ARM7 code. If you on the other hand use the default settings, you'll generate Thumb code for armeabi - and your code will run staggeringly slower  (!!), and audio break-up is inevitable if you're doing anything serious. So: don't bother targeting armeabi devices... and Thumb code is a no-no.

Follow my advice, and you can create sophisticated audio software. Don't follow it, and you're going to find things tough! ;)

I should note that support for  armeabi-v7a *emulator* targets arrived in the Android 4.0 SDK... which makes things a lot easier as well... something worth knowing!

Finally... here is a useful resource on the background to OpenSL ES on Android...:
http://mobilepearls.com/labs/native-android-api/opensles/index.html

Friday 21 October 2011

gdb with the Android NDK - now easy with ddd wrapping ndk-gdb!

You might already know this, but android debugging from gdb is now really, really easy - if you can stomach gdb!

See Android/ndk6/docs/NDK-GDB.html in your NDK documentation.

Basically, the app's libs/armeabi folder must have a gdbserver in it... which is put there automatically by the ndk-build command (remember not to ship your app with gdbserver in it!):

So...: build your app, install with e.g.:

adb install -r myfile.apk

Start your app directly from the Android UI, and in a terminal, change to your product's jni folder, and you're ready to debug!



Using command-line gdb
 

If you prefer command-line gdb, type this:

ndk-gdb com.example.myproject

You'll find that ndk-gdb will break as your app attaches. Set a breakpoint if you want (optional!), e.g.:

b Java_com_example_hellojni_HelloJni_stringFromJNI

Enter "cont" to continue...

And the debugger will now hit your breakpoints - use normal gdb commands to control it!







Using the graphical ddd debugger


If, like me, you prefer to use a graphical debugger, it is really easy to use ddd as a wrapper around gdb!

Many thanks to a blog reader for this suggestion!

It is really easy to use ddd instead... here are the steps!




Preparation:

1. Install ddd ...!
2. Modify your ndk-gdb shells script, to simply comment-out the "exit 1" call when an unexpected argument is passed



Running ddd 



Start your app from Eclipse

In a terminal window, change to your project's jni folder (if you're not there already!)
Run ddd like this - and that is all there is to it!

ddd --debugger ndk-gdb



Eclipse

Using gdb for JNI/NDK from Eclipse is way too painful - I wouldn't bother. :)



Run-time breakpoints


On a final note, you can use this code to force a breakpoint at runtime...


    #define BREAKPOINT __asm__ ("bkpt 0")
    printf ("Hitting breakpoint!");

    BREAKPOINT;
    printf ("Gone past breakpoint!");

Friday 23 September 2011

Mixtikl 5... iOS/Mac/Windows

Work on Mixtikl 5 is keeping me very busy... we've decided that Mixtikl 5 will be the first version we release for Android. However, we'll focus first on finishing-off the iOS / Mac /Windows versions...

Friday 2 September 2011

Android - using findbugs from outside of Eclipse

I've been using findbugs to help track-down issues in Java code.  A great tool, and very easy to use. However, the Eclipse-based plug-in keeps crashing for me with a NullPointerException on certain projects.

The work-around to this is to run the findbugs UI directly.

To use findbugs on Windows, get the latest findbugs distribution from http://findbugs.sourceforge.net/downloads.html, and then create a shortcut along the following lines (note the -X argument, as the JVM might require quite a bit of memory to avoid failing for big projects!):
\java.exe -Xmx1024m -jar C:\findbugs-1.3.9\lib\findbugs.jar -gui

On Mac, simply run something like this directly:
java -Xmx1024m -jar ~/findbugs-1.3.9/lib/findbugs.jar -gui &

It is really use to use - just select File -> New Project, add your class folders to the top pane, your source folders to the bottom pane, and let it run!






Tuesday 30 August 2011

Upgrade to XCode 4.1 - problems with VALID_ARCHS for iOS Simulator builds

Now that I've taken the plunge and moved to XCode 4.x from XCode 3.x ... I had a really weird problem trying to build my iOS projects for the simulator.

The builds failed with an error related to the i386 architecture. I simply couldn't figure-out how to solve this directly with the XCode IDE.

Luckily, I did manage to figure-out a fix: my solution was to edit the project.pbxproj files by hand with vim, and remove a handul of fixed definitions of VALID_ARCHS ... which presumably were legacies from previous XCode versions. This is not the first time that I've been forced to fix XCode projects by hand!

Monday 29 August 2011

Mac - how to build installs the easy way

I was having some issues getting my Mac installers (using packagemaker) working with the latest XCode.

Surfing-around, I found an amazing free tool called Packages. The link is here:

http://s.sudre.free.fr/Software/Packages/about.html

I've changed our own installers to use this, and all my Mac install problems have disappeared with an hours work. :)

Pete

Wednesday 10 August 2011

Audio and MIDI Visualization in iOS and Android

I've spent the past month focused mainly on Intermorphic's Audio/MIDI visualizer framework, dusting-down the work I did (in the main) several years ago, and bringing it all up-to-date.

I've recently experimented with lots of stuff, 2D and 3D, on iOS (UIKit ,Quartz, Core Animation and OpenGL ES) and Android (Canvas and OpenGL ES), and have learned a lot about performance differences between these approaches. I've also learned that OpenGL ES is a truly horrible API in which to get things done. :)

Anyways - things are coming on nicely: the next update for Mixtikl should be another good one!


Wednesday 3 August 2011

Android - turn key events into UTF8

As I've been asked a few times how to turn Android key events into UTF8 data, I figured it might be a good idea to share this info in my blog! It is actually really easy:

  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    int [] unicodePoints = new int[1];
    unicodePoints[0] = event.getUnicodeChar();
    String x = new String(unicodePoints, 0, 1);
    byte utf8Bytes[];
    try {
      utf8Bytes = x.getBytes("UTF-8");
      // Do something with your UTF-8 data here!
      // ....
      // ...
    } catch (UnsupportedEncodingException e) {
      Log.e(TAG, "Failed to key event to UTF8");
      Throw(e);
    }
    return true;
  }

Tuesday 26 July 2011

Mixtikl on Android - with the NDK and C++

Mixtikl is now around 95% complete on Android.

I've actually ported directly with the NDK/C++, without using Juce, as my 2d portability layer for Mixtikl is so highly developed. The port only took around a week (having already done a lot of background work in the previous few months laying the ground for this and possibly other ports!).

The Audio drivers have caused some fun; I've ended-up implementing support for AudioTrack and AudioRecord in native code with pthreads. Audio support in Android really is very poor...

The biggest headache is the basic problem of debugging C++ code on Android; I'm reduced to using trace statements. It helps that 99% of the code in Mixtikl is cross-platform and fully debugged on other platforms!

That said, the latest NDK (R6) is much improved with STL support and what have you; there is no longer any need to use Crystax.

I'm still very likely to use Juce for the Liptikl and Noatikl ports to Android, of course!

Thursday 30 June 2011

Juce, C++ and Android

I've been experimenting with using the fabulous Juce to create programs for Android using C++.

You can find out about Juce here: http://www.rawmaterialsoftware.com/juce.php

If you haven't used Juce yet - give it a go! I've been using it for years now to create cross-platform programs for Mac and Windows...

Monday 31 January 2011

SourceTree - git app for Mac

I've started using SourceTree for painlessly reviewing historical changes in my Mac's git repository.

Steve (the SourceTree developer) has just added a feature which easily allows you to visualise changes between arbitrary versions of any given file, which is far easier than trying to do with the git command-line tools!

Give it a try - its a really nice tool!

Thursday 7 October 2010

Android: creating pop-up menus at specific screen locations

If you need to create a pop-up menu in Android, anchored to a specific View on the screen you could use this simple code as a template:
http://www.londatiga.net/it/how-to-create-quickaction-dialog-in-android/

However, what if you want your pop-up to appear at a particular position on screen, where you don't have a View to anchor to (maybe to overlay some other element on-screen that you don't have ownership of)?

My solution was as follows:
- Create a new Linear Layout to use as a "canvas" - call it my_canvas - and set to fill parent.
- Create a single ImageView (with empty drawable - it'll remain invisible!) within that layout, set to wrap content; call it my_canvas_image.
- We then create dynamically a view based on the layout, with left and right margin properties to pixel-position to the correct place on screen; use the image in that view as the anchor for our pop-up; and remove the view when done.

When your activity is created, grab the ViewGroup used to create the main activity layout like this:

    LayoutInflater inflater = (LayoutInflater)getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ViewGroup appViewGroup = (ViewGroup) inflater.inflate(R.layout.myApp, null);
    setContentView(appView);

When you need to create the pop-up at a given screen location, you can use code like this:


    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    int screenWidth = windowManager.getDefaultDisplay().getWidth();
    int screenHeight = windowManager.getDefaultDisplay().getHeight();
   
    LinearLayout.LayoutParams paramsx = new LinearLayout.LayoutParams(screenWidth, screenHeight);
    paramsx.leftMargin = putPopUpHereX;
    paramsx.topMargin = putPopUpHereY;
       
    LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View canvasView = inflater.inflate(R.layout.my_canvas, null);
   
    appViewGroup.addView(canvasView);

    ImageView imageViewUseAsAnchor = (ImageView) canvasView.findViewById(R.id.my_canvas_image);
   
... the imageViewUseAsAnchor item can then be used as the "anchor" to which to attach the pop-up menu at your required screen location!

When you've finished, you can then remove the "canvas" view with:

    appViewGroup.removeView(canvasView);

Tuesday 28 September 2010

The art of scalable Android Layouts

One of the harder things to figure-out in Android, is how to do nice layouts that work well on different screen sizes. And, as always, I found it difficult to track-down that information!

The key for me in many circumstances is to position elements in percentage terms, generally using fill_parent; this is the opposite of most of the examples I have seen, which tend to use wrap_content! My approach is to ensure that size is defined in relative terms from the top-down using layout_weight; rather than allowing children to dictate their own size (and thereby mess-up the overall layout) via wrap_content...!

Here is a simple example of my approach.

Imagine you have a screen which you want to split into two panels, aligned horizontally, where you want the right-most panel to take-up around 20% of the screen size.

Create a new layout which is a frame layout. Set layout_width and layout_height to fill_parent. We'll use this for most other elements.

Create a LinearLayout within the frame parent. Set orientation to horizontal. Set layout_width and layout_height to fill_parent.

Create two elements (whatever type you need for your panels, maybe ImageView with a drawable background picture if you're just experimenting) as children of your new LinearLayout. Set both to have layout_width of 0dp and layout_weight of 50. If you want one layout to be wider than the other, simple adjust the weights; the element with the larger weight will be bigger proportionately. Make sure you sent layout_height to be fill_parent; otherwise the panels won't properly fill the screen in different configurations!

Want to split-up your right-hand panel into two vertical elements? Replace your right-hand element with a new LinearLayout. Set layout_width to 0dp, and layout_weight to whatever your previous right-hand element was (e.g. 50). Set orientation to vertical. Set layout_height to fill_parent. Put two new elements (again, maybe ImageViews for example...) in the new LinearLayout; set both to have layout_height of 0dp and layout_weight of 50. If you want one layout to be taller than the other, simple adjust the weights. Make sure you sent layout_width to be fill_parent; otherwise (as before!) the panels won't properly fill the screen in different configurations.

And so on! If you need to use an invisible spacer, you can simply follow the above approach and use an empty LinearLayout with the appropriate layout_weight.

This makes sense once you've tried it out; remember to test the layout you create with a variety of AVD sizes!

Thursday 16 September 2010

Android - building the PDK using the SDK, and adding a system service

Just how difficult can it be to:
- download the Android source code
- figure-out how to add a system service
- call that service from an activity installed to the system after the event
- test this all out with the Android emulator.

The answer is: a lot harder than you might think!

Because this was a complete pain, involving a lot of fiddling-around and tracking-down and experimentation, I thought it would be a good to set-down the steps I had to follow. I really hope it helps somebody else out at some point!

I'm assuming that you're using Windows XP, Vista or 7 as your base machine.

Downloading the Android Source Code (Platform Development Kit - PDK)

The Android Platform Development Kit (PDK) download instructions are here.

http://source.android.com/source/download.html
First things first: install VirtualBox and install Ubuntu 32-bit as a Virtual Machine under Virtual Box.

Then install Android under this Virtual Machine.

You’ll require the java5 jdk.
The 64-bit build does not always work properly... it depends on which version of Android you’re using...:
   
http://groups.google.com/group/android-building/browse_thread/thread/193332fd6850a2a
All the open-source codelines that mirror Google's internal froyo
branches (i.e. froyo and the various android-2.2) use 1.5 like
Google's matching internal codelines do, since that is the version
that Google developed and tested with through the entire froyo
development cycle.
We backported the changes to switch to version 1.6 (64-bit) from our
internal master branch to the open-source master just as we switched
our internal master branch to 1.6, since from that point there was no
requirement for contributions to the open-source master branch to
build under 1.5 any more.
If you need to use the 64-bit version, then in order to complete the sudo apt-get install git-core ... command, you'll need to do this to get the correct JDK configured:
# Tell the system where to get the JDK...
sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
# Update the source list:
sudo apt-get update
# Install sun-java6-jdk;
sudo apt-get install sun-java6-jdk
The build problems otherwise are very simiilar to those for the 32-bit system.
However, various blocker issues arose. I gave up, and reverted to 32-bit building...

IMPORTANT NOTE: use the following as your repo init command... otherwise, you’ll get “work-in-progress” which might not build (if you want a different Android source code tag other than froyo, then feel free to use one):

repo init -u git://android.git.kernel.org/platform/manifest.git -b  froyo

If you have problems with Java, try doing this:

sudo update-java-alternatives -s java-1.5.0-sun

Building the Android Source Code

The Android PDK build instructions are here:

http://pdk.android.com/online-pdk/guide/build_system.html

Here is what I did.
cd ~/mydroid
lunch
# Choose 2. simulator
make
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.2
TARGET_PRODUCT=sim
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=true
TARGET_BUILD_TYPE=debug
TARGET_BUILD_APPS=
TARGET_ARCH=x86
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=MASTER
============================================

Note: the alternative of trying choosecombo (selecting: simulator, debug, eng) isn’t recommended by Google heads; they recommend the use of lunch.

To fix this error...:
usr/include/bits/fcntl2.h:51: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT in second argument needs 3 arguments

... I had to edit frameworks/base/core/jni/android_server_Watchdog.cpp
... and add this new argument at the end of the function call where the reference to O_CREAT:
    , 0644);

To fix this error...:
frameworks/base/opengl/libagl/egl.cpp: In member function ‘virtual EGLBoolean android::egl_window_surface_v2_t::swapBuffers()’: frameworks/base/opengl/libagl/egl.cpp:554: internal compiler error: in add_phi_arg, at tree-phinodes.c:391

... I had to remove the const on line 554 in frameworks/base/opengl/libagl/egl.cpp

If you get problems reported when building, related to a missing file called asoundlib.h, you’ll need to install these packages and then re-attempt the make:
libasound2-dev
lib32asound2-dev

If you get problems reported when building, related to a missing files called something similar to wx/wxprec.h, you’ll need to install this package (WxWidgets) and then re-attempt the make:
sudo  apt-get install libwxgtk2.6-0 libwxbase2.6-0 libwxbase2.6-dev

If you get problems reported when building like this:
|~/mydroid/development/simulator/app/DeviceWindow.cpp:175: undefined reference to `wxImage::HasAlpha() const'

You’ll have to edit:

development/simulator/app/DeviceWindow.cpp - comment-out the lines to HasAlpha()
development/simulator/app/MainFrame.cpp - comment-out the lines to AppendSeparator()

Modify external/stlport/stl/_num_put.c in line 351. to use the first form of the #ifdef block (the second one, which would otherwise be chosen, causes compilation to fail).

Remove external/gtest/Android.mk, external/gtest/test/src/Android.mk and external/gtest/test/Android.mk (which had STL-related problems in my system!)


Installing the Android SDK

Download the Android 2.2 SDK from here:
Expand the download file to e.g. ~/Downloads/android-sdk-linux_x86

Add this to your PATH, e.g. (but: make sure this comes before any other Android-related paths...):

export PATH=$PATH:~/Downloads/android-sdk-linux_x86
Restart your shell!

cd ~/Downloads/android-sdk-linux_x86/tools
export ANDROID_SWT=/home/mycompany/Downloads/android-sdk-linux_x86/tools/lib/x86
android
... Select and install the SDK Platform Android 2.2, API 8, revision 2
Further notes on installing the Android SDK 2.2
Now, copy the Platform SDK so that the PDK can find it!

    # Create a new terminal, and then...
cd ~/mydroid
export PATH=/home/mycompany/mydroid/out/host/linux-x86/bin:$PATH
# Note that when you first do this, ~/mydroid/out/host/linux-x86/platforms and ~/mydroid/out/host/linux-x86/add-ons do NOT exist!
cp -pR \
~/Downloads/android-sdk-linux_x86/platforms \
~/Downloads/android-sdk-linux_x86/platforms \
out/host/linux-x86

Running the emulator with the newly built image

# Create a new terminal, and then...
cd ~/mydroid
export PATH=~/mydroid/out/host/linux-x86/bin:$PATH
export ANDROID_SWT="~/mydroid/out/host/linux-x86/framework"
# Now-up the emulator with the correct image!
./out/host/linux-x86/bin/emulator -system out/target/product/generic/system.img -sysdir out/target/product/generic -kernel prebuilt/android-arm/kernel/kernel-qemu -data out/target/product/generic/userdata.img

Adding a new service to the System

With reference to:
http://groups.google.com/group/android-porting/browse_thread/thread/f9a383ce949d1557?pli=1

Create a new service as frameworks/base/packages/IMyTest, as a very rough clone of frameworks/base/packages/SettingsProvider

Edit build/target/product/core.mk, to add the following line after the line showing DefaultContainerService :

        MyTestService \

Create various files:

mydroid/frameworks/base/packages/MyTestService/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany">
       <application android:label="@string/service_name">
          <service android:name=".MyTestService"
                   android:enabled="true"
                   android:exported="true"/>
       </application>
</manifest>
   
mydroid/frameworks/base/packages/MyTestService/Android.mk


LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := MyTestService
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)

mydroid/frameworks/base/packages/MyTestService/src/com/mycompany/MyTestService.java

package com.mycompany;
import com.mycompany.IMyTestService;
import com.android.internal.content.PackageHelper;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
import android.app.IntentService;
import android.util.DisplayMetrics;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import android.os.FileUtils;
import android.provider.Settings;
//
// Based on src/com/android/defcontainer/DefaultContainerService.java
// Also see:
//   frameworks/base/packages/MyTestService/AndroidManifest.mk
//   frameworks/base/packages/MyTestService/Android.mk
//   frameworks/base/core/java/com/mycompany/IMyTestService.aidl
//   frameworks/base/Android.mk
//
// Note that the .aidl file can be used by external activity projects.
//
public class MyTestService extends IntentService {
    private static final String TAG = "MyTestService";
    private IMyTestService.Stub mBinder = new IMyTestService.Stub()
    {
          public void setValue(int val)
          {
                  Log.i(TAG, "In setValue! ");
          }
    };
    public MyTestService () {
          super("MyTestService");
          setIntentRedelivery(true);
    }
    @Override
    protected void onHandleIntent(Intent intent)
    {
          Log.i(TAG, "onHandleIntent!");
    }
    public IBinder onBind(Intent intent) {
          Log.i(TAG, "onBind!");
          return mBinder;
    }
}

mydroid/frameworks/base/packages/MyTestService/res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
       <!-- service name  -->
       <string name="service_name">MyTestService</string>
</resources>

mydroid/frameworks/base/core/java/com/mycompany/IMyTestService.aidl

package com.mycompany;
interface IMyTestService {
/**
* {@hide}
*/
void setValue(int val);
}
The interface file will need to be added to the build system, so edit frameworks/base/Android.mk
... and add the following at the end of the list of LOCAL_SRC_FILES:

core/java/com/mycompany/IMyTestService.aidl \

Type this to make... if you don’t do the make clobber, your new MyTestService.apk file won’t be built!

make clobber
make

You will now see this error reported:

(unknown): error 3: Added class IMyTestService to package android.os
(unknown): error 3: Added class IMyTestService.Stub to package android.os
******************************
You have tried to change the API from what has been previously approved.
To make these errors go away, you have two choices:
 1) You can add "@hide" javadoc comments to the methods, etc. listed in the
    errors above.
 2) You can update current.xml by executing the following command:
       make update-api
    To submit the revised current.xml to the main Android repository,
    you will need approval.
******************************

Type this to work-around this:

    make update-api

Run this to verify that your new service is now in the build images...:
ls -ltr ~/mydroid/out/target/product/generic/system/app/MyTestService.apk
find . -name "*.img" | xargs -- grep "MyTestService.apk"

... This should show that the code you’ve created has appeared in ./userdata.img ...
Edit this to look for IMyTestService,  to verify that it has been added!

~/mydroid/frameworks/base/api/current.xml

5. Test the service APk is picked-up by the emulator...

Start up the emulator (using the previous long command line!)
Using adb logcat, you will find the message saying that the package has changed.
I/PackageManager(   68): /system/app/MyTestService.apk changed; collecting cert

6. Test Program

To test the service, create a "Hello World" activity using the project wizard under Eclipse on Windows.

MyServiceTest.java:
package com.mycompany.MyServiceTest;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import com.mycompany.IMyTestService;
public class MyServiceTest extends Activity {
  /** Called when the activity is first created. */
  IMyTestService imts;
  private ServiceConnection mConnection = new ServiceConnection()
  {
      @Override
      public void onServiceConnected(ComponentName className, IBinder service)
      {
          Log.d("MyServiceTest", "onServiceConnected");
          imts= IMyTestService.Stub.asInterface(service);
          try
          {
              // Try a dummy call to the service!
              imts.setValue(7); // Any value would do, this is just a test!
          }
          catch (Exception e)
          {
              e.printStackTrace();
          }
      }
      @Override
      public void onServiceDisconnected(ComponentName arg0) {
          Log.d("MyServiceTest", "onServiceDisconnected");
          imts = null;
      }
  };
  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      Log.d("MyServiceTest", "onCreate");
      setContentView(R.layout.main);
      // Bind-in to our service!
      Log.d("MyServiceTest", "try to bind service");
      Intent intent = new Intent();
      intent.setClassName("com.mycompany", "com.mycompany.MyTestService");
      bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
      Log.d("MyServiceTest", "done try to bind service");
  }
}

Copy the IMyTestService.aidl file from your Linux box, in your Windows/Eclipse project’s src/com/mycompany sub-folder (you’ll first have to create this folder in your activity’s project folders).

Refresh your project files from the Eclipse menu and rebuild/debug.

This should run benignly under Windows, warning that it was unable to find the service.

Copy the apk file to your Linux VM, and install using:

adb install MyServiceTest.apk

Run the app using the emulator’s launcher; you should see the service used!
I/MyTestService(  360): onBind!
D/MyServiceTest(  354): onServiceConnected
I/MyTestService(  360): In setValue!

Easy, wasn't it? :-D