google play services rock
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
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
• 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