Autosaving of form fields in browsers

I’ve once again lost a decent sized chunk of text in a textarea, due to a browser crash. I’ve started wondering about whether browsers should implement autosaving for textareas. Our usage patterns have got to the point where we use browsers to write large chunks of text on a regular basis.

Generally when I’m writing an extended chunk of text, I will do so in a text editor, and only copy the text into a website when I have to. However, this is a poor workaround.

Some websites implement their own autosaving. Whilst this is a great move on their part, I think that it’s time to look at how autosaving could be supported at the browser level in a standard fashion. Browsers already maintain a significant amount of state after a crash, tabs, positions within tabs, etc. Extending this to include form entries shouldn’t be challenging.

The main difficulty with this proposal is the privacy and security implications. But these are not insoluble.

Firstly, when a browser first considers autosaving a field, it could ask you whether to enable it, with options like Yes once, Yes always, Yes for entire site, and the equivalent No options.

Secondly, an extension of the idea of autocomplete="false". Replace the autocomplete attribute with private="true". I like this as it specifies intent rather than behaviour. The browser can then interpret that private fields shouldn’t have autocomplete, that they shouldn’t autosave, and other behaviours that are meaningful for the private field.

I think this is something that’d be worth W3C consideration. Or alternatively one of the browsers should pick this feature up as a point of difference. I’ve already lost more text than I’d like because of browser crashes.

IEnumerable, ReadOnlyCollection, and the missing interface

I’ve been thinking on and off about the appropriate return signature for a method that returns an immutable list of objects, sparked off by reading Eric Lippert’s article, Arrays considered somewhat harmful, and my belief that the value of functional program and growth of parallelism means that immutability is desirable most of the time.

However, once you decide to return an immutable collection, what type do you return?

IEnumerable is not really appropriate. The problem is that an IEnumerable may possibly be only evaluable a single time, or may cost for every evaluation of it you perform. This means that you end up with consumers of your method having to use ToList() or ToArray() to flatten the IEnumerable before consuming it, which is wasteful when your method is returning a bounded collection.

So the only choice you have with .NET is ReadOnlyCollection. Which is okay, but not ideal, I believe.

Firstly this involves specifying a return signature as a concrete type. I prefer my method signatures to be interfaces when primitives are not being used, so they only specify behavour. This also means that you can’t return an object that doesn’t use ReadOnlyCollection as a base class.

The second issue is that ReadOnlyCollection implements ICollection and IList. Whilst the implementation of methods such as Add are explicit, the fact ReadOnlyCollection implements interfaces with methods that are invalid for it creates a class of bugs only findable at run time. Have a look at the following code.

public ReadOnlyCollection<object> GetReadOnly()
{
	ReadOnlyCollection<object> readOnly = new List<object>().AsReadOnly();
}

public void ShowIssue()
{
	ReadOnlyCollection<object> readOnly = GetReadOnly();
	// The next line prevented at compile time
	// readOnly.Add(new object());

	// However this code compiles, unfortunately
	IList<object> iList = GetReadOnly()
	iList.Add(new object()); // Fails with an exception at runtime
}

I think that it would have been sensible for .NET to have had an interface that inherits IEnumerable, that represents a readonly bounded collection, called something like IReadOnlyCollection. It would have a Count and allow read only access to the elements by index. ICollection and IList would both inherit this interface, and ReadOnlyCollection would be the implementation of it.

Update: Firstly, this article doesn’t really cover the differences between immutable and read only. The ReadOnlyCollection doesn’t provide any methods to change the collection membership. However ReadOnlyCollection is only a wrapper around the List, and it does not guarantee that the underlying list is not changed.

Links that may be of interest:

Using Policy Injection and Attributes to preempt calls to non-functioning systems

It’s a waste of processor cycles and user time to make web service calls to systems that are not currently functioning. I was involved in building a solution that allows code that depends on non-functioning systems to be skipped entirely. Code simply needs to be attributed with the systems it uses. Then a policy injection handler will throw an exception without even calling that code if a system is known to be unavailable.

I document the parts of the system I built in this article. The moving parts of involved in this solution are:

  • Agents. The agents are the classes that make web service calls to external systems.
  • FunctionalArea attributes. Agent interfaces are marked up with these attributes to indicate dependencies on external systems.
  • FunctionalAreaUnavailableException – thrown to indicate an agent method call has been made involving an unavailable system.
  • SystemStatusAgent. This service keeps track of the unavailability of systems, by receiving information from the application when a FunctionalAreaUnavailableException is thrown, and through its own monitoring. I don’t document it in this blog post.
  • The InterceptorBehavior. Policy injection causes this to be ran before each agent method call. It throws a FunctionalAreaUnavailableException when an attributed agent has a web service exception, or instead of a method call involving a system the SystemStatusAgent considers unavailable.
  • Global exception handling. Catches FunctionalAreaUnavailableExceptions, notifies the SystemStatusAgent of them, and shows the user an error indicating the system they are trying to work with is currently unavailable. I don’t document it in this blog post.

I was able to policy inject all Agents as they were constructed in our AgentFactory. I wished to use configuration based injection. But (if my memory serves me right) with Unity 2.0 policy injection, you can’t use configuration to generated an injected object that is of a concrete type. I had to specify interception behaviours (Unity synonym for policy handler) in code to use the required interceptor, TransparentProxyInterceptor.

PolicyInjectionHelper makes injection simple for us.

public class PolicyInjectionHelper
{
	private ReadOnlyCollection<Type> mInterceptorTypes;

	/// <summary>
	/// Construct a new interception helper that will provide objects with 
	/// policy injection, using interceptors of the given types, in the
	/// given order.
	/// </summary>
	/// <param name="interceptorTypes">
	/// Type of the interceptors - all must derive from IInterceptionBehavior
	/// </param>
	public PolicyInjectionHelper(IEnumerable<Type> interceptorTypes)
	{
		DBC.Assert(
			interceptorTypes.All(typeof(IInterceptionBehavior).IsAssignableFrom),
			"All interceptors must derive from IInterceptionBehavior");
		mInterceptorTypes = interceptorTypes.ToList().AsReadOnly();
	}

	/// <summary>
	/// Derive a policy inject object from the provided object
	/// </summary>
	public T PolicyInject<T>(T obj) where T : class
	{
		T result = obj;
		if (mInterceptorTypes.Any())
			result = Intercept.ThroughProxy<T>(
				result,
				new TransparentProxyInterceptor(),
				ConstructInterceptorInstances());
		return result;
	}

	private IEnumerable<IInterceptionBehavior> 
		ConstructInterceptorInstances()
	{
		return mInterceptorTypes.
			Select(type => Activator.CreateInstance(type) as IInterceptionBehavior);
	}
}

I utilised this in our AgentFactory as this simplified and abbreviated code shows. The PolicyInjectionHelper means that whilst the interception behaviours couldn’t be specified in config, they are easy to see in code (RequiresFunctionalAreaPolicyHandler below).

public class AgentFactory {
	private static readonly PolicyInjectionHelper mPolicyInjectionHelper = 
		mFunctionalAreaPolicyHandlerEnabled
		? new PolicyInjectionHelper(
			new List<Type> { typeof(RequiresFunctionalAreaPolicyHandler) })
		: new PolicyInjectionHelper(Enumerable.Empty<Type>());
				
	// Snipped singleton code

	// Real construction is more complex
	public T ConstructAgent<T>()
		where T : IBaseAgent
	{
		T agent = GetAgent<T>();
		return mPolicyInjectionHelper.PolicyInject(agent);
	}
}

A pair of FunctionalArea attributes allow agent interfaces and methods to be attributed to indicate dependencies

/// <summary>
/// Decorate agent interfaces and agent interface classes with this attribute to indicate
/// that they require a particular functional area to be available.
/// When decorated methods, or methods within a decorated interface are
/// called, a FunctionalAreaUnavailableException is thrown if
/// the area is unavailable, or if a SoapException occurs during
/// the method
/// </summary>
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class FunctionalAreaRequiredAttribute : Attribute 
{
	public FunctionalAreaRequiredAttribute(FunctionalArea pArea)
	{
		Area = pArea;
	}

	public FunctionalArea Area { get; private set; }

	public bool Equals(FunctionalAreaRequiredAttribute other)
	{
		return !ReferenceEquals(null, other) && 
			   (ReferenceEquals(this, other) || Equals(other.Area, Area));
	}

	public override bool Equals(object obj)
	{
		return !ReferenceEquals(null, obj) &&
			   (ReferenceEquals(this, obj) || Equals(obj as FunctionalAreaRequiredAttribute));
	}

	public override int GetHashCode()
	{
		return Area.GetHashCode();
	}
}

/// <summary>
/// Decorate methods with this attribute to indicate that the functional
/// area requirements of their containing class should be ignored
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class IgnoreRequiredFunctionalAreaAttribute : Attribute { }

Here is an example of how the attributes are used

[FunctionalAreaRequired(FunctionalArea.SystemX)]
public interface IAnAgent : IBaseAgent
{
	void AMethod();

	[IgnoreRequiredFunctionalArea]
	void NotThisMethod();
}
	
public interface IAnotherAgent : IBaseAgent
{
	[FunctionalAreaRequired(FunctionalArea.SystemY)]
	void AMethod();

	void NotThisMethod();
}

The FunctionalAreaUnavailableException itself is very simple.

public class FunctionalAreaUnavailableException : ApplicationException
{
	private const string MessageTemplate = 
		"Functional area {0} unavailable";

	public FunctionalAreaUnavailableException(FunctionalArea area, 
		Exception ex = null)
		: base(string.Format(MessageTemplate, area), ex)
	{
		Area = area;
	}

	public FunctionalArea Area { get; private set; }
}

I introduced a BaseInterceptionBehavior as I quickly found all my IInterceptionBehavior implementations had commonalities.

public abstract class BaseInterceptionBehavior : IInterceptionBehavior
{
	#region IInterceptionBehavior members

	/// <summary>
	/// Returns a flag indicating if this behavior will actually do anything when invoked.
	/// </summary>
	public bool WillExecute { get { return true; } }

	/// <summary>
	/// Check if an intercepted method invocation should be processed, and process it if
	/// interception is required.
	/// </summary>
	public IMethodReturn Invoke(IMethodInvocation input, 
		GetNextInterceptionBehaviorDelegate getNext)
	{
		return IsInterceptionRequired(input)
			? ProcessInvocation(input, () => getNext()(input, getNext))
			: getNext()(input, getNext);
	}

	/// <summary>
	/// Returns the interfaces required by the behavior for the objects it intercepts. 
	/// </summary>
	public virtual IEnumerable<Type> GetRequiredInterfaces()
	{
		return Enumerable.Empty<Type>();
	}

	#endregion

	/// <summary>
	/// Should this method invocation be intercepted?
	/// </summary>
	protected virtual bool IsInterceptionRequired(IMethodInvocation input)
	{
		// !input.MethodBase.IsSpecialName means properties 
		// aren't intercepted, and also ToString et. al.
		return input.MethodBase.IsPublic && 
			!input.MethodBase.IsSpecialName;
	}

	/// <summary>
	/// Process the intercepted method invocation
	/// </summary>
	protected abstract IMethodReturn ProcessInvocation(IMethodInvocation input, 
		Func<IMethodReturn> processNext);
}

This is the interception behaviour that throws FunctionalAreaUnavailableException as previously described.

/// <summary>
/// Provides agents with functional area enhancements. 
/// If an agent or method is attributed as requiring a functional area:
/// - Call to methods for which the System Status agent considers a 
///   functional area unavailable will throw a 
///   FunctionalAreaUnavailableException exception without the method 
///   being invoked.
/// - SoapExceptions in methods will be wrapped in a 
///   FunctionalAreaUnavailableException for the attributed functional area
/// </summary>
/// <remarks>
/// An assumption is made only methods should have this behaviour - 
/// it uses the standard BaseInterceptionBehavior.IsInterceptionRequired 
/// criteria
/// </remarks>
public class RequiresFunctionalAreaPolicyHandler 
	: BaseInterceptionBehavior
{
	protected override IMethodReturn ProcessInvocation(
		IMethodInvocation input, Func<IMethodReturn> processNext)
	{
		MethodBase method = input.MethodBase;
		FunctionalArea? area = GetFunctionalAreaRequired(method);
		IMethodReturn result;
		if (area.HasValue && !SystemStatus.IsOperational(area.Value))
		{
			// Don't even call the method if
			// the FunctionalArea is unavailable
			result = input.CreateExceptionMethodReturn(
				new FunctionalAreaUnavailableException(area.Value));
		}
		else
		{
			result = processNext();
            Exception ex = result.Exception;
			Type areaExType = typeof(FunctionalAreaUnavailableException);
			if (area.HasValue &&
				ContainsException(ex, typeof(SoapException)) &&
				!ContainsException(ex, areaExType))
			{
				result = input.CreateExceptionMethodReturn(
					new FunctionalAreaUnavailableException(area.Value, 
						ex));
			}
		}

		return result;
	}

	private static FunctionalArea? GetFunctionalAreaRequired(
		MethodBase pMethod)
	{
		return IgnoreRequiredAttributes(pMethod)
			 ? (FunctionalArea?)null
			 : GetAttrs(pMethod).
				 Select(attribute => attribute.Area).
				 SingleOrDefault();
	}
	
	private static IEnumerable<FunctionalAreaRequiredAttribute> GetAttrs(
		MethodBase pMethod)
	{
		var methodAttrs = pMethod.
			GetCustomAttributes<FunctionalAreaRequiredAttribute>();
		var typeAttrs = pMethod.DeclaringType.
			GetCustomAttributes<FunctionalAreaRequiredAttribute>(
				true, true);
		return methodAttrs.Union(typeAttrs);
	}

	private static bool IgnoreRequiredAttributes(MethodBase pMethod)
	{
		return pMethod.
			GetCustomAttributes<IgnoreRequiredFunctionalAreaAttribute>().
			FirstOrDefault() != null;
	}

	private static bool ContainsException(
		Exception pException, Type pSearch)
	{
		return pException != null && 
			(pSearch.IsAssignableFrom(pException.GetType()) ||
			ContainsException(pException.InnerException, pSearch));
	}
}

And that’s that.

Policy Injection has its costs. Apart from the additional complexity that policy injection introduces to your code, the usage of the proxy class introduces some overhead to every usage of a policy injected object. And you should be sure that a requirement is a cross cutting concern – do some reading about Aspect-oriented programming for some ideas about how policy injection should be used.

System availability is a cross-cutting concern for agents, which are defined as the set of classes that provide access to external systems in the application I’m dealing with. And the cost of accessing agents through a proxy is fractional compared to the cost of the actual web service calls involved.

This work resulted in a better user user experience when systems are down by providing fast failure, rather than making users wait for timeouts known to be inevitable. The SystemStatusAgent also provides monitoring of the health of the systems the application depends on. I found Unity policy injection wasn’t entirely intuitive. But I’m pleased with the outcome.

Android is the mobile platform of the moment

If you’re going to choose a single mobile platform to develop for in the next year, there are three main alternatives: Android; iPhone; and Windows Phone 7. It’s my belief Android is the platform for developers new to mobile.

Colleagues I talk to are surprised about my opinion. iPhone has the Apple design x-factor, and appears to still be the in thing around my office. And Windows mobile has a new, effective and quite unique design aesthetic applied to its user interface.

However there are reasons that Android looks like the best of the bunch.

The most immediately important reason is price. The cheapest Android phone I can find on Pricespy is an LG GT540 Optimus at NZ$281. The HTC Tattoo and Sony Ericsson Xperia X10 Mini are similar prices. The cheapest iPhone on there is an iPhone 3G 8GB for NZ$599. Whilst I can’t find any pricing for Windows Phone 7 at the moment, the high minimum specifications lead me to conclude it’s prices will be in the iPhone price range.

Gadget fetishists will spend a lot of money to have a really nice piece of kit that’s well designed. But Android phones are starting to get cheap enough to appeal to consumers who are looking for just a little more than a vanilla mobile. As consumers start to realise that smartphones are finally priced within their reach, a whole new audience is going to be available to Android developers.

But longer term there’s another important factor. As an open platform, Android is going to be used as the basis of more and more non-phone devices. I can’t make my points more eloquently than this article: Tipping Points and The Future of Electronics. But I’ll leave you with a quote from it: “But that’s beside the point, which is this: saying that Android is fragmented as a phone platform by comparing it to the iPhone is like saying the iPhone App Store is closed by comparing it to the PC. It’s the wrong comparison. Instead, think of it this way: Android is the most unified electronics device platform in the industry’s history.”.

I think Android is going to become a bigger market than the other two platforms, both for applications, and for skills. Android is a great platform to be involved in for a software engineer. I’m excited to be learning it.

Android and multiple Intents for an Activity

Android’s Intent resolution can find Activities to act on a datatype. But using this resolution, a single activity can’t consume a datatype in multiple ways.

An application I’m working on has an activity that can consume a datatype in multiple ways. When you think about it, this isn’t an uncommon scenario. For example, within a Twitter application, when an imaginary ComposeTweetActivity is called for a twitter user, you may wish to either send a direct tweet to the user, or a reply to the user. Assuming that our imaginary Twitter application uses the URI structure twitter-user://username to describe a user, putting the following into your AndroidManifest.xml would make sense:

<activity android:name=".ui.ComposeTweetActivity">
    <intent-filter>
        <action android:name="com.sample.twitter.action.TWEET_DIRECT" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="twitter-user" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.sample.twitter.action.TWEET_REPLY" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="twitter-user" />
    </intent-filter>
</activity>

It would be nice to be able to form a query which would find you both intent-filters so, for example, when a user bought up the context menu of a twitter user, you could show the Direct Message and Reply options.

However, there is no way to do this with the Android API to do this that I know of.

I had hoped originally to use ContextMenu.addIntentOptions() to populate my context menus. Underneath it, this call uses PackageManager.queryIntentActivityOptions() to get the list of intents. However queryIntentActivityOptions() doesn’t find intent-filters, it finds activities with at least one matching intent-filter. Meaning you only get one result per activity. The result also provides no information about the intent-filter that matched your intent.

This is a coherent behaviour – the method finds activities. However, I do think it would have been sensible to support scenarios such as mine, and provide a way to search for intent-filters.

My workaround is to create fake activities for any activity with more than one action, that then hand off to the real activity. The sample manifest I included in the question becomes

<activity android:name=".ui.ComposeTweetActivity" />
<activity android:name=".ui.ComposeTweetActivityDirect">
    <intent-filter>
        <action android:name="com.sample.twitter.action.TWEET_DIRECT" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="twitter-user" />
    </intent-filter>
</activity> 
<activity android:name=".ui.ComposeTweetActivityReply">
    <intent-filter>
        <action android:name="com.sample.twitter.action.TWEET_REPLY" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="twitter-user" />
    </intent-filter>
</activity>

With ComposeTweetActivityDirect and ComposeTweetActivityReply simply handing off to ComposeTweetActivity with the appropriate action and data.

So you can see how the code works, here’s the onCreateContextMenu code that you’d use on the twitter user list page.

@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
	super.onCreateContextMenu(menu, view, menuInfo);
	AdapterView.AdapterContextMenuInfo info =
			(AdapterView.AdapterContextMenuInfo) menuInfo;
	Intent intent = new Intent(null, getUriOfSelectedUser(info.targetView));
	intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);

	// Search and populate the menu with acceptable offering applications.
	menu.addIntentOptions(
			0, // Menu group to which new items will be added
			0, // Unique item ID (none)
			0, // Order for the items (none)
			this.getComponentName(), // The current Activity name
			null, // Specific items to place first (none)
			intent, // Intent created above that describes our requirements
			0, // Additional flags to control items (none)
			null); // Array of MenuItems that correlate to specific items
					// (none)
}

And here’s the code of ComposeTweetActivityDirect (ComposeTweetActivityReply is obviously almost identical).

public class ComposeTweetActivityDirect extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// TODO: No error handling
		Intent sourceIntent = getIntent();		
		Intent newIntent = new Intent(this, ComposeTweetActivity.class);
		newIntent.setAction(ComposeTweetActivity.DIRECT_ACTION_NAME);
		newIntent.setData(sourceIntent.getData());
		startActivity(newIntent);
		finish();
	}
}

And the code in ComposeTweetActivity to receive the intent

Intent intent = getIntent();
String action = intent.getAction();
if (DIRECT_ACTION_NAME.equals(action))
	StartDirectTweetForIntent(intent);
else if (REPLY_ACTION_NAME.equals(action))
	StartReplyForIntent(intent);

At some stage I’d like to deal with in a more elegant fashion, probably by building myself a library, but until that point, I’ll be using this workaround.

The seeds for this post were planted by my Stack Overflow question.