Download - Google Play Services Rock

Transcript
Page 1: Google Play Services Rock

Google Play Services

with Xamarin

Building Apps that rock

Page 2: Google Play Services Rock

+Peter Friese

@peterfriese

Page 3: Google Play Services Rock

What is Google Play

Services?

Page 4: Google Play Services Rock

• Set of APIs by Google

Google Play Services

Page 5: Google Play Services Rock

• Set of APIs by Google

• Available for Devices running

Gingerbread and higher

Google Play Services

Page 6: Google Play Services Rock

• Set of APIs by Google

• Available for Devices running

Gingerbread and higher

• Access the latest in Google

technology

Google Play Services

Page 7: Google Play Services Rock

• Set of APIs by Google

• Available for Devices running

Gingerbread and higher

• Access the latest in Google

technology

• Frequent updates

Google Play Services

Page 8: Google Play Services Rock

• Set of APIs by Google

• Available for Devices running

Gingerbread and higher

• Access the latest in Google

technology

• Frequent updates

• One standard way to connect

and authorise

Google Play Services

Page 9: Google Play Services Rock

Device

Google Play Services Library

Google Play Services

Drive Service

Your App

Google API Client

Maps

Google+

Wallet

Games Services

Page 10: Google Play Services Rock

How to Integrate Google

Play Services

Page 11: Google Play Services Rock

Three simple steps to success

Add Google

Play Services

to your project

Start using our

APIsProfit!

Page 12: Google Play Services Rock

it's a little

bit morecomplicated.

Actually,

Image: http://en.wikipedia.org/wiki/Turf_maze

Page 13: Google Play Services Rock

• Create new Android project

Xamarin StudioProject setup

Page 14: Google Play Services Rock

Xamarin Studio

• Create new Android project

Project setup

Page 15: Google Play Services Rock

• Create new Android project

• Download Google Play

Services

Xamarin StudioProject setup

Page 16: Google Play Services Rock

Xamarin Studio

• Create new Android project

• Download Google Play

Services

Project setup

Page 17: Google Play Services Rock

• Create new Android project

• Download Google Play

Services

• Add the NuGet component

Xamarin StudioProject setup

Page 18: Google Play Services Rock

Xamarin Studio

• Create new Android project

• Download Google Play

Services

• Add the NuGet component

Project setup

Page 19: Google Play Services Rock

• Create a new project

Google Developers ConsoleProject setup

Page 20: Google Play Services Rock

Google Developers Console

• Create a new project

Project setup

Page 21: Google Play Services Rock

• Create a new project

• Configure the Consent Screen

Google Developers ConsoleProject setup

Page 22: Google Play Services Rock

Google Developers Console

• Create a new project

• Configure the Consent Screen

Project setup

Page 23: Google Play Services Rock

• Create a new project

• Configure the Consent Screen

• Create a Client Configuration

Google Developers ConsoleProject setup

Page 24: Google Play Services Rock

Google Developers Console

• Create a new project

• Configure the Consent Screen

• Create a Client Configuration

Project setup$ keytool -list -storepass android -keystore ~/.local/share/Xamarin/Mono\ for\ Android/debug.keystore !Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry androiddebugkey, 14-May-2014, PrivateKeyEntry, Certificate fingerprint (SHA1): CA:FE:BA:BE:DE:AD:BE:EF:DE:ED:BE:AD:FE:ED:DE:AF:CA:FE:BA:BE

Page 25: Google Play Services Rock

• Create a new project

• Configure the Consent Screen

• Create a Client Configuration

Google Developers ConsoleProject setup

Page 26: Google Play Services Rock

• Create a new project

• Configure the Consent Screen

• Create a Client Configuration

• Activate APIs

Google Developers ConsoleProject setup

Page 27: Google Play Services Rock

Connecting Your App

with Google

Page 28: Google Play Services Rock

protected bool ServicesConnected(){ var resultCode = GooglePlayServicesUtil.IsGooglePlayServicesAvailable (this); if (resultCode == ConnectionResult.Success) { Log.Debug (logTag, "Google Play Services are available"); return true; } else { Log.Debug (logTag, "Connection failed. Attempting resolution."); GooglePlayServicesUtil.GetErrorDialog (resultCode, this, ConnectionFailureResolutionRequest).Show (); return false; }}

BaseActivity.cs

Checking for a compatible GMS version

Page 29: Google Play Services Rock

protected override void OnCreate (Bundle savedInstanceState){ base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (DriveClass.Api) .AddScope (DriveClass.ScopeFile) .AddApi(PlusClass.Api) .AddScope(PlusClass.ScopePlusLogin) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); }}

BaseActivity.cs

Configuring GoogleApiClient

Page 30: Google Play Services Rock

protected override void OnCreate (Bundle savedInstanceState){ base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (DriveClass.Api) .AddScope (DriveClass.ScopeFile) .AddApi(PlusClass.Api) .AddScope(PlusClass.ScopePlusLogin) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); }}

BaseActivity.cs

Configuring GoogleApiClient

Add multiple APIs and Scopes

Page 31: Google Play Services Rock

protected override void OnCreate (Bundle savedInstanceState){ base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (DriveClass.Api) .AddScope (DriveClass.ScopeFile) .AddApi(PlusClass.Api) .AddScope(PlusClass.ScopePlusLogin) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); }}

BaseActivity.cs

Configuring GoogleApiClient

Set up callbacks

Page 32: Google Play Services Rock

protected override void OnStart (){ base.OnStart (); googleApiClient.Connect();} !protected override void OnStop (){ if (googleApiClient != null) { googleApiClient.Disconnect (); } base.OnStop ();}

BaseActivity.cs

Connecting GoogleApiClient

Page 33: Google Play Services Rock

BaseActivity.cs

Callbacks - The Good

public class BaseDemoActivity : Activity, IGoogleApiClientConnectionCallbacks{ public virtual void OnConnected (Bundle connectionHint) { Log.Debug (logTag, "Google API client connected.”); // let the good stuff happen here } public void OnConnectionSuspended (int cause) { Log.Debug (logTag, "Google API client connection suspended."); // deactivate UI components, etc. }}

Page 34: Google Play Services Rock

BaseActivity.cs

Callbacks - The Bad

public class BaseDemoActivity : Activity, IGoogleApiClientOnConnectionFailedListener{ public void OnConnectionFailed (ConnectionResult result) { if (result.HasResolution) { try { result.StartResolutionForResult (this, RequestCodeResolution); } catch (IntentSender.SendIntentException ex) { Log.Error (logTag, "Exception while starting resolution activity", ex); } } else { GooglePlayServicesUtil.GetErrorDialog (result.ErrorCode, this, 0).Show (); return; } }}

Page 35: Google Play Services Rock

BaseActivity.cs

Callbacks - The Bad

public class BaseDemoActivity : Activity, IGoogleApiClientOnConnectionFailedListener{ public void OnConnectionFailed (ConnectionResult result) { if (result.HasResolution) { try { result.StartResolutionForResult (this, RequestCodeResolution); } catch (IntentSender.SendIntentException ex) { Log.Error (logTag, "Exception while starting resolution activity", ex); } } else { GooglePlayServicesUtil.GetErrorDialog (result.ErrorCode, this, 0).Show (); return; } }}

Try to resolve this error by asking for the user’s consent

Page 36: Google Play Services Rock

A Closer Look at Some

of the Services

Page 37: Google Play Services Rock

?

Page 38: Google Play Services Rock

Google Drive

Record demos

Reading / writing files

App folders

Page 39: Google Play Services Rock

• Cloud storage powered by

Google’s infrastructure

Google Drive

Page 40: Google Play Services Rock

• Cloud storage powered by

Google’s infrastructure

• 15 GB for free, upgrades at

competitive prices

Google Drive

Page 41: Google Play Services Rock

• Cloud storage powered by

Google’s infrastructure

• 15 GB for free, upgrades at

competitive prices

• Automatic synchronisation

Google Drive

Page 42: Google Play Services Rock

• Cloud storage powered by

Google’s infrastructure

• 15 GB for free, upgrades at

competitive prices

• Automatic synchronisation

• Android, iOS, Mac, Windows,

Web

Google Drive

Page 43: Google Play Services Rock

• Cloud storage powered by

Google’s infrastructure

• 15 GB for free, upgrades at

competitive prices

• Automatic synchronisation

• Android, iOS, Mac, Windows,

Web

• UI controls (create / pick files)

Google Drive

Page 44: Google Play Services Rock

protected override void OnCreate (Bundle savedInstanceState){ base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (DriveClass.Api) .AddScope (DriveClass.ScopeFile) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); }}

BaseActivity.cs

Google Drive - Connecting

Page 45: Google Play Services Rock

protected override void OnCreate (Bundle savedInstanceState){ base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (DriveClass.Api) .AddScope (DriveClass.ScopeFile) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); }}

BaseActivity.cs

Configuring GoogleApiClient

Supported scopes:

• DriveClass.ScopeFile (drive.file)

• DriveClass.ScopeAppFolder (drive.appfolder)

More about scopes: https://developers.google.com/drive/web/scopes

Page 46: Google Play Services Rock

Google DriveList Files

Page 47: Google Play Services Rock

public class ListFilesActivity : BaseDemoActivity, IResultCallback{ private bool hasMore; private string nextPageToken; private ListView listView; protected ResultsAdapter resultsAdapter;

protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); SetContentView (Resource.Layout.ListFiles); hasMore = true; listView = (ListView) FindViewById (Resource.Id.ListViewResults); resultsAdapter = new ResultsAdapter (this); listView.Adapter = resultsAdapter; listView.Scroll += (object sender, AbsListView.ScrollEventArgs e) => { if (nextPageToken != null && e.FirstVisibleItem + e.VisibleItemCount + 5 < e.TotalItemCount) {

ListFilesActivity.cs

Called for paging list of files.

Page 48: Google Play Services Rock

public class ListFilesActivity : BaseDemoActivity, IResultCallback{ private bool hasMore; private string nextPageToken; private ListView listView; protected ResultsAdapter resultsAdapter;

protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); SetContentView (Resource.Layout.ListFiles); hasMore = true; listView = (ListView) FindViewById (Resource.Id.ListViewResults); resultsAdapter = new ResultsAdapter (this); listView.Adapter = resultsAdapter; listView.Scroll += (object sender, AbsListView.ScrollEventArgs e) => { if (nextPageToken != null && e.FirstVisibleItem + e.VisibleItemCount + 5 < e.TotalItemCount) { RetrieveNextPage(); } } ; }

protected override void OnStop() {

ListFilesActivity.cs

General view setup

Page 49: Google Play Services Rock

base.OnCreate (bundle); SetContentView (Resource.Layout.ListFiles); hasMore = true; listView = (ListView) FindViewById (Resource.Id.ListViewResults); resultsAdapter = new ResultsAdapter (this); listView.Adapter = resultsAdapter; listView.Scroll += (object sender, AbsListView.ScrollEventArgs e) => { if (nextPageToken != null && e.FirstVisibleItem + e.VisibleItemCount + 5 < e.TotalItemCount) { RetrieveNextPage(); } } ; }

protected override void OnStop() { base.OnStop(); resultsAdapter.Clear(); } public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); RetrieveNextPage(); }

ListFilesActivity.cs

Fetch next page when scrolling

Page 50: Google Play Services Rock

listView.Scroll += (object sender, AbsListView.ScrollEventArgs e) => { if (nextPageToken != null && e.FirstVisibleItem + e.VisibleItemCount + 5 < e.TotalItemCount) { RetrieveNextPage(); } } ; }

protected override void OnStop() { base.OnStop(); resultsAdapter.Clear(); } public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); RetrieveNextPage(); } private void RetrieveNextPage () { if (!hasMore) { return; } var query = new QueryClass.Builder () .SetPageToken (nextPageToken)

ListFilesActivity.cs

Clear adapter on stop.

Page 51: Google Play Services Rock

}

protected override void OnStop() { base.OnStop(); resultsAdapter.Clear(); } public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); RetrieveNextPage(); } private void RetrieveNextPage () { if (!hasMore) { return; } var query = new QueryClass.Builder () .SetPageToken (nextPageToken) .Build (); DriveClass.DriveApi.Query (GoogleApiClient, query).SetResultCallback (this); } public void OnResult (Java.Lang.Object result) {

ListFilesActivity.cs

Fetch data as soon as we’re connected

Page 52: Google Play Services Rock

} public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); RetrieveNextPage(); } private void RetrieveNextPage () { if (!hasMore) { return; } var query = new QueryClass.Builder () .SetPageToken (nextPageToken) .Build (); DriveClass.DriveApi.Query (GoogleApiClient, query).SetResultCallback (this); } public void OnResult (Java.Lang.Object result) { var metadataBufferResult = result.JavaCast<IDriveApiMetadataBufferResult> (); if (metadataBufferResult != null) { if (!metadataBufferResult.Status.IsSuccess) { ShowMessage ("Problems while retrieving files."); }

ListFilesActivity.cs

Use token to keep track of position

Page 53: Google Play Services Rock

return; } var query = new QueryClass.Builder () .SetPageToken (nextPageToken) .Build (); DriveClass.DriveApi.Query (GoogleApiClient, query).SetResultCallback (this); } public void OnResult (Java.Lang.Object result) { var metadataBufferResult = result.JavaCast<IDriveApiMetadataBufferResult> (); if (metadataBufferResult != null) { if (!metadataBufferResult.Status.IsSuccess) { ShowMessage ("Problems while retrieving files."); } resultsAdapter.Append (metadataBufferResult.MetadataBuffer); nextPageToken = metadataBufferResult.MetadataBuffer.NextPageToken; hasMore = nextPageToken != null; } }}

ListFilesActivity.cs

Fetch results from metadata buffer

Page 54: Google Play Services Rock

Google DrivePick Files & Folders

Page 55: Google Play Services Rock

public override void OnConnected (Bundle connectionHint){ base.OnConnected (connectionHint); var intentSender = DriveClass.DriveApi .NewOpenFileActivityBuilder () .SetMimeType (new string[]{ "text/plain", "text/html" } ) .Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); }}protected override void OnActivityResult (int requestCode, Result resultCode, Intent data){ base.OnActivityResult (requestCode, resultCode, data);

PickFileWithOpenerActivity.cs

Page 56: Google Play Services Rock

public override void OnConnected (Bundle connectionHint){ base.OnConnected (connectionHint); var intentSender = DriveClass.DriveApi .NewOpenFileActivityBuilder () .SetMimeType (new string[]{ "text/plain", "text/html" } ) .Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); }}protected override void OnActivityResult (int requestCode, Result resultCode, Intent data){ base.OnActivityResult (requestCode, resultCode, data);

PickFileWithOpenerActivity.cs

Create new file picker

Page 57: Google Play Services Rock

public override void OnConnected (Bundle connectionHint){ base.OnConnected (connectionHint); var intentSender = DriveClass.DriveApi .NewOpenFileActivityBuilder () .SetMimeType (new string[]{ "text/plain", "text/html" } ) .Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); }}protected override void OnActivityResult (int requestCode, Result resultCode, Intent data){ base.OnActivityResult (requestCode, resultCode, data);

PickFileWithOpenerActivity.cs

Mime types to show in picker

Page 58: Google Play Services Rock

public override void OnConnected (Bundle connectionHint){ base.OnConnected (connectionHint); var intentSender = DriveClass.DriveApi .NewOpenFileActivityBuilder () .SetMimeType (new string[]{ "text/plain", "text/html" } ) .Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); }}protected override void OnActivityResult (int requestCode, Result resultCode, Intent data){ base.OnActivityResult (requestCode, resultCode, data);

PickFileWithOpenerActivity.cs

Start picker intent

Page 59: Google Play Services Rock

.Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); }}protected override void OnActivityResult (int requestCode, Result resultCode, Intent data){ base.OnActivityResult (requestCode, resultCode, data); if (requestCode == RequestCodeOpener) { if (resultCode == Result.Ok) { var driveId = data.GetParcelableExtra (OpenFileActivityBuilder.ExtraResponseDriveId); ShowMessage ("Selected folder with ID: " + driveId); } Finish (); } else { base.OnActivityResult (requestCode, resultCode, data); }}

PickFileWithOpenerActivity.cs

Page 60: Google Play Services Rock

.Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); }}protected override void OnActivityResult (int requestCode, Result resultCode, Intent data){ base.OnActivityResult (requestCode, resultCode, data); if (requestCode == RequestCodeOpener) { if (resultCode == Result.Ok) { var driveId = data.GetParcelableExtra (OpenFileActivityBuilder.ExtraResponseDriveId); ShowMessage ("Selected folder with ID: " + driveId); } Finish (); } else { base.OnActivityResult (requestCode, resultCode, data); }}

PickFileWithOpenerActivity.cs

Returning from picker?

Page 61: Google Play Services Rock

.Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); }}protected override void OnActivityResult (int requestCode, Result resultCode, Intent data){ base.OnActivityResult (requestCode, resultCode, data); if (requestCode == RequestCodeOpener) { if (resultCode == Result.Ok) { var driveId = data.GetParcelableExtra (OpenFileActivityBuilder.ExtraResponseDriveId); ShowMessage ("Selected folder with ID: " + driveId); } Finish (); } else { base.OnActivityResult (requestCode, resultCode, data); }}

PickFileWithOpenerActivity.cs

Get metadata from extras

Page 62: Google Play Services Rock

Activity Recogniton

Image by Martijn van Dalen

https://www.flickr.com/photos/martijnvandalen/4591360652

Page 63: Google Play Services Rock

Activity RecognitionDemo

Page 64: Google Play Services Rock

protected override void OnCreate (Bundle savedInstanceState){ base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (ActivityRecognition.Api) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); }}

BaseActivity.cs

Activity Recognition - Connecting

Page 65: Google Play Services Rock

protected override void OnCreate (Bundle savedInstanceState){ base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (ActivityRecognition.Api) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); }}

BaseActivity.cs

Activity Recognition - Connecting

Page 66: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="ActivityRecognitionDemos.ActivityRecognitionDemos"> <uses-sdk /> <application android:label="ActivityRecognitionDemos"> </application> <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" /></manifest>

AndroidManifest.xml

Activity Recognition - Permissions

Page 67: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="ActivityRecognitionDemos.ActivityRecognitionDemos"> <uses-sdk /> <application android:label="ActivityRecognitionDemos"> </application> <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" /></manifest>

AndroidManifest.xml

Activity Recognition - Permissions

Page 68: Google Play Services Rock

public override void OnConnected (Bundle connectionHint){ var intent = new Intent (this, typeof(ActivityRecognitionIntentService)); activityRecognitionPendingIntent = PendingIntent.GetService ( this, 0, intent, PendingIntentFlags.UpdateCurrent); ActivityRecognition.ActivityRecognitionApi.RequestActivityUpdates (GoogleApiClient, DetectionInterval, activityRecognitionPendingIntent);}

MainActivity.cs

Starting Activity Recognition

Page 69: Google Play Services Rock

public override void OnConnected (Bundle connectionHint){ var intent = new Intent (this, typeof(ActivityRecognitionIntentService)); activityRecognitionPendingIntent = PendingIntent.GetService ( this, 0, intent, PendingIntentFlags.UpdateCurrent); ActivityRecognition.ActivityRecognitionApi.RequestActivityUpdates (GoogleApiClient, DetectionInterval, activityRecognitionPendingIntent);}

MainActivity.cs

Starting Activity Recognition

Page 70: Google Play Services Rock

public override void OnConnected (Bundle connectionHint){ var intent = new Intent (this, typeof(ActivityRecognitionIntentService)); activityRecognitionPendingIntent = PendingIntent.GetService ( this, 0, intent, PendingIntentFlags.UpdateCurrent); ActivityRecognition.ActivityRecognitionApi.RequestActivityUpdates (GoogleApiClient, DetectionInterval, activityRecognitionPendingIntent);}

MainActivity.cs

Starting Activity Recognition

Page 71: Google Play Services Rock

[Service][IntentFilter(new String[]{"ActivityRecognitionIntentService"})]public class ActivityRecognitionIntentService : IntentService{ protected override void OnHandleIntent (Android.Content.Intent intent) { if (ActivityRecognitionResult.HasResult (intent)) { var result = ActivityRecognitionResult.ExtractResult (intent); var mostProbableActivity = result.MostProbableActivity; var confidence = mostProbableActivity.Confidence; var activityType = mostProbableActivity.Type; var name = GetActivityName (activityType); } } ! protected string GetActivityName(int activityType) { switch (activityType) { } } }

ActivityRecognitionIntentService.cs

Receiving Activity Updates

Page 72: Google Play Services Rock

[Service][IntentFilter(new String[]{"ActivityRecognitionIntentService"})]public class ActivityRecognitionIntentService : IntentService{ protected override void OnHandleIntent (Android.Content.Intent intent) { if (ActivityRecognitionResult.HasResult (intent)) { var result = ActivityRecognitionResult.ExtractResult (intent); var mostProbableActivity = result.MostProbableActivity; var confidence = mostProbableActivity.Confidence; var activityType = mostProbableActivity.Type; var name = GetActivityName (activityType); } } ! protected string GetActivityName(int activityType) { switch (activityType) { } } }

ActivityRecognitionIntentService.cs

Receiving Activity Updates

Page 73: Google Play Services Rock

[Service][IntentFilter(new String[]{"ActivityRecognitionIntentService"})]public class ActivityRecognitionIntentService : IntentService{ protected override void OnHandleIntent (Android.Content.Intent intent) { if (ActivityRecognitionResult.HasResult (intent)) { var result = ActivityRecognitionResult.ExtractResult (intent); var mostProbableActivity = result.MostProbableActivity; var confidence = mostProbableActivity.Confidence; var activityType = mostProbableActivity.Type; var name = GetActivityName (activityType); } } ! protected string GetActivityName(int activityType) { switch (activityType) { } } }

ActivityRecognitionIntentService.cs

Receiving Activity Updates

Page 74: Google Play Services Rock

[Service][IntentFilter(new String[]{"ActivityRecognitionIntentService"})]public class ActivityRecognitionIntentService : IntentService{ protected override void OnHandleIntent (Android.Content.Intent intent) { if (ActivityRecognitionResult.HasResult (intent)) { var result = ActivityRecognitionResult.ExtractResult (intent); var mostProbableActivity = result.MostProbableActivity; var confidence = mostProbableActivity.Confidence; var activityType = mostProbableActivity.Type; var name = GetActivityName (activityType); } } ! protected string GetActivityName(int activityType) { switch (activityType) { } } }

ActivityRecognitionIntentService.cs

Receiving Activity Updates

Page 75: Google Play Services Rock

[Service][IntentFilter(new String[]{"ActivityRecognitionIntentService"})]public class ActivityRecognitionIntentService : IntentService{ protected override void OnHandleIntent (Android.Content.Intent intent) { if (ActivityRecognitionResult.HasResult (intent)) { var result = ActivityRecognitionResult.ExtractResult (intent); var mostProbableActivity = result.MostProbableActivity; var confidence = mostProbableActivity.Confidence; var activityType = mostProbableActivity.Type; var name = GetActivityName (activityType); } } ! protected string GetActivityName(int activityType) { switch (activityType) { } } }

ActivityRecognitionIntentService.cs

Receiving Activity Updates

Page 76: Google Play Services Rock

Maps

Image: Wikipedia

http://bit.ly/10L5SC1

Page 77: Google Play Services Rock

• Obtaining a Maps Key

Google Developers ConsoleMaps Setup

Page 78: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android=“http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName=“1.0" package="com.google.xamarin.sample.GoogleMapsAndroidDemos">

<uses-sdk /> <application android:label="GoogleMapsAndroidDemos"> <meta-data android:name=“com.google.android.maps.v2.API_KEY" android:value="AAzaXXXv3LKXXX0yJb-dXX0xxxWo0XxX_XXXxAg" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission

AndroidManifest.xml

Maps Key and Permissions

Page 79: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android=“http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName=“1.0" package="com.google.xamarin.sample.GoogleMapsAndroidDemos">

<uses-sdk /> <application android:label="GoogleMapsAndroidDemos"> <meta-data android:name=“com.google.android.maps.v2.API_KEY" android:value="AAzaXXXv3LKXXX0yJb-dXX0xxxWo0XxX_XXXxAg" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission

AndroidManifest.xml

Maps Key and Permissions

Maps API Key

Page 80: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android=“http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName=“1.0" package="com.google.xamarin.sample.GoogleMapsAndroidDemos">

<uses-sdk /> <application android:label="GoogleMapsAndroidDemos"> <meta-data android:name=“com.google.android.maps.v2.API_KEY" android:value="AAzaXXXv3LKXXX0yJb-dXX0xxxWo0XxX_XXXxAg" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission

AndroidManifest.xml

Maps Key and Permissions

GMS Version

Page 81: Google Play Services Rock

android:versionName=“1.0" package="com.google.xamarin.sample.GoogleMapsAndroidDemos">

<uses-sdk /> <application android:label="GoogleMapsAndroidDemos"> <meta-data android:name=“com.google.android.maps.v2.API_KEY" android:value="AAzaXXXv3LKXXX0yJb-dXX0xxxWo0XxX_XXXxAg" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /></manifest>

Maps Key and Permissions

AndroidManifest.xml

Page 82: Google Play Services Rock

MapsStreet View

Page 83: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/StreetViewActivity" android:layout_width="match_parent" android:layout_height="match_parent" class="com.google.android.gms.maps.StreetViewPanoramaFragment" /></FrameLayout>

StreetViewActivity.axml

Layout

Page 84: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/StreetViewActivity" android:layout_width="match_parent" android:layout_height="match_parent" class="com.google.android.gms.maps.StreetViewPanoramaFragment" /></FrameLayout>

StreetViewActivity.axml

Layout

Page 85: Google Play Services Rock

protected override void OnCreate (Bundle bundle){ base.OnCreate (bundle); SetContentView (Resource.Layout.StreetViewActivity);

svp = (FragmentManager.FindFragmentById<StreetViewPanoramaFragment> (Resource.Id.StreetViewActivity)).StreetViewPanorama; svp.SetPosition (new LatLng(51.493896, -0.146866));}

StreetViewActivity.cs

StreetView - Set Position

Page 86: Google Play Services Rock

protected override void OnCreate (Bundle bundle){ base.OnCreate (bundle); SetContentView (Resource.Layout.StreetViewActivity);

svp = (FragmentManager.FindFragmentById<StreetViewPanoramaFragment> (Resource.Id.StreetViewActivity)).StreetViewPanorama; svp.SetPosition (new LatLng(51.493896, -0.146866));}

StreetViewActivity.cs

StreetView - Set Position

Page 87: Google Play Services Rock

protected override void OnCreate (Bundle bundle){ base.OnCreate (bundle); SetContentView (Resource.Layout.StreetViewActivity);

svp = (FragmentManager.FindFragmentById<StreetViewPanoramaFragment> (Resource.Id.StreetViewActivity)).StreetViewPanorama; svp.SetPosition (new LatLng(51.493896, -0.146866));}

StreetViewActivity.cs

StreetView - Set Position

Page 88: Google Play Services Rock

protected void walkToOffice() { long duration = 500; float tilt = 0; var camera = new StreetViewPanoramaCamera.Builder () .Zoom (svp.PanoramaCamera.Zoom) .Bearing (svp.PanoramaCamera.Bearing) .Tilt (tilt) .Build (); svp.AnimateTo (camera, duration); svp.SetPosition (new LatLng(51.493896, -0.146866));}

StreetViewActivity.cs

StreetView - Move Camera

Page 89: Google Play Services Rock

protected void walkToOffice() { long duration = 500; float tilt = 0; var camera = new StreetViewPanoramaCamera.Builder () .Zoom (svp.PanoramaCamera.Zoom) .Bearing (svp.PanoramaCamera.Bearing) .Tilt (tilt) .Build (); svp.AnimateTo (camera, duration); svp.SetPosition (new LatLng(51.493896, -0.146866));}

StreetViewActivity.cs

StreetView - Move Camera

Page 90: Google Play Services Rock

protected void walkToOffice() { long duration = 500; float tilt = 0; var camera = new StreetViewPanoramaCamera.Builder () .Zoom (svp.PanoramaCamera.Zoom) .Bearing (svp.PanoramaCamera.Bearing) .Tilt (tilt) .Build (); svp.AnimateTo (camera, duration); svp.SetPosition (new LatLng(51.493896, -0.146866));}

StreetViewActivity.cs

StreetView - Move Camera

Customizing user-controlled functionality:

• PanningGesturesEnabled

• UserNavigationEnabled

• ZoomGesturesEnabled

• StreetNamesEnabled

Page 91: Google Play Services Rock

MapsIndoor Maps

Page 92: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/IndoorMaps" android:layout_width="match_parent" android:layout_height="match_parent" class="com.google.android.gms.maps.MapFragment" /></FrameLayout>

StreetViewActivity.axml

Layout

Page 93: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/IndoorMaps" android:layout_width="match_parent" android:layout_height="match_parent" class="com.google.android.gms.maps.MapFragment" /></FrameLayout>

StreetViewActivity.axml

Layout

Page 94: Google Play Services Rock

protected override void OnCreate (Bundle bundle){ base.OnCreate (bundle); SetContentView (Resource.Layout.IndoorMapsActivity);

if (map == null) { map = (FragmentManager.FindFragmentById<MapFragment> (Resource.Id.IndoorMaps)).Map; if (map != null) { var camera = CameraUpdateFactory.NewLatLngZoom ( new LatLng(51.493896, -0.146866), 18); map.MoveCamera (camera); } }}

IndoorMapsViewActivity.cs

Indoor Maps

Page 95: Google Play Services Rock

protected override void OnCreate (Bundle bundle){ base.OnCreate (bundle); SetContentView (Resource.Layout.IndoorMapsActivity);

if (map == null) { map = (FragmentManager.FindFragmentById<MapFragment> (Resource.Id.IndoorMaps)).Map; if (map != null) { var camera = CameraUpdateFactory.NewLatLngZoom ( new LatLng(51.493896, -0.146866), 18); map.MoveCamera (camera); } }}

IndoorMapsViewActivity.cs

Indoor Maps

Page 96: Google Play Services Rock

protected override void OnCreate (Bundle bundle){ base.OnCreate (bundle); SetContentView (Resource.Layout.IndoorMapsActivity);

if (map == null) { map = (FragmentManager.FindFragmentById<MapFragment> (Resource.Id.IndoorMaps)).Map; if (map != null) { var camera = CameraUpdateFactory.NewLatLngZoom ( new LatLng(51.493896, -0.146866), 18); map.MoveCamera (camera); } }}

IndoorMapsViewActivity.cs

Indoor Maps

Page 97: Google Play Services Rock

Google+

Page 98: Google Play Services Rock

• Powerful identity provider

Google+

Page 99: Google Play Services Rock

• Powerful identity provider

• Over the Air Installs (OTA)

Google+

Page 100: Google Play Services Rock

• Powerful identity provider

• Over the Air Installs (OTA)

• Drive engagement via

interactive posts

Google+

Page 101: Google Play Services Rock
Page 102: Google Play Services Rock
Page 103: Google Play Services Rock
Page 104: Google Play Services Rock
Page 105: Google Play Services Rock
Page 106: Google Play Services Rock
Page 107: Google Play Services Rock

No tap required, log-in will happen automatically!

Page 108: Google Play Services Rock
Page 109: Google Play Services Rock
Page 110: Google Play Services Rock

• Use Google+ Sign-in button

Google+ OTA

Page 111: Google Play Services Rock

• Use Google+ Sign-in button

• Set App package name on the

button

Google+ OTA

Page 112: Google Play Services Rock

• Use Google+ Sign-in button

• Set App package name on the

button

• Use the same scopes on web

and in the app

Google+ OTA

Page 113: Google Play Services Rock

• Use Google+ Sign-in button

• Set App package name on the

button

• Use the same scopes on web

and in the app

• Configure consent screen

Google+ OTA

Page 114: Google Play Services Rock

• Use Google+ Sign-in button

• Set App package name on the

button

• Use the same scopes on web

and in the app

• Configure consent screen

• Meet quality thresholds

Google+ OTA

Page 115: Google Play Services Rock

Google+Interactive Posts

Page 116: Google Play Services Rock

Google+Interactive Posts

Page 117: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android=“http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.google.xamarin.GooglePlusAndroidDemos"> <uses-sdk /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <application android:label="GooglePlusAndroidDemos"> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /></manifest>

AndroidManifest.xml

Permissions

Page 118: Google Play Services Rock

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android=“http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.google.xamarin.GooglePlusAndroidDemos"> <uses-sdk /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <application android:label="GooglePlusAndroidDemos"> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /></manifest>

AndroidManifest.xml

Permissions

Page 119: Google Play Services Rock

const int RequestCodeInterActivePost = 1;

var callToActionUrl = Android.Net.Uri.Parse ( GetString (Resource.String.plus_example_deep_link_url) + action);var callToActionDeepLinkId = GetString (Resource.String.plus_example_deep_link_id) + action;var intent = new PlusShare.Builder (this) .AddCallToAction ( LabelViewItem, callToActionUrl, callToActionDeepLinkId) .SetContentUrl (Android.Net.Uri.Parse ( GetString (Resource.String.plus_example_deep_link_url))) .SetContentDeepLinkId ( GetString (Resource.String.plus_example_deep_link_id), null, null, null) .SetText (sendEditText.Text.ToString ()) .Intent;

MainActivity.cs

Create Interactive Posts

Page 120: Google Play Services Rock

const int RequestCodeInterActivePost = 1;

var callToActionUrl = Android.Net.Uri.Parse ( GetString (Resource.String.plus_example_deep_link_url) + action);var callToActionDeepLinkId = GetString (Resource.String.plus_example_deep_link_id) + action;var intent = new PlusShare.Builder (this) .AddCallToAction ( LabelViewItem, callToActionUrl, callToActionDeepLinkId) .SetContentUrl (Android.Net.Uri.Parse ( GetString (Resource.String.plus_example_deep_link_url))) .SetContentDeepLinkId ( GetString (Resource.String.plus_example_deep_link_id), null, null, null) .SetText (sendEditText.Text.ToString ()) .Intent;

MainActivity.cs

Create Interactive Posts

Page 121: Google Play Services Rock

const int RequestCodeInterActivePost = 1;

var callToActionUrl = Android.Net.Uri.Parse ( GetString (Resource.String.plus_example_deep_link_url) + action);var callToActionDeepLinkId = GetString (Resource.String.plus_example_deep_link_id) + action;var intent = new PlusShare.Builder (this) .AddCallToAction ( LabelViewItem, callToActionUrl, callToActionDeepLinkId) .SetContentUrl (Android.Net.Uri.Parse ( GetString (Resource.String.plus_example_deep_link_url))) .SetContentDeepLinkId ( GetString (Resource.String.plus_example_deep_link_id), null, null, null) .SetText (sendEditText.Text.ToString ()) .Intent; !StartActivityForResult(intent, RequestCodeInterActivePost);

MainActivity.cs

Create Interactive Posts

Page 122: Google Play Services Rock

var callToActionDeepLinkId = GetString (Resource.String.plus_example_deep_link_id) + action;var intent = new PlusShare.Builder (this) .AddCallToAction ( LabelViewItem, callToActionUrl, callToActionDeepLinkId) .SetContentUrl (Android.Net.Uri.Parse ( GetString (Resource.String.plus_example_deep_link_url))) .SetContentDeepLinkId ( GetString (Resource.String.plus_example_deep_link_id), null, null, null) .SetText (sendEditText.Text.ToString ()) .Intent; !StartActivityForResult(intent, RequestCodeInterActivePost);

MainActivity.cs

Create Interactive Posts

Page 123: Google Play Services Rock

[Activity (Label = "ParseDeepLinkActivity")] [IntentFilter (new[]{"com.google.android.apps.plus.VIEW_DEEP_LINK"}, DataScheme="vnd.google.deeplink", Categories=new[]{Intent.CategoryDefault, Intent.CategoryBrowsable} )]public class ParseDeepLinkActivity : BaseActivity{ const string logTag = "ParseDeepLinkActivity"; protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); var deepLinkId = PlusShare.GetDeepLinkId (Intent); var target = ProcessDeepLinkId (deepLinkId); if (target != null) { StartActivity (target); } }

ParseDeepLinkActivity.cs

Parse Deep Links

Page 124: Google Play Services Rock

[Activity (Label = "ParseDeepLinkActivity")] [IntentFilter (new[]{"com.google.android.apps.plus.VIEW_DEEP_LINK"}, DataScheme="vnd.google.deeplink", Categories=new[]{Intent.CategoryDefault, Intent.CategoryBrowsable} )]public class ParseDeepLinkActivity : BaseActivity{ const string logTag = "ParseDeepLinkActivity"; protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); var deepLinkId = PlusShare.GetDeepLinkId (Intent); var target = ProcessDeepLinkId (deepLinkId); if (target != null) { StartActivity (target); } }

ParseDeepLinkActivity.cs

Parse Deep Links

Page 125: Google Play Services Rock

[Activity (Label = "ParseDeepLinkActivity")] [IntentFilter (new[]{"com.google.android.apps.plus.VIEW_DEEP_LINK"}, DataScheme="vnd.google.deeplink", Categories=new[]{Intent.CategoryDefault, Intent.CategoryBrowsable} )]public class ParseDeepLinkActivity : BaseActivity{ const string logTag = "ParseDeepLinkActivity"; protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); var deepLinkId = PlusShare.GetDeepLinkId (Intent); var target = ProcessDeepLinkId (deepLinkId); if (target != null) { StartActivity (target); } } protected Intent ProcessDeepLinkId(string deepLinkId) { Intent route = null; var uri = Android.Net.Uri.Parse (deepLinkId);

Parse Deep Links

ParseDeepLinkActivity.cs

Page 126: Google Play Services Rock

base.OnCreate (bundle); var deepLinkId = PlusShare.GetDeepLinkId (Intent); var target = ProcessDeepLinkId (deepLinkId); if (target != null) { StartActivity (target); } } protected Intent ProcessDeepLinkId(string deepLinkId) { Intent route = null; var uri = Android.Net.Uri.Parse (deepLinkId); if (uri.Path.StartsWith (GetString (Resource.String.plus_example_deep_link_id))) { Toast.MakeText (this, string.Format (“Deep link was { 0}", uri.Path.ToString ()), ToastLength.Long) .Show (); } else { Log.Debug (TAG, "We cannot handle this"); } return route; }}

ParseDeepLinkActivity.cs

Parse Deep Links

Page 127: Google Play Services Rock

Recap

Page 128: Google Play Services Rock

?

Page 129: Google Play Services Rock

Google Play Services https://developer.android.com/google/play-services/

Page 130: Google Play Services Rock

Q & A

Page 131: Google Play Services Rock

What’s next?Material Android Design from Concept to Implementation (I + II) Thursday, 1 pm (Franklin Salon)

C# is in My Ears and in My Eyes Thursday, 4:15 pm (Linnaeus Salon)

youtube.com/GoogleDevelopers

Page 132: Google Play Services Rock

+PeterFriese

@

Thank you!

#Xamarin+Google

Page 133: Google Play Services Rock