In the previous article we got a Presentation working on a second display working, but we lost backward compatibility. In this article we’ll address that.



Firstly we’ll look at restoring our backward compatibility. What is important to remember is that multiple independent displays are only supported on Android 4.2 (SDK 17) onwards, so we cannot achieve that behaviour when our code runs on an older device. However what is pretty easy to achieve is to write an app which will use dual displays where they are supported, but still offers the basic functionality, in this case displaying the display characteristics, on a single display on devices running earlier versions of Android.

In order to achieve this, we need to analyse our code, and determine what parts of our code are supported on specific OS versions. The way to do this is to switch the minSdk setting in our Android Manifest back to 4. When lint is next run on our MainActivity (you may need to make a minor change to this file, then save it), it will flag errors on any code which is not compatible with SDK 4. In our case it is the entirety of our MyPresentation class (which extends Presentation – introduced in SDK 17), and all references to DisplayManager – again introduced in SDK 17.

At this point it should be fairly obvious, I hope, why I created multiInit() as a method rather than simply embedding it in onCreate(). This method contains our SDK 17 specific code, so we can put a check around the call to this method to ensure that it only gets called on devices running SDK 17 and higher:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); populate(findViewById(R.id.main), getWindowManager() .getDefaultDisplay()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { multiInit(); } } 1 2 3 4 5 6 7 8 9 10 11 12 @Override protected void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_main ) ; populate ( findViewById ( R . id . main ) , getWindowManager ( ) . getDefaultDisplay ( ) ) ; if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR1 ) { multiInit ( ) ; } }

That’s all well and good, but it still does not stop those errors that we’re getting. The important distinction here is that these are not errors generated by the Java compiler, they are being generated by lint – a tool which is part of the Android build process. While it is possible to turn this particular error off, or downgrade it to a warning, that can be counter-productive because it doesn’t actually warn us of other similar issues.

We know that multiInit will only be called on systems running SDK 17 or higher, so we can add an annotation which allows us to inform lint that this code will only be called on such systems:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private void multiInit() { DisplayManager dm = (DisplayManager) getSystemService(DISPLAY_SERVICE); if (dm != null) { Display[] displays = dm.getDisplays( DisplayManager.DISPLAY_CATEGORY_PRESENTATION); for (Display display : displays) { mPresentation = new MyPresentation(this, display); mPresentation.show(); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @TargetApi ( Build . VERSION_CODES . JELLY_BEAN_MR1 ) private void multiInit ( ) { DisplayManager dm = ( DisplayManager ) getSystemService ( DISPLAY_SERVICE ) ; if ( dm ! = null ) { Display [ ] displays = dm . getDisplays ( DisplayManager . DISPLAY_CATEGORY_PRESENTATION ) ; for ( Display display : displays ) { mPresentation = new MyPresentation ( this , display ) ; mPresentation . show ( ) ; } } }

For the MyPresentation class we can do the same thing, only at class level:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public class MyPresentation extends Presentation { . . . } 1 2 3 4 5 6 7 @TargetApi ( Build . VERSION_CODES . JELLY_BEAN_MR1 ) public class MyPresentation extends Presentation { . . . }

What is important to note is that it is only possible to create a new MyPresentation object through multiInit. We have protected that method using a version check, so we have a degree of certainty that we’ll never try to create an instance of this class on a pre-SDK 17 device, which would result in a run time error.

if we run this on an older device (in this case an emulator running 2.1) we can see that our app still functions perfectly in its primary mode – to show the characteristics of the display – it’s just that the ability to control multiple displays independently has been degraded for this device:

The only real trick to getting this feature degradation backwards compatibility model working is to organise the code such that blocks which are specific to particular SDK version are cleanly encapsulated within their own methods, then we can easily put in the necessary version checks and annotations to provide additional options / features when the SDK supports them.

This code is still not fit for production because we still don’t handle connection and disconnection of the display while the MainActivity is running. In the next part of this series we’ll do precisely that.

The source code for this article can be found here.

© 2012 – 2013, Mark Allison. All rights reserved.

Related

Copyright © 2012 Styling Android. All Rights Reserved.

Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.