diff --git a/docs/media/android_project_overview.jpg b/docs/media/android_project_overview.jpg new file mode 100644 index 0000000000..d741cae5ff Binary files /dev/null and b/docs/media/android_project_overview.jpg differ diff --git a/docs/media/android_screenshot_main_ui.png b/docs/media/android_screenshot_main_ui.png new file mode 100644 index 0000000000..4962d8bea1 Binary files /dev/null and b/docs/media/android_screenshot_main_ui.png differ diff --git a/docs/media/android_screenshot_navbar.png b/docs/media/android_screenshot_navbar.png new file mode 100644 index 0000000000..7ba64ce8e8 Binary files /dev/null and b/docs/media/android_screenshot_navbar.png differ diff --git a/docs/media/android_screenshot_settings.png b/docs/media/android_screenshot_settings.png new file mode 100644 index 0000000000..92fee556fc Binary files /dev/null and b/docs/media/android_screenshot_settings.png differ diff --git a/docs/media/android_studio_credentials_debug.png b/docs/media/android_studio_credentials_debug.png new file mode 100644 index 0000000000..e4ba2fdb30 Binary files /dev/null and b/docs/media/android_studio_credentials_debug.png differ diff --git a/solutions/android/VirtualAssistantClient/app/src/debug/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/configuration/DefaultConfiguration.java b/solutions/android/VirtualAssistantClient/app/src/debug/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/configuration/DefaultConfiguration.java index 190dbfc847..6dd923e39e 100644 --- a/solutions/android/VirtualAssistantClient/app/src/debug/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/configuration/DefaultConfiguration.java +++ b/solutions/android/VirtualAssistantClient/app/src/debug/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/configuration/DefaultConfiguration.java @@ -3,17 +3,16 @@ public class DefaultConfiguration { // Replace below with your own subscription key - public static final String SPEECH_SERVICE_SUBSCRIPTION_KEY = "YOUR_KEY_HERE";//TODO + public static final String SPEECH_SERVICE_SUBSCRIPTION_KEY = "ccbc14f1bb2a476187a831c1dcc6a3fc"; - public static final String DIRECT_LINE_SPEECH_SECRET_KEY = "YOUR_DIRECTLINE_SPEECH_KEY_HERE";//TODO + // Ryan's latest Bot : calendar, email, to-do, and point of interest skills + public static final String DIRECT_LINE_SPEECH_SECRET_KEY = "aeRqQEhGPaM.cwA.TkU.3pFGkwEK7X8m4IxFujVOhURSByvHEuVVi1Na_u8yBHI";//aeRqQEhGPaM.cwA.TkU.3pFGkwEK7X8m4IxFujVOhURSByvHEuVVi1Na_u8yBHI - public static final String SPEECH_SERVICE_SUBSCRIPTION_KEY_REGION = "westus2";//TODO + // Replace below with your own service region (e.g., "westus"). + public static final String SPEECH_SERVICE_SUBSCRIPTION_KEY_REGION = "westus2"; public static final String USER_NAME = "User"; - public static final String USER_FROM_ID = "YOUR_USER_FROM_ID_HERE";//TODO + public static final String USER_FROM_ID = "625d6da6-1862-452b-b0ba-f2fa1ea5a7c5"; - public static final String LOCALE = "en-us"; - - public static final String GEOLOCATION_LAT = "47.617305"; - public static final String GEOLOCATION_LON = "-122.192217"; + public static final String LOCALE = "en-us"; //note: attempted to use Locale.getDefault().toString(); however it gives a string the bot doesn't recognize } \ No newline at end of file diff --git a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/main/MainActivity.java b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/main/MainActivity.java index 1c26206f16..e6489f98ed 100644 --- a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/main/MainActivity.java +++ b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/main/MainActivity.java @@ -37,12 +37,12 @@ import com.microsoft.bot.builder.solutions.directlinespeech.model.Configuration; import com.microsoft.bot.builder.solutions.virtualassistant.R; import com.microsoft.bot.builder.solutions.virtualassistant.activities.BaseActivity; -import com.microsoft.bot.builder.solutions.virtualassistant.activities.settings.SettingsActivity; import com.microsoft.bot.builder.solutions.virtualassistant.activities.main.actionslist.ActionsAdapter; import com.microsoft.bot.builder.solutions.virtualassistant.activities.main.actionslist.ActionsViewholder; import com.microsoft.bot.builder.solutions.virtualassistant.activities.main.chatlist.ChatAdapter; -import com.microsoft.bot.builder.solutions.virtualassistant.activities.main.chatlist.ViewholderBot; import com.microsoft.bot.builder.solutions.virtualassistant.activities.main.chatlist.ItemOffsetDecoration; +import com.microsoft.bot.builder.solutions.virtualassistant.activities.main.chatlist.ViewholderBot; +import com.microsoft.bot.builder.solutions.virtualassistant.activities.settings.SettingsActivity; import com.microsoft.bot.builder.solutions.virtualassistant.assistant.VoiceInteractionActivity; import org.greenrobot.eventbus.EventBus; @@ -235,10 +235,18 @@ protected void permissionGranted(String manifestPermission) { @Override protected void serviceConnected() { - // this code is triggered when a user launches the app a second+ time and the app has permission + // At this point, speechServiceBinder should not be null. + // this code is triggered after the service is bound. + // Binding is started in onStart(), so expect this callback to trigger after onStart() if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { initializeAndConnect(); } + + try { + speechServiceBinder.startLocationUpdates(); + } catch (RemoteException e) { + e.printStackTrace(); + } } @Override @@ -254,20 +262,6 @@ public boolean onNavigationItemSelected(MenuItem item) { case R.id.nav_menu_reset_bot: speechServiceBinder.resetBot(); break; - case R.id.nav_menu_location: - String json = speechServiceBinder.getConfiguration(); - Configuration configuration = gson.fromJson(json, new TypeToken(){}.getType()); - speechServiceBinder.sendLocationEvent(configuration.geolat, configuration.geolon); - break; - case R.id.nav_menu_welcome_req: - speechServiceBinder.requestWelcomeCard(); - break; - case R.id.nav_menu_emulate_activity_msg: - // {"attachmentLayout": "carousel","attachments": [{"content": {XXXX PASTE ADAPTIVE CARD HERE XXXX},"contentType": "application/vnd.microsoft.card.adaptive"}],"channelData": {"conversationalAiData": {"requestInfo": {"interactionId": "b9ad8f12-e459-4a73-a542-919224e83b0a","requestType": 0,"version": "0.2"}}},"channelId": "directlinespeech","conversation": {"id": "490b89e7-ab99-4ec6-b0c8-4cc612d5e4ce","isGroup": false},"entities": [],"from": {"id": "vakonadj"},"id": "a27c2f8da5a845a3942f6a880562114f","inputHint": "expectingInput","recipient": {"id": "490b89e7-ab99-4ec6-b0c8-4cc612d5e4ce|0000"},"replyToId": "c3174265-3b0a-49a1-bdb1-e55c477b8c36","serviceUrl": "PersistentConnection","speak": "Injected Test Message","text": "Injected Test Message","timestamp": "2019-04-25T18:17:12.3964213+00:00","type": "message"} - final String testJson = - "{\"attachmentLayout\": \"carousel\",\"attachments\": [{\"content\": {\"body\": [{\"items\": [{\"columns\": [{\"items\": [{\"color\": \"accent\",\"id\": \"Name\",\"separation\": \"none\",\"size\": \"large\",\"spacing\": \"none\",\"text\": \"City Center Plaza\",\"type\": \"TextBlock\",\"weight\": \"bolder\"}, {\"id\": \"AvailableDetails\",\"isSubtle\": true,\"separation\": \"none\",\"spacing\": \"none\",\"text\": \"Parking Garage\",\"type\": \"TextBlock\"}, {\"color\": \"dark\",\"id\": \"Address\",\"isSubtle\": true,\"separation\": \"none\",\"spacing\": \"none\",\"text\": \"474 108th Avenue Northeast, Bellevue, West Bellevue\",\"type\": \"TextBlock\",\"wrap\": true}, {\"color\": \"dark\",\"id\": \"Hours\",\"isSubtle\": true,\"separation\": \"none\",\"spacing\": \"none\",\"text\": \"\",\"type\": \"TextBlock\",\"wrap\": true}],\"type\": \"Column\",\"verticalContentAlignment\": \"Center\",\"width\": \"90\"}],\"type\": \"ColumnSet\"}],\"type\": \"Container\"}, {\"items\": [{\"id\": \"Image\",\"type\": \"Image\",\"url\": \"https://atlas.microsoft.com/map/static/png?api-version=1.0&layer=basic&style=main&zoom=15¢er=-122.19475,47.61426&width=512&height=512&subscription-key=X0_-LfxI-A-iXxsBGb62ZZJfdfr5mbw9LiG8-cL6quM\"}],\"separator\": true,\"type\": \"Container\"}],\"id\": \"PointOfInterestViewCard\",\"speak\": \"City Center Plaza at 474 108th Avenue Northeast\",\"type\": \"AdaptiveCard\",\"version\": \"1.0\"},\"contentType\": \"application/vnd.microsoft.card.adaptive\"}, {\"content\": {\"body\": [{\"type\": \"TextBlock\",\"size\": \"Medium\",\"weight\": \"Bolder\",\"text\": \"Publish Adaptive Card schema\"},{\"type\": \"ColumnSet\",\"columns\": [{\"type\": \"Column\",\"items\": [{\"type\": \"Image\",\"style\": \"Person\",\"url\": \"https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg\",\"size\": \"Small\"}],\"width\": \"auto\"},{\"type\": \"Column\",\"items\": [{\"type\": \"TextBlock\",\"weight\": \"Bolder\",\"text\": \"Matt Hidinger\",\"wrap\": true},{\"type\": \"TextBlock\",\"spacing\": \"None\",\"text\": \"Created {{DATE(2017-02-14T06:08:39Z, SHORT)}}\",\"isSubtle\": true,\"wrap\": true}],\"width\": \"stretch\"}]},{\"type\": \"TextBlock\",\"text\": \"Now that we have defined the main rules and features of the format, we need to produce a schema and publish it to GitHub. The schema will be the starting point of our reference documentation.\",\"wrap\": true},{\"type\": \"FactSet\",\"facts\": [{\"title\": \"Board:\",\"value\": \"Adaptive Card\"},{\"title\": \"List:\",\"value\": \"Backlog\"},{\"title\": \"Assigned to:\",\"value\": \"Matt Hidinger\"},{\"title\": \"Due date:\",\"value\": \"Not set\"}]}],\"actions\": [{\"type\": \"Action.ShowCard\",\"title\": \"Set due date\",\"card\": {\"type\": \"AdaptiveCard\",\"body\": [{\"type\": \"Input.Date\",\"id\": \"dueDate\"}],\"actions\": [{\"type\": \"Action.Submit\",\"title\": \"OK\"}]}},{\"type\": \"Action.ShowCard\",\"title\": \"Comment\",\"card\": {\"type\": \"AdaptiveCard\",\"body\": [{\"type\": \"Input.Text\",\"id\": \"comment\",\"placeholder\": \"Enter your comment\",\"isMultiline\": true}],\"actions\": [{\"type\": \"Action.Submit\",\"title\": \"OK\"}]}}],\"id\": \"PointOfInterestViewCard\",\"speak\": \"Plaza Center at 10901 NE 9th St\",\"type\": \"AdaptiveCard\",\"version\": \"1.0\"},\"contentType\": \"application/vnd.microsoft.card.adaptive\"}],\"channelData\": {\"conversationalAiData\": {\"requestInfo\": {\"interactionId\": \"b9ad8f12-e459-4a73-a542-919224e83b0a\",\"requestType\": 0,\"version\": \"0.2\"}}},\"channelId\": \"directlinespeech\",\"conversation\": {\"id\": \"490b89e7-ab99-4ec6-b0c8-4cc612d5e4ce\",\"isGroup\": false},\"entities\": [],\"from\": {\"id\": \"vakonadj\"},\"id\": \"a27c2f8da5a845a3942f6a880562114f\",\"inputHint\": \"expectingInput\",\"recipient\": {\"id\": \"490b89e7-ab99-4ec6-b0c8-4cc612d5e4ce|0000\"},\"replyToId\": \"c3174265-3b0a-49a1-bdb1-e55c477b8c36\",\"serviceUrl\": \"PersistentConnection\",\"speak\": \"What do you think of these?,1 - City Center Plaza at 474 108th Avenue Northeast.,2 - Plaza Center at 10901 NE 9th St.\",\"text\": \"What do you think of these?\",\"timestamp\": \"2019-04-25T18:17:12.3964213+00:00\",\"type\": \"message\"}"; - speechServiceBinder.injectReceivedActivity(testJson); - break; case R.id.nav_menu_show_assistant_settings: startActivity(new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS)); break; diff --git a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/settings/SettingsActivity.java b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/settings/SettingsActivity.java index 6c76aeaad2..68c4876daf 100644 --- a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/settings/SettingsActivity.java +++ b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/activities/settings/SettingsActivity.java @@ -3,8 +3,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.os.RemoteException; import android.support.annotation.Nullable; import android.support.design.widget.TextInputEditText; @@ -16,7 +14,6 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.microsoft.bot.builder.solutions.directlinespeech.ConfigurationManager; import com.microsoft.bot.builder.solutions.directlinespeech.model.Configuration; import com.microsoft.bot.builder.solutions.virtualassistant.R; import com.microsoft.bot.builder.solutions.virtualassistant.activities.BaseActivity; @@ -40,8 +37,6 @@ public class SettingsActivity extends BaseActivity { @BindView(R.id.bot_id) TextInputEditText botId; @BindView(R.id.user_id) TextInputEditText userId; @BindView(R.id.locale) TextInputEditText locale; - @BindView(R.id.geolocation_lat) TextInputEditText locationLat; - @BindView(R.id.geolocation_lon) TextInputEditText locationLon; @BindView(R.id.history_linecount) TextInputEditText historyLinecount; @BindView(R.id.spinner_timezone) Spinner spinnerTimezone; @@ -88,7 +83,7 @@ protected void serviceConnected() { showConfiguration(); } - @OnEditorAction({R.id.history_linecount, R.id.service_key, R.id.service_region, R.id.bot_id, R.id.user_id, R.id.locale, R.id.geolocation_lat, R.id.geolocation_lon}) + @OnEditorAction({R.id.history_linecount, R.id.service_key, R.id.service_region, R.id.bot_id, R.id.user_id, R.id.locale}) boolean onEditorAction(int actionId, KeyEvent key){ boolean handled = false; if (actionId == EditorInfo.IME_ACTION_SEND || (key != null && key.getKeyCode() == KeyEvent.KEYCODE_ENTER)) { @@ -140,8 +135,6 @@ private void showConfiguration(){ botId.setText(configuration.botId); userId.setText(configuration.userId); locale.setText(configuration.locale); - locationLat.setText(configuration.geolat); - locationLon.setText(configuration.geolon); int iHistoryLinecount = configuration.historyLinecount==null?1:configuration.historyLinecount; String historyLineCount = String.valueOf(iHistoryLinecount); @@ -160,8 +153,6 @@ private void saveConfiguration(){ configuration.botId = botId.getText().toString(); configuration.userId = userId.getText().toString(); configuration.locale = locale.getText().toString(); - configuration.geolat = locationLat.getText().toString(); - configuration.geolon = locationLon.getText().toString(); // history linecount configuration.historyLinecount = Integer.valueOf(historyLinecount.getText().toString()); diff --git a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/LocationProvider.java b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/LocationProvider.java index bf257346b7..ac55ae7e46 100644 --- a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/LocationProvider.java +++ b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/LocationProvider.java @@ -1,9 +1,14 @@ package com.microsoft.bot.builder.solutions.virtualassistant.service; import android.Manifest; +import android.annotation.SuppressLint; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; import android.os.Looper; import android.support.v4.app.ActivityCompat; import android.util.Log; @@ -27,11 +32,12 @@ public class LocationProvider { private static final String LOGTAG = "LocationProvider"; // STATE - private boolean isGettingLocationUpdates; - private FusedLocationProviderClient fusedLocationClient; + private boolean isPlayStoreInstalled; + private FusedLocationProviderClient fusedLocationClient;// dependency on Play Store private LocationCallback locationCallback; private Context context; private LocationProviderCallback locationProviderCallback; + private LocationListener locationListener; // INTERFACE public interface LocationProviderCallback { @@ -42,50 +48,66 @@ public LocationProvider(Context context, LocationProviderCallback locationProvid this.context = context; this.locationProviderCallback = locationProviderCallback; // check if Google Play Services is installed - isGettingLocationUpdates = checkPlayServices(); + isPlayStoreInstalled = checkPlayServices(); } protected void startLocationUpdates() { - if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - - fusedLocationClient = LocationServices.getFusedLocationProviderClient(context); - - // register to receive future location updates - LocationRequest locationRequest = new LocationRequest() - .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) - .setInterval(LOCATION_UPDATE_INTERVAL) - .setSmallestDisplacement(LOCATION_UPDATE_DISTANCE); - - // Create LocationSettingsRequest object using location request - LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); - builder.addLocationRequest(locationRequest); - LocationSettingsRequest locationSettingsRequest = builder.build(); - - // Check whether location settings are satisfied - // https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient - SettingsClient settingsClient = LocationServices.getSettingsClient(context); - settingsClient.checkLocationSettings(locationSettingsRequest); - locationCallback = new LocationCallback() { - @Override - public void onLocationResult(LocationResult locationResult) { - // Inform the Bot of the location change - Location location = locationResult.getLastLocation(); + if (isPlayStoreInstalled) { + if (fusedLocationClient == null) { + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + + fusedLocationClient = LocationServices.getFusedLocationProviderClient(context); + + // register to receive future location updates + LocationRequest locationRequest = new LocationRequest() + .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY) + .setInterval(LOCATION_UPDATE_INTERVAL); + + // If the device doesn't move it prevents location callbacks - not good for dev't + boolean isDebug = ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); + if (!isDebug) locationRequest.setSmallestDisplacement(LOCATION_UPDATE_DISTANCE); + + // Create LocationSettingsRequest object using location request + LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); + builder.addLocationRequest(locationRequest); + LocationSettingsRequest locationSettingsRequest = builder.build(); + + // Check whether location settings are satisfied + // https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient + SettingsClient settingsClient = LocationServices.getSettingsClient(context); + settingsClient.checkLocationSettings(locationSettingsRequest); + locationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + // Inform the Bot of the location change + Location location = locationResult.getLastLocation(); + if (location != null && locationProviderCallback != null) { + locationProviderCallback.onLocationResult(location); + } + } + }; + + fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()); + } else { + //this will trigger the first time the app is run - just retry after getting permission + Log.i(LOGTAG, "Missing ACCESS_FINE_LOCATION permission"); + } + } else { + // an Android Activity just launched and wants the coordinates. Trigger callback. + fusedLocationClient.getLastLocation().addOnSuccessListener(location -> { if (location != null && locationProviderCallback != null) { locationProviderCallback.onLocationResult(location); } - } - }; - - fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()); + }); + } } else { - //this will trigger the first time the app is run - just retry after getting permission - Log.e(LOGTAG, "Missing ACCESS_FINE_LOCATION permission"); + locationUpdatesWithoutGoogleServices(); } } public void stopLocationUpdates(){ // stop location updates - if (isGettingLocationUpdates && fusedLocationClient != null && locationCallback != null) { + if (isPlayStoreInstalled && fusedLocationClient != null && locationCallback != null) { fusedLocationClient.removeLocationUpdates(locationCallback); } } @@ -102,4 +124,48 @@ private boolean checkPlayServices() { return true; } + private void locationUpdatesWithoutGoogleServices(){ + if (locationListener == null) { + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + + locationListener = getLocationListener(); + LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + boolean isDebug = ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); + + if (isDebug) { + // do not request minDistance when developing + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_UPDATE_INTERVAL, 0, locationListener); + } else { + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_UPDATE_INTERVAL, LOCATION_UPDATE_DISTANCE, locationListener); + } + } + } + } + + private LocationListener getLocationListener(){ + return new LocationListener() { + @Override + public void onLocationChanged(Location location) { + if (location != null && locationProviderCallback != null) { + locationProviderCallback.onLocationResult(location); + } + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + + } + + @Override + public void onProviderEnabled(String provider) { + + } + + @Override + public void onProviderDisabled(String provider) { + + } + }; + } + } diff --git a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/SpeechService.java b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/SpeechService.java index f5b78f18ac..a22984d069 100644 --- a/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/SpeechService.java +++ b/solutions/android/VirtualAssistantClient/app/src/main/java/com/microsoft/bot/builder/solutions/virtualassistant/service/SpeechService.java @@ -190,8 +190,6 @@ public void onCreate() { configuration.userName = DefaultConfiguration.USER_NAME; configuration.userId = DefaultConfiguration.USER_FROM_ID; configuration.locale = DefaultConfiguration.LOCALE; - configuration.geolat = DefaultConfiguration.GEOLOCATION_LAT; - configuration.geolon = DefaultConfiguration.GEOLOCATION_LON; configuration.historyLinecount = Integer.MAX_VALUE - 1; configurationManager.setConfiguration(configuration); } @@ -300,8 +298,6 @@ private void startForegroundService() { // Start foreground service startForeground(STOP_FOREGROUND_REMOVE, notification); - startLocationUpdates(); - Log.d(TAG_FOREGROUND_SERVICE, "startForegroundService() complete"); } diff --git a/solutions/android/VirtualAssistantClient/app/src/main/res/layout/activity_settings.xml b/solutions/android/VirtualAssistantClient/app/src/main/res/layout/activity_settings.xml index fdc980b669..a90eec0274 100644 --- a/solutions/android/VirtualAssistantClient/app/src/main/res/layout/activity_settings.xml +++ b/solutions/android/VirtualAssistantClient/app/src/main/res/layout/activity_settings.xml @@ -99,46 +99,6 @@ android:imeOptions="actionDone"/> - - - - - - - - - - - - - - - - - - Open navigation drawer Close navigation drawer - WebView UI - Send Location Event - Send Welcome Event - Disable Overlay Show Textinput Show full conversation - Inject Adaptive Card Event Restart Conversation Enable KWS Settings diff --git a/solutions/android/VirtualAssistantClient/build.gradle b/solutions/android/VirtualAssistantClient/build.gradle index 6103339801..05d4e830c2 100644 --- a/solutions/android/VirtualAssistantClient/build.gradle +++ b/solutions/android/VirtualAssistantClient/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.1' + classpath 'com.android.tools.build:gradle:3.4.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/BotConnectorActivity.java b/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/BotConnectorActivity.java index 8a416d15de..d82b34632a 100644 --- a/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/BotConnectorActivity.java +++ b/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/BotConnectorActivity.java @@ -5,6 +5,10 @@ import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; +/** + * Activity model + * see https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0#activity-object + */ public class BotConnectorActivity { @SerializedName("attachmentLayout") @@ -32,6 +36,9 @@ public void setAttachmentLayout(String attachmentLayout) { @SerializedName("conversation") @Expose private ConversationBot conversation; + @SerializedName("code") + @Expose + private String code; @SerializedName("entities") @Expose private List entities = null; @@ -47,6 +54,9 @@ public void setAttachmentLayout(String attachmentLayout) { @SerializedName("locale") @Expose private String locale; + @SerializedName("name") + @Expose + private String name; @SerializedName("recipient") @Expose private Recipient recipient; @@ -254,4 +264,20 @@ public SuggestedActions getSuggestedActions() { public void setSuggestedActions(SuggestedActions suggestedActions) { this.suggestedActions = suggestedActions; } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/InputHints.java b/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/InputHints.java index 2bf22f609e..f3a5e0ef9d 100644 --- a/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/InputHints.java +++ b/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/client/model/InputHints.java @@ -25,48 +25,48 @@ */ @JsonAdapter(InputHints.Adapter.class) public enum InputHints { - - ACCEPTINGINPUT("acceptingInput"), - - IGNORINGINPUT("ignoringInput"), - - EXPECTINGINPUT("expectingInput"); - - private String value; - - InputHints(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return String.valueOf(value); - } - - public static InputHints fromValue(String text) { - for (InputHints b : InputHints.values()) { - if (String.valueOf(b.value).equals(text)) { - return b; - } + + ACCEPTINGINPUT("acceptingInput"), + + IGNORINGINPUT("ignoringInput"), + + EXPECTINGINPUT("expectingInput"); + + private String value; + + InputHints(String value) { + this.value = value; } - return null; - } - public static class Adapter extends TypeAdapter { - @Override - public void write(final JsonWriter jsonWriter, final InputHints enumeration) throws IOException { - jsonWriter.value(enumeration.getValue()); + public String getValue() { + return value; } @Override - public InputHints read(final JsonReader jsonReader) throws IOException { - String value = jsonReader.nextString(); - return InputHints.fromValue(String.valueOf(value)); + public String toString() { + return String.valueOf(value); + } + + public static InputHints fromValue(String text) { + for (InputHints b : InputHints.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final InputHints enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public InputHints read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return InputHints.fromValue(String.valueOf(value)); + } } - } } diff --git a/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/com/microsoft/bot/builder/solutions/directlinespeech/model/Configuration.java b/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/com/microsoft/bot/builder/solutions/directlinespeech/model/Configuration.java index 5728035982..3c0695da9d 100644 --- a/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/com/microsoft/bot/builder/solutions/directlinespeech/model/Configuration.java +++ b/solutions/android/VirtualAssistantClient/directlinespeech/src/main/java/com/microsoft/bot/builder/solutions/directlinespeech/model/Configuration.java @@ -25,14 +25,6 @@ public class Configuration { @Expose public String locale; - @SerializedName("geolat") - @Expose - public String geolat; - - @SerializedName("geolon") - @Expose - public String geolon; - @SerializedName("user_name") @Expose public String userName; @@ -52,8 +44,6 @@ public boolean isEmpty(){ botId==null&& userId==null&& locale==null&& - geolat==null&& - geolon==null&& userName==null&& historyLinecount==null&& currentTimezone==null; diff --git a/solutions/android/VirtualAssistantClient/readme.md b/solutions/android/VirtualAssistantClient/readme.md index e0d358962e..bd51a76605 100644 --- a/solutions/android/VirtualAssistantClient/readme.md +++ b/solutions/android/VirtualAssistantClient/readme.md @@ -1,24 +1,36 @@ + # Virtual Assistant Android Client Application +## Project Overview +### Diagram +![project overview diagram](/docs/media/android_project_overview.jpg) +### Description +- The User is able to interact with the bot via widgets on the home-screen or the main UI of the app. +- Optional: Bot responses can be broadcast to additional apps - these apps don't need to implement the SpeechSdk. ## Building the Project ### Prerequisites -- [Create a Virtual Assistant](/docs/tutorials/csharp/virtualassistant.md) to setup your environment. - -### Step 1 - Credentials - -Edit `DefaultConfiguration.java` to provide the Speech Cognitive Service key, Speech Channel Secret and UserId which will be used to perform speech operations and identify the user uniquely. Note that there are two versions, one for debug and one for release build flavors. +1. [Create a Virtual Assistant](/docs/tutorials/csharp/virtualassistant.md) to setup your environment. -The [Speech enabling your Virtual Assistant](https://github.com/microsoft/botframework-solutions/blob/master/docs/howto/assistant/csharp/speechenablement.md) documentation covers the following two steps which you must perform to retrieve the Speech Cognitive Service Key and Speech Channel secret. +2. The [Speech enabling your Virtual Assistant](https://github.com/microsoft/botframework-solutions/blob/master/docs/howto/assistant/csharp/speechenablement.md) documentation covers the following two steps which you must perform to retrieve the Speech Cognitive Service Subscription Key and Speech Channel Secret Key. - [Create a Speech Cognitive Service Key](https://github.com/microsoft/botframework-solutions/blob/master/docs/howto/assistant/csharp/speechenablement.md#create-a-microsoft-speech-instance) - [Add the Speech Channel to your assistant](https://github.com/microsoft/botframework-solutions/blob/master/docs/howto/assistant/csharp/speechenablement.md#add-the-speech-channel-to-your-assistant) -The UserId is a unique identifier for all messages generated by the user, this is typically combind with [Linked Accounts](https://github.com/microsoft/botframework-solutions/blob/master/docs/howto/assistant/linkedaccounts.md). + + +### Step 1 - Credentials + +Edit `DefaultConfiguration.java` to provide the *SPEECH_SERVICE_SUBSCRIPTION_KEY*, *DIRECT_LINE_SPEECH_SECRET_KEY* and *USER_FROM_ID* which will be used to perform speech operations and identify the user uniquely. + +![Android Studio showing credentials](/docs/media/android_studio_credentials_debug.png) +**Note:** that there are two versions, one for debug and one for release build flavors. + +The USER_FROM_ID is a unique identifier for all messages generated by the user, this is typically combined with [Linked Accounts](https://github.com/microsoft/botframework-solutions/blob/master/docs/howto/assistant/linkedaccounts.md). ### Step 2 - Deploy -1. Select the desired build flavor (debug or release) +1. Select the desired build flavor (debug or release) and ensure credentials are set for the desired build flavor 2. Deploy to emulator or device ## Using the Project @@ -28,43 +40,40 @@ The UserId is a unique identifier for all messages generated by the user, this i - Slide away the service notification ### The UI -1. The Mic button is the only graphic immediately visible - press it to make a voice request. (The app will sense when you've finished speaking) - -2. The Navigation Drawer (on the left side of the screen) provides the following functionality: -- Bot Configuration -- App Configuration -- Reset Bot -- Send Location Event -- Send Welcome Event -- Inject Adaptive Card Event +#### Main UI +![Main UI](/docs/media/android_screenshot_main_ui.png) +The Mic button is the only graphic immediately visible - press it to make a voice request. (The app will sense when you've finished speaking) +#### Navigation Bar +![Navigation Bar](/docs/media/android_screenshot_navbar.png) +The Navigation Drawer (on the left side of the screen) provides the following functionality: +- Settings +- Restart Conversation - Show Assistant Settings +- Show Full Conversation - Show Textinput +#### Settings +![Navigation Bar](/docs/media/android_screenshot_settings.png) +The DefaultConfiguration (see [Credentials](#step-1---credentials)) provides the initial data for this screen. +The user may edit the data and the changes are saved and used immediately. +**Note:** user-edited data is lost when the app is uninstalled ### Functionality Overview #### Bot Configuration The data on this screen is originally read from "DefaultConfiguration.java". Changing the values on this screen will update the stored data and used immediately. NOTE: the stored data persists between app installs -#### App Configuration +#### Settings Settings that are specific to the app can be set here -#### Reset Bot +#### Restart Conversation If the bot enters a problematic state, you can reset it. -#### Send Location Event -Send a location event using the latitude and longitude values found in the "DefaultConfiguration.java" - -#### Send Welcome Event -Triggers the Bot to respond with a default "Welcome" card - -#### Inject Adaptive Card Event -To test rendering of an Adaptive Card. First, create the adaptive card Json at [Adaptive Cards Designer](https://adaptivecards.io/designer/ "Adaptive Cards Designer"). -Second, copy the generated Json into MainActivity.onNavigationItemSelected(). -Finally run the app and "inject" the adaptive card to see what it looks like when sent by the Bot. - #### Show Assistant Settings Shortcut to the Android Assistant Settings +#### Show Full Conversation +Shows the users' requests on the right side of the UI + #### Show Textinput Shows the text input field to make requests without needing to speak