SlideShare ist ein Scribd-Unternehmen logo
1 von 133
Downloaden Sie, um offline zu lesen
Google Play Services 
with Xamarin 
Building Apps that rock
+Peter Friese 
@peterfriese
What is Google Play 
Services?
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 Library 
Device 
Drive Service 
Google Play Services 
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 
APIs Profit!
it's a little 
bit more 
complicated. 
Actually, 
Image: http://en.wikipedia.org/wiki/Turf_maze
Project setup 
Xamarin Studio 
• Create new Android project
Project setup 
Xamarin Studio 
• Create new Android project
Project setup 
Xamarin Studio 
• Create new Android project 
• Download Google Play 
Services
Project setup 
Xamarin Studio 
• Create new Android project 
• Download Google Play 
Services
Project setup 
Xamarin Studio 
• Create new Android project 
• Download Google Play 
Services 
• Add the NuGet component
Project setup 
Xamarin Studio 
• Create new Android project 
• Download Google Play 
Services 
• Add the NuGet component
Project setup 
Google Developers Console 
• Create a new project
Project setup 
Google Developers Console 
• Create a new project
Project setup 
Google Developers Console 
• Create a new project 
• Configure the Consent Screen
Project setup 
Google Developers Console 
• Create a new project 
• Configure the Consent Screen
Project setup 
Google Developers Console 
• Create a new project 
• Configure the Consent Screen 
• Create a Client Configuration
Project setup 
$ keytool -list -storepass android 
Google Developers Console 
• Create a new project 
• Configure the Consent Screen 
• Create a Client Configuration 
-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
Project setup 
Google Developers Console 
• Create a new project 
• Configure the Consent Screen 
• Create a Client Configuration
Project setup 
Google Developers Console 
• Create a new project 
• Configure the Consent Screen 
• Create a Client Configuration 
• Activate APIs
Connecting Your App 
with Google
Checking for a compatible GMS version 
BaseActivity.cs 
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; 
} 
}
Configuring GoogleApiClient 
BaseActivity.cs 
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 (); 
} 
}
Configuring GoogleApiClient 
BaseActivity.cs 
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 (); 
} 
} 
Add multiple APIs and Scopes
Configuring GoogleApiClient 
BaseActivity.cs 
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 (); 
} 
} 
Set up callbacks
Connecting GoogleApiClient 
BaseActivity.cs 
protected override void OnStart () 
{ 
base.OnStart (); 
googleApiClient.Connect(); 
} 
! 
protected override void OnStop () 
{ 
if (googleApiClient != null) { 
googleApiClient.Disconnect (); 
} 
base.OnStop (); 
}
Callbacks - The Good 
BaseActivity.cs 
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. 
} 
}
Callbacks - The Bad 
BaseActivity.cs 
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; 
} 
} 
}
Callbacks - The Bad 
BaseActivity.cs 
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
?
Record demos 
Reading / writing files 
App folders 
Google Drive
Google Drive 
• 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 - Connecting 
BaseActivity.cs 
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 (); 
} 
}
Configuring GoogleApiClient 
BaseActivity.cs 
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 (); 
} 
} 
Supported scopes: 
• DriveClass.ScopeFile (drive.file) 
• DriveClass.ScopeAppFolder (drive.appfolder) 
More about scopes: 
https://developers.google.com/drive/web/scopes
List Files 
Google Drive
ListFilesActivity.cs 
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) { 
Called for paging list of files.
public class ListFilesActivity : BaseDemoActivity, IResultCallback 
{ 
private bool hasMore; 
private string nextPageToken; 
private ListView listView; 
protected ResultsAdapter resultsAdapter; 
ListFilesActivity.cs 
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() 
{ 
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) => { 
ListFilesActivity.cs 
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(); 
} 
Fetch next page when scrolling
listView.Scroll += (object sender, AbsListView.ScrollEventArgs e) => { 
if (nextPageToken != null && e.FirstVisibleItem + e.VisibleItemCount + 5 
< e.TotalItemCount) { 
RetrieveNextPage(); 
} 
} ; 
ListFilesActivity.cs 
} 
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) 
Clear adapter on stop.
} 
protected override void OnStop() 
{ 
base.OnStop(); 
resultsAdapter.Clear(); 
ListFilesActivity.cs 
} 
public override void OnConnected (Bundle connectionHint) 
{ 
base.OnConnected (connectionHint); 
RetrieveNextPage(); 
} 
private void RetrieveNextPage () 
{ 
if (!hasMore) { 
return; 
Fetch data as soon as we’re connected 
} 
var query = new QueryClass.Builder () 
.SetPageToken (nextPageToken) 
.Build (); 
DriveClass.DriveApi.Query (GoogleApiClient, query).SetResultCallback (this); 
} 
public void OnResult (Java.Lang.Object result) 
{
} 
public override void OnConnected (Bundle connectionHint) 
{ 
base.OnConnected (connectionHint); 
RetrieveNextPage(); 
ListFilesActivity.cs 
} 
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."); 
} 
Use token to keep track of position
return; 
} 
var query = new QueryClass.Builder () 
.SetPageToken (nextPageToken) 
.Build (); 
ListFilesActivity.cs 
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; 
} 
} 
} 
Fetch results from metadata buffer
Pick Files & Folders 
Google Drive
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 
public override void OnConnected (Bundle connectionHint) 
{ 
base.OnConnected (connectionHint); 
var intentSender = DriveClass.DriveApi 
Create new file picker 
.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 
Mime types to show in picker 
.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); 
} 
} 
Start picker intent 
protected override void OnActivityResult (int requestCode, Result resultCode, Intent 
data) 
{ 
base.OnActivityResult (requestCode, resultCode, data);
.Build (GoogleApiClient); 
try { 
StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); 
} catch (IntentSender.SendIntentException ex) { 
Log.Warn (logTag, "Unable to send intent", ex); 
PickFileWithOpenerActivity.} 
cs 
} 
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); 
} 
}
.Build (GoogleApiClient); 
try { 
StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); 
} catch (IntentSender.SendIntentException ex) { 
Log.Warn (logTag, "Unable to send intent", ex); 
PickFileWithOpenerActivity.} 
cs 
} 
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); 
} 
} 
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); 
PickFileWithOpenerActivity.} 
cs 
} 
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); 
} 
} 
Get metadata from extras
Activity Recogniton 
Image by Martijn van Dalen 
https://www.flickr.com/photos/martijnvandalen/4591360652
Demo 
Activity Recognition
Activity Recognition - Connecting 
BaseActivity.cs 
protected override void OnCreate (Bundle savedInstanceState) 
{ 
base.OnCreate (savedInstanceState); 
if (googleApiClient == null) { 
googleApiClient = new GoogleApiClientBuilder (this) 
.AddApi (ActivityRecognition.Api) 
.AddConnectionCallbacks (this) 
.AddOnConnectionFailedListener (this) 
.Build (); 
} 
}
Activity Recognition - Connecting 
BaseActivity.cs 
protected override void OnCreate (Bundle savedInstanceState) 
{ 
base.OnCreate (savedInstanceState); 
if (googleApiClient == null) { 
googleApiClient = new GoogleApiClientBuilder (this) 
.AddApi (ActivityRecognition.Api) 
.AddConnectionCallbacks (this) 
.AddOnConnectionFailedListener (this) 
.Build (); 
} 
}
Activity Recognition - Permissions 
AndroidManifest.xml 
<?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>
Activity Recognition - Permissions 
AndroidManifest.xml 
<?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>
Starting Activity Recognition 
MainActivity.cs 
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); 
}
Starting Activity Recognition 
MainActivity.cs 
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); 
}
Starting Activity Recognition 
MainActivity.cs 
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); 
}
Receiving Activity Updates 
ActivityRecognitionIntentService.cs 
[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) { 
} 
} 
}
Receiving Activity Updates 
ActivityRecognitionIntentService.cs 
[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) { 
} 
} 
}
Receiving Activity Updates 
ActivityRecognitionIntentService.cs 
[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) { 
} 
} 
}
Receiving Activity Updates 
ActivityRecognitionIntentService.cs 
[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) { 
} 
} 
}
Receiving Activity Updates 
ActivityRecognitionIntentService.cs 
[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) { 
} 
} 
}
Maps 
Image: Wikipedia 
http://bit.ly/10L5SC1
Maps Setup 
Google Developers Console 
• Obtaining a Maps Key
Maps Key and Permissions 
AndroidManifest.xml 
<?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
Maps Key and Permissions 
AndroidManifest.xml 
<?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 
Maps API Key 
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
Maps Key and Permissions 
AndroidManifest.xml 
<?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 
GMS Version 
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:versionName=“1.0" 
package="com.google.xamarin.sample.GoogleMapsAndroidDemos"> 
Maps Key and Permissions 
AndroidManifest.xml 
<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>
Street View 
Maps
Layout 
StreetViewActivity.axml 
<?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>
Layout 
StreetViewActivity.axml 
<?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>
StreetView - Set Position 
StreetViewActivity.cs 
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)); 
}
StreetView - Set Position 
StreetViewActivity.cs 
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)); 
}
StreetView - Set Position 
StreetViewActivity.cs 
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)); 
}
StreetView - Move Camera 
StreetViewActivity.cs 
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)); 
}
StreetView - Move Camera 
StreetViewActivity.cs 
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)); 
}
StreetView - Move Camera 
StreetViewActivity.cs 
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)); 
} 
Customizing user-controlled functionality: 
• PanningGesturesEnabled 
• UserNavigationEnabled 
• ZoomGesturesEnabled 
• StreetNamesEnabled
Indoor Maps 
Maps
Layout 
StreetViewActivity.axml 
<?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>
Layout 
StreetViewActivity.axml 
<?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>
Indoor Maps 
IndoorMapsViewActivity.cs 
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); 
} 
} 
}
Indoor Maps 
IndoorMapsViewActivity.cs 
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); 
} 
} 
}
Indoor Maps 
IndoorMapsViewActivity.cs 
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); 
} 
} 
}
Google+
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
No tap required, log-in will happen automatically!
Google+ OTA 
• 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
Interactive Posts 
Google+
Interactive Posts 
Google+
Permissions 
AndroidManifest.xml 
<?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>
Permissions 
AndroidManifest.xml 
<?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>
Create Interactive Posts 
MainActivity.cs 
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;
Create Interactive Posts 
MainActivity.cs 
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;
Create Interactive Posts 
const int RequestCodeInterActivePost = 1; 
var callToActionUrl = Android.Net.Uri.Parse ( 
MainActivity.cs 
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);
var callToActionDeepLinkId = 
GetString (Resource.String.plus_example_deep_link_id) + action; 
Create Interactive Posts 
var intent = new PlusShare.Builder (this) 
.AddCallToAction ( 
LabelViewItem, 
callToActionUrl, 
callToActionDeepLinkId) 
MainActivity.cs 
.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);
Parse Deep Links 
ParseDeepLinkActivity.cs 
[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); 
} 
}
Parse Deep Links 
ParseDeepLinkActivity.cs 
[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); 
} 
}
Parse Deep Links 
ParseDeepLinkActivity.cs 
[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);
base.OnCreate (bundle); 
Parse Deep Links 
var deepLinkId = PlusShare.GetDeepLinkId (Intent); 
var target = ProcessDeepLinkId (deepLinkId); 
if (target != null) { 
StartActivity (target); 
ParseDeepLinkActivity.cs 
} 
} 
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; 
} 
}
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
Thank you! 
+PeterFriese 
@ 
#Xamarin+Google
Google Play Services Rock

Weitere ähnliche Inhalte

Was ist angesagt?

Getting your app ready for android n
Getting your app ready for android nGetting your app ready for android n
Getting your app ready for android nSercan Yusuf
 
20110525[Taipei GTUG] titanium mobile簡介
20110525[Taipei GTUG] titanium mobile簡介20110525[Taipei GTUG] titanium mobile簡介
20110525[Taipei GTUG] titanium mobile簡介Justin Lee
 
Introducing Vuex in your project
Introducing Vuex in your projectIntroducing Vuex in your project
Introducing Vuex in your projectDenny Biasiolli
 
Session #8 adding magic to your app
Session #8  adding magic to your appSession #8  adding magic to your app
Session #8 adding magic to your appVitali Pekelis
 
A realtime infrastructure for Android apps: Firebase may be what you need..an...
A realtime infrastructure for Android apps: Firebase may be what you need..an...A realtime infrastructure for Android apps: Firebase may be what you need..an...
A realtime infrastructure for Android apps: Firebase may be what you need..an...Alessandro Martellucci
 
Lowering in C#: What really happens with your code?, from NDC Oslo 2019
Lowering in C#: What really happens with your code?, from NDC Oslo 2019Lowering in C#: What really happens with your code?, from NDC Oslo 2019
Lowering in C#: What really happens with your code?, from NDC Oslo 2019David Wengier
 
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"IT Event
 
CiklumJavaSat_15112011:Alex Kruk VMForce
CiklumJavaSat_15112011:Alex Kruk VMForceCiklumJavaSat_15112011:Alex Kruk VMForce
CiklumJavaSat_15112011:Alex Kruk VMForceCiklum Ukraine
 
A (very) opinionated guide to MSBuild and Project Files
A (very) opinionated guide to MSBuild and Project FilesA (very) opinionated guide to MSBuild and Project Files
A (very) opinionated guide to MSBuild and Project FilesDavid Wengier
 
Android Design Patterns
Android Design PatternsAndroid Design Patterns
Android Design PatternsGodfrey Nolan
 
Project presentation(View calender)
Project presentation(View calender)Project presentation(View calender)
Project presentation(View calender)Ikhtiar Khan Sohan
 
Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"Provectus
 
Bootiful Development with Spring Boot and React - Belfast JUG 2018
Bootiful Development with Spring Boot and React - Belfast JUG 2018Bootiful Development with Spring Boot and React - Belfast JUG 2018
Bootiful Development with Spring Boot and React - Belfast JUG 2018Matt Raible
 
Docker and Django Meet For A Tango - London Meetup
Docker and Django Meet For A Tango - London MeetupDocker and Django Meet For A Tango - London Meetup
Docker and Django Meet For A Tango - London Meetupfrentrup
 
Bootiful Development with Spring Boot and React - Dublin JUG 2018
Bootiful Development with Spring Boot and React - Dublin JUG 2018Bootiful Development with Spring Boot and React - Dublin JUG 2018
Bootiful Development with Spring Boot and React - Dublin JUG 2018Matt Raible
 

Was ist angesagt? (20)

Getting your app ready for android n
Getting your app ready for android nGetting your app ready for android n
Getting your app ready for android n
 
20110525[Taipei GTUG] titanium mobile簡介
20110525[Taipei GTUG] titanium mobile簡介20110525[Taipei GTUG] titanium mobile簡介
20110525[Taipei GTUG] titanium mobile簡介
 
Introducing Vuex in your project
Introducing Vuex in your projectIntroducing Vuex in your project
Introducing Vuex in your project
 
Session #8 adding magic to your app
Session #8  adding magic to your appSession #8  adding magic to your app
Session #8 adding magic to your app
 
A realtime infrastructure for Android apps: Firebase may be what you need..an...
A realtime infrastructure for Android apps: Firebase may be what you need..an...A realtime infrastructure for Android apps: Firebase may be what you need..an...
A realtime infrastructure for Android apps: Firebase may be what you need..an...
 
mobl
moblmobl
mobl
 
Lowering in C#: What really happens with your code?, from NDC Oslo 2019
Lowering in C#: What really happens with your code?, from NDC Oslo 2019Lowering in C#: What really happens with your code?, from NDC Oslo 2019
Lowering in C#: What really happens with your code?, from NDC Oslo 2019
 
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
 
CiklumJavaSat_15112011:Alex Kruk VMForce
CiklumJavaSat_15112011:Alex Kruk VMForceCiklumJavaSat_15112011:Alex Kruk VMForce
CiklumJavaSat_15112011:Alex Kruk VMForce
 
Google Web Toolkit
Google Web ToolkitGoogle Web Toolkit
Google Web Toolkit
 
Progressive What Apps?
Progressive What Apps?Progressive What Apps?
Progressive What Apps?
 
Android TDD
Android TDDAndroid TDD
Android TDD
 
A (very) opinionated guide to MSBuild and Project Files
A (very) opinionated guide to MSBuild and Project FilesA (very) opinionated guide to MSBuild and Project Files
A (very) opinionated guide to MSBuild and Project Files
 
Android Design Patterns
Android Design PatternsAndroid Design Patterns
Android Design Patterns
 
Project presentation(View calender)
Project presentation(View calender)Project presentation(View calender)
Project presentation(View calender)
 
Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"
 
Bootiful Development with Spring Boot and React - Belfast JUG 2018
Bootiful Development with Spring Boot and React - Belfast JUG 2018Bootiful Development with Spring Boot and React - Belfast JUG 2018
Bootiful Development with Spring Boot and React - Belfast JUG 2018
 
Docker and Django Meet For A Tango - London Meetup
Docker and Django Meet For A Tango - London MeetupDocker and Django Meet For A Tango - London Meetup
Docker and Django Meet For A Tango - London Meetup
 
Bootiful Development with Spring Boot and React - Dublin JUG 2018
Bootiful Development with Spring Boot and React - Dublin JUG 2018Bootiful Development with Spring Boot and React - Dublin JUG 2018
Bootiful Development with Spring Boot and React - Dublin JUG 2018
 
Firebase ng2 zurich
Firebase ng2 zurichFirebase ng2 zurich
Firebase ng2 zurich
 

Ähnlich wie Google Play Services Rock

Cómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte locoCómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte locoGemma Del Olmo
 
Android Meetup Slovenia #2 - Making your app location-aware
Android Meetup Slovenia #2 - Making your app location-awareAndroid Meetup Slovenia #2 - Making your app location-aware
Android Meetup Slovenia #2 - Making your app location-awareInfinum
 
Infinum Android Talks #9 - Making your app location-aware
Infinum Android Talks #9 - Making your app location-awareInfinum Android Talks #9 - Making your app location-aware
Infinum Android Talks #9 - Making your app location-awareInfinum
 
Building Grails Plugins - Tips And Tricks
Building Grails Plugins - Tips And TricksBuilding Grails Plugins - Tips And Tricks
Building Grails Plugins - Tips And TricksMike Hugo
 
[Android] Google Service Play & Google Maps
[Android] Google Service Play & Google Maps[Android] Google Service Play & Google Maps
[Android] Google Service Play & Google MapsNatã Melo
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsLoiane Groner
 
Gretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with GradleGretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with GradleAndrey Hihlovsky
 
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingBDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingJohn Ferguson Smart Limited
 
Introduction to Cloud Computing with Google Cloud
Introduction to Cloud Computing with Google CloudIntroduction to Cloud Computing with Google Cloud
Introduction to Cloud Computing with Google Cloudwesley chun
 
Google Cloud Platform 2014Q1 - Starter Guide
Google Cloud Platform   2014Q1 - Starter GuideGoogle Cloud Platform   2014Q1 - Starter Guide
Google Cloud Platform 2014Q1 - Starter GuideSimon Su
 
Intro to the Google Cloud for Developers
Intro to the Google Cloud for DevelopersIntro to the Google Cloud for Developers
Intro to the Google Cloud for DevelopersLynn Langit
 
Testing your application on Google App Engine
Testing your application on Google App EngineTesting your application on Google App Engine
Testing your application on Google App EngineInphina Technologies
 
Testing Your Application On Google App Engine
Testing Your Application On Google App EngineTesting Your Application On Google App Engine
Testing Your Application On Google App EngineIndicThreads
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideNode.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideMek Srunyu Stittri
 
Webdriver with Thucydides - TdT@Cluj #18
Webdriver with Thucydides - TdT@Cluj #18Webdriver with Thucydides - TdT@Cluj #18
Webdriver with Thucydides - TdT@Cluj #18Tabăra de Testare
 
Angular 4 with firebase
Angular 4 with firebaseAngular 4 with firebase
Angular 4 with firebaseAnne Bougie
 
Single Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StorySingle Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StoryKon Soulianidis
 
TDC2016SP - Trilha Android
TDC2016SP - Trilha AndroidTDC2016SP - Trilha Android
TDC2016SP - Trilha Androidtdc-globalcode
 

Ähnlich wie Google Play Services Rock (20)

Cómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte locoCómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte loco
 
Android Meetup Slovenia #2 - Making your app location-aware
Android Meetup Slovenia #2 - Making your app location-awareAndroid Meetup Slovenia #2 - Making your app location-aware
Android Meetup Slovenia #2 - Making your app location-aware
 
Infinum Android Talks #9 - Making your app location-aware
Infinum Android Talks #9 - Making your app location-awareInfinum Android Talks #9 - Making your app location-aware
Infinum Android Talks #9 - Making your app location-aware
 
Building Grails Plugins - Tips And Tricks
Building Grails Plugins - Tips And TricksBuilding Grails Plugins - Tips And Tricks
Building Grails Plugins - Tips And Tricks
 
[Android] Google Service Play & Google Maps
[Android] Google Service Play & Google Maps[Android] Google Service Play & Google Maps
[Android] Google Service Play & Google Maps
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applications
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Gretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with GradleGretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with Gradle
 
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingBDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
 
Introduction to Cloud Computing with Google Cloud
Introduction to Cloud Computing with Google CloudIntroduction to Cloud Computing with Google Cloud
Introduction to Cloud Computing with Google Cloud
 
Google Cloud Platform 2014Q1 - Starter Guide
Google Cloud Platform   2014Q1 - Starter GuideGoogle Cloud Platform   2014Q1 - Starter Guide
Google Cloud Platform 2014Q1 - Starter Guide
 
Intro to the Google Cloud for Developers
Intro to the Google Cloud for DevelopersIntro to the Google Cloud for Developers
Intro to the Google Cloud for Developers
 
Testing your application on Google App Engine
Testing your application on Google App EngineTesting your application on Google App Engine
Testing your application on Google App Engine
 
Testing Your Application On Google App Engine
Testing Your Application On Google App EngineTesting Your Application On Google App Engine
Testing Your Application On Google App Engine
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideNode.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java side
 
Webdriver with Thucydides - TdT@Cluj #18
Webdriver with Thucydides - TdT@Cluj #18Webdriver with Thucydides - TdT@Cluj #18
Webdriver with Thucydides - TdT@Cluj #18
 
Angular 4 with firebase
Angular 4 with firebaseAngular 4 with firebase
Angular 4 with firebase
 
Single Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StorySingle Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle Story
 
Night Watch with QA
Night Watch with QANight Watch with QA
Night Watch with QA
 
TDC2016SP - Trilha Android
TDC2016SP - Trilha AndroidTDC2016SP - Trilha Android
TDC2016SP - Trilha Android
 

Mehr von Peter Friese

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsPeter Friese
 
Firebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopFirebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopPeter Friese
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsPeter Friese
 
Firebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroesFirebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroesPeter Friese
 
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift LeedsPeter Friese
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in SwiftPeter Friese
 
Firebase for Apple Developers
Firebase for Apple DevelopersFirebase for Apple Developers
Firebase for Apple DevelopersPeter Friese
 
Building Apps with SwiftUI and Firebase
Building Apps with SwiftUI and FirebaseBuilding Apps with SwiftUI and Firebase
Building Apps with SwiftUI and FirebasePeter Friese
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebasePeter Friese
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebasePeter Friese
 
6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase Auth6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase AuthPeter Friese
 
Five Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase AuthFive Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase AuthPeter Friese
 
Building High-Quality Apps for Google Assistant
Building High-Quality Apps for Google AssistantBuilding High-Quality Apps for Google Assistant
Building High-Quality Apps for Google AssistantPeter Friese
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google Peter Friese
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on GoogleBuilding Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on GooglePeter Friese
 
What's new in Android Wear 2.0
What's new in Android Wear 2.0What's new in Android Wear 2.0
What's new in Android Wear 2.0Peter Friese
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android WearPeter Friese
 
Google+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and AndroidGoogle+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and AndroidPeter Friese
 
Cross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-InCross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-InPeter Friese
 
Bring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with RobolectricBring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with RobolectricPeter Friese
 

Mehr von Peter Friese (20)

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Firebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopFirebase & SwiftUI Workshop
Firebase & SwiftUI Workshop
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Firebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroesFirebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroes
 
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
 
Firebase for Apple Developers
Firebase for Apple DevelopersFirebase for Apple Developers
Firebase for Apple Developers
 
Building Apps with SwiftUI and Firebase
Building Apps with SwiftUI and FirebaseBuilding Apps with SwiftUI and Firebase
Building Apps with SwiftUI and Firebase
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and Firebase
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and Firebase
 
6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase Auth6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase Auth
 
Five Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase AuthFive Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase Auth
 
Building High-Quality Apps for Google Assistant
Building High-Quality Apps for Google AssistantBuilding High-Quality Apps for Google Assistant
Building High-Quality Apps for Google Assistant
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on GoogleBuilding Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google
 
What's new in Android Wear 2.0
What's new in Android Wear 2.0What's new in Android Wear 2.0
What's new in Android Wear 2.0
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android Wear
 
Google+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and AndroidGoogle+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and Android
 
Cross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-InCross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-In
 
Bring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with RobolectricBring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with Robolectric
 

Kürzlich hochgeladen

5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
Test Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendTest Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendArshad QA
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....ShaimaaMohamedGalal
 

Kürzlich hochgeladen (20)

5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Test Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendTest Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and Backend
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....
 

Google Play Services Rock

  • 1. Google Play Services with Xamarin Building Apps that rock
  • 3. What is Google Play Services?
  • 4. Google Play Services • Set of APIs by Google
  • 5. Google Play Services • Set of APIs by Google • Available for Devices running Gingerbread and higher
  • 6. Google Play Services • Set of APIs by Google • Available for Devices running Gingerbread and higher • Access the latest in Google technology
  • 7. Google Play Services • Set of APIs by Google • Available for Devices running Gingerbread and higher • Access the latest in Google technology • Frequent updates
  • 8. 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
  • 9. Google Play Services Library Device Drive Service Google Play Services Your App Google API Client Maps Google+ Wallet Games Services
  • 10. How to Integrate Google Play Services
  • 11. Three simple steps to success Add Google Play Services to your project Start using our APIs Profit!
  • 12. it's a little bit more complicated. Actually, Image: http://en.wikipedia.org/wiki/Turf_maze
  • 13. Project setup Xamarin Studio • Create new Android project
  • 14. Project setup Xamarin Studio • Create new Android project
  • 15. Project setup Xamarin Studio • Create new Android project • Download Google Play Services
  • 16. Project setup Xamarin Studio • Create new Android project • Download Google Play Services
  • 17. Project setup Xamarin Studio • Create new Android project • Download Google Play Services • Add the NuGet component
  • 18. Project setup Xamarin Studio • Create new Android project • Download Google Play Services • Add the NuGet component
  • 19. Project setup Google Developers Console • Create a new project
  • 20. Project setup Google Developers Console • Create a new project
  • 21. Project setup Google Developers Console • Create a new project • Configure the Consent Screen
  • 22. Project setup Google Developers Console • Create a new project • Configure the Consent Screen
  • 23. Project setup Google Developers Console • Create a new project • Configure the Consent Screen • Create a Client Configuration
  • 24. Project setup $ keytool -list -storepass android Google Developers Console • Create a new project • Configure the Consent Screen • Create a Client Configuration -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
  • 25. Project setup Google Developers Console • Create a new project • Configure the Consent Screen • Create a Client Configuration
  • 26. Project setup Google Developers Console • Create a new project • Configure the Consent Screen • Create a Client Configuration • Activate APIs
  • 27. Connecting Your App with Google
  • 28. Checking for a compatible GMS version BaseActivity.cs 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; } }
  • 29. Configuring GoogleApiClient BaseActivity.cs 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 (); } }
  • 30. Configuring GoogleApiClient BaseActivity.cs 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 (); } } Add multiple APIs and Scopes
  • 31. Configuring GoogleApiClient BaseActivity.cs 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 (); } } Set up callbacks
  • 32. Connecting GoogleApiClient BaseActivity.cs protected override void OnStart () { base.OnStart (); googleApiClient.Connect(); } ! protected override void OnStop () { if (googleApiClient != null) { googleApiClient.Disconnect (); } base.OnStop (); }
  • 33. Callbacks - The Good BaseActivity.cs 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. } }
  • 34. Callbacks - The Bad BaseActivity.cs 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; } } }
  • 35. Callbacks - The Bad BaseActivity.cs 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
  • 36. A Closer Look at Some of the Services
  • 37. ?
  • 38. Record demos Reading / writing files App folders Google Drive
  • 39. Google Drive • Cloud storage powered by Google’s infrastructure
  • 40. Google Drive • Cloud storage powered by Google’s infrastructure • 15 GB for free, upgrades at competitive prices
  • 41. Google Drive • Cloud storage powered by Google’s infrastructure • 15 GB for free, upgrades at competitive prices • Automatic synchronisation
  • 42. Google Drive • Cloud storage powered by Google’s infrastructure • 15 GB for free, upgrades at competitive prices • Automatic synchronisation • Android, iOS, Mac, Windows, Web
  • 43. 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)
  • 44. Google Drive - Connecting BaseActivity.cs 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 (); } }
  • 45. Configuring GoogleApiClient BaseActivity.cs 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 (); } } Supported scopes: • DriveClass.ScopeFile (drive.file) • DriveClass.ScopeAppFolder (drive.appfolder) More about scopes: https://developers.google.com/drive/web/scopes
  • 47. ListFilesActivity.cs 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) { Called for paging list of files.
  • 48. public class ListFilesActivity : BaseDemoActivity, IResultCallback { private bool hasMore; private string nextPageToken; private ListView listView; protected ResultsAdapter resultsAdapter; ListFilesActivity.cs 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() { General view setup
  • 49. 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) => { ListFilesActivity.cs 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(); } Fetch next page when scrolling
  • 50. listView.Scroll += (object sender, AbsListView.ScrollEventArgs e) => { if (nextPageToken != null && e.FirstVisibleItem + e.VisibleItemCount + 5 < e.TotalItemCount) { RetrieveNextPage(); } } ; ListFilesActivity.cs } 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) Clear adapter on stop.
  • 51. } protected override void OnStop() { base.OnStop(); resultsAdapter.Clear(); ListFilesActivity.cs } public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); RetrieveNextPage(); } private void RetrieveNextPage () { if (!hasMore) { return; Fetch data as soon as we’re connected } var query = new QueryClass.Builder () .SetPageToken (nextPageToken) .Build (); DriveClass.DriveApi.Query (GoogleApiClient, query).SetResultCallback (this); } public void OnResult (Java.Lang.Object result) {
  • 52. } public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); RetrieveNextPage(); ListFilesActivity.cs } 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."); } Use token to keep track of position
  • 53. return; } var query = new QueryClass.Builder () .SetPageToken (nextPageToken) .Build (); ListFilesActivity.cs 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; } } } Fetch results from metadata buffer
  • 54. Pick Files & Folders Google Drive
  • 55. 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);
  • 56. PickFileWithOpenerActivity.cs public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); var intentSender = DriveClass.DriveApi Create new file picker .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);
  • 57. PickFileWithOpenerActivity.cs public override void OnConnected (Bundle connectionHint) { base.OnConnected (connectionHint); var intentSender = DriveClass.DriveApi Mime types to show in picker .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);
  • 58. 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); } } Start picker intent protected override void OnActivityResult (int requestCode, Result resultCode, Intent data) { base.OnActivityResult (requestCode, resultCode, data);
  • 59. .Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); PickFileWithOpenerActivity.} cs } 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); } }
  • 60. .Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); PickFileWithOpenerActivity.} cs } 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); } } Returning from picker?
  • 61. .Build (GoogleApiClient); try { StartIntentSenderForResult (intentSender, RequestCodeOpener, null, 0, 0, 0); } catch (IntentSender.SendIntentException ex) { Log.Warn (logTag, "Unable to send intent", ex); PickFileWithOpenerActivity.} cs } 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); } } Get metadata from extras
  • 62. Activity Recogniton Image by Martijn van Dalen https://www.flickr.com/photos/martijnvandalen/4591360652
  • 64. Activity Recognition - Connecting BaseActivity.cs protected override void OnCreate (Bundle savedInstanceState) { base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (ActivityRecognition.Api) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); } }
  • 65. Activity Recognition - Connecting BaseActivity.cs protected override void OnCreate (Bundle savedInstanceState) { base.OnCreate (savedInstanceState); if (googleApiClient == null) { googleApiClient = new GoogleApiClientBuilder (this) .AddApi (ActivityRecognition.Api) .AddConnectionCallbacks (this) .AddOnConnectionFailedListener (this) .Build (); } }
  • 66. Activity Recognition - Permissions AndroidManifest.xml <?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>
  • 67. Activity Recognition - Permissions AndroidManifest.xml <?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>
  • 68. Starting Activity Recognition MainActivity.cs 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); }
  • 69. Starting Activity Recognition MainActivity.cs 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); }
  • 70. Starting Activity Recognition MainActivity.cs 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); }
  • 71. Receiving Activity Updates ActivityRecognitionIntentService.cs [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) { } } }
  • 72. Receiving Activity Updates ActivityRecognitionIntentService.cs [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) { } } }
  • 73. Receiving Activity Updates ActivityRecognitionIntentService.cs [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) { } } }
  • 74. Receiving Activity Updates ActivityRecognitionIntentService.cs [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) { } } }
  • 75. Receiving Activity Updates ActivityRecognitionIntentService.cs [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) { } } }
  • 76. Maps Image: Wikipedia http://bit.ly/10L5SC1
  • 77. Maps Setup Google Developers Console • Obtaining a Maps Key
  • 78. Maps Key and Permissions AndroidManifest.xml <?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
  • 79. Maps Key and Permissions AndroidManifest.xml <?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 Maps API Key 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
  • 80. Maps Key and Permissions AndroidManifest.xml <?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 GMS Version android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission
  • 81. android:versionName=“1.0" package="com.google.xamarin.sample.GoogleMapsAndroidDemos"> Maps Key and Permissions AndroidManifest.xml <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>
  • 83. Layout StreetViewActivity.axml <?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>
  • 84. Layout StreetViewActivity.axml <?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>
  • 85. StreetView - Set Position StreetViewActivity.cs 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)); }
  • 86. StreetView - Set Position StreetViewActivity.cs 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)); }
  • 87. StreetView - Set Position StreetViewActivity.cs 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)); }
  • 88. StreetView - Move Camera StreetViewActivity.cs 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)); }
  • 89. StreetView - Move Camera StreetViewActivity.cs 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)); }
  • 90. StreetView - Move Camera StreetViewActivity.cs 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)); } Customizing user-controlled functionality: • PanningGesturesEnabled • UserNavigationEnabled • ZoomGesturesEnabled • StreetNamesEnabled
  • 92. Layout StreetViewActivity.axml <?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>
  • 93. Layout StreetViewActivity.axml <?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>
  • 94. Indoor Maps IndoorMapsViewActivity.cs 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); } } }
  • 95. Indoor Maps IndoorMapsViewActivity.cs 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); } } }
  • 96. Indoor Maps IndoorMapsViewActivity.cs 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); } } }
  • 98. Google+ • Powerful identity provider
  • 99. Google+ • Powerful identity provider • Over the Air Installs (OTA)
  • 100. Google+ • Powerful identity provider • Over the Air Installs (OTA) • Drive engagement via interactive posts
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107. No tap required, log-in will happen automatically!
  • 108.
  • 109.
  • 110. Google+ OTA • Use Google+ Sign-in button
  • 111. Google+ OTA • Use Google+ Sign-in button • Set App package name on the button
  • 112. Google+ OTA • Use Google+ Sign-in button • Set App package name on the button • Use the same scopes on web and in the app
  • 113. 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
  • 114. 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
  • 117. Permissions AndroidManifest.xml <?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>
  • 118. Permissions AndroidManifest.xml <?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>
  • 119. Create Interactive Posts MainActivity.cs 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;
  • 120. Create Interactive Posts MainActivity.cs 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;
  • 121. Create Interactive Posts const int RequestCodeInterActivePost = 1; var callToActionUrl = Android.Net.Uri.Parse ( MainActivity.cs 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);
  • 122. var callToActionDeepLinkId = GetString (Resource.String.plus_example_deep_link_id) + action; Create Interactive Posts var intent = new PlusShare.Builder (this) .AddCallToAction ( LabelViewItem, callToActionUrl, callToActionDeepLinkId) MainActivity.cs .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);
  • 123. Parse Deep Links ParseDeepLinkActivity.cs [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); } }
  • 124. Parse Deep Links ParseDeepLinkActivity.cs [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); } }
  • 125. Parse Deep Links ParseDeepLinkActivity.cs [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);
  • 126. base.OnCreate (bundle); Parse Deep Links var deepLinkId = PlusShare.GetDeepLinkId (Intent); var target = ProcessDeepLinkId (deepLinkId); if (target != null) { StartActivity (target); ParseDeepLinkActivity.cs } } 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; } }
  • 127. Recap
  • 128. ?
  • 129. Google Play Services https://developer.android.com/google/play-services/
  • 130. Q & A
  • 131. 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
  • 132. Thank you! +PeterFriese @ #Xamarin+Google