Thursday, 27 December 2012

Daydream: Interactive Screen Savers

Posted by Daniel Sandler, a software engineer on the Android System UI team
Daydream


I’ve always loved screen savers. Supposedly they exist for a practical purpose: protecting that big, expensive monitor from the ghosts of spreadsheets past.



But I’ve always imagined that your computer is secretly hoping you’ll stand up and walk away for a bit. Just long enough for that idle timer to expire…so it can run off and play for a little while. Draw a picture, set off fireworks, explore the aerodynamics of kitchen appliances, whatever—while always ready to get back to work at a keystroke or nudge of the mouse.



Daydream, new in Android 4.2, brings this kind of laid-back, whimsical experience to Android phones and tablets that would otherwise be sleeping. If you haven’t checked it out, you can turn it on in the Settings app, in Display > Daydream; touch When to Daydream to enable the feature when charging.



An attract mode for apps


Apps that support Daydream can take advantage of the full Android UI toolkit in this mode, which means it’s easy to take existing components of your app — including layouts, animations, 3D, and custom views—and remix them for a more ambient presentation. And since you can use touchscreen input in this mode as well, you can provide a richly interactive experience if you choose.



Daydream provides an opportunity for your app to show off a little bit. You can choose to hide some of your app’s complexity in favor of one or more visually compelling experiences that can entertain from across a room, possibly drawing the user into your full app, like a video game’s attract mode.



Figure 1. Google Currents scrolls stories past in a smooth, constantly-moving wall of news.



Google Currents is a great example of this approach: as a Daydream, it shows a sliding wall of visually-interesting stories selected from your editions. Touch a story, however, and Currents will show it to you full-screen; touch again to read it in the full Currents app.



The architecture of a Daydream



Each Daydream implementation is a subclass of android.service.dreams.DreamService. When you extend DreamService, you’ll have access to a simple Activity-like lifecycle API.



Key methods on DreamService to override in your subclass (don’t forget to call the superclass implementation):




Important methods on DreamService that you may want to call:




  • setContentView() — set the scene for your Daydream. Can be a layout XML resource ID or an instance of View, even a custom View you implement yourself.

  • setInteractive(boolean) — by default, your Daydream will exit if the user touches the screen, like a classic screen saver. If you want the user to be able to touch and interact with your Views, call setInteractive(true).

  • setFullscreen(boolean) — convenience method for hiding the status bar (see below).

  • setScreenBright(boolean) — by default, Daydreams keep the screen on at full brightness, which may not be appropriate for some situations (for example, dark rooms); setting this to false will reduce the display brightness to a very low level.



Finally, to advertise your Daydream to the system, create a <service> for it in your AndroidManifest.xml:



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-sdk android:targetSdkVersion="17" android:minSdkVersion="17" />

<application>
<service
android:name=".ExampleDaydream"
android:exported="true"
android:label="@string/my_daydream_name">
<intent-filter>
<action android:name="android.service.dreams.DreamService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.service.dream"
android:resource="@xml/dream_info" />
</service>
</application>
</manifest>


The <meta-data> tag is optional; it allows you to point to an XML resource that specifies a settings Activity specific to your Daydream. The user can reach it by tapping the settings icon next to your Daydream’s name in the Settings app.



<!-- res/xml/dream_info.xml -->
<?xml version="1.0" encoding="utf-8"?>
<dream xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.app/.ExampleDreamSettingsActivity" />


Here's an example to get you going: a classic screen saver, the bouncing logo, implemented using a TimeAnimator to give you buttery-smooth 60Hz animation.




Figure 2. Will one of them hit the corner?



public class BouncerDaydream extends DreamService {
@Override
public void onDreamingStarted() {
super.onDreamingStarted();

// Our content view will take care of animating its children.
final Bouncer bouncer = new Bouncer(this);
bouncer.setLayoutParams(new
ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
bouncer.setSpeed(200); // pixels/sec

// Add some views that will be bounced around.
// Here I'm using ImageViews but they could be any kind of
// View or ViewGroup, constructed in Java or inflated from
// resources.
for (int i=0; i<5; i++) {
final FrameLayout.LayoutParams lp
= new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
final ImageView image = new ImageView(this);
image.setImageResource(R.drawable.android);
image.setBackgroundColor(0xFF004000);
bouncer.addView(image, lp);
}

setContentView(bouncer);
}
}

public class Bouncer extends FrameLayout implements TimeAnimator.TimeListener {
private float mMaxSpeed;
private final TimeAnimator mAnimator;
private int mWidth, mHeight;

public Bouncer(Context context) {
this(context, null);
}

public Bouncer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public Bouncer(Context context, AttributeSet attrs, int flags) {
super(context, attrs, flags);
mAnimator = new TimeAnimator();
mAnimator.setTimeListener(this);
}

/**
* Start the bouncing as soon as we’re on screen.
*/
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mAnimator.start();
}

/**
* Stop animations when the view hierarchy is torn down.
*/
@Override
public void onDetachedFromWindow() {
mAnimator.cancel();
super.onDetachedFromWindow();
}

/**
* Whenever a view is added, place it randomly.
*/
@Override
public void addView(View v, ViewGroup.LayoutParams lp) {
super.addView(v, lp);
setupView(v);
}

/**
* Reposition all children when the container size changes.
*/
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
for (int i=0; i<getChildCount(); i++) {
setupView(getChildAt(i));
}
}

/**
* Bouncing view setup: random placement, random velocity.
*/
private void setupView(View v) {
final PointF p = new PointF();
final float a = (float) (Math.random()*360);
p.x = mMaxSpeed * (float)(Math.cos(a));
p.y = mMaxSpeed * (float)(Math.sin(a));
v.setTag(p);
v.setX((float) (Math.random() * (mWidth - v.getWidth())));
v.setY((float) (Math.random() * (mHeight - v.getHeight())));
}

/**
* Every TimeAnimator frame, nudge each bouncing view along.
*/
public void onTimeUpdate(TimeAnimator animation, long elapsed, long dt_ms) {
final float dt = dt_ms / 1000f; // seconds
for (int i=0; i<getChildCount(); i++) {
final View view = getChildAt(i);
final PointF v = (PointF) view.getTag();

// step view for velocity * time
view.setX(view.getX() + v.x * dt);
view.setY(view.getY() + v.y * dt);

// handle reflections
final float l = view.getX();
final float t = view.getY();
final float r = l + view.getWidth();
final float b = t + view.getHeight();
boolean flipX = false, flipY = false;
if (r > mWidth) {
view.setX(view.getX() - 2 * (r - mWidth));
flipX = true;
} else if (l < 0) {
view.setX(-l);
flipX = true;
}
if (b > mHeight) {
view.setY(view.getY() - 2 * (b - mHeight));
flipY = true;
} else if (t < 0) {
view.setY(-t);
flipY = true;
}
if (flipX) v.x *= -1;
if (flipY) v.y *= -1;
}
}

public void setSpeed(float s) {
mMaxSpeed = s;
}
}


This example code is handy for anything you want to show the user without burning it into the display (like a simple graphic or an error message), and it also makes a great starting point for more complex Daydream projects.



A few more idle thoughts




  • First, do no harm: Daydream is meant to run when a device is charging. However, if the Daydream consumes too much CPU, charging might happen very slowly or not at all! The system will stop your Daydream if it detects that the device is not charging, so make sure your code leaves enough power to charge the battery in a reasonable amount of time.

  • Respect the lockscreen: Daydream runs on top of the secure keyguard, which means that if you might be showing sensitive content, you need to give the user tools to control that content. For example, Photo Table and Photo Frame allow the user to select the albums from which photos will be displayed (avoiding embarrassing slideshows).

  • Screen brightness: Think about where you expect your Daydream to be used and adjust the screen brightness accordingly using setScreenBright() and possibly even using darker or brighter colors as necessary. A bedside clock will need to be dimmer than a desk clock; if you expect your Daydream to serve both purposes you'll need to give the user a choice.

  • To hide the status bar or not: Many users will need instant access to the battery level and time of day, so you should avoid using setFullscreen(), particularly if your Daydream is more informational than artistic. Daydream will start with the status bar in “lights out” mode (View.SYSTEM_UI_FLAG_LOW_PROFILE), where it’s quite unobtrusive but still shows the clock and charge status.

  • When to use settings: In general, you have a little latitude for adding extra knobs and dials to Daydream settings. After all, this is a personalization feature, so users should be encouraged to tweak things until they feel at home. Sometimes, though, a more compelling experience can come from taking an artistic stand: giving the user a choice from a small number of polished, beautiful configurations (rather than providing all the controls of a commercial airline cockpit).

  • There can be more than one: If you discover that your settings allow the user to pick between a few radically different display modes, consider splitting your Daydream into multiple DreamService implementations. For example, the photo gallery in Android 4.2 provides both the Photo Table and Photo Frame Daydreams.

  • Use an Activity for development: Most Android development tools are optimized for developing and debugging conventional Android apps; since DreamService and Activity are so similar, it can be useful to create a testing Activity that hosts the same content view as your DreamService. This way you can launch and test your code easily from your IDE as if it were any other Android project.



OK, that’s enough for now; you have the tools to go build Daydream support into your apps. Have fun with it — if you do, your users will have fun too. Oh, and when you upload your shiny new APK to Google Play, be sure to add a note to your app’s description so that users searching for Daydreams can discover it.



Further reading and samples



  • API docs for DreamService

  • Sample code: BouncerDaydream, complete project for the code snippets in this post

  • Sample code: WebView, a Daydream that shows an HTML page

  • Sample code: Colors, a Daydream that demonstrates OpenGL ES 2.0 and TextureView


Thursday, 20 December 2012

Localize Your Promotional Graphics on Google Play

Posted by Ellie Powers, Product Manager on the Google Play team



Google Play is your way to reach millions and millions of Android users around the world. In fact, since the start of 2011, the number of countries where you can sell apps has increased from 30 to over 130 — including most recently, the launch of paid app support in Israel, Mexico, the Czech Republic, Poland, Brazil and Russia, and fully two-thirds of revenue for apps on Google Play comes from outside of the United States.



To help you capitalize on this growing international audience, it’s now even easier to market your apps to users around the world, by adding images and a video URL to your Google Play store listing for each of Google Play’s 49 languages, just as you’ve been able to add localized text.






A localized feature graphic can show translated text or add local flavor to your app — for example, changing its theme to reflect local holidays. Always make sure that your feature graphic works at different sizes.



Once you’ve localized your app, you’ll want to make sure users in all languages can understand what your app does and how it can benefit them. Review the graphics guidelines and get started with localized graphics.



Localized screenshots make it clear to the user that they’ll be able to use your app in their language. As you’re adding localized screenshots, remember that a lot of people will be getting new tablets for the holidays, and loading up with new apps, so you’ll want to include localized tablet screenshots to show off your tablet layouts.



With localized videos, you can now include a language-appropriate voiceover and text, and of course show the app running in the user’s language.



Ready to add localized images and videos to your store listing? To add localized graphics and video to your apps, you need to use the Google Play Developer Console preview — once you add localized graphics, you won’t be able to edit the app using the old version anymore. Those of you who use APK Expansion Files will now want to try the new Developer Console because it now includes this feature. We’ll be adding support for Multiple APK very soon. Once you’ve saved your application in the new Developer Console, automated translations become available to users on the web and devices — with no work from you.



What are you doing to help your app reach a global audience?


Tuesday, 11 December 2012

The 2012 Android Developer Survey


The Android Developer Relations team is passionate about making Android app development a great experience, so we're asking all of you involved in building Android apps -- from engineers, to product managers, and distribution and support folks -- to let us know what you think.







We want to better understand the challenges you face when planning, designing, writing, and distributing your Android apps, so we've put together a brief (10-15min) survey that will help us test our assumptions and allow us to create better tools and resources for you.




We've had a great response from thousands of Android developers who have already responded - thank you! If you haven't yet filled in the survey, you can find it here: 2012 Android Developer Survey.



We'll be closing this year's survey this Sunday (December 17th) at 12pm Pacific Time, so be sure to get your responses in before then.



To keep the survey short and simple, there are no sections for general comments. That's because we want to hear your thoughts, questions, suggestions, and complaints all year. If there's anything you'd like to share with us, you can let us know by posting to us (publicly or privately) on Google+ at +Android Developers or using the hash tag #AndroidDev.



We can't always respond, but we're paying close attention to everything you have to say.



As always, we're looking forward to hearing your thoughts!

Monday, 10 December 2012

In-App Billing Version 3

Posted by Bruno Oliveira of the Android Developer Relations Team



In-app Billing has come a long way since it was first announced on Google Play (then Android Market). One year and a half later, the vast majority of top-grossing apps on Google Play use In-app Billing and thousands of developers monetize apps through try-and-buy, virtual goods, as well as subscriptions.



In-app Billing is expanding again, making it even more powerful and flexible so you can continue to build successful applications. Version 3 introduces the following new features:

  • An improved design that makes applications simpler to write, debug and maintain. Integrations that previously required several hundred lines of code can now be implemented in as few as 50.

  • More robust architecture resulting in fewer lost transactions.

  • Local caching for faster API calls.

  • Long-anticipated functionality such as the ability to consume managed purchases and query for product information.

In-app Billing version 3 is available now and lets you sell both in-app items and (since February 2013) subscriptions, including subscriptions with free trials. It is supported by Android 2.2+ devices running the latest version of the Google Play Store (over 90% of active devices).

Instead of the four different application components required by the asynchronous structure of the previous release, the new version of the API allows developers to make synchronous requests and handle responses directly from within a single Activity, all of which are accomplished with just a few lines of code. The reduced implementation cost makes this a great opportunity for developers who are implementing new in-app billing solutions.



Easier to Implement



In contrast to the earlier model of asynchronous notification through a background service, the new API is now synchronous and reports the result of a purchase immediately to the application. This eliminates the necessity to integrate the handling of asynchronous purchase results into the application's lifecycle, which significantly simplifies the code that a developer must write in order to sell an in-app item.



To launch a purchase, simply obtain a buy Intent from the API and start it:



Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
MY_SKU, ITEM_TYPE_INAPP, developerPayload);

PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT);
if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
// Start purchase flow (this brings up the Google Play UI).
// Result will be delivered through onActivityResult().
startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(),
Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
}


Then, handle the purchase result that's delivered to your Activity's onActivityResult() method:



public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RC_BUY) {
int responseCode = data.getIntExtra(RESPONSE_CODE);
String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
String signature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);

// handle purchase here (for a permanent item like a premium upgrade,
// this means dispensing the benefits of the upgrade; for a consumable
// item like "X gold coins", typically the application would initiate
// consumption of the purchase here)
}
}


Also, differently from the previous version, all purchases are now managed by Google Play, which means the ownership of a given item can be queried at any time. To implement the same mechanics as unmanaged items, applications can consume the item immediately upon purchase and provision the benefits of the item upon successful consumption.



Local Caching



The API leverages a new feature of the Google Play store application which caches In-app Billing information locally on the device, making it readily available to applications. With this feature, many API calls will be serviced through cache lookups instead of a network connection to Google Play, which significantly speeds up the API's response time. For example, an application could query the owned items using this call:



Bundle bundle = mService.getPurchases(3, mContext.getPackageName(), ITEM_TYPE_INAPP);
if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
ArrayList mySkus, myPurchases, mySignatures;
mySkus = bundle.getStringArrayList(RESPONSE_INAPP_ITEM_LIST);
myPurchases = bundle.getStringArrayList(RESPONSE_INAPP_PURCHASE_DATA_LIST);
mySignatures = bundle.getStringArrayList(RESPONSE_INAPP_PURCHASE_SIGNATURE_LIST);

// handle items here
}


Querying for owned items was an expensive server call in previous versions of the API, so developers were discouraged from doing so frequently. However, since the new version implements local caching, applications can now make this query every time they start running, and as often as necessary thereafter.



Product Information



The API also introduces a long-anticipated feature: the ability to query in-app product information directly from Google Play. Developers can now programmatically obtain an item's title, description and price. No currency conversion or formatting is necessary: prices are reported in the user's currency and formatted according to their locale:



Bundle bundle = mService.getSkuDetails(3, "com.example.myapp", 
ITEM_TYPE_INAPP, skus); // skus is a Bundle with the list of SKUs to query
if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
List detailsList = bundle.getStringArrayList(RESPONSE_SKU_DETAILS_LIST);
for (String details : detailsList) {
// details is a JSON string with
// SKU details (title, description, price, ...)
}
}


This means that, for example, developers can update prices in Developer Console and then use this API call to show the updated prices in the application (such as for a special promotion or sale) with no need to update the application's code to change the prices displayed to the user.



Sample Application



In addition to the API, we are releasing a new sample application that illustrates how to implement In-app Billing. It also contains helper classes that implement commonly-written boilerplate code such as marshalling and unmarshalling data structures from JSON strings and Bundles, signature verification, as well as utilities that automatically manage background work in order to allow developers to call the API directly from the UI thread of their application. We highly recommend that developers who are new to In-app Billing leverage the code in this sample, as it further simplifies the process of implemention. The sample application is available for download through the Android SDK Manager.



App-Specific Keys



Along with the other changes introduced with In-app Billing Version 3, we have also improved the way Licensing and In-app Billing keys are managed. Keys are now set on a per-app basis, instead of a per-developer basis and are available on the “Services & APIs” page for each application on Google Play Developer Console preview. Your existing applications will continue to work with their current keys.



Get Started!



To implement In-app Billing in your application using the new API, start with the updated In-App Billing documentation and take the Selling In-App Products training class.

Monday, 3 December 2012

New Google Maps Android API now part of Google Play services

Posted by Reto Meier, Evan Rapoport, and Andrew Foster



Google Play services is our new platform that offers you better integration with Google products, and which provides greater agility for quickly rolling out new capabilities for you to use within your apps. Today we’re launching Google Play services v2.0, which includes two new APIs, including perhaps our most frequently requested upgrade: Maps.



Google Maps Android API



The new version of the API allows developers to bring many of the recent features of Google Maps for Android to your Android apps. We’re excited to make this API available as part of Google Play services supporting devices from Froyo onwards (API level 8+).



The new API uses vector-based maps that support 2D and 3D views, and allow users to tilt and rotate the map with simple gestures. Along with the layers you’ve come to know from Google Maps such as satellite, hybrid, terrain and traffic, the new API lets you include indoor maps for many major airports and shopping centers in your app.



One of most common feature requests we’ve heard on Android is support for Map Fragments. With this new API, adding a map to your Activity is as simple as:



<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />


Check out this image from updated Trulia Android app (which goes live tomorrow), that users can use to search for a place to buy or rent in 3D.





The new API is simpler to use, so that creating markers and info windows is easy. Polylines, Polygons, Ground Overlays and Tile Overlays can all now be added to the map with just a few lines of code.



To get started follow the getting started instructions to obtain an API Key. Then download and configure the Google Play services SDK using the SDK Manager. Check the Google Maps for Android API documentation for more details. If you haven't got it already, you'll need to download the Android SDK first.



More than 800,000 sites around the world already use our mapping APIs to create amazing and useful apps. We hope you enjoy using this new addition to the Google Maps API family, and building mapping experiences that were never before possible on a mobile device.



Photo Sphere



In Android 4.2, we introduced Photo Sphere mode in the Camera, which you can use to create amazing, immersive panoramas just like you see in Street View on Google Maps. Today we’re excited to announce new APIs and documentation that empower developers, businesses, and photographers to explore new uses of Photo Sphere for work and for play.



We’ve made Photo Sphere an open format so anyone can create and view them on the web or on mobile devices.



A Photo sphere is simply an image file (like a JPG) that has in it text-based metadata, an open format created by Adobe called XMP. The metadata describes the Photo Sphere’s dimensions and how it should be rendered within the interactive Photo Sphere viewer you see in Android, Google+, and Google Maps.



If you’d like to programmatically or manually add the XMP metadata into panoramic images not created by the Photo Sphere camera in Android, stay tuned today for more details on the metadata and how to apply it to your photos programmatically later.



In the new Google Play services, we’ve added APIs to give you the ability to check whether an image is a Photo Sphere and then open it up in the Photo Sphere viewer.



// This listener will be called with information about the given panorama.
OnPanoramaInfoLoadedListener infoLoadedListener =
new OnPanoramaInfoLoadedListener() {
@Override
public void onPanoramaInfoLoaded(ConnectionResult result,
Intent viewerIntent) {
if (result.isSuccess()) {
// If the intent is not null, the image can be shown as a
// panorama.
if (viewerIntent != null) {
// Use the given intent to start the panorama viewer.
startActivity(viewerIntent);
}
}

// If viewerIntent is null, the image is not a viewable panorama.
}
};

// Create client instance and connect to it.
PanoramaClient client = ...
...

// Once connected to the client, initiate the asynchronous check on whether
// the image is a viewable panorama.
client.loadPanoramaInfo(infoLoadedListener, panoramaUri);

To learn more about Google Play services and the APIs available to you through it, visit the new Google Services area of the Android Developers site.

Followers