Example Archive

During my last project I’ve encountered two problems which caused my app to crash which I did not expect to happen because the exact same code was working within other activities. These crashes were triggered by simple things like a press on the search button of the mobile phone or the creation of a dialog. When analyzing this behavior I’ve identified a nested TabHost View (a TabHost containing another TabHost which contains the Activity) as the cause for the crashes.

One TabHost contains another TabHost. The last TabHost contains the actual content.The problematic setup is shown in the picture to the left. As you can see the outer TabHost contains a Tab which Activity itself is the inner TabHost. The content of this TabHost will be placed between both tabs. So this is a design which doesn’t seem to be so out of the world.

In the following I’m going to point out the two problems and the solutions which I’ve found so that you might be able to solve similar problems. To demonstrate the problems I’ve created an example project which causes the errors and one which contains the proposed solutions where the functions are working.

In this post I will introduce a library which you can use to display elements from any java.util.Collection (e.g. LinkedList, ArrayList, HashSet, Queue, Stack, TreeSet,…) within a ListView or a Spinner. It’s even possible to use this library to implement any layout you want for your entries (e.g. a multi-line entry, an entry with including an image, etc…).

So this library is a very generic approach to display various data sets with custom layouts in a Spinner or a ListView.

To demonstrate the basic usage of this adapter I’ve created a simple example application which is using the library for a spinner adapter for a HashSet of UUIDs and a list adapter to display the single parts of the UUID together with an image.

Feel free to download the library and to reuse it within your own projects. You can also examine the source code at our Google code repository (http://code.google.com/p/android-collections-list-and-spinner-adapter/) by browsing the source or by checking it out using svn.

Basic Usage

Initialization

To ensure an easy usage of the library the CollectionsAdapter provides constructors which are closely related to the default android ArrayAdapter. The basic usage includes the use of the adapter with a simple TextView resource id or with a layout resource id and a TextView resource id which is located within this layout. When you are using this methods the objects toString method will be used to populate the give TextView element with text.

The corresponding constructors of the com.appsolut.adapter.collections. CollectionsAdapter<E>  class have the following signature:

public CollectionsAdapter(Context context, int textViewResourceId, Collection<E> content)
public CollectionsAdapter(Context context, int layoutResourceId, int textViewResourceId, Collection<E> content)

Where the parameters should be:

    • context – The context for the adapter. This could be your Activity.
    • layoutResourceId – If you are using the constructor which also uses a give layout resource id this parameter should point to this layout.
    • textViewResourceId – The resource id which is referring to the text view element used to fill in the text.
    • content – The collection containing the data. The collection can be modified afterwards.

 

In the example project this is demonstrated by the adapter for the spinner:

final Set<UUID> spinnerContent = new HashSet<UUID>();
for(int i = 0; i < 10; i++) {
	spinnerContent.add(UUID.randomUUID());
}
final CollectionsAdapter<UUID> spinnerAdapter = new CollectionsAdapter<UUID>(this, android.R.layout.simple_spinner_item, spinnerContent);
final Spinner spinner = (Spinner)findViewById(R.id.exampleSpinner);
spinner.setAdapter(spinnerAdapter);

Modifying the Data

If you later on want to modify the data you can either do this directly within the corresponding collection and then calling the notifyDataSetChanged() of the adapter to signal that you have modified the data or you can use the provided methods within the adapter which will take care of this. The signature of these methods is just like within the Collection interface:

      • public boolean add(E object)
      • public boolean addAll(Collection<? extends E> c)
      • public boolean remove(E object)
      • public boolean removeAll(Collection<?> c)
      • public void clear()

The boolean return value of the add and remove methods indicate if the data of your collection has been actually changed by the method call.

So as you can see the basic usage of the adapter is very simple.

 

Defining custom Layouts

If you want to use custom layouts within the adapter you can use the third available constructor of the com.appsolut.adapter.collections. CollectionsAdapter<E> class:

public CollectionsAdapter(Context context, Collection<E> content, ICollectionsAdapterViewFactory<E> viewFactory)

This constructor requires a context, the collection which will be used as data basis for the adapter and an com.appsolut.adapter.collections.view.ICollectionsAdapterViewFactory<E> viewFactory. The ICollectionsAdapterViewFactory<E> interface defines a method which will be called to display an element. During this method call you can create your own layout:

public View getView(int position, View convertView, ViewGroup parent, E item, LayoutInflater inflater, Context context)

If you have already created custom views for an adapter the method should look familiar to you. There are just some new parameters which ensure an easy creation of the view. If custom views within an adapter are completely new you should check out my previous posting on this topic first (http://xinfo.de/2011/03/using-custom-layouts-for-spinner-or-listview-entries-in-android/) and the return here.

So let’s take a look at the parameters of the method:

      • position – indicates the position of the element within the data set which should be displayed
      • convertView – “The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.” – taken from the original Android API
      • parent – The parent view to which you should attach your view
      • item – The item which should be displayed by this view
      • inflater – The layout inflater service used to inflate xml layouts
      • context – The current context

In the example project I’ve implemented such an ICollectionsAdapterViewFactory which will populate a layout consisting of an ImageView and two TextViews:

public class NumberedViewFactory<T> implements
		ICollectionsAdapterViewFactory<T> {

	@Override
	public View getView(int position, View convertView,
			ViewGroup parent, T item, LayoutInflater inflater,
			Context context) {
		View layoutView;
		if (convertView == null) {
			layoutView = inflater.inflate(R.layout.listview_entry, parent, false);
		} else {
			layoutView = convertView;
		}
		final TextView segmentText = (TextView)layoutView.findViewById(R.id.textSegment);
		segmentText.setText(item.toString());

		final TextView segmentNumber = (TextView)layoutView.findViewById(R.id.segmentNumber);
		segmentNumber.setText(Integer.toString(position) + ". ");

		final ImageView imageView = (ImageView)layoutView.findViewById(R.id.image);
		if(position%2 == 0) {
			imageView.setBackgroundResource(android.R.drawable.presence_online);
		} else {
			imageView.setBackgroundResource(android.R.drawable.presence_invisible);
		}

		return layoutView;
	}

}

Summary

I hope you can use this flexible library to populate your ListViews and Spinners with various data sets. If you have any problems or suggestions on how to improve this library feel free to post comments.

by Kevin Kratzer

Think of an iOS app which plays an HTML5 video you can stop, play and interact with. This is exactly what we’re going to build in this tutorial.

The HTML

Because only small changes are necessary, we will start by editing the HTML/JS code provided in this article. First, we attach a JavaScript onclick attribute to every elements’ surrounding

 via the setupItems() function:

var t = $(items[i][3]);

(Note that the encoding is necessary because the above string is given as function parameter).

As you can see, Overlay.call() is called with the appropriate item id when clicking on a box. Call() works similar to the way we used windows.location to submit data to the iOS parent app in a previous article – it takes the id and forwards the page to something like ?cmd=call&param=2. This string  can be read later by the iOS app.

That’s it! Now fire up Xcode and create a new view based application. Call it App Solut InteractiveVideoInterface and open your ViewController header file. Put this code in:

@interface App_Solut_InteractiveVideoInterfaceViewController : UIViewController {

IBOutlet UIWebView *webView;
IBOutlet UILabel *label;
}
@property(retain, nonatomic) UIWebView *webView;
@property(retain, nonatomic) UILabel *label;

@end

Save the file, switch to interface builder and edit your ViewControllers’ xib file. Drag both a webView and a label to the screen and connect the just created outlets. To do so, control-drag from your webview object to the files’ owner and select delegate. Then do the same vice-versa: Control-drag (or right click-drag if you own a standard mouse) from files’ owner to both the webview and the label and select the appropriate entries from the list. You have now created the connection between your code and your interface.

In your implementation file synthesize both the label and the webview:

@synthesize webView;
@synthesize label;

Similar to the webview interface code we add the following function to catch all webpage requests.

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *url = [[request URL] absoluteString];
NSArray *urlArray = [url componentsSeparatedByString:@"?"];
NSString *cmd = @"";
NSMutableArray *paramsToPass = nil;
if([urlArray count] > 1) {
NSString *paramsString = [urlArray objectAtIndex:1];
NSArray *urlParamsArray = [paramsString componentsSeparatedByString:@"&"];
cmd = [[[urlParamsArray objectAtIndex:0] componentsSeparatedByString:@"="] objectAtIndex:1];
int numCommands = [urlParamsArray count];
paramsToPass = [[NSMutableArray alloc] initWithCapacity:numCommands-1];
for(int i = 1; i < numCommands; i++){
NSString *aParam = [[[urlParamsArray objectAtIndex:i] componentsSeparatedByString:@"="] objectAtIndex:1];
[paramsToPass addObject:aParam];
}
}
if([cmd compare:@"call"] == NSOrderedSame) {
NSString *message = [[paramsToPass objectAtIndex:0]stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
[label setText:[NSString stringWithFormat:@"You just clicked button #%@", message]];
}
/* Only load the page if it is not an appcall */
NSRange aSubStringRange = [url rangeOfString:@"appcall"];
if(aSubStringRange.length != 0){
NSLog(@"App call found: request cancelled");
return NO;
} else {
return YES;
}
}

You can see that the value given in the parameter param is attached to a string which is then set as the label text. In this scenario, every video overlay will have it’s own ID send to the app when clicked on. From this point your imagination is set free. You can at this point react to the users click and do whatever you want.

Download sample project

Again, you can find a sample project at GitHub: InteractiveVideoInterface-Example

A WebView element is a smart and easy way to integrate both platform-independent and dynamic content into an ordinary iOS app. While using it to display webpages might be simple things become more complicated when some kind of interaction between the parent app and the webpage is needed. At the moment there is no common way for a page to send any messages to its WebView controller. This article shows basic principles of a two-way communication, providing a work-around for the function missing.
At the end you’ll find a download link to a full iOS sample project.

Calling JavaScript code from your App

This part is easy: Assuming you have a WebView set up in your project displaying just any page. There is a method called stringByEvaluatingJavaScriptFromString which does the job. Example:

[myWebView stringByEvaluatingJavaScriptFromString:@"alert('Hello World!');"];

Will display – you guessed it – an alert message sent by your webpage. That way you can call any JavaScript function available to your webpage from outside, making it a snap to alter pages on the fly.

Calling functions in your App from inside a WebView

As I mentioned earlier, the other way around is far more complicated. First, we have to think about a way a webpage can communicate to an app. Fortunately, the WebView element provides some interesting attributes and events.
First of all an event called shouldStartLoadWithRequest is fired when a page is requested. Here is an example implementation:

-(BOOL)myWebView:(UIWebView*)myWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNaviagtionType)navigationType {
   [...]
}

The great thing about this function is that it is called before the page actually gets loaded and that it expects a boolean as a return. Returning YES makes the WebView start loading the desired page while returning NO stops it from doing so.
Now the plan is as follows: A JavaScript function loads a fictional page including some GET parameters via window.location. Because of the special URL keyword the app is able to figure out whether a real page should be loaded or an in-app function is called (namely an app-call). At that point the function can stop the request and execute the action or just let it load.
So the above function needs to get two things done:

  • check whether a normal link or an app call is requested and returning the proper boolean
  • extract given parameters and check if they fit into predefined actions

Based on a sample by tetontech this is the code we are going to talk about:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
 NSString *url = [[request URL] absoluteString];
 NSLog(@"Requesting: %@",url);
 NSArray *urlArray = [url componentsSeparatedByString:@"?"];
 NSString *cmd = @"";
 NSMutableArray *paramsToPass = nil;
 // isolate command and parameters
 if([urlArray count] < 1){
 NSString *paramsString = [urlArray objectAtIndex:1];
 NSArray *urlParamsArray = [paramsString componentsSeparatedByString:@"&"];
 cmd = [[[urlParamsArray objectAtIndex:0] componentsSeparatedByString:@"="] objectAtIndex:1];
 int numCommands = [urlParamsArray count];
 paramsToPass = [[NSMutableArray alloc] initWithCapacity:numCommands-1];
 for(int i = 1; i < numCommands; i++){
 NSString *aParam = [[[urlParamsArray objectAtIndex:i] componentsSeparatedByString:@"="] objectAtIndex:1];
 [paramsToPass addObject:aParam];
 }
 }
 if([cmd compare:@"toggleWorking"] == NSOrderedSame){
 NSLog(@"Turning working indicator...");
 if([UIApplication sharedApplication].networkActivityIndicatorVisible == NO) {
 [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
 NSLog(@"...on");
 } else {
 [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
 NSLog(@"...off");
 }
 } else if([cmd compare:@"logMessage"] == NSOrderedSame) {
 NSString *message = [[paramsToPass objectAtIndex:0] stringByReplacingOccurrencesOfString:@"%20" withString:@" "];
 NSString *message = [[paramsToPass objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
 NSLog(@"Received JS message: %@",message);
 }
 // only load the page if it is the initial index.html file
 NSRange aSubStringRange = [url rangeOfString:@"index.html"];
 if(aSubStringRange.length != 0){
 return YES;
 } else {
 NSLog(@"App call found: request cancelled");
 return NO;
 }
}

As stated before this function extracts and isolates every parameter and command given and runs through a couple of if-statements to check whether a command is recognized. If that’s the case additional arguments might be used to execute an action.

In this example there are just two possible actions to call: By requesting ?cmd=toggleWorking a spinning wheel in iPads’ tab bar is turned on or off depending on it’s current state. Second action logMessage might be called like this: ?cmd=logMessage&param=Hello%20World. It will forward any given message to the debug console as a log.

NSRange aSubStringRange = [url rangeOfString:@"index.html"];
if(aSubStringRange.length != 0){

The last check is really important! Assuming your mainpage is named index.html this makes sure the page gets loaded once at start. Other requests are blocked right now but you can most likely think of different approaches to make sure app calls don’t get loaded.

Download sample project

Header over to GitHub and download the WebViewInterface-Example
Source:

When developing Apps for the Android OS you might end up in the situation where you have common Activities or Services which could be reused in multiple Apps with just a little modification. As you don’t want to copy these classes into every single project – which would lead to a hardly maintainable code – you would seek a way to reference the source of these classes from multiple projects. For non-android applications an approach for this problem would be to break up the code into multiple libraries so that the required functionality can be referenced from multiple projects. As long as you are not interacting with external resources this attempt is also possible in Android projects (build a jar and reference it from the projects). But if you are using external resources these classes can’t be used within a library. This is caused by the fact that the Android SDK won’t generate matching ids within the “R” class for external resources which are included in a referenced jar file. In this post I want to show you different solutions for this problem which can avoid the necessity to copy the source code into the projects.