Showing posts with label Google Play services. Show all posts
Showing posts with label Google Play services. Show all posts

11/22/17

Moving Past GoogleApiClient








Posted by Sam Stern, Developer Programs Engineer

The release of version 11.6.0 of the Google Play services SDK moves a number of popular APIs to a new paradigm for accessing Google APIs on Android. We have reworked the APIs to reduce boilerplate, improve UX, and simplify authentication and authorization.



The primary change in this release is the introduction of new Task
and href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApi">GoogleApi
based APIs to replace the GoogleApiClient access pattern.


The following APIs are newly updated to eliminate the use of
GoogleApiClient:


  • Auth - updated the Google Sign In and Credentials APIs.
  • Drive - updated the Drive and Drive Resource APIs.
  • Fitness - updated the Ble, Config, Goals, History,
    Recording, Sensors, and Sessions APIs.
  • Games - updated the Achievements, Events, Games, Games
    Metadata, Invitations, Leaderboards, Notifications, Player Stats, Players,
    Realtime Multiplayer, Snapshots, Turn Based Multiplayer, and Videos APIs.
  • Nearby - updated the Connections and Messages
    APIs.


These APIs join others that made the switch in previous releases, such as the
Awareness, Cast, Places, Location, and Wallet APIs.


The Past: Using GoogleApiClient



Here is a simple Activity that demonstrates how one would access the Google
Drive API using GoogleApiClient using a previous version of the
Play services SDK:




class="prettyprint">public class MyActivity extends AppCompatActivity implements
GoogleApiClient.OnConnectionFailedListener,
GoogleApiClient.ConnectionCallbacks {

private static final int RC_SIGN_IN = 9001;

private GoogleApiClient mGoogleApiClient;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

GoogleSignInOptions options =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Drive.SCOPE_FILE)
.build();

mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addConnectionCallbacks(this)
.addApi(Auth.GOOGLE_SIGN_IN_API, options)
.addApi(Drive.API)
.build();
}

// ...
// Not shown: code to handle sign in flow
// ...

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
// GoogleApiClient connection failed, most API calls will not work...
}

@Override
public void onConnected(@Nullable Bundle bundle) {
// GoogleApiClient is connected, API calls should succeed...
}

@Override
public void onConnectionSuspended(int i) {
// ...
}

private void createDriveFile() {
// If this method is called before "onConnected" then the app will crash,
// so the developer has to manage multiple callbacks to make this simple
// Drive API call.
Drive.DriveApi.newDriveContents(mGoogleApiClient)
.setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() {
// ...
});
}
}


The code is dominated by the concept of a connection, despite using the
simplified "automanage" feature. A GoogleApiClient is only
connected when all APIs are available and the user has signed in (when APIs
require it).



This model has a number of pitfalls:


  • Any connection failure prevents use of any of the requested APIs, but using
    multiple GoogleApiClient objects is unwieldy.
  • The concept of a "connection" is inappropriately overloaded. Connection
    failures can be result from Google Play services being missing or from
    authentication issues.
  • The developer has to track the connection state, because making some calls
    before onConnected is called will result in a crash.
  • Making a simple API call can mean waiting for two callbacks. One to wait
    until the GoogleApiClient is connected and another for the API call
    itself.

The Future: Using GoogleApi



Over the years the need to replace GoogleApiClient became apparent,
so we set out to completely abstract the "connection" process and make it easier
to access individual Google APIs without boilerplate.



Rather than tacking multiple APIs onto a single API client, each API now has a
purpose-built client object class that extends GoogleApi. Unlike
with GoogleApiClient there is no performance cost to creating many
client objects. Each of these client objects abstracts the connection logic,
connections are automatically managed by the SDK in a way that maximizes both
speed and efficiency.


Authenticating with GoogleSignInClient



When using GoogleApiClient, authentication was part of the
"connection" flow. Now that you no longer need to manage connections, you
should use the new GoogleSignInClient class to initiate
authentication:




class="prettyprint">public class MyNewActivity extends AppCompatActivity {

private static final int RC_SIGN_IN = 9001;

private GoogleSignInClient mSignInClient;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

GoogleSignInOptions options =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Drive.SCOPE_FILE)
.build();

mSignInClient = GoogleSignIn.getClient(this, options);
}

private void signIn() {
// Launches the sign in flow, the result is returned in onActivityResult
Intent intent = mSignInClient.getSignInIntent();
startActivityForResult(intent, RC_SIGN_IN);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == RC_SIGN_IN) {
Task<GoogleSignInAccount> task =
GoogleSignIn.getSignedInAccountFromIntent(data);
if (task.isSuccessful()) {
// Sign in succeeded, proceed with account
GoogleSignInAccount acct = task.getResult();
} else {
// Sign in failed, handle failure and update UI
// ...
}
}
}
}

Making Authenticated API Calls



Making API calls to authenticated APIs is now much simpler and does not require
waiting for multiple callbacks.




class="prettyprint"> private void createDriveFile() {
// Get currently signed in account (or null)
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);

// Synchronously check for necessary permissions
if (!GoogleSignIn.hasPermissions(account, Drive.SCOPE_FILE)) {
// Note: this launches a sign-in flow, however the code to detect
// the result of the sign-in flow and retry the API call is not
// shown here.
GoogleSignIn.requestPermissions(this, RC_DRIVE_PERMS,
account, Drive.SCOPE_FILE);
return;
}

DriveResourceClient client = Drive.getDriveResourceClient(this, account);
client.createContents()
.addOnCompleteListener(new OnCompleteListener<DriveContents>() {
@Override
public void onComplete(@NonNull Task<DriveContents> task) {
// ...
}
});
}


Before making the API call we add an inline check to make sure that we have
signed in and that the sign in process granted the scopes we require.



The call to createContents() is simple, but it's actually taking
care of a lot of complex behavior. If the connection to Play services has not
yet been established, the call is queued until there is a connection. This is in
contrast to the old behavior where calls would fail or crash if made before
connecting.



In general, the new GoogleApi-based APIs have the following
benefits:


  • No connection logic, calls that require a connection are queued until a
    connection is available. Connections are pooled when appropriate and torn down
    when not in use, saving battery and preventing memory leaks.
  • Sign in is completely separated from APIs that consume
    GoogleSignInAccount which makes it easier to use authenticated APIs
    throughout your app.
  • Asynchronous API calls use the new Task API rather than
    PendingResult, which allows for easier management and
    chaining.


These new APIs will improve your development process and enable you to make
better apps.


Next Steps



Ready to get started with the new Google Play services SDK?



Happy building!



Read more

6/15/17

Reduce friction with the new Location APIs

Posted by Aaron Stacy, Software Engineer, Google Play services


The 11.0.0 release of the Google Play services SDK includes a new way to access
href="https://developers.google.com/android/reference/com/google/android/gms/location/LocationServices">LocationServices.
The new APIs do not require your app to manually manage a connection to Google
Play services through a GoogleApiClient. This reduces boilerplate
and common pitfalls in your app.



Read more below, or head straight to href="https://github.com/googlesamples/android-play-location">the updated
location samples on GitHub.


Why not use GoogleApiClient?



The LocationServices APIs allow you to access device location, set up geofences,
prompt the user to enable location on the device and more. In order to access
these services, the app must connect to Google Play services, which can involve
error-prone connection logic. For example, can you spot the crash in the app
below?



Note: we'll assume our app has the
ACCESS_FINE_LOCATION permission, which is required to get the
user's exact location using the LocationServices APIs.




class="prettyprint">public class MainActivity extends AppCompatActivity implements
GoogleApiClient.OnConnectionFailedListener {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

GoogleApiClient client = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(LocationServices.API)
.build();
client.connect();

PendingResult result =
LocationServices.FusedLocationApi.requestLocationUpdates(
client, LocationRequest.create(), pendingIntent);

result.setResultCallback(new ResultCallback() {
@Override
public void onResult(@NonNull Status status) {
Log.d(TAG, "Result: " + status.getStatusMessage());
}
});
}

// ...
}


If you pointed to the requestLocationUpdates() call, you're right!
That call throws an IllegalStateException, since the
GoogleApiClient is has not yet connected. The call to
connect() is asynchronous.



While the code above looks like it should work, it's missing a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks">ConnectionCallbacks
argument to the GoogleApiClient builder. The call to request
location updates should only be made after the onConnected callback
has fired:




class="prettyprint">public class MainActivity extends AppCompatActivity implements
GoogleApiClient.OnConnectionFailedListener,
GoogleApiClient.ConnectionCallbacks {

private GoogleApiClient client;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

client = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.build();

client.connect();
}

@Override
public void onConnected(@Nullable Bundle bundle) {
PendingResult result =
LocationServices.FusedLocationApi.requestLocationUpdates(
client, LocationRequest.create(), pendingIntent);

result.setResultCallback(new ResultCallback() {
@Override
public void onResult(@NonNull Status status) {
Log.d(TAG, "Result: " + status.getStatusMessage());
}
});
}

// ...
}


Now the code works, but it's not ideal for a few reasons:


  • It would be hard to refactor into shared classes if, for instance, you wanted
    to access Location Services in multiple activities.
  • The app connects optimistically in onCreate even if Location
    Services are not needed until later (for example, after user input).
  • It does not handle the case where the app fails to connect to Google Play
    services.
  • There is a lot of boilerplate connection logic before getting started with
    location updates.

A better developer experience



The new LocationServices APIs are much simpler and will make your
code less error prone. The connection logic is handled automatically, and you
only need to attach a single completion listener:




class="prettyprint">public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

FusedLocationProviderClient client =
LocationServices.getFusedLocationProviderClient(this);

client.requestLocationUpdates(LocationRequest.create(), pendingIntent)
.addOnCompleteListener(new OnCompleteListener() {
@Override
public void onComplete(@NonNull Task task) {
Log.d("MainActivity", "Result: " + task.getResult());
}
});
}
}


The new API immediately improves the code in a few ways:


  • The API calls automatically wait for the service connection to be
    established, which removes the need to wait for onConnected before
    making requests.
  • It uses the href="https://firebase.googleblog.com/2016/09/become-a-firebase-taskmaster-part-1.html">Task
    API which makes it easier to compose asynchronous operations.
  • The code is self-contained and could easily be moved into a shared utility
    class or similar.
  • You don't need to understand the underlying connection process to start
    coding.

What happened to all of the callbacks?



The new API will automatically resolve certain connection failures for you, so
you don't need to write code that for things like prompting the user to update
Google Play services. Rather than exposing connection failures globally in the
href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult))">onConnectionFailed
method, connection problems will fail the Task with an href="https://developers.google.com/android/reference/com/google/android/gms/common/api/ApiException">ApiException:




class="prettyprint"> client.requestLocationUpdates(LocationRequest.create(), pendingIntent)
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ApiException) {
Log.w(TAG, ((ApiException) e).getStatusMessage());
} else {
Log.w(TAG, e.getMessage());
}
}
});

Try it for yourself



Try the new LocationServices APIs out for yourself in your own app
or head over to the href="https://github.com/googlesamples/android-play-location">android-play-location
samples on GitHub and see more examples of how the new clients reduce
boilerplate and simplify logic.

Read more

2/6/17

Fashion gets a digital upgrade with the Google Awareness API




Posted by Jeremy Brook, Group Creative Business Partner, the ZOO





Last summer,
we made the Awareness API
available to all developers through Google Play services for the first time,
providing a powerful and unified sensing platform that enables apps to be aware
of all aspects of a user's environment. By using a combination of context
signals, such as location, physical activity, weather and nearby beacons,
developers can better understand their users individually and provide more
engaging and customized mobile app experiences.


We have already seen some great implementations of the API in obvious scenarios,
such as shopping for a new home in the neighborhood or recommending a music
playlist while starting a jog. For New York Fashion Week, we explored other
creative integrations of the Awareness API and collaborated with H&M Group's
digital fashion house Ivyrevel and
its Fashion Tech Lab to bring couture into the digital age with the 'Data
Dress,' a personalized dress designed entirely based on a user's context signals.








Currently under development, the Android app specifically uses the Snapshot
API
within the platform to passively monitor each user's daily activity and
lifestyle with their permission. Where do you regularly eat out for dinner or
hang out with friends? Are they more casual or formal meetups? What's the usual
weather when you're outside? After the course of a week, the user's context
signals are passed through an algorithm that creates a digitally tailored dress
design for the user to purchase.








The Android app is launching in closed alpha stage, and is currently being
tested by selected global style influencers including Ivyrevel's co-founder Kenza Zouiten. If
you want a truly 'tailored' digital experience, sign up here to participate in a future trial of the
app before the public release.












Read more
loading...