Google Play Services Rock

Post on 24-Jun-2015

929 views 5 download

Tags:

description

Google Play Services are a unified SDK that allow you to quickly and easily integrate Google features into your Android apps. Being rolled out to millions of Android devices, they provide easy access to Google services and allow you to innovate quickly. In this session, I will give you a rundown of the core services available via Google Play Services and give you an overview of the new APIs that ship as a part of Google Play Services. We will also have a look at some of the lesser known features that will enable you to build apps that truly rock.

Transcript of Google Play Services Rock

Google Play Services

with Xamarin

Building Apps that rock

+Peter Friese

@peterfriese

What is Google Play

Services?

• Set of APIs by Google

Google Play Services

• Set of APIs by Google

• Available for Devices running

Gingerbread and higher

Google Play Services

• Set of APIs by Google

• Available for Devices running

Gingerbread and higher

• Access the latest in Google

technology

Google Play Services

• Set of APIs by Google

• Available for Devices running

Gingerbread and higher

• Access the latest in Google

technology

• Frequent updates

Google Play Services

• 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

Device

Google Play Services Library

Google Play Services

Drive Service

Your App

Google API Client

Maps

Google+

Wallet

Games Services

How to Integrate Google

Play Services

Three simple steps to success

Add Google

Play Services

to your project

Start using our

APIsProfit!

it's a little

bit morecomplicated.

Actually,

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

• Create new Android project

Xamarin StudioProject setup

Xamarin Studio

• Create new Android project

Project setup

• Create new Android project

• Download Google Play

Services

Xamarin StudioProject setup

Xamarin Studio

• Create new Android project

• Download Google Play

Services

Project setup

• Create new Android project

• Download Google Play

Services

• Add the NuGet component

Xamarin StudioProject setup

Xamarin Studio

• Create new Android project

• Download Google Play

Services

• Add the NuGet component

Project setup

• Create a new project

Google Developers ConsoleProject setup

Google Developers Console

• Create a new project

Project setup

• Create a new project

• Configure the Consent Screen

Google Developers ConsoleProject setup

Google Developers Console

• Create a new project

• Configure the Consent Screen

Project setup

• Create a new project

• Configure the Consent Screen

• Create a Client Configuration

Google Developers ConsoleProject setup

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

• Create a new project

• Configure the Consent Screen

• Create a Client Configuration

Google Developers ConsoleProject setup

• Create a new project

• Configure the Consent Screen

• Create a Client Configuration

• Activate APIs

Google Developers ConsoleProject setup

Connecting Your App

with Google

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

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

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

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

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

BaseActivity.cs

Connecting GoogleApiClient

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. }}

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; } }}

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

A Closer Look at Some

of the Services

?

Google Drive

Record demos

Reading / writing files

App folders

• Cloud storage powered by

Google’s infrastructure

Google Drive

• Cloud storage powered by

Google’s infrastructure

• 15 GB for free, upgrades at

competitive prices

Google Drive

• Cloud storage powered by

Google’s infrastructure

• 15 GB for free, upgrades at

competitive prices

• Automatic synchronisation

Google Drive

• Cloud storage powered by

Google’s infrastructure

• 15 GB for free, upgrades at

competitive prices

• Automatic synchronisation

• Android, iOS, Mac, Windows,

Web

Google Drive

• 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

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

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

Google DriveList Files

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.

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

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

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.

}

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

} 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

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

Google DrivePick Files & Folders

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

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

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

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

.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

.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?

.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

Activity Recogniton

Image by Martijn van Dalen

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

Activity RecognitionDemo

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

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

<?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

<?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

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

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

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

[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

[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

[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

[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

[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

Maps

Image: Wikipedia

http://bit.ly/10L5SC1

• Obtaining a Maps Key

Google Developers ConsoleMaps Setup

<?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

<?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

<?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

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

MapsStreet View

<?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

<?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

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

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

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

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

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

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

MapsIndoor Maps

<?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

<?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

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

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

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

Google+

• Powerful identity provider

Google+

• Powerful identity provider

• Over the Air Installs (OTA)

Google+

• Powerful identity provider

• Over the Air Installs (OTA)

• Drive engagement via

interactive posts

Google+

No tap required, log-in will happen automatically!

• Use Google+ Sign-in button

Google+ OTA

• Use Google+ Sign-in button

• Set App package name on the

button

Google+ OTA

• Use Google+ Sign-in button

• Set App package name on the

button

• Use the same scopes on web

and in the app

Google+ OTA

• 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

• 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

Google+Interactive Posts

Google+Interactive Posts

<?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

<?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

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

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

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

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

[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

[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

[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

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

Recap

?

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

Q & A

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

+PeterFriese

@

Thank you!

#Xamarin+Google