summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Dickow <jjdickow@gmail.com>2014-03-20 12:30:51 -0400
committerJustin Dickow <jjdickow@gmail.com>2014-03-20 12:30:51 -0400
commit6a3c9abdec54e8cbe12df6b224cc8e7b5ac16ddb (patch)
treec001b50877a31bddd8f1efdcbd7d5443e7e1501b
parente08642ff3d5634763d3362015a08ef2aa0651b79 (diff)
downloadsmartdevicelink-6a3c9abdec54e8cbe12df6b224cc8e7b5ac16ddb.tar.gz
Added Mike's tester application (not ideal but easy to update)
Signed-off-by: Justin Dickow <jjdickow@gmail.com>
-rw-r--r--SDL_Android/LivioSdlUtilities/.classpath9
-rw-r--r--SDL_Android/LivioSdlUtilities/.project33
-rw-r--r--SDL_Android/LivioSdlUtilities/AndroidManifest.xml16
-rw-r--r--SDL_Android/LivioSdlUtilities/bin/AndroidManifest.xml16
-rw-r--r--SDL_Android/LivioSdlUtilities/bin/R.txt67
-rw-r--r--SDL_Android/LivioSdlUtilities/bin/jarlist.cache3
-rw-r--r--SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-hdpi/ic_launcher.pngbin0 -> 9193 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-mdpi/ic_launcher.pngbin0 -> 5057 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-xhdpi/ic_launcher.pngbin0 -> 14068 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/BuildConfig.java6
-rw-r--r--SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/R.java144
-rw-r--r--SDL_Android/LivioSdlUtilities/proguard-project.txt20
-rw-r--r--SDL_Android/LivioSdlUtilities/project.properties16
-rw-r--r--SDL_Android/LivioSdlUtilities/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/res/drawable/arrow_left.pngbin0 -> 3201 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/res/drawable/arrow_right.pngbin0 -> 3238 bytes
-rw-r--r--SDL_Android/LivioSdlUtilities/res/layout/json_flipper_dialog.xml36
-rw-r--r--SDL_Android/LivioSdlUtilities/res/layout/listview.xml8
-rw-r--r--SDL_Android/LivioSdlUtilities/res/layout/sdl_message_listview_row.xml28
-rw-r--r--SDL_Android/LivioSdlUtilities/res/layout/simple_listview_with_image.xml20
-rw-r--r--SDL_Android/LivioSdlUtilities/res/layout/textview.xml13
-rw-r--r--SDL_Android/LivioSdlUtilities/res/values-v11/styles.xml11
-rw-r--r--SDL_Android/LivioSdlUtilities/res/values-v14/styles.xml12
-rw-r--r--SDL_Android/LivioSdlUtilities/res/values/strings.xml86
-rw-r--r--SDL_Android/LivioSdlUtilities/res/values/styles.xml20
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/IpAddress.java36
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlConstants.java101
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlImageItem.java68
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlLogMessage.java124
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlRequestFactory.java609
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseFactory.java206
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseTracker.java109
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlService.java1131
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlImageAdapter.java59
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlMessageAdapter.java94
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseAlertDialog.java156
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseImageListDialog.java22
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseMultipleListViewDialog.java66
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseOkCancelDialog.java61
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseSingleListViewDialog.java49
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ImageListDialog.java35
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/IndeterminateProgressDialog.java16
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/JsonFlipperDialog.java95
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ListViewDialog.java21
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/MultipleListViewDialog.java32
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/SingleJsonDialog.java23
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewAlertDialog.java31
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewOkCancelDialog.java48
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumClickListener.java11
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumComparator.java20
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlButton.java234
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlCommand.java224
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlImageType.java92
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlInteractionMode.java78
-rwxr-xr-xSDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlLanguage.java165
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlSpeechCapability.java97
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTextAlignment.java81
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTransportType.java36
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlUpdateMode.java93
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlVehicleData.java54
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/CommandButton.java89
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuItem.java85
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuManager.java334
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/SubmenuButton.java109
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/AndroidUtils.java158
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/ApplicationPreferences.java188
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Counter.java47
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/DownCounter.java26
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/MathUtils.java21
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/SdlUtils.java254
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/StringUtils.java66
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Timeout.java120
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/UpCounter.java26
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/WifiUtils.java61
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/MinMaxInputFilter.java53
-rw-r--r--SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/SeekBarCalculator.java104
-rw-r--r--SDL_Android/LivioTesterApp/.classpath9
-rw-r--r--SDL_Android/LivioTesterApp/.project33
-rw-r--r--SDL_Android/LivioTesterApp/AndroidManifest.xml49
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/css/style.css9
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/AddCommand.html50
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/AddSubmenu.html33
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/Alert.html46
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/ChangeRegistration.html167
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/CreateInteractionChoiceSet.html43
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteCommand.html29
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteFile.html28
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteInteractionChoiceSet.html29
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteSubmenu.html28
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/GetDTCs.html28
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/ListFiles.html22
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/PerformInteraction.html85
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/PutFile.html80
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/ReadDIDs.html35
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/ScrollableMessage.html43
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/SetAppIcon.html29
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/SetMediaClockTimer.html71
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/Show.html71
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/Slider.html54
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/Speak.html66
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/SubscribeToButtons.html72
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/UnsubscribeFromButtons.html72
-rw-r--r--SDL_Android/LivioTesterApp/assets/help_docs/html/index.html43
-rw-r--r--SDL_Android/LivioTesterApp/bin/AndroidManifest.xml46
-rw-r--r--SDL_Android/LivioTesterApp/bin/LivioSdlTester.apkbin0 -> 562467 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/R.txt214
-rw-r--r--SDL_Android/LivioTesterApp/bin/classes.dexbin0 -> 1787080 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/jarlist.cache3
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable-hdpi/ic_launcher.pngbin0 -> 6946 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable-ldpi/ic_launcher.pngbin0 -> 3256 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable-mdpi/ic_launcher.pngbin0 -> 4711 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable-xhdpi/ic_launcher.pngbin0 -> 11545 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add.pngbin0 -> 578 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add_to_favorites.pngbin0 -> 5900 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/anchor.pngbin0 -> 8289 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/game_pad.pngbin0 -> 2315 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_close.pngbin0 -> 1653 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_launcher.pngbin0 -> 3256 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove.pngbin0 -> 254 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove_from_favorites.pngbin0 -> 5815 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/rocket.pngbin0 -> 8132 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/select_an_image.pngbin0 -> 1236 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/undo.pngbin0 -> 3997 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_in.pngbin0 -> 5863 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_out.pngbin0 -> 5460 bytes
-rw-r--r--SDL_Android/LivioTesterApp/bin/resources.ap_bin0 -> 118438 bytes
-rw-r--r--SDL_Android/LivioTesterApp/gen/com/livio/sdl/R.java87
-rw-r--r--SDL_Android/LivioTesterApp/gen/com/livio/sdltester/BuildConfig.java6
-rw-r--r--SDL_Android/LivioTesterApp/gen/com/livio/sdltester/R.java340
-rw-r--r--SDL_Android/LivioTesterApp/proguard-project.txt20
-rw-r--r--SDL_Android/LivioTesterApp/project.properties16
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable-hdpi/ic_launcher.pngbin0 -> 7004 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable-ldpi/ic_launcher.pngbin0 -> 3314 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable-mdpi/ic_launcher.pngbin0 -> 4769 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable-xhdpi/ic_launcher.pngbin0 -> 11603 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/add.pngbin0 -> 1375 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/add_to_favorites.pngbin0 -> 9704 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/anchor.pngbin0 -> 13779 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/game_pad.pngbin0 -> 3626 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/ic_close.pngbin0 -> 1469 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/ic_launcher.pngbin0 -> 3314 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/remove.pngbin0 -> 517 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/remove_from_favorites.pngbin0 -> 9498 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/rocket.pngbin0 -> 12514 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/select_an_image.pngbin0 -> 2665 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/undo.pngbin0 -> 6599 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/zoom_in.pngbin0 -> 9860 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/drawable/zoom_out.pngbin0 -> 9036 bytes
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/activity_help.xml6
-rwxr-xr-xSDL_Android/LivioTesterApp/res/layout/add_command.xml57
-rwxr-xr-xSDL_Android/LivioTesterApp/res/layout/add_submenu.xml17
-rwxr-xr-xSDL_Android/LivioTesterApp/res/layout/alert.xml61
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/change_registration.xml34
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/choice_set_item.xml50
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/create_choice_interaction_set.xml23
-rwxr-xr-xSDL_Android/LivioTesterApp/res/layout/get_dtcs.xml17
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/main.xml21
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/offline_mode.xml8
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/perform_interaction.xml58
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/put_file.xml40
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/read_dids.xml23
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/scrollable_message.xml40
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/sdl_connection.xml26
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/set_media_clock_timer.xml64
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/show.xml167
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/slider.xml58
-rw-r--r--SDL_Android/LivioTesterApp/res/layout/speak.xml22
-rw-r--r--SDL_Android/LivioTesterApp/res/menu/menu_help.xml6
-rw-r--r--SDL_Android/LivioTesterApp/res/menu/menu_main.xml9
-rw-r--r--SDL_Android/LivioTesterApp/res/values-sw600dp/dimens.xml8
-rw-r--r--SDL_Android/LivioTesterApp/res/values-sw720dp-land/dimens.xml9
-rw-r--r--SDL_Android/LivioTesterApp/res/values-v11/styles.xml11
-rw-r--r--SDL_Android/LivioTesterApp/res/values-v14/styles.xml12
-rw-r--r--SDL_Android/LivioTesterApp/res/values/dimens.xml7
-rw-r--r--SDL_Android/LivioTesterApp/res/values/strings.xml55
-rw-r--r--SDL_Android/LivioTesterApp/res/values/styles.xml27
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/HelpActivity.java133
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/LivioSdlTesterPreferences.java79
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/MainActivity.java1276
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/SdlTesterImageResource.java56
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddCommandDialog.java121
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddSubMenuDialog.java55
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonSubscriptionDialog.java60
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonUnsubscriptionDialog.java60
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChangeRegistrationDialog.java55
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChoiceItemDialog.java105
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/CreateInteractionChoiceSetDialog.java136
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteCommandDialog.java51
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteFileDialog.java65
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteInteractionDialog.java45
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteSubmenuDialog.java48
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/GetDtcsDialog.java54
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PerformInteractionDialog.java181
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PutFileDialog.java172
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ReadDidsDialog.java60
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ScrollableMessageDialog.java103
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlAlertDialog.java137
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlConnectionDialog.java61
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetAppIconDialog.java45
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetMediaClockTimerDialog.java112
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ShowDialog.java186
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SliderDialog.java155
-rw-r--r--SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SpeakDialog.java67
205 files changed, 13352 insertions, 0 deletions
diff --git a/SDL_Android/LivioSdlUtilities/.classpath b/SDL_Android/LivioSdlUtilities/.classpath
new file mode 100644
index 000000000..51769745b
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/SDL_Android/LivioSdlUtilities/.project b/SDL_Android/LivioSdlUtilities/.project
new file mode 100644
index 000000000..b26cca7c3
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>LivioSdlUtilities</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/SDL_Android/LivioSdlUtilities/AndroidManifest.xml b/SDL_Android/LivioSdlUtilities/AndroidManifest.xml
new file mode 100644
index 000000000..cc4968ff5
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.livio.sdl"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="18" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme" >
+ </application>
+
+</manifest>
diff --git a/SDL_Android/LivioSdlUtilities/bin/AndroidManifest.xml b/SDL_Android/LivioSdlUtilities/bin/AndroidManifest.xml
new file mode 100644
index 000000000..46682414e
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/bin/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.livio.sdl"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="18" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme" >
+ </application>
+
+</manifest>
diff --git a/SDL_Android/LivioSdlUtilities/bin/R.txt b/SDL_Android/LivioSdlUtilities/bin/R.txt
new file mode 100644
index 000000000..269cbdb5a
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/bin/R.txt
@@ -0,0 +1,67 @@
+int drawable ic_launcher 0x7f020000
+int id iv_rowImage 0x7f060004
+int id listView 0x7f060000
+int id textview 0x7f060006
+int id tv_messageDetail 0x7f060003
+int id tv_messageName 0x7f060001
+int id tv_rowText 0x7f060005
+int id tv_timestamp 0x7f060002
+int layout listview 0x7f030000
+int layout sdl_message_listview_row 0x7f030001
+int layout simple_listview_with_image 0x7f030002
+int layout textview 0x7f030003
+int string add_command_command_name 0x7f040004
+int string add_command_image_type 0x7f040007
+int string add_command_parent_menu 0x7f040006
+int string add_command_vr_keyword 0x7f040005
+int string alert_duration_ms 0x7f040023
+int string alert_duration_s 0x7f040022
+int string alert_line1 0x7f04001e
+int string alert_line2 0x7f04001f
+int string alert_line3 0x7f040020
+int string alert_sound_enabled 0x7f040021
+int string app_name 0x7f040000
+int string choice_name 0x7f040018
+int string clock 0x7f04002d
+int string clock_hrs 0x7f04002e
+int string clock_mins 0x7f04002f
+int string clock_secs 0x7f040030
+int string colon 0x7f040031
+int string did_location 0x7f04000c
+int string ecu_name 0x7f04000b
+int string hmi_language 0x7f04001c
+int string item_number 0x7f040016
+int string item_number1 0x7f040017
+int string language 0x7f04001b
+int string max_choices 0x7f040015
+int string media 0x7f04002c
+int string media_clock_timer_mode 0x7f040032
+int string media_track 0x7f04002b
+int string metadata_line1 0x7f040025
+int string metadata_line2 0x7f040026
+int string metadata_line3 0x7f040027
+int string metadata_line4 0x7f040028
+int string metadata_lines 0x7f040024
+int string negative_button 0x7f040003
+int string no_commands_to_delete 0x7f040008
+int string no_submenus_to_delete 0x7f040009
+int string positive_button 0x7f040002
+int string scrollable_message_clear 0x7f040014
+int string scrollable_message_hint 0x7f040013
+int string scrollable_message_text 0x7f040012
+int string sdl_disconnected 0x7f040001
+int string slider_footer 0x7f04000e
+int string slider_start_position 0x7f040010
+int string slider_ticks 0x7f04000f
+int string slider_title 0x7f04000d
+int string status_bar 0x7f04002a
+int string submenu_name 0x7f04000a
+int string text_alignment 0x7f040029
+int string text_to_speak 0x7f04001d
+int string timeout 0x7f040011
+int string units_milliseconds 0x7f040034
+int string units_seconds 0x7f040033
+int string use_artwork 0x7f04001a
+int string voice_keyword 0x7f040019
+int style AppBaseTheme 0x7f050000
+int style AppTheme 0x7f050001
diff --git a/SDL_Android/LivioSdlUtilities/bin/jarlist.cache b/SDL_Android/LivioSdlUtilities/bin/jarlist.cache
new file mode 100644
index 000000000..0565465f2
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/bin/jarlist.cache
@@ -0,0 +1,3 @@
+# cache for current jar dependency. DO NOT EDIT.
+# format is <lastModified> <length> <SHA-1> <path>
+# Encoding is UTF-8
diff --git a/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-hdpi/ic_launcher.png b/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..bcfa0581f
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-mdpi/ic_launcher.png b/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..85848ff49
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-xhdpi/ic_launcher.png b/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..916901e98
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/bin/res/crunch/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/BuildConfig.java b/SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/BuildConfig.java
new file mode 100644
index 000000000..040b48001
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package com.livio.sdl;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+} \ No newline at end of file
diff --git a/SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/R.java b/SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/R.java
new file mode 100644
index 000000000..d0fb61fd1
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/gen/com/livio/sdl/R.java
@@ -0,0 +1,144 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package com.livio.sdl;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static int ic_launcher=0x7f020000;
+ }
+ public static final class id {
+ public static int iv_rowImage=0x7f060004;
+ public static int listView=0x7f060000;
+ public static int textview=0x7f060006;
+ public static int tv_messageDetail=0x7f060003;
+ public static int tv_messageName=0x7f060001;
+ public static int tv_rowText=0x7f060005;
+ public static int tv_timestamp=0x7f060002;
+ }
+ public static final class layout {
+ public static int listview=0x7f030000;
+ public static int sdl_message_listview_row=0x7f030001;
+ public static int simple_listview_with_image=0x7f030002;
+ public static int textview=0x7f030003;
+ }
+ public static final class string {
+ /** Add Command
+ */
+ public static int add_command_command_name=0x7f040004;
+ public static int add_command_image_type=0x7f040007;
+ public static int add_command_parent_menu=0x7f040006;
+ public static int add_command_vr_keyword=0x7f040005;
+ public static int alert_duration_ms=0x7f040023;
+ public static int alert_duration_s=0x7f040022;
+ public static int alert_line1=0x7f04001e;
+ public static int alert_line2=0x7f04001f;
+ public static int alert_line3=0x7f040020;
+ public static int alert_sound_enabled=0x7f040021;
+ /** General Strings
+ */
+ public static int app_name=0x7f040000;
+ public static int choice_name=0x7f040018;
+ public static int clock=0x7f04002d;
+ public static int clock_hrs=0x7f04002e;
+ public static int clock_mins=0x7f04002f;
+ public static int clock_secs=0x7f040030;
+ public static int colon=0x7f040031;
+ public static int did_location=0x7f04000c;
+ /** Get DTCs and Read DIDs
+ */
+ public static int ecu_name=0x7f04000b;
+ public static int hmi_language=0x7f04001c;
+ public static int item_number=0x7f040016;
+ /** Choice Set Item
+ */
+ public static int item_number1=0x7f040017;
+ /** Change Registration
+ */
+ public static int language=0x7f04001b;
+ /** Create Interaction Choice Set
+ */
+ public static int max_choices=0x7f040015;
+ public static int media=0x7f04002c;
+ public static int media_clock_timer_mode=0x7f040032;
+ public static int media_track=0x7f04002b;
+ public static int metadata_line1=0x7f040025;
+ public static int metadata_line2=0x7f040026;
+ public static int metadata_line3=0x7f040027;
+ public static int metadata_line4=0x7f040028;
+ /** Show Dialog
+ */
+ public static int metadata_lines=0x7f040024;
+ public static int negative_button=0x7f040003;
+ /** Delete Command
+ */
+ public static int no_commands_to_delete=0x7f040008;
+ /** Delete Submenu
+ */
+ public static int no_submenus_to_delete=0x7f040009;
+ /** Dialog Strings
+ */
+ public static int positive_button=0x7f040002;
+ public static int scrollable_message_clear=0x7f040014;
+ public static int scrollable_message_hint=0x7f040013;
+ /** Scrollable Message
+ */
+ public static int scrollable_message_text=0x7f040012;
+ public static int sdl_disconnected=0x7f040001;
+ public static int slider_footer=0x7f04000e;
+ public static int slider_start_position=0x7f040010;
+ public static int slider_ticks=0x7f04000f;
+ /** Slider
+ */
+ public static int slider_title=0x7f04000d;
+ public static int status_bar=0x7f04002a;
+ /** Add Submenu
+ */
+ public static int submenu_name=0x7f04000a;
+ public static int text_alignment=0x7f040029;
+ /** Alert Dialog
+ */
+ public static int text_to_speak=0x7f04001d;
+ public static int timeout=0x7f040011;
+ public static int units_milliseconds=0x7f040034;
+ /** Unit Suffixes
+ */
+ public static int units_seconds=0x7f040033;
+ public static int use_artwork=0x7f04001a;
+ public static int voice_keyword=0x7f040019;
+ }
+ public static final class style {
+ /**
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+
+
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+
+
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+
+ API 11 theme customizations can go here.
+
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+
+ API 14 theme customizations can go here.
+ */
+ public static int AppBaseTheme=0x7f050000;
+ /** Application theme.
+ All customizations that are NOT specific to a particular API-level can go here.
+ */
+ public static int AppTheme=0x7f050001;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/proguard-project.txt b/SDL_Android/LivioSdlUtilities/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/SDL_Android/LivioSdlUtilities/project.properties b/SDL_Android/LivioSdlUtilities/project.properties
new file mode 100644
index 000000000..e0da0c82e
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/project.properties
@@ -0,0 +1,16 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
+android.library.reference.1=../../../../Projects/git/SDL_Genivi/SDL_Android/SmartDeviceLinkProxyAndroid
diff --git a/SDL_Android/LivioSdlUtilities/res/drawable-hdpi/ic_launcher.png b/SDL_Android/LivioSdlUtilities/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/res/drawable-mdpi/ic_launcher.png b/SDL_Android/LivioSdlUtilities/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/res/drawable-xhdpi/ic_launcher.png b/SDL_Android/LivioSdlUtilities/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/res/drawable/arrow_left.png b/SDL_Android/LivioSdlUtilities/res/drawable/arrow_left.png
new file mode 100644
index 000000000..1810182d1
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/drawable/arrow_left.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/res/drawable/arrow_right.png b/SDL_Android/LivioSdlUtilities/res/drawable/arrow_right.png
new file mode 100644
index 000000000..3775bf55e
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/drawable/arrow_right.png
Binary files differ
diff --git a/SDL_Android/LivioSdlUtilities/res/layout/json_flipper_dialog.xml b/SDL_Android/LivioSdlUtilities/res/layout/json_flipper_dialog.xml
new file mode 100644
index 000000000..3d600d42c
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/layout/json_flipper_dialog.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/textview"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:textSize="16sp"
+ android:padding="10dip"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageButton android:id="@+id/ib_moveLeft"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:src="@drawable/arrow_left"
+ android:background="#00FFFFFF"/>
+
+ <ImageButton android:id="@+id/ib_moveRight"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:src="@drawable/arrow_right"
+ android:background="#00FFFFFF"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/SDL_Android/LivioSdlUtilities/res/layout/listview.xml b/SDL_Android/LivioSdlUtilities/res/layout/listview.xml
new file mode 100644
index 000000000..0382fd455
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/layout/listview.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/listView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+
+</ListView>
diff --git a/SDL_Android/LivioSdlUtilities/res/layout/sdl_message_listview_row.xml b/SDL_Android/LivioSdlUtilities/res/layout/sdl_message_listview_row.xml
new file mode 100644
index 000000000..052235281
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/layout/sdl_message_listview_row.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextView
+ android:id="@+id/tv_messageName"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:textStyle="bold"/>
+
+ <TextView
+ android:id="@+id/tv_timestamp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"/>
+
+ <TextView
+ android:id="@+id/tv_messageDetail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/tv_messageName"/>
+
+</RelativeLayout>
diff --git a/SDL_Android/LivioSdlUtilities/res/layout/simple_listview_with_image.xml b/SDL_Android/LivioSdlUtilities/res/layout/simple_listview_with_image.xml
new file mode 100644
index 000000000..64bec19e2
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/layout/simple_listview_with_image.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip">
+
+ <ImageView android:id="@+id/iv_rowImage"
+ android:layout_width="100dip"
+ android:layout_height="100dip" />
+
+ <TextView android:id="@+id/tv_rowText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical|center_horizontal"/>
+
+</LinearLayout>
diff --git a/SDL_Android/LivioSdlUtilities/res/layout/textview.xml b/SDL_Android/LivioSdlUtilities/res/layout/textview.xml
new file mode 100644
index 000000000..70a52ea9a
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/layout/textview.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:padding="10dip"/>
+
+</ScrollView> \ No newline at end of file
diff --git a/SDL_Android/LivioSdlUtilities/res/values-v11/styles.xml b/SDL_Android/LivioSdlUtilities/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/SDL_Android/LivioSdlUtilities/res/values-v14/styles.xml b/SDL_Android/LivioSdlUtilities/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/SDL_Android/LivioSdlUtilities/res/values/strings.xml b/SDL_Android/LivioSdlUtilities/res/values/strings.xml
new file mode 100644
index 000000000..362a8fd9d
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/values/strings.xml
@@ -0,0 +1,86 @@
+<resources>
+
+<!-- General Strings -->
+ <string name="app_name">Livio SDL Tester</string>
+ <string name="sdl_disconnected">SDL has been disconnected</string>
+
+<!-- Dialog Strings -->
+ <string name="positive_button">OK</string>
+ <string name="negative_button">Cancel</string>
+
+<!-- Add Command -->
+ <string name="add_command_command_name">Command Name</string>
+ <string name="add_command_vr_keyword">Voice Recognition Keyword</string>
+ <string name="add_command_parent_menu">Parent Menu:</string>
+ <string name="add_command_image_type">Image Type:</string>
+
+<!-- Delete Command -->
+ <string name="no_commands_to_delete">There are no commands to delete.</string>
+
+<!-- Delete Submenu -->
+ <string name="no_submenus_to_delete">There are no submenus to delete.</string>
+
+<!-- Add Submenu -->
+ <string name="submenu_name">Submenu Name</string>
+
+<!-- Get DTCs and Read DIDs -->
+ <string name="ecu_name">ECU Name (0&#8211;65535)</string>
+ <string name="did_location">DID Location (0&#8211;65535)</string>
+
+<!-- Slider -->
+ <string name="slider_title">Slider Title</string>
+ <string name="slider_footer">Slider Footer</string>
+ <string name="slider_ticks">Number of slider ticks: </string>
+ <string name="slider_start_position">Start position: </string>
+ <string name="timeout">Timeout: </string>
+
+<!-- Scrollable Message -->
+ <string name="scrollable_message_text">"\"\'My mind,\' he said, \'rebels at stagnation. Give me problems, give me work, give me the most abstruse cryptogram or the most intricate analysis, and I am in my own proper atmosphere. I can dispense then with artificial stimulants. But I abhor the dull routine of existence. I crave for mental exaltation. That is why I have chosen my own particular profession,&#8211;or rather created it, for I am the only one in the world.\'\"\n\n&#8211; Sherlock Holmes by Sir Arthur Conan Doyle"</string>
+ <string name="scrollable_message_hint">Enter a long message (500 chars max)</string>
+ <string name="scrollable_message_clear">Clear Text</string>
+
+<!-- Create Interaction Choice Set -->
+ <string name="max_choices">You have reached the max number of choices.</string>
+ <string name="item_number">Item #</string>
+
+<!-- Choice Set Item -->
+ <string name="item_number1">Item #1</string>
+ <string name="choice_name">Choice Name</string>
+ <string name="voice_keyword">Voice Recognition Keyword</string>
+ <string name="use_artwork">Use artwork?</string>
+
+<!-- Change Registration -->
+ <string name="language">Language</string>
+ <string name="hmi_language">HMI Display Language</string>
+
+<!-- Alert Dialog -->
+ <string name="text_to_speak">Text to Speak</string>
+ <string name="alert_line1">Alert Line #1</string>
+ <string name="alert_line2">Alert Line #2</string>
+ <string name="alert_line3">Alert Line #3</string>
+ <string name="alert_sound_enabled">Alert sound enabled</string>
+ <string name="alert_duration_s">Alert Duration (in seconds)</string>
+ <string name="alert_duration_ms">Alert Duration (in milliseconds)</string>
+
+<!-- Show Dialog -->
+ <string name="metadata_lines">Metadata Lines</string>
+ <string name="metadata_line1">Metadata Line #1</string>
+ <string name="metadata_line2">Metadata Line #2</string>
+ <string name="metadata_line3">Metadata Line #3</string>
+ <string name="metadata_line4">Metadata Line #4</string>
+ <string name="text_alignment">Text Alignment</string>
+ <string name="status_bar">Status Bar</string>
+ <string name="media_track">Media Track</string>
+ <string name="media">Media</string>
+ <string name="clock">Clock</string>
+ <string name="clock_hrs">HH</string>
+ <string name="clock_mins">MM</string>
+ <string name="clock_secs">SS</string>
+ <string name="colon">:</string>
+
+ <string name="media_clock_timer_mode">Timer Mode</string>
+
+<!-- Unit Suffixes -->
+ <string name="units_seconds"> s</string>
+ <string name="units_milliseconds"> ms</string>
+</resources>
diff --git a/SDL_Android/LivioSdlUtilities/res/values/styles.xml b/SDL_Android/LivioSdlUtilities/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/IpAddress.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/IpAddress.java
new file mode 100644
index 000000000..2e123dec7
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/IpAddress.java
@@ -0,0 +1,36 @@
+package com.livio.sdl;
+
+/**
+ * Represents an IP address and port number as 2 strings - one string for the address
+ * itself and another string for the associated TCP port number.
+ *
+ * @author Mike Burke
+ *
+ */
+public class IpAddress {
+
+ private String ipAddress, tcpPort;
+
+ public IpAddress(String ipAddress, String tcpPort) {
+ this.ipAddress = ipAddress;
+ this.tcpPort = tcpPort;
+ }
+
+ public IpAddress(String ipAddress, int tcpPort){
+ this(ipAddress, String.valueOf(tcpPort));
+ }
+
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public String getTcpPort() {
+ return tcpPort;
+ }
+
+ @Override
+ public String toString(){
+ return new StringBuilder().append(ipAddress).append(":").append(tcpPort).toString();
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlConstants.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlConstants.java
new file mode 100644
index 000000000..805eaf2d6
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlConstants.java
@@ -0,0 +1,101 @@
+package com.livio.sdl;
+
+public final class SdlConstants {
+
+ private SdlConstants() {}
+
+ public static final class AddCommandConstants{
+ private AddCommandConstants(){}
+
+ public static final int INVALID_PARENT_ID = -1;
+ public static final int ROOT_PARENT_ID = 0;
+ public static final int DEFAULT_POSITION = 0;
+ public static final int MINIMUM_COMMAND_ID = 0x00;
+ public static final int MAXIMUM_COMMAND_ID = 2000000000;
+ }
+
+ public static final class AddSubmenuConstants{
+ private AddSubmenuConstants(){}
+
+ public static final int DEFAULT_POSITION = 0;
+ }
+
+ public static final class InteractionChoiceSetConstants{
+ private InteractionChoiceSetConstants(){}
+
+ public static final int MINIMUM_CHOICE_SET_ID = 0x00;
+ public static final int MAXIMUM_CHOICE_SET_ID = 2000000000;
+ }
+
+ public static final class GetDtcsConstants{
+ private GetDtcsConstants(){}
+
+ public static final int MINIMUM_ECU_ID = 0x00;
+ public static final int MAXIMUM_ECU_ID = 0xFFFF;
+ }
+
+ public static final class PerformInteractionConstants{
+ private PerformInteractionConstants(){}
+
+ public static final int MINIMUM_TIMEOUT = 5; // seconds
+ public static final int MAXIMUM_TIMEOUT = 100; // seconds
+ public static final int INVALID_TIMEOUT = -1;
+
+ public static final int EXPECTED_REPSONSE_TIME_OFFSET = 2000; // ms, or 2 s
+ }
+
+ public static final class ReadDidsConstants{
+ private ReadDidsConstants(){}
+
+ public static final int MINIMUM_ECU_ID = 0x00;
+ public static final int MAXIMUM_ECU_ID = 0xFFFF;
+
+ public static final int MINIMUM_DID_LOCATION = 0x00;
+ public static final int MAXIMUM_DID_LOCATION = 0xFFFF;
+ }
+
+ public static final class ScrollableMessageConstants{
+ private ScrollableMessageConstants(){}
+
+ public static final int TIMEOUT_MINIMUM = 1; // seconds
+ public static final int TIMEOUT_MAXIMUM = 65; // seconds
+
+ public static final int MESSAGE_LENGTH_MAX = 500; // characters
+
+ public static final int EXPECTED_REPSONSE_TIME_OFFSET = 2000; // ms, or 2 s
+ }
+
+ public static final class AlertConstants{
+ private AlertConstants(){}
+
+ public static final int ALERT_TIME_MINIMUM = 3; // seconds
+ public static final int ALERT_TIME_MAXIMUM = 10; // seconds
+
+ public static final int EXPECTED_REPSONSE_TIME_OFFSET = 2000; // ms, or 2 s
+ }
+
+ public static final class SetMediaClockTimerConstants{
+ private SetMediaClockTimerConstants(){}
+
+ public static final int HOURS_MINIMUM = 0; // hours
+ public static final int HOURS_MAXIMUM = 59; // hours
+ public static final int MINUTES_MINIMUM = 0; // minutes
+ public static final int MINUTES_MAXIMUM = 59; // minutes
+ public static final int SECONDS_MINIMUM = 0; // seconds
+ public static final int SECONDS_MAXIMUM = 59; // seconds
+ }
+
+ public static final class SliderConstants{
+ private SliderConstants(){}
+
+ public static final int NUM_OF_TICKS_MIN = 2; // ticks
+ public static final int NUM_OF_TICKS_MAX = 26; // ticks
+ public static final int START_POSITION_MIN = 1;
+ public static final int START_POSITION_MAX = NUM_OF_TICKS_MAX;
+ public static final int TIMEOUT_MIN = 1; // second
+ public static final int TIMEOUT_MAX = 65; // seconds
+
+ public static final int EXPECTED_REPSONSE_TIME_OFFSET = 2000; // ms, or 2 s
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlImageItem.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlImageItem.java
new file mode 100644
index 000000000..516799faf
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlImageItem.java
@@ -0,0 +1,68 @@
+package com.livio.sdl;
+
+import java.util.Comparator;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdl.utils.SdlUtils;
+import com.smartdevicelink.proxy.rpc.Image;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.ImageType;
+
+/**
+ * Represents an SDL image object. This includes the bitmap object itself,
+ * a filename to be used on SDL and an ImageType representing the type of image
+ * we're looking at.
+ *
+ * @author Mike Burke
+ *
+ */
+public class SdlImageItem {
+
+ public static class SdlImageItemComparator implements Comparator<SdlImageItem>{
+ @Override
+ public int compare(SdlImageItem lhs, SdlImageItem rhs) {
+ return lhs.getImageName().compareTo(rhs.getImageName());
+ }
+ }
+
+ private Bitmap bitmap;
+ private String imageName;
+ private FileType imageType;
+
+ public SdlImageItem(Bitmap bitmap, String imageName, FileType imageType) {
+ this.bitmap = bitmap;
+ this.imageName = imageName;
+ this.imageType = imageType;
+ }
+
+ public Bitmap getBitmap() {
+ return bitmap;
+ }
+
+ public String getImageName() {
+ return imageName;
+ }
+
+ public FileType getImageType(){
+ return imageType;
+ }
+
+ public Image toImage(){
+ CompressFormat format = SdlUtils.convertImageTypeToCompressFormat(imageType);
+
+ Image image = new Image();
+ image.setImageType(ImageType.DYNAMIC);
+ image.setValue(imageName);
+ image.setBulkData(AndroidUtils.bitmapToRawBytes(bitmap, format));
+
+ return image;
+ }
+
+ public static Image toImage(SdlImageItem item){
+ return item.toImage();
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlLogMessage.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlLogMessage.java
new file mode 100644
index 000000000..4fa1cf367
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlLogMessage.java
@@ -0,0 +1,124 @@
+package com.livio.sdl;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import com.livio.sdl.utils.SdlUtils;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.RPCResponse;
+
+/**
+ * The SdlLogMessage class holds applicable data to be used for logging an RPC message so that
+ * we don't have to keep a strong reference to the large RPC message object sitting around just
+ * to show a log message for it.
+ *
+ * @author Mike Burke
+ *
+ */
+public class SdlLogMessage {
+
+ public static final String REQUEST = "request";
+ public static final String RESPONSE = "response";
+ public static final String NOTIFICATION = "notification";
+
+ private static final String DATE_FORMAT = "hh:mm:ss";
+ private static final String POSITIVE_RESPONSE = "(positive response)";
+ private static final String NEGATIVE_RESPONSE = "(negative response)";
+ private static final String POSITIVE_REQUEST = "(request)";
+ private static final String POSITIVE_NOTIFICATION = "(notification)";
+
+ private String timeStamp;
+ private String details;
+ private String functionName;
+ private boolean success = true;
+ private String messageType;
+ private String jsonData;
+ private int correlationId = -1;
+
+ public SdlLogMessage(RPCMessage rpcm) {
+ setFields(rpcm);
+ }
+
+ // read information from the input RPC Message and fills in the appropriate fields for this class.
+ private void setFields(RPCMessage rpcm){
+ // get what type of message this is - request, response or notification
+ messageType = rpcm.getMessageType();
+
+ // apply a timestamp
+ SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT, Locale.US);
+ timeStamp = sdf.format(new Date());
+
+ details = "";
+
+ if(messageType.equals(RESPONSE)){
+ // input message was a response, which is slightly more detailed than requests or notifications, so we'll have a separate method for responses.
+ setResponseFields((RPCResponse) rpcm);
+ }
+ else if(messageType.equals(REQUEST)){
+ // requests need to append "(request)" to the input function name.
+ functionName = new StringBuilder().append(rpcm.getFunctionName()).append(" ").append(POSITIVE_REQUEST).toString();
+ correlationId = ((RPCRequest) rpcm).getCorrelationID();
+ }
+ else if(messageType.equals(NOTIFICATION)){
+ // notifications need to append "(notification)" to the input function name.
+ functionName = new StringBuilder().append(rpcm.getFunctionName()).append(" ").append(POSITIVE_NOTIFICATION).toString();
+ }
+
+ // set the JSON string
+ jsonData = SdlUtils.getJsonString(rpcm);
+ }
+
+ // set data for a response type message
+ private void setResponseFields(RPCResponse response){
+ success = response.getSuccess();
+ correlationId = response.getCorrelationID();
+
+ if(success){
+ // if the response was successful, set the details to success and show the function name as a positive response
+ details = response.getResultCode().name();
+ functionName = new StringBuilder().append(response.getFunctionName()).append(" ").append(POSITIVE_RESPONSE).toString();
+ }
+ else{
+ // if the response was unsuccessful, show a detailed explanation in details and set function name as a negative response
+ functionName = new StringBuilder().append(response.getFunctionName()).append(" ").append(NEGATIVE_RESPONSE).toString();
+
+ // if the response has extra info, we'll append it after the negative response.
+ String info = response.getInfo();
+ StringBuilder builder = new StringBuilder().append(response.getResultCode().name());
+ if(info != null){
+ builder.append(": ").append(info);
+ }
+ details = builder.toString();
+ }
+ }
+
+ public String getTimeStamp() {
+ return timeStamp;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public boolean getSuccess(){
+ return success;
+ }
+
+ public String getMessageType(){
+ return messageType;
+ }
+
+ public String getJsonData(){
+ return jsonData;
+ }
+
+ public int getCorrelationId(){
+ return correlationId;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlRequestFactory.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlRequestFactory.java
new file mode 100644
index 000000000..53de4f6e9
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlRequestFactory.java
@@ -0,0 +1,609 @@
+package com.livio.sdl;
+
+import java.util.Vector;
+
+import com.livio.sdl.utils.MathUtils;
+import com.livio.sdl.utils.SdlUtils;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.TTSChunkFactory;
+import com.smartdevicelink.proxy.rpc.AddCommand;
+import com.smartdevicelink.proxy.rpc.AddSubMenu;
+import com.smartdevicelink.proxy.rpc.Alert;
+import com.smartdevicelink.proxy.rpc.ChangeRegistration;
+import com.smartdevicelink.proxy.rpc.Choice;
+import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSet;
+import com.smartdevicelink.proxy.rpc.DeleteCommand;
+import com.smartdevicelink.proxy.rpc.DeleteFile;
+import com.smartdevicelink.proxy.rpc.DeleteInteractionChoiceSet;
+import com.smartdevicelink.proxy.rpc.DeleteSubMenu;
+import com.smartdevicelink.proxy.rpc.GetDTCs;
+import com.smartdevicelink.proxy.rpc.Image;
+import com.smartdevicelink.proxy.rpc.PerformInteraction;
+import com.smartdevicelink.proxy.rpc.PutFile;
+import com.smartdevicelink.proxy.rpc.ReadDID;
+import com.smartdevicelink.proxy.rpc.ScrollableMessage;
+import com.smartdevicelink.proxy.rpc.SetAppIcon;
+import com.smartdevicelink.proxy.rpc.SetMediaClockTimer;
+import com.smartdevicelink.proxy.rpc.Show;
+import com.smartdevicelink.proxy.rpc.Slider;
+import com.smartdevicelink.proxy.rpc.Speak;
+import com.smartdevicelink.proxy.rpc.StartTime;
+import com.smartdevicelink.proxy.rpc.SubscribeButton;
+import com.smartdevicelink.proxy.rpc.TTSChunk;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButton;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
+import com.smartdevicelink.proxy.rpc.enums.Language;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+import com.smartdevicelink.proxy.rpc.enums.UpdateMode;
+
+/**
+ * A static factory class that instantiates various types of RPCRequest subclasses based on the
+ * input parameters.
+ *
+ * @author Mike Burke
+ *
+ */
+public final class SdlRequestFactory {
+
+ private SdlRequestFactory() {}
+
+ /**
+ * Creates an AddCommand RPCRequest.
+ *
+ * @param name The name of the command. Cannot be null or empty string
+ * @param position The position in the menu list
+ * @param parentId The command's parent id
+ * @param vrCommands A CSV string input that is parsed into voice-rec strings
+ * @param imageName The image name of any image associated with the command
+ * @return The created AddCommand request
+ */
+ public static RPCRequest addCommand(String name, int position, int parentId, String vrCommands, String imageName){
+ if(name == null){
+ throw new NullPointerException();
+ }
+ if(name.length() <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ AddCommand result = new AddCommand();
+ result.setMenuParams(SdlUtils.menuParams(name, position, parentId));
+ if(vrCommands != null && vrCommands.length() > 0){
+ result.setVrCommands(SdlUtils.voiceRecognitionVector(vrCommands));
+ }
+ if(imageName != null){
+ result.setCmdIcon(SdlUtils.dynamicImage(imageName));
+ }
+ return result;
+ }
+
+ /**
+ * Creates an AddSubmenu RPCRequest.
+ *
+ * @param submenuName The name of the submenu. Cannot be null or empty string
+ * @param position The position in the menu list
+ * @return The created AddSubMenu request
+ */
+ public static RPCRequest addSubmenu(String submenuName, int position){
+ if(submenuName == null){
+ throw new NullPointerException();
+ }
+ if(submenuName.length() <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ AddSubMenu result = new AddSubMenu();
+ result.setMenuName(submenuName);
+ result.setPosition(position);
+ return result;
+ }
+
+ /**
+ * Creates a SubscribeButton RPCRequest.
+ *
+ * @param name The ButtonName that is being subscribed to
+ * @return The created SubscribeButton request
+ */
+ public static RPCRequest subscribeButton(ButtonName name){
+ if(name == null){
+ throw new NullPointerException();
+ }
+
+ SubscribeButton result = new SubscribeButton();
+ result.setButtonName(name);
+ return result;
+ }
+
+ /**
+ * Creates a UnsubscribeButton RPCRequest
+ *
+ * @param name The ButtonName that is being unsubscribed from
+ * @return The created UnsubscribeButton request
+ */
+ public static RPCRequest unsubscribeButton(ButtonName name){
+ if(name == null){
+ throw new NullPointerException();
+ }
+
+ UnsubscribeButton result = new UnsubscribeButton();
+ result.setButtonName(name);
+ return result;
+ }
+
+ /**
+ * Creates a ChangeRegistration request
+ *
+ * @param mainLang Main language to set. Cannot be null
+ * @param hmiLang HMI language to set. Cannot be null
+ * @return The ChangeRegistration request
+ */
+ public static RPCRequest changeRegistration(Language mainLang, Language hmiLang){
+ if(mainLang == null || hmiLang == null){
+ throw new NullPointerException();
+ }
+
+ ChangeRegistration result = new ChangeRegistration();
+ result.setLanguage(mainLang);
+ result.setHmiDisplayLanguage(hmiLang);
+ return result;
+ }
+
+ /**
+ * Creates a CreateInteractionChoiceSet request.
+ *
+ * @param choiceSet The choices contained within the CreateInteractionChoiceSet. Cannot be null or empty vector.
+ * @return The created CreateInteractionChoiceSet request
+ */
+ public static RPCRequest createInteractionChoiceSet(Vector<Choice> choiceSet){
+ if(choiceSet == null){
+ throw new NullPointerException();
+ }
+ if(choiceSet.size() <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ CreateInteractionChoiceSet result = new CreateInteractionChoiceSet();
+ result.setChoiceSet(choiceSet);
+ return result;
+ }
+
+ /**
+ * Creates a DeleteCommand request.
+ *
+ * @param commandId The id of the command to delete. Must be a valid ID value.
+ * @return The created DeleteCommand request
+ */
+ public static RPCRequest deleteCommand(int commandId){
+ if(commandId < SdlConstants.AddCommandConstants.MINIMUM_COMMAND_ID ||
+ commandId > SdlConstants.AddCommandConstants.MAXIMUM_COMMAND_ID){
+ throw new IllegalArgumentException();
+ }
+
+ DeleteCommand result = new DeleteCommand();
+ result.setCmdID(commandId);
+ return result;
+ }
+
+ /**
+ * Creates a DeleteSubMenu request.
+ *
+ * @param menuId The id of the command to delete. Must be a valid ID value.
+ * @return The created DeleteSubMenu request
+ */
+ public static RPCRequest deleteSubmenu(int menuId){
+ if(menuId < SdlConstants.AddCommandConstants.MINIMUM_COMMAND_ID ||
+ menuId > SdlConstants.AddCommandConstants.MAXIMUM_COMMAND_ID){
+ throw new IllegalArgumentException();
+ }
+
+ DeleteSubMenu result = new DeleteSubMenu();
+ result.setMenuID(menuId);
+ return result;
+ }
+
+ /**
+ * Creates a DeleteFile request.
+ *
+ * @param fileName The file name of the image to delete. Cannot be null.
+ * @return The created DeleteFile request
+ */
+ public static RPCRequest deleteFile(String fileName){
+ if(fileName == null){
+ throw new NullPointerException();
+ }
+
+ DeleteFile result = new DeleteFile();
+ result.setSmartDeviceLinkFileName(fileName);
+ return result;
+ }
+
+ /**
+ * Creates a DeleteInteracionChoiceSet request.
+ *
+ * @param id The id of the choice set to delete. Must be a valid id value.
+ * @return
+ */
+ public static RPCRequest deleteInteractionChoiceSet(int id){
+ if(id < SdlConstants.InteractionChoiceSetConstants.MINIMUM_CHOICE_SET_ID || id > SdlConstants.InteractionChoiceSetConstants.MAXIMUM_CHOICE_SET_ID){
+ throw new IllegalArgumentException();
+ }
+
+ DeleteInteractionChoiceSet result = new DeleteInteractionChoiceSet();
+ result.setInteractionChoiceSetID(id);
+ return result;
+ }
+
+
+ /**
+ * Creates a GetDTCs request.
+ *
+ * @param ecuId The ECU id to get DTCs for. Must be a valid ECU id value.
+ * @return The created GetDTCs request
+ */
+ public static RPCRequest getDtcs(int ecuId){
+ if(ecuId < SdlConstants.GetDtcsConstants.MINIMUM_ECU_ID || ecuId > SdlConstants.GetDtcsConstants.MAXIMUM_ECU_ID){
+ throw new IllegalArgumentException();
+ }
+
+ GetDTCs result = new GetDTCs();
+ result.setEcuName(ecuId);
+ return result;
+ }
+
+ /**
+ * Creates a GetDTCs request.
+ *
+ * @param ecuId
+ * @return
+ */
+ public static RPCRequest getDtcs(String ecuId){
+ try{
+ return getDtcs(Integer.parseInt(ecuId));
+ }
+ catch(NumberFormatException e){
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Creates a PerformInteraction request.
+ *
+ * @param title The title of the interaction
+ * @param voicePrompt The CSV string representing the vector of voice-rec inputs
+ * @param choiceIds The ids of the choices to present to the user. Cannot be null or empty.
+ * @param mode The interaction mode for this interaction
+ * @param timeout The timeout for the interaction. Must be within timeout range.
+ * @return The created PerformInteraction request
+ */
+ public static RPCRequest performInteraction(String title, String voicePrompt, Vector<Integer> choiceIds, InteractionMode mode, int timeout){
+ if(choiceIds == null || choiceIds.size() <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ if( (timeout < MathUtils.convertSecsToMillisecs(SdlConstants.PerformInteractionConstants.MINIMUM_TIMEOUT) ||
+ timeout > MathUtils.convertSecsToMillisecs(SdlConstants.PerformInteractionConstants.MAXIMUM_TIMEOUT) ) &&
+ (timeout != SdlConstants.PerformInteractionConstants.INVALID_TIMEOUT) ){
+ throw new IllegalArgumentException();
+ }
+
+ PerformInteraction result = new PerformInteraction();
+
+ // set the title
+ if(title == null || title.length() <= 0){
+ title = " ";
+ }
+ result.setInitialText(title);
+
+ // set the voice prompt
+ if(voicePrompt == null || voicePrompt.length() <= 0){
+ voicePrompt = " ";
+ }
+ Vector<TTSChunk> ttsChunks = TTSChunkFactory.createSimpleTTSChunks(voicePrompt);
+ result.setInitialPrompt(ttsChunks);
+
+ // set the interaction mode
+ result.setInteractionMode(mode);
+
+ // set the choice set ids
+ result.setInteractionChoiceSetIDList(choiceIds);
+
+ // set the timeout
+ if(timeout != SdlConstants.PerformInteractionConstants.INVALID_TIMEOUT){
+ result.setTimeout(timeout);
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a PerformInteraction request.
+ *
+ * @param title The title of the interaction
+ * @param voicePrompt The CSV string representing the vector of voice-rec inputs
+ * @param choiceIds The ids of the choices to present to the user. Cannot be null or empty.
+ * @param mode The interaction mode for this interaction
+ * @return The created PerformInteraction request
+ */
+ public static RPCRequest performInteraction(String title, String voicePrompt, Vector<Integer> choiceIds, InteractionMode mode){
+ return performInteraction(title, voicePrompt, choiceIds, mode, SdlConstants.PerformInteractionConstants.INVALID_TIMEOUT);
+ }
+
+ /**
+ * Creates a PutFile request.
+ *
+ * @param fileName The file name for the file. Cannot be null or empty.
+ * @param type Type of file this request will contain. Cannot be null.
+ * @param persistent Whether or not the file is persistent on the head-unit
+ * @param rawBytes The raw bytes of the file to send. Cannot be null or empty.
+ * @return The created PutFile request
+ */
+ public static RPCRequest putFile(String fileName, FileType type, boolean persistent, byte[] rawBytes){
+ if(fileName == null || type == null || rawBytes == null){
+ throw new NullPointerException();
+ }
+ if(fileName.length() <= 0 || rawBytes.length <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ PutFile result = new PutFile();
+ result.setSmartDeviceLinkFileName(fileName);
+ result.setBulkData(rawBytes);
+ result.setPersistentFile(persistent);
+ result.setFileType(type);
+ return result;
+ }
+
+ /**
+ * Creates a ReadDID request
+ *
+ * @param ecu The ECU id. Must be a valid ECU id value.
+ * @param did The DID id. Must be a valid DID id value.
+ * @return The created ReadDid request
+ */
+ public static RPCRequest readDid(int ecu, int did){
+ Vector<Integer> dids = new Vector<Integer>(1);
+ dids.add(did);
+ return readDid(ecu, dids);
+ }
+
+ /**
+ * Creates a ReadDID request.
+ *
+ * @param ecu The ECU id. Must be a valid ECU id value.
+ * @param dids A vector of DID ids. Cannot be null or empty and all values must be valid DID id values.
+ * @return The created ReadDID request
+ */
+ public static RPCRequest readDid(int ecu, Vector<Integer> dids){
+ if(dids == null){
+ throw new NullPointerException();
+ }
+
+ if(ecu < SdlConstants.ReadDidsConstants.MINIMUM_ECU_ID || ecu > SdlConstants.ReadDidsConstants.MAXIMUM_ECU_ID ||
+ dids.size() <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ for(Integer did : dids){
+ if(did < SdlConstants.ReadDidsConstants.MINIMUM_DID_LOCATION || did > SdlConstants.ReadDidsConstants.MAXIMUM_DID_LOCATION){
+ throw new IllegalArgumentException();
+ }
+ }
+
+ ReadDID result = new ReadDID();
+ result.setEcuName(ecu);
+ result.setDidLocation(dids);
+ return result;
+ }
+
+ /**
+ * Creates a ScrollableMessage request
+ *
+ * @param msg The message to show on the head-unit. Cannot be null and max length is 500 chars.
+ * @param timeoutInMs The timeout for the request in milliseconds. Must be within timeout range for ScrollableMessage.
+ * @return The created ScrollableMessage request
+ */
+ public static RPCRequest scrollableMessage(String msg, int timeoutInMs){
+ if(msg == null){
+ throw new NullPointerException();
+ }
+ if(msg.length() > SdlConstants.ScrollableMessageConstants.MESSAGE_LENGTH_MAX ||
+ timeoutInMs < MathUtils.convertSecsToMillisecs(SdlConstants.ScrollableMessageConstants.TIMEOUT_MINIMUM) ||
+ timeoutInMs > MathUtils.convertSecsToMillisecs(SdlConstants.ScrollableMessageConstants.TIMEOUT_MAXIMUM) ){
+ throw new IllegalArgumentException();
+ }
+
+ ScrollableMessage result = new ScrollableMessage();
+ result.setScrollableMessageBody(msg);
+ result.setTimeout(timeoutInMs);
+ return result;
+ }
+
+ /**
+ * Creates an Alert request
+ *
+ * @param textToSpeak The text that should be spoken on the vehicle sound system
+ * @param line1 The first line in the alert
+ * @param line2 The second line in the alert
+ * @param line3 The third line in the alert
+ * @param playTone Whether or not to play a tone
+ * @param toneDuration The length time to show the alert. Must be within timeout range for Alert.
+ * @return The created Alert request
+ */
+ public static RPCRequest alert(String textToSpeak, String line1, String line2, String line3, boolean playTone, int toneDuration){
+ if(toneDuration < MathUtils.convertSecsToMillisecs(SdlConstants.AlertConstants.ALERT_TIME_MINIMUM) ||
+ toneDuration > MathUtils.convertSecsToMillisecs(SdlConstants.AlertConstants.ALERT_TIME_MAXIMUM) ){
+ throw new IllegalArgumentException();
+ }
+
+ Alert result = new Alert();
+ if(textToSpeak != null && textToSpeak.length() > 0){
+ result.setTtsChunks(SdlUtils.createTextToSpeechVector(textToSpeak));
+ }
+ if(line1 != null && line1.length() > 0){
+ result.setAlertText1(line1);
+ }
+ if(line2 != null && line2.length() > 0){
+ result.setAlertText2(line2);
+ }
+ if(line3 != null && line3.length() > 0){
+ result.setAlertText3(line3);
+ }
+
+ result.setPlayTone(playTone);
+ result.setDuration(toneDuration);
+
+ return result;
+ }
+
+ /**
+ * Creates a SetAppIcon request.
+ *
+ * @param iconName The name of the image to show as the app icon. Cannot be null or empty.
+ * @return The created SetAppIcon request
+ */
+ public static RPCRequest setAppIcon(String iconName){
+ if(iconName == null){
+ throw new NullPointerException();
+ }
+ if(iconName.length() <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ SetAppIcon result = new SetAppIcon();
+ result.setSmartDeviceLinkFileName(iconName);
+ return result;
+ }
+
+ /**
+ * Creates a SetMediaClockTimer request.
+ *
+ * @param mode The UpdateMode for the clock timer. Cannot be null.
+ * @param startTime The start time for the clock timer
+ * @return The created SetMediaClockTimer request
+ */
+ public static RPCRequest setMediaClockTimer(UpdateMode mode, StartTime startTime){
+ if(mode == null){
+ throw new NullPointerException();
+ }
+
+ SetMediaClockTimer result = new SetMediaClockTimer();
+ result.setUpdateMode(mode);
+ if(startTime != null){
+ result.setStartTime(startTime);
+ }
+ return result;
+ }
+
+
+ /**
+ * Creates a SetMediaClockTimer request.
+ *
+ * @param mode The UpdateMode for the clock timer. Cannot be null.
+ * @param hours The hours place of the clock
+ * @param minutes The minutes place of the clock
+ * @param seconds The seconds place of the clock
+ * @return The created SetMediaClockTimer request
+ */
+ public static RPCRequest setMediaClockTimer(UpdateMode mode, int hours, int minutes, int seconds){
+ return setMediaClockTimer(mode, SdlUtils.createStartTime(hours, minutes, seconds));
+ }
+
+ /**
+ * Creates a SetMediaClockTimer request.
+ *
+ * @param mode The UpdateMode for the clock timer. Cannot be null.
+ * @return
+ */
+ public static RPCRequest setMediaClockTimer(UpdateMode mode){
+ return setMediaClockTimer(mode, null);
+ }
+
+ /**
+ * Creates a Show request.
+ *
+ * @param line1 The first line of text on the HMI
+ * @param line2 The second line of text on the HMI
+ * @param line3 The third line of text on the HMI
+ * @param line4 The fourth line of text on the HMI (not available for media apps)
+ * @param statusBar The text of the status bar
+ * @param alignment The TextAlignment of the text
+ * @param imageName The name of the image to show on the HMI
+ * @return The created Show request
+ */
+ public static RPCRequest show(String line1, String line2, String line3, String line4, String statusBar, TextAlignment alignment, String imageName){
+ Show result = new Show();
+ if(line1 != null && line1.length() > 0){
+ result.setMainField1(line1);
+ }
+ if(line2 != null && line2.length() > 0){
+ result.setMainField2(line2);
+ }
+ if(line3 != null && line3.length() > 0){
+ result.setMainField3(line3);
+ }
+ if(line4 != null && line4.length() > 0){
+ result.setMainField4(line4);
+ }
+ if(statusBar != null && statusBar.length() > 0){
+ result.setStatusBar(statusBar);
+ }
+ if(alignment != null){
+ result.setAlignment(alignment);
+ }
+ if(imageName != null){
+ Image image = SdlUtils.dynamicImage(imageName);
+ result.setGraphic(image);
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a Slider request.
+ *
+ * @param header The header/title text for the slider. Cannot be null.
+ * @param footer The footer text for the slider
+ * @param numOfTicks The total number of ticks available in the slider. Must be a valid ticks value.
+ * @param startPosition The initial position of the slider. Must be a valid start position value.
+ * @param timeout The timeout of the slider. Must be a valid slider timeout value.
+ * @return The created Slider request
+ */
+ public static RPCRequest slider(String header, String footer, int numOfTicks, int startPosition, int timeout){
+ if(header == null){
+ throw new NullPointerException();
+ }
+ if(numOfTicks < SdlConstants.SliderConstants.NUM_OF_TICKS_MIN || numOfTicks > SdlConstants.SliderConstants.NUM_OF_TICKS_MAX ||
+ startPosition < SdlConstants.SliderConstants.START_POSITION_MIN || startPosition > numOfTicks ||
+ timeout < MathUtils.convertSecsToMillisecs(SdlConstants.SliderConstants.TIMEOUT_MIN) ||
+ timeout > MathUtils.convertSecsToMillisecs(SdlConstants.SliderConstants.TIMEOUT_MAX) ){
+ throw new IllegalArgumentException();
+ }
+
+ Slider result = new Slider();
+ result.setSliderHeader(header);
+ result.setSliderFooter(SdlUtils.voiceRecognitionVector(footer));
+ result.setNumTicks(numOfTicks);
+ result.setPosition(startPosition);
+ result.setTimeout(timeout);
+ return result;
+ }
+
+ /**
+ * Creates a Speak request.
+ *
+ * @param text The text to speak. Cannot be null.
+ * @param speechCapabilities The type of input - either text, phoneme type or silence. Cannot be null.
+ * @return The created Speak request
+ */
+ public static RPCRequest speak(String text, SpeechCapabilities speechCapabilities){
+ if(text == null || speechCapabilities == null){
+ throw new NullPointerException();
+ }
+
+ Speak result = new Speak();
+ result.setTtsChunks(SdlUtils.createTextToSpeechVector(text, speechCapabilities));
+ return result;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseFactory.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseFactory.java
new file mode 100644
index 000000000..b6ee7c2c3
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseFactory.java
@@ -0,0 +1,206 @@
+package com.livio.sdl;
+
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.constants.Names;
+import com.smartdevicelink.proxy.interfaces.IProxyListenerALM;
+import com.smartdevicelink.proxy.rpc.AddCommandResponse;
+import com.smartdevicelink.proxy.rpc.AddSubMenuResponse;
+import com.smartdevicelink.proxy.rpc.AlertResponse;
+import com.smartdevicelink.proxy.rpc.ChangeRegistrationResponse;
+import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSetResponse;
+import com.smartdevicelink.proxy.rpc.DeleteCommandResponse;
+import com.smartdevicelink.proxy.rpc.DeleteFileResponse;
+import com.smartdevicelink.proxy.rpc.DeleteInteractionChoiceSetResponse;
+import com.smartdevicelink.proxy.rpc.DeleteSubMenuResponse;
+import com.smartdevicelink.proxy.rpc.GetDTCsResponse;
+import com.smartdevicelink.proxy.rpc.GetVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.ListFilesResponse;
+import com.smartdevicelink.proxy.rpc.PerformInteractionResponse;
+import com.smartdevicelink.proxy.rpc.PutFileResponse;
+import com.smartdevicelink.proxy.rpc.ReadDIDResponse;
+import com.smartdevicelink.proxy.rpc.ResetGlobalPropertiesResponse;
+import com.smartdevicelink.proxy.rpc.ScrollableMessageResponse;
+import com.smartdevicelink.proxy.rpc.SetAppIconResponse;
+import com.smartdevicelink.proxy.rpc.SetGlobalPropertiesResponse;
+import com.smartdevicelink.proxy.rpc.SetMediaClockTimerResponse;
+import com.smartdevicelink.proxy.rpc.ShowResponse;
+import com.smartdevicelink.proxy.rpc.SliderResponse;
+import com.smartdevicelink.proxy.rpc.SpeakResponse;
+import com.smartdevicelink.proxy.rpc.SubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.SubscribeVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.UnsubscribeVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.enums.Result;
+
+/**
+ * A factory class to generate responses based on the input requests.
+ *
+ * @author Mike Burke
+ *
+ */
+public final class SdlResponseFactory {
+
+ private SdlResponseFactory() {}
+
+ /**
+ * Creates a generic "Success" response for the input request with no input checking. The created
+ * response is sent back to the appropriate method of the input listener.
+ *
+ * @param request The request to respond to
+ * @param listener The listener to inform when the response is complete
+ */
+ public static void sendGenericResponseForRequest(RPCRequest request, IProxyListenerALM listener){
+ if(listener == null){
+ throw new NullPointerException();
+ }
+
+ final String reqName = request.getFunctionName();
+ final int correlationId = request.getCorrelationID();
+
+ // this is really bad
+ if(reqName == Names.Alert){
+ AlertResponse result = new AlertResponse();
+ setSuccessParams(result, correlationId);
+ listener.onAlertResponse(result);
+ }
+ else if(reqName == Names.Speak){
+ SpeakResponse result = new SpeakResponse();
+ setSuccessParams(result, correlationId);
+ listener.onSpeakResponse(result);
+ }
+ else if(reqName == Names.Show){
+ ShowResponse result = new ShowResponse();
+ setSuccessParams(result, correlationId);
+ listener.onShowResponse(result);
+ }
+ else if(reqName == Names.SubscribeButton){
+ SubscribeButtonResponse result = new SubscribeButtonResponse();
+ setSuccessParams(result, correlationId);
+ listener.onSubscribeButtonResponse(result);
+
+ }
+ else if(reqName == Names.UnsubscribeButton){
+ UnsubscribeButtonResponse result = new UnsubscribeButtonResponse();
+ setSuccessParams(result, correlationId);
+ listener.onUnsubscribeButtonResponse(result);
+ }
+ else if(reqName == Names.AddCommand){
+ AddCommandResponse result = new AddCommandResponse();
+ setSuccessParams(result, correlationId);
+ listener.onAddCommandResponse(result);
+ }
+ else if(reqName == Names.DeleteCommand){
+ DeleteCommandResponse result = new DeleteCommandResponse();
+ setSuccessParams(result, correlationId);
+ listener.onDeleteCommandResponse(result);
+ }
+ else if(reqName == Names.AddSubMenu){
+ AddSubMenuResponse result = new AddSubMenuResponse();
+ setSuccessParams(result, correlationId);
+ listener.onAddSubMenuResponse(result);
+ }
+ else if(reqName == Names.DeleteSubMenu){
+ DeleteSubMenuResponse result = new DeleteSubMenuResponse();
+ setSuccessParams(result, correlationId);
+ listener.onDeleteSubMenuResponse(result);
+ }
+ else if(reqName == Names.SetGlobalProperties){
+ SetGlobalPropertiesResponse result = new SetGlobalPropertiesResponse();
+ setSuccessParams(result, correlationId);
+ listener.onSetGlobalPropertiesResponse(result);
+ }
+ else if(reqName == Names.ResetGlobalProperties){
+ ResetGlobalPropertiesResponse result = new ResetGlobalPropertiesResponse();
+ setSuccessParams(result, correlationId);
+ listener.onResetGlobalPropertiesResponse(result);
+ }
+ else if(reqName == Names.SetMediaClockTimer){
+ SetMediaClockTimerResponse result = new SetMediaClockTimerResponse();
+ setSuccessParams(result, correlationId);
+ listener.onSetMediaClockTimerResponse(result);
+ }
+ else if(reqName == Names.CreateInteractionChoiceSet){
+ CreateInteractionChoiceSetResponse result = new CreateInteractionChoiceSetResponse();
+ setSuccessParams(result, correlationId);
+ listener.onCreateInteractionChoiceSetResponse(result);
+ }
+ else if(reqName == Names.DeleteInteractionChoiceSet){
+ DeleteInteractionChoiceSetResponse result = new DeleteInteractionChoiceSetResponse();
+ setSuccessParams(result, correlationId);
+ listener.onDeleteInteractionChoiceSetResponse(result);
+ }
+ else if(reqName == Names.PerformInteraction){
+ PerformInteractionResponse result = new PerformInteractionResponse();
+ setSuccessParams(result, correlationId);
+ listener.onPerformInteractionResponse(result);
+ }
+ else if(reqName == Names.Slider){
+ SliderResponse result = new SliderResponse();
+ setSuccessParams(result, correlationId);
+ listener.onSliderResponse(result);
+ }
+ else if(reqName == Names.ScrollableMessage){
+ ScrollableMessageResponse result = new ScrollableMessageResponse();
+ setSuccessParams(result, correlationId);
+ listener.onScrollableMessageResponse(result);
+ }
+ else if(reqName == Names.ChangeRegistration){
+ ChangeRegistrationResponse result = new ChangeRegistrationResponse();
+ setSuccessParams(result, correlationId);
+ listener.onChangeRegistrationResponse(result);
+ }
+ else if(reqName == Names.PutFile){
+ PutFileResponse result = new PutFileResponse();
+ setSuccessParams(result, correlationId);
+ listener.onPutFileResponse(result);
+ }
+ else if(reqName == Names.DeleteFile){
+ DeleteFileResponse result = new DeleteFileResponse();
+ setSuccessParams(result, correlationId);
+ listener.onDeleteFileResponse(result);
+ }
+ else if(reqName == Names.ListFiles){
+ ListFilesResponse result = new ListFilesResponse();
+ setSuccessParams(result, correlationId);
+ listener.onListFilesResponse(result);
+ }
+ else if(reqName == Names.SetAppIcon){
+ SetAppIconResponse result = new SetAppIconResponse();
+ setSuccessParams(result, correlationId);
+ listener.onSetAppIconResponse(result);
+ }
+ else if(reqName == Names.SubscribeVehicleData){
+ SubscribeVehicleDataResponse result = new SubscribeVehicleDataResponse();
+ setSuccessParams(result, correlationId);
+ listener.onSubscribeVehicleDataResponse(result);
+ }
+ else if(reqName == Names.UnsubscribeVehicleData){
+ UnsubscribeVehicleDataResponse result = new UnsubscribeVehicleDataResponse();
+ setSuccessParams(result, correlationId);
+ listener.onUnsubscribeVehicleDataResponse(result);
+ }
+ else if(reqName == Names.GetVehicleData){
+ GetVehicleDataResponse result = new GetVehicleDataResponse();
+ setSuccessParams(result, correlationId);
+ listener.onGetVehicleDataResponse(result);
+ }
+ else if(reqName == Names.ReadDID){
+ ReadDIDResponse result = new ReadDIDResponse();
+ setSuccessParams(result, correlationId);
+ listener.onReadDIDResponse(result);
+ }
+ else if(reqName == Names.GetDTCs){
+ GetDTCsResponse result = new GetDTCsResponse();
+ setSuccessParams(result, correlationId);
+ listener.onGetDTCsResponse(result);
+ }
+ }
+
+ private static void setSuccessParams(RPCResponse response, int correlationId){
+ response.setSuccess(true);
+ response.setCorrelationID(correlationId);
+ response.setResultCode(Result.SUCCESS);
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseTracker.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseTracker.java
new file mode 100644
index 000000000..e7e6fa3bf
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlResponseTracker.java
@@ -0,0 +1,109 @@
+package com.livio.sdl;
+
+import android.util.SparseArray;
+
+import com.livio.sdl.utils.Timeout;
+import com.smartdevicelink.proxy.RPCRequest;
+
+/**
+ * Tracks outgoing requests to core and sets a timeout for when the response
+ * should come back from core. If a request isn't received by the time
+ * the timeout expires, it should be assumed that SDL core disconnected.
+ *
+ * @author Mike Burke
+ *
+ */
+public class SdlResponseTracker {
+
+ /**
+ * Listener interface for SdlResponseTracker. Contains callback method
+ * for when any request times out.
+ *
+ * @author Mike Burke
+ *
+ */
+ public interface Listener{
+ public void onRequestTimedOut();
+ }
+
+ /**
+ * Default timeout to receive a response for a typical request.
+ */
+ public static final int DEFAULT_TIMEOUT = 5000; // 5 seconds
+
+ private SparseArray<RPCRequest> requests = new SparseArray<RPCRequest>();
+ private SparseArray<Timeout> timeouts = new SparseArray<Timeout>();
+
+ private Listener listener;
+
+ public SdlResponseTracker(Listener l) {
+ this.listener = l;
+ }
+
+ /**
+ * Adds a request to the tracker with a default timeout.
+ *
+ * @param request The request to track
+ */
+ public void add(RPCRequest request){
+ add(request, DEFAULT_TIMEOUT);
+ }
+
+ /**
+ * Adds a request to the tracker with a specified timeout.
+ *
+ * @param request The request to track
+ * @param duration The timeout duration (in ms)
+ */
+ public void add(RPCRequest request, int duration){
+ int corrId = request.getCorrelationID();
+ requests.put(corrId, request);
+
+ Timeout timeout = new Timeout(duration, new Timeout.Listener() {
+ @Override public void onTimeoutCancelled() {}
+
+ @Override
+ public void onTimeoutCompleted() {
+ notifyRequestTimedOut();
+ }
+ });
+ timeout.start();
+ timeouts.put(corrId, timeout);
+ }
+
+ /**
+ * Removes the specified correlation id from the tracker. Also automatically
+ * cancels the associated timer for the specified correlation id.
+ *
+ * @param corrId The correlation id to stop tracking
+ * @return The request that was removed
+ */
+ public RPCRequest remove(int corrId){
+ timeouts.get(corrId).cancel();
+ timeouts.remove(corrId);
+
+ RPCRequest result = requests.get(corrId);
+ requests.remove(corrId);
+ return result;
+ }
+
+ /**
+ * Clears all requests being tracked and cancels their associated timeouts.
+ */
+ public void clear(){
+ if(requests != null){
+ // loop through requests and remove each one
+ for(int numItems = requests.size()-1; numItems >= 0; numItems--){
+ RPCRequest request = requests.valueAt(numItems);
+ remove(request.getCorrelationID());
+ }
+ }
+ }
+
+ private void notifyRequestTimedOut(){
+ if(listener != null){
+ listener.onRequestTimedOut();
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlService.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlService.java
new file mode 100644
index 000000000..72f3d78b4
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/SdlService.java
@@ -0,0 +1,1131 @@
+package com.livio.sdl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Vector;
+
+import android.annotation.SuppressLint;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.livio.sdl.enums.SdlButton;
+import com.livio.sdl.menu.CommandButton;
+import com.livio.sdl.menu.CommandButton.OnClickListener;
+import com.livio.sdl.menu.MenuItem;
+import com.livio.sdl.menu.MenuManager;
+import com.livio.sdl.menu.SubmenuButton;
+import com.livio.sdl.utils.UpCounter;
+import com.smartdevicelink.exception.SmartDeviceLinkException;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.SmartDeviceLinkProxyALM;
+import com.smartdevicelink.proxy.constants.Names;
+import com.smartdevicelink.proxy.interfaces.IProxyListenerALM;
+import com.smartdevicelink.proxy.rpc.AddCommand;
+import com.smartdevicelink.proxy.rpc.AddCommandResponse;
+import com.smartdevicelink.proxy.rpc.AddSubMenu;
+import com.smartdevicelink.proxy.rpc.AddSubMenuResponse;
+import com.smartdevicelink.proxy.rpc.Alert;
+import com.smartdevicelink.proxy.rpc.AlertManeuverResponse;
+import com.smartdevicelink.proxy.rpc.AlertResponse;
+import com.smartdevicelink.proxy.rpc.ChangeRegistrationResponse;
+import com.smartdevicelink.proxy.rpc.Choice;
+import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSet;
+import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSetResponse;
+import com.smartdevicelink.proxy.rpc.DeleteCommand;
+import com.smartdevicelink.proxy.rpc.DeleteCommandResponse;
+import com.smartdevicelink.proxy.rpc.DeleteFile;
+import com.smartdevicelink.proxy.rpc.DeleteFileResponse;
+import com.smartdevicelink.proxy.rpc.DeleteInteractionChoiceSet;
+import com.smartdevicelink.proxy.rpc.DeleteInteractionChoiceSetResponse;
+import com.smartdevicelink.proxy.rpc.DeleteSubMenu;
+import com.smartdevicelink.proxy.rpc.DeleteSubMenuResponse;
+import com.smartdevicelink.proxy.rpc.DialNumberResponse;
+import com.smartdevicelink.proxy.rpc.EndAudioPassThruResponse;
+import com.smartdevicelink.proxy.rpc.GenericResponse;
+import com.smartdevicelink.proxy.rpc.GetDTCsResponse;
+import com.smartdevicelink.proxy.rpc.GetVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.ListFilesResponse;
+import com.smartdevicelink.proxy.rpc.OnAudioPassThru;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.OnCommand;
+import com.smartdevicelink.proxy.rpc.OnDriverDistraction;
+import com.smartdevicelink.proxy.rpc.OnHMIStatus;
+import com.smartdevicelink.proxy.rpc.OnLanguageChange;
+import com.smartdevicelink.proxy.rpc.OnPermissionsChange;
+import com.smartdevicelink.proxy.rpc.OnTBTClientState;
+import com.smartdevicelink.proxy.rpc.OnVehicleData;
+import com.smartdevicelink.proxy.rpc.PerformAudioPassThruResponse;
+import com.smartdevicelink.proxy.rpc.PerformInteraction;
+import com.smartdevicelink.proxy.rpc.PerformInteractionResponse;
+import com.smartdevicelink.proxy.rpc.PutFile;
+import com.smartdevicelink.proxy.rpc.PutFileResponse;
+import com.smartdevicelink.proxy.rpc.ReadDIDResponse;
+import com.smartdevicelink.proxy.rpc.ResetGlobalPropertiesResponse;
+import com.smartdevicelink.proxy.rpc.ScrollableMessage;
+import com.smartdevicelink.proxy.rpc.ScrollableMessageResponse;
+import com.smartdevicelink.proxy.rpc.SetAppIconResponse;
+import com.smartdevicelink.proxy.rpc.SetDisplayLayoutResponse;
+import com.smartdevicelink.proxy.rpc.SetGlobalPropertiesResponse;
+import com.smartdevicelink.proxy.rpc.SetMediaClockTimerResponse;
+import com.smartdevicelink.proxy.rpc.ShowConstantTBTResponse;
+import com.smartdevicelink.proxy.rpc.ShowResponse;
+import com.smartdevicelink.proxy.rpc.Slider;
+import com.smartdevicelink.proxy.rpc.SliderResponse;
+import com.smartdevicelink.proxy.rpc.SpeakResponse;
+import com.smartdevicelink.proxy.rpc.SubscribeButton;
+import com.smartdevicelink.proxy.rpc.SubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.SubscribeVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButton;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.UnsubscribeVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.UpdateTurnListResponse;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.Language;
+import com.smartdevicelink.transport.TCPTransportConfig;
+
+/**
+ * Performs all interactions with Smart Device Link in a long-running service that
+ * clients can bind to in order to send information to the vehicle.
+ *
+ * @author Mike Burke
+ *
+ */
+public class SdlService extends Service implements IProxyListenerALM{
+
+ /* ********** Nested Classes ********** */
+
+ /**
+ * Messages that can be received by a bound client.
+ *
+ * @author Mike Burke
+ *
+ */
+ public static class ClientMessages{
+ /**
+ * Message.what integer called when SDL has successfully created a connection.
+ */
+ public static final int SDL_CONNECTED = 0;
+ /**
+ * Message.what integer called when SDL has disconnected.
+ */
+ public static final int SDL_DISCONNECTED = 1;
+ /**
+ * Message.what integer called when the main HMI is first displayed.
+ */
+ public static final int SDL_HMI_FIRST_DISPLAYED = 2;
+ /**
+ * Message.what integer called when a RPCResponse result has been received.
+ */
+ public static final int ON_MESSAGE_RESULT = 3;
+ /**
+ * Message.what integer called when a ServiceMessages.REQUEST_SUBMENU_LIST message has been received.
+ */
+ public static final int SUBMENU_LIST_RECEIVED = 4;
+ /**
+ * Message.what integer called when a ServiceMessages.REQUEST_COMMAND_LIST message has been received.
+ */
+ public static final int COMMAND_LIST_RECEIVED = 5;
+ /**
+ * Message.what integer called when a ServiceMessages.REQUEST_BUTTON_SUBSCRIPTIONS message has been received.
+ */
+ public static final int BUTTON_SUBSCRIPTIONS_RECEIVED = 6;
+ /**
+ * Message.what integer called when a ServiceMessages.REQUEST_INTERACTION_SETS message has been received.
+ */
+ public static final int INTERACTION_SETS_RECEIVED = 7;
+ /**
+ * Message.what integer called when a ServiceMessages.REQUEST_PUT_FILES message has been received.
+ */
+ public static final int PUT_FILES_RECEIVED = 8;
+ }
+
+ /**
+ * Messages that can be sent to the service by a bound client.
+ *
+ * @author Mike Burke
+ *
+ */
+ public static class ServiceMessages{
+ /**
+ * Message.what integer used to register your activity as a client bound to this service.
+ */
+ public static final int REGISTER_CLIENT = 0;
+ /**
+ * Message.what integer used to unregister your activity as a client bound to this service.
+ */
+ public static final int UNREGISTER_CLIENT = 1;
+ /**
+ * Message.what integer commanding the service to attempt an SDL connection.
+ */
+ public static final int CONNECT = 2;
+ /**
+ * Message.what integer commanding the service to disconnect an existing SDL connection.
+ */
+ public static final int DISCONNECT = 3;
+ /**
+ * Message.what integer commanding the service to reset the SDL connection.
+ */
+ public static final int RESET = 4;
+ /**
+ * Message.what integer setting the service to Offline mode.
+ */
+ public static final int OFFLINE_MODE = 5;
+ /**
+ * Message.what integer commanding the service to send an RPCRequest.
+ */
+ public static final int SEND_MESSAGE = 6;
+ /**
+ * Message.what integer commanding the service to respond with a list of existing submenus that have been added.
+ */
+ public static final int REQUEST_SUBMENU_LIST = 7;
+ /**
+ * Message.what integer commanding the service to respond with a list of existing commands that have been added.
+ */
+ public static final int REQUEST_COMMAND_LIST = 8;
+ /**
+ * Message.what integer commanding the service to respond with a list of buttons that have been subscribed to.
+ */
+ public static final int REQUEST_BUTTON_SUBSCRIPTIONS = 9;
+ /**
+ * Message.what integer commanding the service to respond with a list of interaction sets created so far.
+ */
+ public static final int REQUEST_INTERACTION_SETS = 10;
+ /**
+ * Message.what integer commanding the service to respond with a list of put file images added so far.
+ */
+ public static final int REQUEST_PUT_FILES = 11;
+ }
+
+ /**
+ * Messages that can be shown on the vehicle head-unit. Any static
+ * text that your app would like to show on the head-unit can be defined
+ * in this class.
+ *
+ * @author Mike Burke
+ *
+ */
+ protected static class MetadataMessages{
+ public static final String BLANK = " ";
+ public static final String APP_NAME = "Livio SDL Tester";
+ public static final String APP_SLOGAN = "More Music, Less Work";
+ }
+
+ /* ********** Static variables ********** */
+ protected IpAddress currentIp; // keeps track of the current ip address in case we need to reset
+ private static final boolean IS_MEDIA_APP = true; /* All of these variables */
+ private static final Language DEFAULT_LANGUAGE = Language.EN_US; /* are needed to start up */
+ private static final String APP_ID = "appId"; /* the SDL proxy object */
+ private static final boolean WIFI_AUTO_RECONNECT = true; /* */
+
+ protected static boolean debug = false;
+
+ /* ********** Instance variables ********** */
+ protected List<Messenger> clients = null; // list of bound clients
+
+ protected UpCounter correlationIdGenerator = new UpCounter(100); // id generator for correlation ids
+ protected UpCounter commandIdGenerator = new UpCounter(100); // id generator for commands & submenus
+
+ protected MenuManager menuManager = new MenuManager();
+ protected MenuManager choiceSetManager = new MenuManager();
+ protected SdlResponseTracker responseTracker;
+ protected List<SdlButton> buttonSubscriptions = new ArrayList<SdlButton>();
+ protected List<String> addedImageNames = new ArrayList<String>();
+ protected Handler serviceHandler = new Handler();
+
+ protected SmartDeviceLinkProxyALM sdlProxy = null; // the proxy object which sends our requests and receives responses
+ protected boolean isConnected = false;
+ protected boolean offlineMode = false;
+ protected boolean alreadyDisplayed = false;
+
+ protected Toast toast = null;
+
+ /* ********** Messenger methods to & from the client ********** */
+
+ protected final Messenger messenger = new Messenger(new IncomingHandler());
+
+ @SuppressLint("HandlerLeak")
+ protected class IncomingHandler extends Handler{
+ @Override
+ public void handleMessage(Message msg){
+ switch(msg.what){
+ case ServiceMessages.REGISTER_CLIENT:
+ registerClient(msg.replyTo);
+ break;
+ case ServiceMessages.UNREGISTER_CLIENT:
+ unregisterClient(msg.replyTo);
+ break;
+ case ServiceMessages.CONNECT:
+ offlineMode = false;
+ initialize();
+ IpAddress inputIp = (IpAddress) msg.obj;
+ startSdlProxy(inputIp);
+ break;
+ case ServiceMessages.DISCONNECT:
+ offlineMode = true;
+ initialize();
+ stopSdlProxy();
+ sendMessageToRegisteredClients(Message.obtain(null, ClientMessages.SDL_DISCONNECTED));
+ break;
+ case ServiceMessages.RESET:
+ initialize();
+ resetProxy();
+ break;
+ case ServiceMessages.OFFLINE_MODE:
+ offlineMode = true;
+ initialize();
+ stopSdlProxy();
+ break;
+ case ServiceMessages.SEND_MESSAGE:
+ onSendMessageReceived((RPCRequest) msg.obj);
+ break;
+ case ServiceMessages.REQUEST_SUBMENU_LIST:
+ submenuListRequested(msg.replyTo, msg.arg1);
+ break;
+ case ServiceMessages.REQUEST_COMMAND_LIST:
+ commandListRequested(msg.replyTo, msg.arg1);
+ break;
+ case ServiceMessages.REQUEST_BUTTON_SUBSCRIPTIONS:
+ buttonSubscriptionsRequested(msg.replyTo, msg.arg1);
+ break;
+ case ServiceMessages.REQUEST_INTERACTION_SETS:
+ interactionSetsRequested(msg.replyTo, msg.arg1);
+ break;
+ case ServiceMessages.REQUEST_PUT_FILES:
+ putFilesRequested(msg.replyTo, msg.arg1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Registers a client to receive all communication from this service.
+ *
+ * @param client The client to register
+ */
+ protected void registerClient(Messenger client){
+ if(clients == null){
+ clients = new ArrayList<Messenger>();
+ }
+
+ clients.add(client);
+ }
+
+ /**
+ * Removes a client from receiving all communication from this service.
+ *
+ * @param client The client to remove
+ */
+ protected void unregisterClient(Messenger client){
+ if(clients != null && clients.size() > 0){
+ clients.remove(client);
+ }
+ }
+
+ /**
+ * Sends a message to all registered clients.
+ *
+ * @param msg The message to send
+ */
+ protected void sendMessageToRegisteredClients(Message msg){
+ if(clients != null){
+ for(Messenger client : clients){
+ sendMessageToClient(client, msg);
+ }
+ }
+ }
+
+ /**
+ * Sends a message to a single client.
+ *
+ * @param client The client to reply to
+ * @param msg The message to send
+ */
+ protected void sendMessageToClient(Messenger client, Message msg){
+ try {
+ client.send(msg);
+ } catch (RemoteException e) {
+ // if we can't send to this client, let's remove it
+ unregisterClient(client);
+ }
+ }
+
+ /**
+ * Sends an RPCResponse message to all registered clients.
+ *
+ * @param response The response to send
+ */
+ protected void sendMessageResponse(RPCMessage response){
+ Message msg = Message.obtain(null, ClientMessages.ON_MESSAGE_RESULT);
+ msg.obj = response;
+ sendMessageToRegisteredClients(msg);
+ }
+
+ /**
+ * Sends the list of available sub-menus to the listening messenger client.
+ *
+ * @param listener The client to reply to
+ * @param reqCode The request code sent with the initial request
+ */
+ protected void submenuListRequested(Messenger listener, int reqCode){
+ Message msg = Message.obtain(null, ClientMessages.SUBMENU_LIST_RECEIVED);
+ msg.obj = getSubmenuList();
+ msg.arg1 = reqCode;
+ sendMessageToClient(listener, msg);
+ }
+
+ /**
+ * Sends the list of available commands to the listening messenger client.
+ *
+ * @param listener The client to reply to
+ * @param reqCode The request code sent with the initial request
+ */
+ protected void commandListRequested(Messenger listener, int reqCode){
+ Message msg = Message.obtain(null, ClientMessages.COMMAND_LIST_RECEIVED);
+ msg.obj = getCommandList();
+ msg.arg1 = reqCode;
+ sendMessageToClient(listener, msg);
+ }
+
+ /**
+ * Sends the list of button subscriptions to the listening messenger client.
+ *
+ * @param listener The client to reply to
+ * @param reqCode The request code sent with the initial request
+ */
+ protected void buttonSubscriptionsRequested(Messenger listener, int reqCode){
+ Message msg = Message.obtain(null, ClientMessages.BUTTON_SUBSCRIPTIONS_RECEIVED);
+ msg.obj = getButtonSubscriptions();
+ msg.arg1 = reqCode;
+ sendMessageToClient(listener, msg);
+ }
+
+ /**
+ * Sends the list of interaction sets to the listening messenger client.
+ *
+ * @param listener The client to reply to
+ * @param reqCode The request code sent with the initial request
+ */
+ protected void interactionSetsRequested(Messenger listener, int reqCode){
+ Message msg = Message.obtain(null, ClientMessages.INTERACTION_SETS_RECEIVED);
+ msg.obj = getInteractionSets();
+ msg.arg1 = reqCode;
+ sendMessageToClient(listener, msg);
+ }
+
+ /**
+ * Sends the list of put files to the listening messenger client.
+ *
+ * @param listener The client to reply to
+ * @param reqCode The request code sent with the initial request
+ */
+ protected void putFilesRequested(Messenger listener, int reqCode){
+ Message msg = Message.obtain(null, ClientMessages.PUT_FILES_RECEIVED);
+ msg.obj = getPutFiles();
+ msg.arg1 = reqCode;
+ sendMessageToClient(listener, msg);
+ }
+
+ /* ********** Android service life cycle methods ********** */
+ @Override
+ public void onCreate() {
+ log("onCreate called");
+ initialize();
+ super.onCreate();
+ }
+
+ private void initialize(){
+ isConnected = false;
+ alreadyDisplayed = false;
+
+ if(responseTracker == null){
+ responseTracker = new SdlResponseTracker(new SdlResponseTracker.Listener() {
+ @Override
+ public void onRequestTimedOut() {
+ if(isConnected && !offlineMode){
+ // if any sdl request times out, we will assume we disconnected.
+ showToast("A request timed out. You may need to re-start SDL core.");
+ Message msg = Message.obtain(null, ClientMessages.SDL_DISCONNECTED);
+ sendMessageToRegisteredClients(msg);
+ }
+ }
+ });
+ }
+ else{
+ responseTracker.clear();
+ }
+
+ correlationIdGenerator.reset();
+ commandIdGenerator.reset();
+
+ menuManager.clear();
+ choiceSetManager.clear();
+ buttonSubscriptions.clear();
+ addedImageNames.clear();
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return messenger.getBinder();
+ }
+
+
+ /* ********** Proxy life cycle methods ********** */
+ /**
+ * Starts up SDL if it isn't already started. If SDL is already started, this method does nothing.
+ * To reset the proxy, use the resetProxy method.
+ *
+ * @param inputIp The IP address to attempt a connection on
+ */
+ protected void startSdlProxy(final IpAddress inputIp){
+ if(sdlProxy == null){
+ sdlProxy = createSdlProxyObject(inputIp);
+ }
+ }
+
+ /**
+ * Creates a SmartDeviceLinkProxyALM object and automatically attempts a connection
+ * to the input IP address.
+ *
+ * @param inputIp The IP address to attempt a connection on
+ * @return The created SmartDeviceLinkProxyALM object
+ */
+ protected SmartDeviceLinkProxyALM createSdlProxyObject(IpAddress inputIp){
+ int tcpPort = Integer.parseInt(inputIp.getTcpPort());
+ String ipAddress = inputIp.getIpAddress();
+ String appName = getResources().getString(R.string.app_name);
+
+ SmartDeviceLinkProxyALM result = null;
+ try {
+ result = new SmartDeviceLinkProxyALM((IProxyListenerALM)this, null, appName, null, null,
+ null, IS_MEDIA_APP, null, DEFAULT_LANGUAGE, DEFAULT_LANGUAGE, APP_ID,
+ null, false, false, new TCPTransportConfig(tcpPort, ipAddress, WIFI_AUTO_RECONNECT));
+ currentIp = inputIp;
+ } catch (SmartDeviceLinkException e) {
+ e.printStackTrace();
+ }
+
+ return result;
+ }
+
+ /**
+ * Disposes of any current proxy object if it exists and automatically creates a new
+ * proxy connection to the previously connected IP address.
+ */
+ protected void resetProxy(){
+ stopSdlProxy();
+ startSdlProxy(currentIp);
+ }
+
+ /**
+ * Disposes of any current proxy object and sets the object to null so it cannot be
+ * used again.
+ */
+ protected void stopSdlProxy(){
+ if(sdlProxy != null && sdlProxy.getIsConnected() && sdlProxy.getAppInterfaceRegistered()){
+ try {
+ sdlProxy.dispose();
+ } catch (SmartDeviceLinkException e) {
+ e.printStackTrace();
+ }
+ }
+
+ sdlProxy = null;
+ }
+
+ /* ********** Proxy communication methods ********** */
+ /**
+ * Called when a message to send is received, adds parameters where appropriate (correlation id,
+ * command id, other command-specific parameters, etc).
+ *
+ * @param command The request to send
+ */
+ protected void onSendMessageReceived(RPCRequest command){
+ if(command == null){
+ throw new NullPointerException("Cannot send a null command.");
+ }
+
+ if(!offlineMode && sdlProxy == null){
+ throw new IllegalStateException("Proxy object is null, so no commands can be sent.");
+ }
+
+ // set any request-specific parameters if needed
+ setRequestSpecificParameters(command);
+
+ // after setting appropriate parameters, send the full, completed response back to the clients
+ sendMessageResponse(command);
+
+ if(!offlineMode){
+ // send the request through SmartDeviceLink
+ sendRpcRequest(command);
+ }
+ else{
+ // in offline mode, we'll just send a "fake" success response.
+ SdlResponseFactory.sendGenericResponseForRequest(command, this);
+ }
+ }
+
+ /**
+ * Sends the input request to the connected SDL proxy instance.
+ *
+ * @param request
+ */
+ protected void sendRpcRequest(RPCRequest request){
+ try {
+ sdlProxy.sendRPCRequest(request);
+ } catch (SmartDeviceLinkException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Sets any command-specific parameters that need to be set. For example, add command and add submenu commands
+ * need to be assigned an ID at this point.
+ *
+ * @param command The RPC command to edit
+ */
+ protected void setRequestSpecificParameters(RPCRequest command){
+ String name = command.getFunctionName();
+
+ // give the command a correlation id
+ command.setCorrelationID(correlationIdGenerator.next());
+
+ if(name.equals(Names.AddCommand)){
+ ((AddCommand) command).setCmdID(commandIdGenerator.next());
+ addToRequestQueue(command);
+ }
+ else if(name.equals(Names.AddSubMenu)){
+ ((AddSubMenu) command).setMenuID(commandIdGenerator.next());
+ addToRequestQueue(command);
+ }
+ else if(name.equals(Names.CreateInteractionChoiceSet)){
+ CreateInteractionChoiceSet choiceSet = (CreateInteractionChoiceSet) command;
+ choiceSet.setInteractionChoiceSetID(commandIdGenerator.next());
+
+ Vector<Choice> choices = choiceSet.getChoiceSet();
+ for(Choice choice : choices){
+ choice.setChoiceID(commandIdGenerator.next());
+ }
+
+ addToRequestQueue(command);
+ }
+ else if(name.equals(Names.Alert)){
+ int timeout = ((Alert) command).getDuration();
+ addToRequestQueue(command, (timeout + SdlConstants.AlertConstants.EXPECTED_REPSONSE_TIME_OFFSET));
+ }
+ else if(name.equals(Names.PerformInteraction)){
+ int timeout = ((PerformInteraction) command).getTimeout();
+ addToRequestQueue(command, (timeout + SdlConstants.PerformInteractionConstants.EXPECTED_REPSONSE_TIME_OFFSET));
+ }
+ else if(name.equals(Names.ScrollableMessage)){
+ int timeout = ((ScrollableMessage) command).getTimeout();
+ addToRequestQueue(command, (timeout + SdlConstants.ScrollableMessageConstants.EXPECTED_REPSONSE_TIME_OFFSET));
+ }
+ else if(name.equals(Names.Slider)){
+ int timeout = ((Slider) command).getTimeout();
+ addToRequestQueue(command, (timeout + SdlConstants.SliderConstants.EXPECTED_REPSONSE_TIME_OFFSET));
+ }
+ else if( name.equals(Names.PutFile) || name.equals(Names.SubscribeButton) || name.equals(Names.SubscribeVehicleData) ||
+ name.equals(Names.UnsubscribeVehicleData) || name.equals(Names.DeleteCommand) ||
+ name.equals(Names.UnsubscribeButton) || name.equals(Names.DeleteInteractionChoiceSet) || name.equals(Names.DeleteSubMenu) ||
+ name.equals(Names.DeleteFile)){
+ addToRequestQueue(command);
+ }
+
+ }
+
+ /**
+ * Adds the input request to the queue of requests that are awaiting responses.
+ *
+ * @param request The request to add
+ */
+ protected void addToRequestQueue(RPCRequest request){
+ responseTracker.add(request);
+ }
+
+ /**
+ * Adds the input request to the queue of requests that are awaiting responses.
+ *
+ * @param request The request to add
+ * @param timeout A timeout that exceeds the expected timeout of the request
+ */
+ protected void addToRequestQueue(RPCRequest request, int timeout){
+ responseTracker.add(request, timeout);
+ }
+
+ /**
+ * Removes the input request from the queue of requests that are awaiting responses.
+ *
+ * @param request The request to remove
+ */
+ protected RPCRequest removeFromRequestQueue(int key){
+ return responseTracker.remove(key);
+ }
+
+ /**
+ * Translates the AddCommand object into a MenuItem object, complete with a click listener.
+ *
+ * @param command The command to translate
+ * @return The translated MenuItem object
+ */
+ protected MenuItem createMenuItem(AddCommand command){
+ final String name = command.getMenuParams().getMenuName();
+ final int id = command.getCmdID();
+ int parentId;
+ final Integer parentInteger = command.getMenuParams().getParentID();
+ if(parentInteger == null){
+ parentId = -1;
+ }
+ else{
+ parentId = parentInteger;
+ }
+
+ final MenuItem result = new CommandButton(name, id, parentId, new OnClickListener(){
+ @Override
+ public void onClick(CommandButton button) {
+ showToast(new StringBuilder().append(name).append(" clicked!").toString());
+ }
+ });
+
+ return result;
+ }
+
+ /**
+ * Translates the AddSubMenu object into a MenuItem object.
+ *
+ * @param command The command to translate
+ * @return The translated MenuItem object
+ */
+ protected MenuItem createMenuItem(AddSubMenu command){
+ final String name = command.getMenuName();
+ final MenuItem result = new SubmenuButton(name, command.getMenuID());
+ return result;
+ }
+
+ /**
+ * Translates the CreateInteractionChoiceSet object into a MenuItem object.
+ *
+ * @param command The command to translate
+ * @return The translated MenuItem object
+ */
+ protected MenuItem createMenuItem(CreateInteractionChoiceSet command){
+ final String name = "Choice Set";
+ final MenuItem result = new SubmenuButton(name, command.getInteractionChoiceSetID());
+ return result;
+ }
+
+ /**
+ * Translates the CreateInteractionChoiceSet object into a MenuItem object, complete with a click listener.
+ *
+ * @param choice The command to translate
+ * @param parentId The parent id of the input choice command
+ * @return The translated MenuItem object
+ */
+ protected MenuItem createMenuItem(Choice choice, final int parentId){
+ final String name = choice.getMenuName();
+ final int id = choice.getChoiceID();
+ final MenuItem result = new CommandButton(name, id, parentId, new OnClickListener(){
+ @Override
+ public void onClick(CommandButton button) {
+ showToast(new StringBuilder().append(name).append(" clicked!").toString());
+ }
+ });
+
+ return result;
+ }
+
+ /**
+ * Creates a copy of the list of submenus added so far.
+ *
+ * @return The copied list of submenu items
+ */
+ protected List<MenuItem> getSubmenuList(){
+ return menuManager.getSubmenus();
+ }
+
+ /**
+ * Creates a copy of the list of commands added so far.
+ *
+ * @return The copied list of command items
+ */
+ protected List<MenuItem> getCommandList(){
+ return menuManager.getCommands();
+ }
+
+ /**
+ * Creates a copy of the choice set menus added so far.
+ *
+ * @return The copied list of choice set items
+ */
+ protected List<MenuItem> getChoiceSetList(){
+ return choiceSetManager.getSubmenus();
+ }
+
+ /**
+ * Creates a copy of the list of button subscriptions added so far.
+ *
+ * @return The copied list of button subscriptions
+ */
+ protected List<SdlButton> getButtonSubscriptions(){
+ if(buttonSubscriptions == null || buttonSubscriptions.size() <= 0){
+ return Collections.emptyList();
+ }
+
+ return new ArrayList<SdlButton>(buttonSubscriptions);
+ }
+
+ /**
+ * Creates a copy of the list of interaction sets added so far.
+ *
+ * @return The copied list of interaction sets
+ */
+ protected List<MenuItem> getInteractionSets(){
+ List<MenuItem> result = choiceSetManager.getSubmenus();
+ if(result == null || result.size() <= 0){
+ return Collections.emptyList();
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a copy of the list of image names added so far.
+ *
+ * @return The copied list of image names
+ */
+ protected List<String> getPutFiles(){
+ if(addedImageNames == null || addedImageNames.size() <= 0){
+ return Collections.emptyList();
+ }
+
+ return new ArrayList<String>(addedImageNames);
+ }
+
+ /**
+ * Posts the input runnable to the Service thread.
+ *
+ * @param runnable The runnable to run
+ */
+ protected void runOnServiceThread(Runnable runnable){
+ serviceHandler.post(runnable);
+ }
+
+ /* ********** IProxyListenerALM interface methods ********** */
+
+ /* Most useful callbacks */
+ @Override
+ public void onOnHMIStatus(OnHMIStatus newStatus) {
+ if(!isConnected){
+ Message msg = Message.obtain(null, ClientMessages.SDL_CONNECTED);
+ sendMessageToRegisteredClients(msg);
+ isConnected = true;
+ }
+
+ if(newStatus.getHmiLevel() == HMILevel.HMI_FULL && !alreadyDisplayed){
+ Message msg = Message.obtain(null, ClientMessages.SDL_HMI_FIRST_DISPLAYED);
+ sendMessageToRegisteredClients(msg);
+ alreadyDisplayed = true;
+ }
+
+ sendMessageResponse(newStatus);
+ }
+
+ @Override
+ public void onProxyClosed(String info, Exception e) {
+ Message msg = Message.obtain(null, ClientMessages.SDL_DISCONNECTED);
+ sendMessageToRegisteredClients(msg);
+ stopSdlProxy();
+ initialize();
+ }
+
+ @Override
+ public void onOnCommand(final OnCommand notification) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ sendMessageResponse(notification);
+
+ int buttonId = notification.getCmdID();
+ menuManager.dispatchClick(buttonId);
+ }
+ });
+ }
+
+ @Override public void onOnButtonPress(final OnButtonPress notification) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ sendMessageResponse(notification);
+
+ ButtonName button = notification.getButtonName();
+ SdlButton sdlButton = SdlButton.translateFromLegacy(button);
+ String text = new StringBuilder().append(sdlButton.toString()).append(" clicked!").toString();
+ showToast(text);
+ }
+ });
+ }
+
+ /* Not very useful callbacks */
+ @Override public void onOnPermissionsChange(OnPermissionsChange notification) {sendMessageResponse(notification);}
+ @Override public void onOnVehicleData(OnVehicleData notification) {sendMessageResponse(notification);}
+ @Override public void onOnAudioPassThru(OnAudioPassThru notification) {sendMessageResponse(notification);}
+ @Override public void onOnLanguageChange(OnLanguageChange notification) {sendMessageResponse(notification);}
+ @Override public void onOnDriverDistraction(OnDriverDistraction notification) {sendMessageResponse(notification);}
+ @Override public void onOnTBTClientState(OnTBTClientState notification) {sendMessageResponse(notification);}
+ @Override public void onError(String info, Exception e) {}
+ @Override public void onOnButtonEvent(OnButtonEvent notification) {sendMessageResponse(notification);}
+
+ /* Message responses */
+ @Override
+ public void onAddCommandResponse(AddCommandResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ MenuItem button = createMenuItem((AddCommand) original);
+ if(button != null){
+ menuManager.addItem(button);
+ }
+ }
+ }
+
+ @Override
+ public void onDeleteCommandResponse(DeleteCommandResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ if(response.getSuccess()){
+ int idToRemove = ((DeleteCommand) original).getCmdID();
+ menuManager.removeItem(idToRemove);
+ }
+ }
+ }
+
+ @Override
+ public void onAddSubMenuResponse(AddSubMenuResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ MenuItem button = createMenuItem((AddSubMenu) original);
+ if(button != null){
+ menuManager.addItem(button);
+ }
+ }
+ }
+
+ @Override
+ public void onDeleteSubMenuResponse(DeleteSubMenuResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ int idToRemove = ((DeleteSubMenu) original).getMenuID();
+ menuManager.removeItem(idToRemove);
+ }
+ }
+ @Override
+ public void onSubscribeButtonResponse(SubscribeButtonResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ SdlButton button = SdlButton.translateFromLegacy(((SubscribeButton) original).getButtonName());
+ buttonSubscriptions.add(button);
+ }
+ }
+
+ @Override
+ public void onUnsubscribeButtonResponse(UnsubscribeButtonResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ SdlButton button = SdlButton.translateFromLegacy(((UnsubscribeButton) original).getButtonName());
+ buttonSubscriptions.remove(button);
+ }
+ }
+
+ @Override
+ public void onCreateInteractionChoiceSetResponse(CreateInteractionChoiceSetResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ // add the parent (choice set) item to the choice set manager
+ CreateInteractionChoiceSet choiceSet = (CreateInteractionChoiceSet) original;
+ MenuItem item = createMenuItem(choiceSet);
+ choiceSetManager.addItem(item);
+
+ // then, add all the parent's children to the choice set manager
+ final int parentId = choiceSet.getInteractionChoiceSetID();
+ Vector<Choice> children = choiceSet.getChoiceSet();
+ for(Choice child : children){
+ item = createMenuItem(child, parentId);
+ choiceSetManager.addItem(item);
+ }
+ }
+ }
+
+ @Override
+ public void onDeleteInteractionChoiceSetResponse(DeleteInteractionChoiceSetResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ // get the choice set ID from the original request and remove it from the choice set manager
+ DeleteInteractionChoiceSet choiceSet = (DeleteInteractionChoiceSet) original;
+ int choiceId = choiceSet.getInteractionChoiceSetID();
+ choiceSetManager.removeItem(choiceId);
+ }
+ }
+
+ @Override
+ public void onPutFileResponse(PutFileResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ // get the choice set ID from the original request and remove it from the choice set manager
+ PutFile putFile = (PutFile) original;
+ String putFileName = putFile.getSmartDeviceLinkFileName();
+ addedImageNames.add(putFileName);
+ }
+ }
+
+ @Override
+ public void onDeleteFileResponse(DeleteFileResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ RPCRequest original = removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess() && original != null){
+ // get the choice set ID from the original request and remove it from the choice set manager
+ DeleteFile deleteFile = (DeleteFile) original;
+ String deleteFileName = deleteFile.getSmartDeviceLinkFileName();
+ addedImageNames.remove(deleteFileName);
+ }
+ }
+ @Override
+ public void onAlertResponse(AlertResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ removeFromRequestQueue(correlationId);
+ }
+
+ @Override
+ public void onPerformInteractionResponse(final PerformInteractionResponse response) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ removeFromRequestQueue(correlationId);
+
+ if(response.getSuccess()){
+ int interactionId = response.getChoiceID();
+ choiceSetManager.dispatchClick(interactionId);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSliderResponse(SliderResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ removeFromRequestQueue(correlationId);
+ }
+
+ @Override
+ public void onScrollableMessageResponse(ScrollableMessageResponse response) {
+ sendMessageResponse(response);
+
+ int correlationId = response.getCorrelationID();
+ removeFromRequestQueue(correlationId);
+ }
+
+ @Override public void onSubscribeVehicleDataResponse(SubscribeVehicleDataResponse response) {sendMessageResponse(response);}
+ @Override public void onUnsubscribeVehicleDataResponse(UnsubscribeVehicleDataResponse response) {sendMessageResponse(response);}
+ @Override public void onGenericResponse(GenericResponse response) {sendMessageResponse(response);}
+ @Override public void onResetGlobalPropertiesResponse(ResetGlobalPropertiesResponse response) {sendMessageResponse(response);}
+ @Override public void onSetGlobalPropertiesResponse(SetGlobalPropertiesResponse response) {sendMessageResponse(response);}
+ @Override public void onSetMediaClockTimerResponse(SetMediaClockTimerResponse response) {sendMessageResponse(response);}
+ @Override public void onShowResponse(ShowResponse response) {sendMessageResponse(response);}
+ @Override public void onSpeakResponse(SpeakResponse response) {sendMessageResponse(response);}
+ @Override public void onGetVehicleDataResponse(GetVehicleDataResponse response) {sendMessageResponse(response);}
+ @Override public void onReadDIDResponse(ReadDIDResponse response) {sendMessageResponse(response);}
+ @Override public void onGetDTCsResponse(GetDTCsResponse response) {sendMessageResponse(response);}
+ @Override public void onPerformAudioPassThruResponse(PerformAudioPassThruResponse response) {sendMessageResponse(response);}
+ @Override public void onEndAudioPassThruResponse(EndAudioPassThruResponse response) {sendMessageResponse(response);}
+ @Override public void onListFilesResponse(ListFilesResponse response) {sendMessageResponse(response);}
+ @Override public void onSetAppIconResponse(SetAppIconResponse response) {sendMessageResponse(response);}
+ @Override public void onChangeRegistrationResponse(ChangeRegistrationResponse response) {sendMessageResponse(response);}
+ @Override public void onSetDisplayLayoutResponse(SetDisplayLayoutResponse response) {sendMessageResponse(response);}
+ @Override public void onAlertManeuverResponse(AlertManeuverResponse response) {sendMessageResponse(response);}
+ @Override public void onShowConstantTBTResponse(ShowConstantTBTResponse response) {sendMessageResponse(response);}
+ @Override public void onUpdateTurnListResponse(UpdateTurnListResponse response) {sendMessageResponse(response);}
+ @Override public void onDialNumberResponse(DialNumberResponse response) {sendMessageResponse(response);}
+
+
+ private void showToast(String msg){
+ if(toast == null){
+ toast = Toast.makeText(SdlService.this, "", Toast.LENGTH_LONG);
+ }
+
+ toast.setText(msg);
+ toast.show();
+ }
+
+ /* ********** Debug & log methods ********** */
+ /**
+ * Enables debug mode for this class and any classes used in this class.
+ *
+ * @param enable Enable flag for debug mode
+ */
+ public static void setDebug(boolean enable){
+ debug = enable;
+ MenuManager.setDebug(enable);
+ }
+
+ private static void log(String msg){
+ if(debug){
+ Log.d("SdlService", msg);
+ }
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlImageAdapter.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlImageAdapter.java
new file mode 100644
index 000000000..2a3ec3ca7
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlImageAdapter.java
@@ -0,0 +1,59 @@
+package com.livio.sdl.adapters;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.livio.sdl.R;
+import com.livio.sdl.SdlImageItem;
+
+public class SdlImageAdapter extends ArrayAdapter<SdlImageItem> {
+
+
+ public SdlImageAdapter(Context context, List<SdlImageItem> objects) {
+ super(context, R.layout.simple_listview_with_image, objects);
+ }
+
+ public SdlImageAdapter(Context context) {
+ super(context, R.layout.simple_listview_with_image);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = convertView;
+
+ if(convertView == null){
+ LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = inflater.inflate(R.layout.simple_listview_with_image, null);
+ }
+
+ SdlImageItem item = getItem(position);
+
+ if(item != null){
+ populateView(view, item);
+ }
+
+
+ return view;
+ }
+
+ /**
+ * Populate the input parent view with information from the SDL log message.
+ *
+ * @param view The view to populate
+ * @param item The data with which to populate the view
+ */
+ private void populateView(View view, SdlImageItem item){
+ ImageView iv_image = (ImageView) view.findViewById(R.id.iv_rowImage);
+ TextView tv_text = (TextView) view.findViewById(R.id.tv_rowText);
+
+ iv_image.setImageBitmap(item.getBitmap());
+ tv_text.setText(item.getImageName());
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlMessageAdapter.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlMessageAdapter.java
new file mode 100644
index 000000000..f1ead1ed1
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/adapters/SdlMessageAdapter.java
@@ -0,0 +1,94 @@
+package com.livio.sdl.adapters;
+
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import com.livio.sdl.R;
+import com.livio.sdl.SdlLogMessage;
+
+/**
+ * A custom adapter class for showing SDL log messages in a listview. The adapter shows the message
+ * name, details and a timestamp of when the message was first logged.
+ *
+ * @author Mike Burke
+ *
+ */
+public class SdlMessageAdapter extends ArrayAdapter<SdlLogMessage> {
+
+ private static final int REQUEST_COLOR = Color.BLUE;
+ private static final int RESPONSE_SUCCESS_COLOR = 0xFF2D9C08; // dark green
+ private static final int RESPONSE_FAILURE_COLOR = Color.RED;
+ private static final int NOTIFICATION_COLOR = Color.BLACK;
+
+ public SdlMessageAdapter(Context context, List<SdlLogMessage> objects) {
+ super(context, R.layout.sdl_message_listview_row, objects);
+ }
+
+ public SdlMessageAdapter(Context context) {
+ super(context, R.layout.sdl_message_listview_row);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = convertView;
+
+ if(convertView == null){
+ LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = inflater.inflate(R.layout.sdl_message_listview_row, null);
+ }
+
+ SdlLogMessage item = getItem(position);
+
+ if(item != null){
+ populateView(view, item);
+ }
+
+
+ return view;
+ }
+
+ /**
+ * Populate the input parent view with information from the SDL log message.
+ *
+ * @param view The view to populate
+ * @param item The data with which to populate the view
+ */
+ private void populateView(View view, SdlLogMessage item){
+ TextView tv_messageName = (TextView) view.findViewById(R.id.tv_messageName);
+ TextView tv_messageDetails = (TextView) view.findViewById(R.id.tv_messageDetail);
+ TextView tv_timestamp = (TextView) view.findViewById(R.id.tv_timestamp);
+
+ // set text values based on input message
+ tv_messageName.setText(item.getFunctionName());
+ tv_messageDetails.setText(item.getDetails());
+ tv_timestamp.setText(item.getTimeStamp());
+
+ String messageType = item.getMessageType();
+
+ // set the text colors based on the inputs
+ if(messageType.equals(SdlLogMessage.REQUEST)){
+ tv_messageName.setTextColor(REQUEST_COLOR);
+ }
+ else if(messageType.equals(SdlLogMessage.RESPONSE)){
+ if(item.getSuccess()){
+ tv_messageName.setTextColor(RESPONSE_SUCCESS_COLOR);
+ tv_messageDetails.setTextColor(RESPONSE_SUCCESS_COLOR);
+ }
+ else{
+ tv_messageName.setTextColor(RESPONSE_FAILURE_COLOR);
+ tv_messageDetails.setTextColor(RESPONSE_FAILURE_COLOR);
+ }
+ }
+ else if(messageType.equals(SdlLogMessage.NOTIFICATION)){
+ tv_messageName.setTextColor(NOTIFICATION_COLOR);
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseAlertDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseAlertDialog.java
new file mode 100644
index 000000000..cb776b764
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseAlertDialog.java
@@ -0,0 +1,156 @@
+package com.livio.sdl.dialogs;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+/**
+ * An abstract base class for custom alert dialogs. The class is basically
+ * just a wrapper using Android's AlertDialog.Builder class to create
+ * relatively simple custom dialogs.
+ *
+ * @author Mike Burke
+ *
+ */
+public abstract class BaseAlertDialog {
+
+ /**
+ * Allows another class to listen for the result of the subclass dialog. Results
+ * are returned as an object.
+ *
+ * @author Mike Burke
+ *
+ */
+ public interface Listener{
+ /**
+ * Should be called when data from the dialog is ready to return.
+ *
+ * @param resultData The data results for the dialog
+ */
+ void onResult(Object resultData);
+ }
+
+ /**
+ * Called after views have been inflated, allowing the subclass an opportunity
+ * to find their custom views using view.findViewById(id).
+ *
+ * @param parent The parent view which was inflated from input resource id.
+ */
+ protected abstract void findViews(View parent);
+
+ // since this is an abstract class, we'll set all variables to protected scope in case subclasses want to use them directly
+ protected Context context;
+ protected AlertDialog dialog;
+ protected Listener listener;
+ protected String title;
+ protected View view;
+ protected boolean cancelable = true;
+
+ /**
+ * Creates a BaseDialog object with context, title and resource id.
+ *
+ * @param context The context of the dialog
+ * @param title The title of the dialog
+ * @param resource The resource id used for inflating the main view of the dialog
+ */
+ public BaseAlertDialog(Context context, String title, int resource) {
+ this.context = context;
+ this.title = title;
+
+ // inflate the view and allow the subclass to locate its views
+ inflateView(resource);
+ findViews(view);
+ }
+
+ /**
+ * Creates the dialog object using the input data.
+ */
+ protected void createDialog(){
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title)
+ .setView(view)
+ .setCancelable(cancelable);
+ dialog = builder.create();
+ }
+
+ /**
+ * Creates the main dialog view from the input resource id.
+ *
+ * @param resourceId The id of the XML view to inflate
+ */
+ protected void inflateView(int resourceId){
+ //grab the system inflater to build views from XML for us
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = inflater.inflate(resourceId, null);
+ }
+
+ //public methods
+ /**
+ * Creates the dialog object if necessary and shows it.
+ */
+ public void show(){
+ if(dialog == null){
+ createDialog();
+ }
+
+ if(!dialog.isShowing()){
+ dialog.show();
+ }
+ }
+
+ /**
+ * If the dialog exists and is currently showing, dismisses it.
+ */
+ public void dismiss(){
+ if(dialog != null && dialog.isShowing()){
+ dialog.dismiss();
+ }
+ }
+
+ /**
+ * Returns whether the dialog is currently showing or not.
+ *
+ * @return True if the dialog is showing, false otherwise
+ */
+ public boolean isShowing(){
+ if(dialog == null){
+ return false;
+ }
+
+ return dialog.isShowing();
+ }
+
+ /**
+ * Sets a listener for any subclasses of BaseDialog.
+ *
+ * @param l The listener to set
+ */
+ public void setListener(Listener l){
+ listener = l;
+ }
+
+ /**
+ * Sets whether or not the dialog is allowed to be cancelled.
+ *
+ * @param cancelable True if this dialog is cancelable, false if not
+ */
+ public void setCancelable(boolean cancelable){
+ this.cancelable = cancelable;
+ if(dialog != null){
+ dialog.setCancelable(cancelable);
+ }
+ }
+
+ /**
+ * Notifies an existing dialog listener that results are ready.
+ *
+ * @param resultData Object containing dialog results
+ */
+ public void notifyListener(Object resultData){
+ if(listener != null){
+ listener.onResult(resultData);
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseImageListDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseImageListDialog.java
new file mode 100644
index 000000000..1ff6519d4
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseImageListDialog.java
@@ -0,0 +1,22 @@
+package com.livio.sdl.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.widget.ListView;
+
+import com.livio.sdl.R;
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.adapters.SdlImageAdapter;
+
+public abstract class BaseImageListDialog extends BaseAlertDialog {
+
+ protected ListView listview;
+
+ public BaseImageListDialog(Context context, String title, List<SdlImageItem> imagesNotAdded) {
+ super(context, title, R.layout.listview);
+ SdlImageAdapter adapter = new SdlImageAdapter(context, imagesNotAdded);
+ listview.setAdapter(adapter);
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseMultipleListViewDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseMultipleListViewDialog.java
new file mode 100644
index 000000000..592f6f1b3
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseMultipleListViewDialog.java
@@ -0,0 +1,66 @@
+package com.livio.sdl.dialogs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.livio.sdl.R;
+import com.livio.sdl.utils.AndroidUtils;
+
+/**
+ * A generic, abstract class representing a ListView dialog that allows the user to select
+ * multiple listview items. Selected items are stored in a protected List<E> object
+ * called selectedItems, which can be used by subclasses of this class.
+ *
+ * @author Mike Burke
+ *
+ * @param <E>
+ */
+public abstract class BaseMultipleListViewDialog<E> extends BaseOkCancelDialog {
+
+ protected ListView listView;
+ protected List<E> selectedItems = new ArrayList<E>();
+
+ public BaseMultipleListViewDialog(Context context, String title, List<E> items) {
+ super(context, title, R.layout.listview);
+ listView.setAdapter(AndroidUtils.createMultipleListViewAdapter(context, items));
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listView = (ListView) parent.findViewById(R.id.listView);
+ listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ toggleItem(((ArrayAdapter<E>) parent.getAdapter()).getItem(position));
+ }
+ });
+ }
+
+ /**
+ * Toggles the input item in the list of selected items. So, if the current item
+ * was not selected, it is added to the list; if the current item was selected,
+ * it is removed from the list.
+ *
+ * @param item
+ */
+ protected void toggleItem(E item){
+ final boolean alreadyInList = selectedItems.contains(item);
+
+ if(alreadyInList){
+ selectedItems.remove(item);
+ }
+ else{
+ selectedItems.add(item);
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseOkCancelDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseOkCancelDialog.java
new file mode 100644
index 000000000..8b5b1afb2
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseOkCancelDialog.java
@@ -0,0 +1,61 @@
+package com.livio.sdl.dialogs;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+
+import com.livio.sdl.R;
+
+/**
+ * An abstract class that represents a dialog with an OK button and a cancel button. Subclasses
+ * can extend this class to create different types of OK/Cancel dialogs. Subclasses
+ * must set positive / negative buttons in their constructor and call createDialog() as the last
+ * line of the constructor.
+ *
+ * @author Mike Burke
+ *
+ */
+public abstract class BaseOkCancelDialog extends BaseAlertDialog {
+
+ protected DialogInterface.OnClickListener okButton, cancelButton;
+
+ public BaseOkCancelDialog(Context context, String title, int resource) {
+ super(context, title, resource);
+ }
+
+ @Override
+ protected void createDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title)
+ .setView(view)
+ .setPositiveButton(context.getResources().getString(R.string.positive_button), okButton)
+ .setNegativeButton(context.getResources().getString(R.string.negative_button), cancelButton)
+ .setCancelable(cancelable);
+ dialog = builder.create();
+ }
+
+ /**
+ * Sets the positive button click listener for the dialog.
+ *
+ * @param okButton The button click listener for the positive button
+ */
+ protected void setPositiveButton(DialogInterface.OnClickListener okButton){
+ this.okButton = okButton;
+ if(dialog != null){
+ dialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getResources().getString(R.string.positive_button), okButton);
+ }
+ }
+
+ /**
+ * Sets the negative button click listener for the dialog.
+ *
+ * @param cancelButton The button click listener for the negative button
+ */
+ protected void setNegativeButton(DialogInterface.OnClickListener cancelButton){
+ this.cancelButton = cancelButton;
+ if(dialog != null){
+ dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getResources().getString(R.string.negative_button), cancelButton);
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseSingleListViewDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseSingleListViewDialog.java
new file mode 100644
index 000000000..206c2eea9
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/BaseSingleListViewDialog.java
@@ -0,0 +1,49 @@
+package com.livio.sdl.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.livio.sdl.R;
+
+/**
+ * A generic, abstract class representing a ListView dialog that allows the user to select
+ * a single listview item. The selected item is returned to the listener as a generic object.
+ *
+ * @author Mike Burke
+ *
+ * @param <E>
+ */
+public abstract class BaseSingleListViewDialog<E> extends BaseAlertDialog {
+
+ // since this class is abstract, we'll keep these variables protected so subclasses can access them directly
+ protected ListView listView;
+ protected ArrayAdapter<E> adapter;
+ protected E selectedItem;
+
+ public BaseSingleListViewDialog(Context context, String title, List<E> items) {
+ super(context, title, R.layout.listview);
+ adapter = new ArrayAdapter<E>(context, android.R.layout.simple_list_item_1, items);
+ listView.setAdapter(adapter);
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listView = (ListView) parent.findViewById(R.id.listView);
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ selectedItem = ((ArrayAdapter<E>) parent.getAdapter()).getItem(position);
+ notifyListener(selectedItem);
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ImageListDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ImageListDialog.java
new file mode 100644
index 000000000..28ee7ef94
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ImageListDialog.java
@@ -0,0 +1,35 @@
+package com.livio.sdl.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+import com.livio.sdl.R;
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.adapters.SdlImageAdapter;
+
+public class ImageListDialog extends BaseImageListDialog {
+
+ public ImageListDialog(Context context, List<SdlImageItem> imagesNotAdded) {
+ super(context, "Select an Image", imagesNotAdded);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listview = (ListView) parent.findViewById(R.id.listView);
+ listview.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ SdlImageItem selectedItem = ((SdlImageAdapter) parent.getAdapter()).getItem(position);
+ notifyListener(selectedItem);
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/IndeterminateProgressDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/IndeterminateProgressDialog.java
new file mode 100644
index 000000000..947567e97
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/IndeterminateProgressDialog.java
@@ -0,0 +1,16 @@
+package com.livio.sdl.dialogs;
+
+import android.app.ProgressDialog;
+import android.content.Context;
+
+public class IndeterminateProgressDialog extends ProgressDialog{
+
+ public IndeterminateProgressDialog(Context context, String title) {
+ super(context);
+ setCancelable(false);
+ setMessage(title);
+ setIndeterminate(true);
+ setTitle(null);
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/JsonFlipperDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/JsonFlipperDialog.java
new file mode 100644
index 000000000..cdaef739e
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/JsonFlipperDialog.java
@@ -0,0 +1,95 @@
+package com.livio.sdl.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import com.livio.sdl.R;
+import com.livio.sdl.SdlLogMessage;
+import com.livio.sdl.utils.SdlUtils;
+
+/**
+ * This dialog shows a single JSON message, but allows the ability to flip back and forth between
+ * all available messages via the arrow buttons at the bottom of the dialog. This dialog will not
+ * be updated when new messages are sent with this dialog open. The dialog must be closed and re-opened
+ * in order to refresh with new values.
+ *
+ * @author Mike Burke
+ *
+ */
+public class JsonFlipperDialog extends BaseAlertDialog {
+
+ private List<SdlLogMessage> jsonMessages;
+ private int currentPosition;
+
+ private TextView text;
+ private ImageButton leftButton, rightButton;
+
+ public JsonFlipperDialog(Context context, List<SdlLogMessage> jsonMessages, int startPosition) {
+ super(context, SdlUtils.makeJsonTitle(jsonMessages.get(startPosition).getCorrelationId()), R.layout.json_flipper_dialog);
+ this.jsonMessages = jsonMessages;
+ this.currentPosition = startPosition;
+ createDialog();
+
+ // since refresh updates the dialog's title, this must be after createDialog() so the dialog isn't null
+ refresh();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ text = (TextView) parent.findViewById(R.id.textview);
+
+ // set up left button
+ leftButton = (ImageButton) parent.findViewById(R.id.ib_moveLeft);
+ leftButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // if we can move left, do it. if not, do nothing
+ if(currentPosition > 0){
+ currentPosition--;
+ refresh();
+ }
+ }
+ });
+
+ // set up right button
+ rightButton = (ImageButton) parent.findViewById(R.id.ib_moveRight);
+ rightButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // if we can move right, do it. if not, do nothing
+ if(currentPosition < (jsonMessages.size()-1) ){
+ currentPosition++;
+ refresh();
+ }
+ }
+ });
+ }
+
+ // refresh the buttons & the text for this dialog
+ private void refresh(){
+ refreshButtons();
+ refreshText();
+ }
+
+ // refreshes the buttons with new position. disables the buttons when we're at the edges of the list.
+ private void refreshButtons(){
+ boolean atStart = (currentPosition == 0);
+ boolean atEnd = (currentPosition == jsonMessages.size()-1);
+
+ leftButton.setEnabled(!atStart);
+ rightButton.setEnabled(!atEnd);
+ }
+
+ // refreshes the text of the dialog - both the title and the main text.
+ private void refreshText(){
+ SdlLogMessage currentMessage = jsonMessages.get(currentPosition);
+ dialog.setTitle(SdlUtils.makeJsonTitle(currentMessage.getCorrelationId()));
+ text.setText(currentMessage.getJsonData());
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ListViewDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ListViewDialog.java
new file mode 100644
index 000000000..3bdcb55ae
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/ListViewDialog.java
@@ -0,0 +1,21 @@
+package com.livio.sdl.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+/**
+ * A generic class representing a ListView dialog that allows the user to select
+ * a single listview item. The selected item is returned to the listener as a generic object.
+ *
+ * @author Mike Burke
+ *
+ * @param <E>
+ */
+public class ListViewDialog<E> extends BaseSingleListViewDialog<E> {
+
+ public ListViewDialog(Context context, String title, List<E> items) {
+ super(context, title, items);
+ createDialog();
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/MultipleListViewDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/MultipleListViewDialog.java
new file mode 100644
index 000000000..4f5b91027
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/MultipleListViewDialog.java
@@ -0,0 +1,32 @@
+package com.livio.sdl.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.content.DialogInterface;
+/**
+ * A generic class representing a ListView dialog that allows the user to select
+ * multiple listview items. Selected items are stored in a protected List<E> object
+ * called selectedItems, which can be used by subclasses of this class.
+ *
+ * @author Mike Burke
+ *
+ * @param <E>
+ */
+public class MultipleListViewDialog<E> extends BaseMultipleListViewDialog<E> {
+
+ public MultipleListViewDialog(Context context, String title, List<E> items) {
+ super(context, title, items);
+ setPositiveButton(positiveButton);
+ createDialog();
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener positiveButton = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ notifyListener(selectedItems);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/SingleJsonDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/SingleJsonDialog.java
new file mode 100644
index 000000000..ac32ae4a2
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/SingleJsonDialog.java
@@ -0,0 +1,23 @@
+package com.livio.sdl.dialogs;
+
+import android.content.Context;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.livio.sdl.SdlLogMessage;
+import com.livio.sdl.utils.SdlUtils;
+
+public class SingleJsonDialog extends TextViewAlertDialog {
+ public SingleJsonDialog(Context context, SdlLogMessage logMessage) {
+ super(context, SdlUtils.makeJsonTitle(logMessage.getCorrelationId()), logMessage.getJsonData());
+
+ // click listener to close dialog when the text is clicked
+ tv.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewAlertDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewAlertDialog.java
new file mode 100644
index 000000000..0a19ce26a
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewAlertDialog.java
@@ -0,0 +1,31 @@
+package com.livio.sdl.dialogs;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.TextView;
+
+import com.livio.sdl.R;
+
+/**
+ * A simple dialog that contains a single, scrollable TextView. The textview
+ * is initialized with a the title and message in the constructor.
+ *
+ * @author Mike Burke
+ *
+ */
+public class TextViewAlertDialog extends BaseAlertDialog {
+
+ protected TextView tv;
+
+ public TextViewAlertDialog(Context context, String dialogTitle, String message){
+ super(context, dialogTitle, R.layout.textview);
+ tv.setText(message);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ tv = (TextView) parent.findViewById(R.id.textview);
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewOkCancelDialog.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewOkCancelDialog.java
new file mode 100644
index 000000000..8d4fadc15
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/dialogs/TextViewOkCancelDialog.java
@@ -0,0 +1,48 @@
+package com.livio.sdl.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.TextView;
+
+import com.livio.sdl.R;
+
+/**
+ * A simple dialog that contains a single, scrollable TextView. The textview
+ * is initialized with a the title and message in the constructor.
+ *
+ * @author Mike Burke
+ *
+ */
+public class TextViewOkCancelDialog extends BaseOkCancelDialog {
+
+ protected TextView tv;
+
+ public TextViewOkCancelDialog(Context context, String dialogTitle, String message){
+ super(context, dialogTitle, R.layout.textview);
+ tv.setText(message);
+ setPositiveButton(okListener);
+ setNegativeButton(cancelListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ tv = (TextView) parent.findViewById(R.id.textview);
+ }
+
+ private DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ notifyListener(true);
+ }
+ };
+
+ private DialogInterface.OnClickListener cancelListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ notifyListener(false);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumClickListener.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumClickListener.java
new file mode 100644
index 000000000..f32a89f91
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumClickListener.java
@@ -0,0 +1,11 @@
+package com.livio.sdl.enums;
+
+/**
+ * Defines a callback for click events that occur on an enumerated type.
+ *
+ * @author Mike Burke
+ *
+ */
+public interface EnumClickListener {
+ <E extends Enum<E>> void OnEnumItemClicked(E selection);
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumComparator.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumComparator.java
new file mode 100644
index 000000000..7d98a69a9
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/EnumComparator.java
@@ -0,0 +1,20 @@
+package com.livio.sdl.enums;
+
+import java.util.Comparator;
+
+/**
+ * This class is simply a comparator for enum values using their toString()
+ * method instead of the standard name() method.
+ *
+ * This allows you to sort your enums by their friendly names, which is much
+ * more useful than sorting by the enum name.
+ *
+ * @author Mike Burke
+ *
+ */
+public class EnumComparator<E extends Enum<E>> implements Comparator<E> {
+ @Override
+ public int compare(E lhs, E rhs) {
+ return lhs.toString().compareTo(rhs.toString());
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlButton.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlButton.java
new file mode 100644
index 000000000..bf25318b6
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlButton.java
@@ -0,0 +1,234 @@
+package com.livio.sdl.enums;
+
+import java.util.Arrays;
+
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+
+/**
+ * <p>
+ * Defines logical buttons which, on a given SDL unit, would correspond to
+ * either physical or soft (touchscreen) buttons. These logical buttons present
+ * a standard functional abstraction which the developer can rely upon,
+ * independent of the SYNC unit. For example, the developer can rely upon the OK
+ * button having the same meaning to the user across SDL platforms.
+ * </p>
+ * <p>
+ * The preset buttons (0-9) can typically be interpreted by the application as
+ * corresponding to some user-configured choices, though the application is free
+ * to interpret these button presses as it sees fit.
+ * </p>
+ * <p>
+ * The application can discover which buttons a given SDL unit implements by
+ * interrogating the ButtonCapabilities parameter of the
+ * RegisterAppInterface response.
+ * </p>
+ *
+ * @since SmartDeviceLink 1.0
+ */
+public enum SdlButton {
+
+ /**
+ * Represents the button usually labeled "OK". A typical use of this button
+ * is for the user to press it to make a selection.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ OK ("Ok"),
+ /**
+ * Represents the seek-left button. A typical use of this button is for the
+ * user to scroll to the left through menu choices one menu item per press.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ SEEK_LEFT ("Seek Left"),
+ /**
+ * Represents the seek-right button. A typical use of this button is for the
+ * user to scroll to the right through menu choices one menu item per press.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ SEEK_RIGHT ("Seek Right"),
+ /**
+ * Represents a turn of the tuner knob in the clockwise direction one tick.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ TUNE_UP ("Tune Up"),
+ /**
+ * Represents a turn of the tuner knob in the counter-clockwise direction
+ * one tick.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ TUNE_DOWN ("Tune Down"),
+ /**
+ * Represents the preset 0 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_0 ("Preset #0"),
+ /**
+ * Represents the preset 1 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_1 ("Preset #1"),
+ /**
+ * Represents the preset 2 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_2 ("Preset #2"),
+ /**
+ * Represents the preset 3 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_3 ("Preset #3"),
+ /**
+ * Represents the preset 4 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_4 ("Preset #4"),
+ /**
+ * Represents the preset 5 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_5 ("Preset #5"),
+ /**
+ * Represents the preset 6 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_6 ("Preset #6"),
+ /**
+ * Represents the preset 7 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_7 ("Preset #7"),
+ /**
+ * Represents the preset 8 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_8 ("Preset #8"),
+ /**
+ * Represents the preset 9 button.
+ *
+ * @since SmartDeviceLink 1.0
+ */
+ PRESET_9 ("Preset #9"),
+
+ ;
+
+ private final String READABLE_NAME;
+
+ private SdlButton(String readableName){
+ this.READABLE_NAME = readableName;
+ }
+
+ /**
+ * Returns an array of the objects in this enum sorted in alphabetical order.
+ *
+ * @return The sorted array
+ */
+ public static SdlButton[] getSortedArray(){
+ SdlButton[] result = values();
+ Arrays.sort(result, new EnumComparator<SdlButton>());
+ return result;
+ }
+
+ /**
+ * Translates a legacy button (ButtonName) to this type of button (SdlButton).
+ *
+ * @param legacyButton The legacy button to translate
+ * @return The appropriate SdlButton for the input
+ */
+ public static SdlButton translateFromLegacy(ButtonName legacyButton){
+ switch(legacyButton){
+ case OK:
+ return OK;
+ case SEEKLEFT:
+ return SEEK_LEFT;
+ case SEEKRIGHT:
+ return SEEK_RIGHT;
+ case TUNEDOWN:
+ return TUNE_DOWN;
+ case TUNEUP:
+ return TUNE_UP;
+ case PRESET_0:
+ return PRESET_0;
+ case PRESET_1:
+ return PRESET_1;
+ case PRESET_2:
+ return PRESET_2;
+ case PRESET_3:
+ return PRESET_3;
+ case PRESET_4:
+ return PRESET_4;
+ case PRESET_5:
+ return PRESET_5;
+ case PRESET_6:
+ return PRESET_6;
+ case PRESET_7:
+ return PRESET_7;
+ case PRESET_8:
+ return PRESET_8;
+ case PRESET_9:
+ return PRESET_9;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Translates this type of button (SdlButton) to a legacy button (ButtonName).
+ *
+ * @param sdlButton The new button to translate
+ * @return The appropriate legacy button for the input
+ */
+ public static ButtonName translateToLegacy(SdlButton sdlButton){
+ switch(sdlButton){
+ case OK:
+ return ButtonName.OK;
+ case SEEK_LEFT:
+ return ButtonName.SEEKLEFT;
+ case SEEK_RIGHT:
+ return ButtonName.SEEKRIGHT;
+ case TUNE_DOWN:
+ return ButtonName.TUNEDOWN;
+ case TUNE_UP:
+ return ButtonName.TUNEUP;
+ case PRESET_0:
+ return ButtonName.PRESET_0;
+ case PRESET_1:
+ return ButtonName.PRESET_1;
+ case PRESET_2:
+ return ButtonName.PRESET_2;
+ case PRESET_3:
+ return ButtonName.PRESET_3;
+ case PRESET_4:
+ return ButtonName.PRESET_4;
+ case PRESET_5:
+ return ButtonName.PRESET_5;
+ case PRESET_6:
+ return ButtonName.PRESET_6;
+ case PRESET_7:
+ return ButtonName.PRESET_7;
+ case PRESET_8:
+ return ButtonName.PRESET_8;
+ case PRESET_9:
+ return ButtonName.PRESET_9;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String toString(){
+ return this.READABLE_NAME;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlCommand.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlCommand.java
new file mode 100644
index 000000000..a9c687c78
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlCommand.java
@@ -0,0 +1,224 @@
+package com.livio.sdl.enums;
+
+
+/**
+ * This is an enumerated list of SDL commands, complete with world-readable names that can be used
+ * in dialogs and menus.
+ *
+ * @author Mike Burke
+ *
+ */
+public enum SdlCommand{
+ /**
+ * SDL Alert command shows a pop-up text and/or voice alert on the head-unit.
+ * @see Alert
+ */
+ ALERT ("Alert"),
+ /**
+ * SDL Speak command performs a Text-to-Speech action on the input text and speaks the input to the user over the vehicle speakers.
+ * @see Speak
+ */
+ SPEAK ("Speak"),
+ /**
+ * SDL Show command updates the main HMI template used for the application. Show is capable of showing up to 4 lines of text.
+ * @see Show
+ */
+ SHOW ("Show"),
+ /**
+ * SDL SubscribeButton command subscribes your application to receive hardware button-presses. For example, when the application is in-use on the head-unit,
+ * and the user presses the PRESET_0 button, your app can intercept that button press and take action on it.
+ * @see SubscribeButton
+ * @see UnsubscribeButton
+ * @see SdlButton
+ * @see ButtonName
+ */
+ SUBSCRIBE_BUTTON ("Subscribe to Buttons"),
+ /**
+ * SDL UnsubscribeButton command unsubscribes your application from any buttons that have been subscribed to.
+ * @see UnsubscribeButton
+ * @see SdlButton
+ * @see ButtonName
+ */
+ UNSUBSCRIBE_BUTTON ("Unsubscribe from Buttons"),
+ /**
+ * SDL AddCommand command creates a software button that will be added into the app's menu on the head-unit. Commands can be added to the root-level
+ * menu or to submenus that have been added by the application.
+ * @see AddCommand
+ * @see DeleteCommand
+ * @see MenuItem
+ * @see CommandButton
+ */
+ ADD_COMMAND ("Add a Command"),
+ /**
+ * SDL DeleteCommand command deletes a command menu item that has been added by the application.
+ * @see DeleteCommand
+ * @see AddCommand
+ */
+ DELETE_COMMAND ("Delete a Command"),
+ /**
+ * SDL AddSubmenu command creates a menu object inside the application's main root-level menu on the head-unit. Submenus can only be added to the
+ * root-level menu, they cannot be nested.
+ * @see AddSubmenu
+ * @see DeleteSubmenu
+ * @see MenuItem
+ * @see SubmenuButton
+ */
+ ADD_SUBMENU ("Add a Submenu"),
+ /**
+ * SDL DeleteSubmenu command deletes a submenu menu item that has been added by the application.
+ * @see DeleteSubmenu
+ * @see AddSubmenu
+ */
+ DELETE_SUB_MENU ("Delete a Submenu"),
+ /**
+ * SDL SetGlobalProperties command manually sets global voice properties for your application. For example,
+ * this command allows the application to change voice prompts for when the user needs help or when a timeout occurs.
+ * @see SetGlobalProperties
+ * @see ResetGlobalProperties
+ */
+// SET_GLOBAL_PROPERTIES ("Set Global Properties"),
+ /**
+ * SDL ResetGlobalProperties command resets global voice properties for your application to the vehicle's default settings.
+ * @see ResetGlobalProperties
+ * @see SetGlobalProperties
+ */
+// RESET_GLOBAL_PROPERTIES ("Reset Global Properties"),
+ /**
+ * SDL SetMediaClockTimer command allows media applications to set a counter that automatically counts up or counts down on the head-unit. The command
+ * also allows the application to pause, resume or clear a previously-existing counter.
+ * @see SetMediaClockTimer
+ */
+ SET_MEDIA_CLOCK_TIMER ("Set Media Clock Timer"),
+ /**
+ * SDL CreateInteractionChoiceSet command allows the application to create a pop-up menu on the head-unit. The set must add the various choices
+ * available for the pop-up menu, including text and an image.
+ * @see CreateInteractionChoiceSet
+ * @see PerformInteraction
+ * @see DeleteInteractionChoiceSet
+ */
+ CREATE_INTERACTION_CHOICE_SET ("Create Interaction Choice Set"),
+ /**
+ * SDL DeleteInteractionChoiceSet command allows the application to delete a choice set that has previously been added through the CreateInteractionChoiceSet command.
+ * @see DeleteInteractionChoiceSet
+ * @see CreateInteractionChoiceSet
+ */
+ DELETE_INTERACTION_CHOICE_SET ("Delete Interaction Choice Set"),
+ /**
+ * SDL PerformInteraction command allows the application to show 1 or more choice sets that have been added through the CreateInteractionChoiceSet command.
+ * @see PerformInteraction
+ * @see CreateInteractionChoiceSet
+ */
+ PERFORM_INTERACTION ("Perform Interaction"),
+ /**
+ * SDL Slider command allows an application to show a volume-style slider element on the head-unit. The application can select how many ticks the slider should have,
+ * the default selection for the timer and a timeout.
+ * @see Slider
+ */
+ SLIDER ("Slider"),
+ /**
+ * SDL ScrollableMessage command allows an application to show a long message that the user may need to scroll through. The message has the ability to show soft buttons
+ * alongside the message.
+ * @see ScrollableMessage
+ */
+ SCROLLABLE_MESSAGE ("Scrollable Message"),
+ /**
+ * SDL ChangeRegistration command allows the application to change the default language for the application on the head-unit.
+ * @see ChangeRegistration
+ */
+ CHANGE_REGISTRATION ("Change Registration"),
+ /**
+ * SDL PutFile command allows the application to send files to be stored on the head-unit. Bitmap, JPEG and PNG images can be sent, in addition to WAV or MP3 formatted audio.
+ * @see PutFile
+ * @see DeleteFile
+ * @see ListFiles
+ * @see SetAppIcon
+ */
+ PUT_FILE ("Put File"),
+ /**
+ * SDL DeleteFile command allows the application to delete files from the head-unit that were previously added to the head-unit through the PutFile command.
+ * @see DeleteFile
+ * @see PutFile
+ * @see ListFiles
+ */
+ DELETE_FILE ("Delete File"),
+ /**
+ * SDL ListFiles command allows the application to retreive a list of files that have been added to the head-unit through the PutFile command.
+ * @see ListFiles
+ * @see PutFile
+ * @see DeleteFile
+ */
+ LIST_FILES ("List Files"),
+ /**
+ * SDL SetAppIcon command allows the application to set an image to be associated with the application. Desired image must first be sent through the PutFile command.
+ * @see SetAppIcon
+ * @see PutFile
+ */
+ SET_APP_ICON ("Set App Icon"),
+ /**
+ * SDL PerformAudioPassThru command allows vehicle voice-inputs from the user to be forwarded to the application. This is useful if the application wants to perform
+ * its own voice-rec implementation to decode user voice-inputs.
+ * @see PerformAudioPassThru
+ * @see EndAudioPassThru
+ */
+ //PERFORM_AUDIO_PASSTHRU ("Perform Audio Pass-through"),
+ /**
+ * SDL EndAudioPassThru command ends a voice-input session previously started through the PerformAudioPassThru command.
+ */
+ //END_AUDIO_PASSTHRU ("End Audio Pass-through"),
+ /**
+ * SDL SubscribeVehicleData command allows the application to subscribe for updates about various vehicle data. For example, fuel state, tire pressure, airbag status and much
+ * more information can be retrieved from the vehicle head-unit and used in the application.
+ * @see SubscribeVehicleData
+ * @see UnsubscribeVehicleData
+ * @see GetVehicleData
+ */
+// SUBSCRIBE_VEHICLE_DATA ("Subscribe to Vehicle Data"),
+ /**
+ * SDL UnsubscribeVehicleData command allows the application to unsubscribe from any vehicle data that has been subscribed to through the SubscribeVehicleData command.
+ * @see UnsubscribeVehicleData
+ * @see SubscribeVehicleData
+ * @see GetVehicleData
+ */
+// UNSUBSCRIBE_VEHICLE_DATA ("Unsubscribe from Vehicle Data"),
+ /**
+ * SDL GetVehicleData command allows the application to retrieve vehicle data from the head-unit. Prior to sending this command, the application must first subscribe
+ * for vehicle data through the SubscribeVehicleData command.
+ * @see GetVehicleData
+ * @see SubscribeVehicleData
+ * @see UnsubcribeVehicleData
+ */
+// GET_VEHICLE_DATA ("Get Vehicle Data"),
+ /**
+ * SDL ReadDID command allows the application to query DIDs for a particular module. DIDs contain useful information for each module, such as part numbers.
+ * @see ReadDID
+ */
+ READ_DIDS ("Read DIDs"),
+ /**
+ * SDL GetDTCs command allows the application to query DTCs for a particular module. DTCs contain diagnostic trouble codes that are set when something
+ * goes wrong in a module.
+ * @see GetDTCs
+ */
+ GET_DTCS ("Get DTCs"),
+ //SHOW_CONSTANT_TBT ("Show Constant TBT"), //TODO - this command doesn't work on SDL core as of 1/23/2014
+ //ALERT_MANEUVER ("Alert Maneuver"), //TODO - this command doesn't work on SDL core as of 1/23/2014
+ //UPDATE_TURN_LIST ("Update Turn List"), //TODO - this command doesn't work on SDL core as of 1/23/2014
+ //DIAL_NUMBER ("Dial Number"), //TODO - this command doesn't work on SDL core as of 1/23/2014
+
+ //Future commands go here.
+
+ ;
+
+ // THIS IS AN ENUM, SO BASICALLY EVERYTHING SHOULD BE FINAL.
+ private final String friendlyName;
+
+ //constructor
+ private SdlCommand(String readableName){
+ this.friendlyName = readableName;
+ }
+
+ @Override
+ public String toString(){
+ return this.friendlyName;
+ }
+}
+
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlImageType.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlImageType.java
new file mode 100644
index 000000000..e24828dda
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlImageType.java
@@ -0,0 +1,92 @@
+package com.livio.sdl.enums;
+
+import java.util.EnumSet;
+
+import com.smartdevicelink.proxy.rpc.enums.ImageType;
+
+
+/**
+ * An enumerated class representing image types on the head-unit. Images can be either
+ * static or dynamic. Static images are contained solely on the head-unit and cannot be
+ * updated or deleted. Dynamic images are contained in the application and sent to the
+ * head-unit through the PutFile command.
+ *
+ * @see ImageType
+ *
+ * @author Mike Burke
+ *
+ */
+public enum SdlImageType {
+
+ /**
+ * Represents a dynamic image. Dynamic images are sent from the application to the head-unit
+ * through the PutFile command and can be referenced by the name sent with PutFile.
+ */
+ DYNAMIC("Dynamic"),
+ /**
+ * Represents a static image. Static images are stored exclusively on the head-unit and cannot
+ * be changed or deleted by applications.
+ */
+ STATIC("Static"),
+
+ ;
+
+ private final String READABLE_NAME;
+ private SdlImageType(String name){
+ this.READABLE_NAME = name;
+ }
+
+ /**
+ * Translates an SdlImageType object to it's associated ImageType object.
+ *
+ * @param input The SdlImageType to convert
+ * @return The associated ImageType
+ */
+ public static ImageType translateToLegacy(SdlImageType input){
+ switch(input){
+ case STATIC:
+ return ImageType.STATIC;
+ case DYNAMIC:
+ return ImageType.DYNAMIC;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Translates an SdlImageType object to it's associated ImageType object.
+ *
+ * @param input The SdlImageType to convert
+ * @return The associated ImageType
+ */
+ public static SdlImageType translateFromLegacy(ImageType input){
+ switch(input){
+ case STATIC:
+ return STATIC;
+ case DYNAMIC:
+ return DYNAMIC;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Allows a reverse-lookup based on the items readable name.
+ *
+ * @param readableName The item's readable name
+ * @return The item with the input readable name if found, null otherwise
+ */
+ public static SdlImageType lookupByReadableName(String readableName) {
+ for (SdlImageType anEnum : EnumSet.allOf(SdlImageType.class)) {
+ if (anEnum.toString().equals(readableName)) {
+ return anEnum;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString(){
+ return this.READABLE_NAME;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlInteractionMode.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlInteractionMode.java
new file mode 100644
index 000000000..cc9825ebc
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlInteractionMode.java
@@ -0,0 +1,78 @@
+package com.livio.sdl.enums;
+
+import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
+
+/**
+ * Represents different types of interaction modes that the user can utilize
+ * when performing an interaction on a choice set list.
+ *
+ * @see InteractionMode
+ *
+ * @author Mike Burke
+ *
+ */
+public enum SdlInteractionMode {
+
+ /**
+ * Represents perform interactions that can only be controlled manually by button click events.
+ */
+ MANUAL_ONLY("Click events"),
+ /**
+ * Represents perform interactions that can only be controlled by voice-rec events.
+ */
+ VOICE_REC_ONLY("Voice-rec events"),
+ /**
+ * Represents perform interactions that can be controlled by click events and voice-rec events.
+ */
+ BOTH("Click and voice-rec events"),
+ ;
+
+ private final String friendlyName;
+
+ private SdlInteractionMode(String str){
+ this.friendlyName = str;
+ }
+
+ /**
+ * Translates an input SdlInteractionMode to its associated InteractionMode object.
+ *
+ * @param from The SdlInteractionMode to translate
+ * @return The InteractionMode object associated with the input SdlInteractionMode object
+ */
+ public static InteractionMode translateToLegacy(SdlInteractionMode from){
+ switch(from){
+ case MANUAL_ONLY:
+ return InteractionMode.MANUAL_ONLY;
+ case VOICE_REC_ONLY:
+ return InteractionMode.VR_ONLY;
+ case BOTH:
+ return InteractionMode.BOTH;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Translates an input InteractionMode to its associated SdlInteractionMode object.
+ *
+ * @param from The InteractionMode to translate
+ * @return The SdlInteractionMode object associated with the input InteractionMode object
+ */
+ public static SdlInteractionMode translateFromLegacy(InteractionMode from){
+ switch(from){
+ case MANUAL_ONLY:
+ return MANUAL_ONLY;
+ case VR_ONLY:
+ return VOICE_REC_ONLY;
+ case BOTH:
+ return BOTH;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String toString(){
+ return friendlyName;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlLanguage.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlLanguage.java
new file mode 100755
index 000000000..c88d90dfb
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlLanguage.java
@@ -0,0 +1,165 @@
+package com.livio.sdl.enums;
+
+import java.util.EnumSet;
+
+/**
+ * Specifies the language to be used for TTS, VR, displayed messages/menus
+ * <p>
+ *
+ * @since SmartDeviceLink 1.0
+ *
+ */
+public enum SdlLanguage {
+ /**
+ * American English
+ */
+ EN_US("EN-US"),
+ /**
+ * Australian English
+ */
+ EN_AU("EN-AU"),
+ /**
+ * British English
+ */
+ EN_GB("EN-GB"),
+
+ /**
+ * Mexico Spanish
+ */
+ ES_MX("ES-MX"),
+ /**
+ * Spain Spanish
+ */
+ ES_ES("ES-ES"),
+
+ /**
+ * France French
+ */
+ FR_FR("FR-FR"),
+ /**
+ * Canadian French
+ */
+ FR_CA("FR-CA"),
+
+ /**
+ * German
+ */
+ DE_DE("DE-DE"),
+
+ /**
+ * Russian
+ */
+ RU_RU("RU-RU"),
+
+ /**
+ * Turkish
+ */
+ TR_TR("TR-TR"),
+
+ /**
+ * Polish
+ */
+ PL_PL("PL-PL"),
+
+ /**
+ * Italian
+ */
+ IT_IT("IT-IT"),
+
+ /**
+ * Swedish
+ */
+ SV_SE("SV-SE"),
+
+ /**
+ * Portuguese
+ */
+ PT_PT("PT-PT"),
+ /**
+ * Brazilian Portuguese
+ */
+ PT_BR("PT-BR"),
+
+ /**
+ * Dutch
+ */
+ NL_NL("NL-NL"),
+
+ /**
+ * Traditional Chinese
+ */
+ ZH_CN("ZH-CN"),
+ /**
+ * Thai Chinese
+ */
+ ZH_TW("ZH-TW"),
+
+ /**
+ * Japanese
+ */
+ JA_JP("JA-JP"),
+
+ /**
+ * Arabic
+ */
+ AR_SA("AR-SA"),
+
+ /**
+ * Korean
+ */
+ KO_KR("KO-KR"),
+
+ /**
+ * Czech
+ */
+ CS_CZ("CS-CZ"),
+
+ /**
+ * Danish
+ */
+ DA_DK("DA-DK"),
+
+ /**
+ * Norwegian
+ */
+ NO_NO("NO-NO"),
+
+ // future languages go here
+
+ ;
+
+ private final String READABLE_NAME;
+
+ private SdlLanguage(String readableName) {
+ this.READABLE_NAME = readableName;
+ }
+
+ //public member methods
+ public String getReadableName(){
+ return this.READABLE_NAME;
+ }
+
+ /**
+ * Returns a Language's name. This method iterates through every enum in the list,
+ * but it won't be used as often as SyncCommand, so we'll sacrifice the reverse look-up
+ * HashMap in this case. Without the HashMap, this method will certainly take longer
+ * to run, but it isn't worth the memory hit since languages will be used
+ * much less often than SyncCommand.
+ *
+ * @param value a String
+ * @return Language -EN-US, ES-MX or FR-CA
+ */
+ public static SdlLanguage lookupByReadableName(String readableName) {
+ for (SdlLanguage anEnum : EnumSet.allOf(SdlLanguage.class)) {
+ if (anEnum.getReadableName().equals(readableName)) {
+ return anEnum;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString(){
+ return this.READABLE_NAME;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlSpeechCapability.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlSpeechCapability.java
new file mode 100644
index 000000000..f99379b3e
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlSpeechCapability.java
@@ -0,0 +1,97 @@
+package com.livio.sdl.enums;
+
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+
+
+/**
+ * Specifies different types of text-to-speech capabilities available through SmartDeviceLink. When
+ * an application wants the vehicle to speak something to the user, the application can deliver
+ * any of the input strings formatted according to the options in this enumerated class.
+ *
+ * @see SpeechCapabilities
+ *
+ * @author Mike Burke
+ */
+public enum SdlSpeechCapability {
+ /**
+ * Represents a standard text-to-speech translation.
+ */
+ TEXT("Text"),
+ /**
+ * Represents a SAPI phoneme text string.
+ */
+ SAPI_PHONEMES("SAPI Phonemes"),
+ /**
+ * Represents a LHPLUS phoneme text string.
+ */
+ LHPLUS_PHONEMES("LHPLUS Phonemes"),
+ /**
+ * Represents a pre-recorded text entry stored exclusively on the head-unit.
+ */
+ PRE_RECORDED("Pre-recorded"),
+ /**
+ * Represents a period of silence, for example, a pause between sentences.
+ */
+ SILENCE("Silence"),
+
+ // future languages go here
+
+ ;
+
+ private final String READABLE_NAME;
+
+ private SdlSpeechCapability(String readableName) {
+ this.READABLE_NAME = readableName;
+ }
+
+ /**
+ * Translates the input SpeechCapabilities object into an SdlSpeechCapability object.
+ *
+ * @param input The SpeechCapabilities object to translate
+ * @return The translated SdlSpeechCapability object
+ */
+ public static SdlSpeechCapability translateFromLegacy(SpeechCapabilities input){
+ switch(input){
+ case TEXT:
+ return TEXT;
+ case LHPLUS_PHONEMES:
+ return LHPLUS_PHONEMES;
+ case SAPI_PHONEMES:
+ return SAPI_PHONEMES;
+ case SILENCE:
+ return SILENCE;
+ case PRE_RECORDED:
+ return PRE_RECORDED;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Translates the input SdlSpeechCapabilities object into a SpeechCapability object.
+ *
+ * @param input The SdlSpeechCapabilities object to translate
+ * @return The translated SpeechCapability object
+ */
+ public static SpeechCapabilities translateToLegacy(SdlSpeechCapability input){
+ switch(input){
+ case TEXT:
+ return SpeechCapabilities.TEXT;
+ case LHPLUS_PHONEMES:
+ return SpeechCapabilities.LHPLUS_PHONEMES;
+ case SAPI_PHONEMES:
+ return SpeechCapabilities.SAPI_PHONEMES;
+ case SILENCE:
+ return SpeechCapabilities.SILENCE;
+ case PRE_RECORDED:
+ return SpeechCapabilities.PRE_RECORDED;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String toString(){
+ return this.READABLE_NAME;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTextAlignment.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTextAlignment.java
new file mode 100644
index 000000000..43830fbe6
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTextAlignment.java
@@ -0,0 +1,81 @@
+package com.livio.sdl.enums;
+
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+
+/**
+ * Used in the Show command, the text alignment input tells the head-unit how
+ * to align the text that is being updated in the Show command.
+ *
+ * @see TextAlignment
+ *
+ * @author Mike Burke
+ *
+ */
+public enum SdlTextAlignment {
+ /**
+ * No change in alignment.
+ */
+ NO_SELECTION("No selection"),
+ /**
+ * Text aligned left.
+ */
+ LEFT_ALIGNED("Left-align"),
+ /**
+ * Text aligned right.
+ */
+ RIGHT_ALIGNED("Right-align"),
+ /**
+ * Text aligned centered.
+ */
+ CENTERED("Center"),
+
+ ;
+
+ private final String READABLE_NAME;
+ private SdlTextAlignment(String readableName){
+ this.READABLE_NAME = readableName;
+ }
+
+ /**
+ * Translates the input TextAlignment object into an SdlTextAlignment object.
+ *
+ * @param input The TextAlignment object to translate
+ * @return The associated SdlTextAlignment object
+ */
+ public static SdlTextAlignment translateFromLegacy(TextAlignment input){
+ switch(input){
+ case LEFT_ALIGNED:
+ return LEFT_ALIGNED;
+ case RIGHT_ALIGNED:
+ return RIGHT_ALIGNED;
+ case CENTERED:
+ return CENTERED;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Translates the input SdlTextAlignment object into a TextAlignment object.
+ *
+ * @param input The SdlTextAlignment object to translate
+ * @return The associated TextAlignment object
+ */
+ public static TextAlignment translateToLegacy(SdlTextAlignment input){
+ switch(input){
+ case LEFT_ALIGNED:
+ return TextAlignment.LEFT_ALIGNED;
+ case RIGHT_ALIGNED:
+ return TextAlignment.RIGHT_ALIGNED;
+ case CENTERED:
+ return TextAlignment.CENTERED;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String toString(){
+ return this.READABLE_NAME;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTransportType.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTransportType.java
new file mode 100644
index 000000000..45e57761b
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlTransportType.java
@@ -0,0 +1,36 @@
+package com.livio.sdl.enums;
+
+
+/**
+ * Represents the different types of connections that are available through SmartDeviceLink.
+ *
+ * @author Mike Burke
+ *
+ */
+public enum SdlTransportType {
+ /**
+ * Represents a BlueTooth connection with SmartDeviceLink.
+ */
+ BLUETOOTH("Bluetooth"),
+ /**
+ * Represents a WiFi connection with SmartDeviceLink.
+ */
+ WIFI("WiFi"),
+ /**
+ * Represents a USB connection with SmartDeviceLink.
+ */
+ USB("USB"),
+
+ ;
+
+ private final String name;
+
+ private SdlTransportType(String friendlyName){
+ this.name = friendlyName;
+ }
+
+ @Override
+ public String toString(){
+ return name;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlUpdateMode.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlUpdateMode.java
new file mode 100644
index 000000000..c1ef7ed62
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlUpdateMode.java
@@ -0,0 +1,93 @@
+package com.livio.sdl.enums;
+
+import com.smartdevicelink.proxy.rpc.enums.UpdateMode;
+
+/**
+ * Represents various different types of actions on the media clock
+ * counter for media applications.
+ *
+ * @see UpdateMode
+ * @see SetMediaClockTimer
+ *
+ * @author Mike Burke
+ *
+ */
+public enum SdlUpdateMode {
+ /**
+ * Indicates that the timer should start counting up from the input time.
+ */
+ COUNT_UP("Count up"),
+ /**
+ * Indicates that the timer should start counting down from the input time.
+ */
+ COUNT_DOWN("Count down"),
+ /**
+ * Indicates that the timer should be paused.
+ */
+ PAUSE("Pause"),
+ /**
+ * Indicates that the timer should be resumed.
+ */
+ RESUME("Resume"),
+ /**
+ * Indicates that the timer should be cleared.
+ */
+ CLEAR("Clear"),
+ ;
+
+ private final String friendlyName;
+ private SdlUpdateMode(String friendlyName){
+ this.friendlyName = friendlyName;
+ }
+
+ @Override
+ public String toString(){
+ return this.friendlyName;
+ }
+
+ /**
+ * Translates the input SdlUpdateMode to the associated UpdateMode object.
+ *
+ * @param input The SdlUpdateMode object to translate
+ * @return The translated UpdateMode object
+ */
+ public static UpdateMode translateToLegacy(SdlUpdateMode input){
+ switch(input){
+ case COUNT_UP:
+ return UpdateMode.COUNTUP;
+ case COUNT_DOWN:
+ return UpdateMode.COUNTDOWN;
+ case PAUSE:
+ return UpdateMode.PAUSE;
+ case RESUME:
+ return UpdateMode.RESUME;
+ case CLEAR:
+ return UpdateMode.CLEAR;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Translates the input UpdateMode to the associated SdlUpdateMode object.
+ *
+ * @param input The UpdateMode object to translate
+ * @return The translated SdlUpdateMode object
+ */
+ public static SdlUpdateMode translateFromLegacy(UpdateMode input){
+ switch(input){
+ case COUNTUP:
+ return COUNT_UP;
+ case COUNTDOWN:
+ return COUNT_DOWN;
+ case PAUSE:
+ return PAUSE;
+ case RESUME:
+ return RESUME;
+ case CLEAR:
+ return CLEAR;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlVehicleData.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlVehicleData.java
new file mode 100644
index 000000000..4bc0a8689
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/enums/SdlVehicleData.java
@@ -0,0 +1,54 @@
+package com.livio.sdl.enums;
+
+public enum SdlVehicleData {
+
+ GPS("GPS Data"),
+ SPEED("Speed"),
+ RPM("Engine RPM"),
+ FUEL_LEVEL("Fuel Level"),
+ FUEL_LEVEL_STATE("Fuel Level State"),
+ FUEL_ECONOMY_INST("Fuel Economy (instant)"),
+ EXTERNAL_TEMP("External Temperature"),
+ VEHICLE_GEAR("Vehicle Gear State"),
+ TIRE_PRESSURE("Tire Pressure"),
+ ODOMETER("Odometer"),
+ SEAT_BELT_STATUS("Seat Belt Status"),
+ BODY_INFO("Body Information"),
+ DEVICE_STATUS("Device Status"),
+ DRIVER_BRAKING("Brake Status"),
+ WIPER_STATUS("Windshield Wiper Status"),
+ FUEL_ECONOMY_OVR("Fuel Economy (overall)"),
+ ENGINE_OIL_LIFE("Engine Oil Life"),
+ HEADLIGHT_STATUS("Headlight Status"),
+ BATTERY_VOLTAGE("Battery Voltage"),
+ BRAKE_TORQUE("Brake Torque"),
+ ENGINE_TORQUE("Engine Torque"),
+ TURBO_BOOST("Turbo Boost"),
+ COOLANT_TEMP("Coolant Temp"),
+ AIR_FUEL_RATIO("Air-Fuel Ratio"),
+ COOLING_HEAD_TEMP("Cooling Head Temp"),
+ ENGINE_OIL_TEMP("Engine Oil Temp"),
+ AIR_INTAKE_TEMP("Air Intake Temp"),
+ GEAR_SHIFT_ADVICE("Gear Shift Advice"),
+ ACCELERATION("Acceleration"),
+ ACC_PEDAL_POSITION("Acceleration Pedal Position"),
+ CLUTCH_PEDAL_POSITION("Clutch Pedal Position"),
+ REVERSE_GEAR_STATUS("Reverse Gear Status"),
+ ACCELERATION_TORQUE("Acceleration Torque"),
+ EV_INFO("EV Info"),
+ AMBIENT_LIGHT_STATUS("Ambient Light Status"),
+
+
+ ;
+
+ private final String friendlyName;
+ private SdlVehicleData(String friendlyName) {
+ this.friendlyName = friendlyName;
+ }
+
+ @Override
+ public String toString(){
+ return this.friendlyName;
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/CommandButton.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/CommandButton.java
new file mode 100644
index 000000000..fadc9a6b2
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/CommandButton.java
@@ -0,0 +1,89 @@
+package com.livio.sdl.menu;
+
+/**
+ * Represents a command button that can be clicked on the SDL-connected head-unit. A command
+ * button contains all the fields contained in the MenuItem parent class, as well as a parent id,
+ * a string representing an image on the head-unit and an OnClickListener.
+ *
+ * @author Mike Burke
+ *
+ */
+public class CommandButton extends MenuItem {
+
+ /**
+ * An interface that defines a click event listener for a command button.
+ *
+ * @author Mike Burke
+ *
+ */
+ public interface OnClickListener{
+ void onClick(CommandButton button);
+ }
+
+ private int parentId = -1;
+ private OnClickListener listener;
+ private String imageName;
+
+ public CommandButton(CommandButton copy){
+ super(copy.getName(), copy.getId(), false);
+ this.parentId = copy.getParentId();
+ this.listener = copy.getOnClickListener();
+ this.imageName = copy.getImageName();
+ }
+
+ public CommandButton(String name, int id) {
+ super(name, id, false);
+ }
+
+ public CommandButton(String name, int id, int parentId) {
+ super(name, id, false);
+ this.parentId = parentId;
+ }
+
+ public CommandButton(String name, int id, OnClickListener listener) {
+ super(name, id, false);
+ this.listener = listener;
+ }
+
+ public CommandButton(String name, int id, int parentId, OnClickListener listener) {
+ this(name, id, parentId, null, listener);
+ }
+
+ public CommandButton(String name, int id, String imageName) {
+ super(name, id, false);
+ this.imageName = imageName;
+ }
+
+ public CommandButton(String name, int id, int parentId, String imageName) {
+ this(name, id, parentId, imageName, null);
+ }
+
+ public CommandButton(String name, int id, int parentId, String imageName, OnClickListener listener) {
+ super(name, id, false);
+ this.parentId = parentId;
+ this.imageName = imageName;
+ this.listener = listener;
+ }
+
+ public int getParentId() {
+ return parentId;
+ }
+
+ public OnClickListener getOnClickListener() {
+ return listener;
+ }
+
+ public String getImageName() {
+ return imageName;
+ }
+
+ /**
+ * Executes the code in the click listener, if it exists.
+ */
+ public void dispatchClickEvent(){
+ if(listener != null){
+ listener.onClick(this);
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuItem.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuItem.java
new file mode 100644
index 000000000..07bd2be42
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuItem.java
@@ -0,0 +1,85 @@
+package com.livio.sdl.menu;
+
+import java.util.Comparator;
+
+/**
+ * Represents a generic menu item - either a submenu or a command. All menu items
+ * have similar fields, for example, a name and an id.
+ *
+ * @see CommandButton, SubmenuButton
+ *
+ * @author Mike Burke
+ *
+ */
+public class MenuItem {
+ /**
+ * Comparator for sorting MenuItem objects based on their id.
+ *
+ * @author Mike Burke
+ *
+ */
+ public static class IdComparator implements Comparator<MenuItem>{
+ @Override
+ public int compare(MenuItem lhs, MenuItem rhs) {
+ final int lId = lhs.getId();
+ final int rId = rhs.getId();
+
+ if(lId > rId){
+ return 1;
+ }
+ else if(lId < rId){
+ return -1;
+ }
+ else{
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * Comparator for sorting MenuItem objects based on their name.
+ *
+ * @author Mike Burke
+ *
+ */
+ public static class NameComparator implements Comparator<MenuItem> {
+ @Override
+ public int compare(MenuItem lhs, MenuItem rhs) {
+ return lhs.getName().compareTo(rhs.getName());
+ }
+ }
+
+ private String name;
+ private int id;
+ private boolean isMenu;
+
+ public MenuItem(MenuItem copy){
+ this.name = copy.getName();
+ this.id = copy.getId();
+ this.isMenu = copy.isMenu();
+ }
+
+ public MenuItem(String name, int id, boolean isMenu) {
+ this.name = name;
+ this.id = id;
+ this.isMenu = isMenu;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public boolean isMenu() {
+ return isMenu;
+ }
+
+ @Override
+ public String toString(){
+ return new StringBuilder().append(name).append(" (").append(id).append(")").toString();
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuManager.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuManager.java
new file mode 100644
index 000000000..b6fef256f
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/MenuManager.java
@@ -0,0 +1,334 @@
+package com.livio.sdl.menu;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * Manages SDL menu items (commands and submenus), keeping an up-to-date list of available
+ * menu items.
+ *
+ * @author Mike Burke
+ *
+ */
+public class MenuManager {
+ /**
+ * Allows iterating over the items in a menu manager object.
+ *
+ * @author Mike Burke
+ *
+ */
+ public static class MenuIterator implements Iterator<MenuItem>{
+
+ private final MenuManager items;
+ private int currentIndex = 0;
+
+ private MenuIterator(MenuManager items){
+ this.items = items;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return (currentIndex < (items.size()));
+ }
+
+ @Override
+ public MenuItem next() {
+ return items.getItemAt(currentIndex++);
+ }
+
+ @Override
+ public void remove() {
+ // don't allow removing items through the iterator
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static boolean debug = false;
+ private SparseArray<MenuItem> menuItems;
+
+ // constructors
+ public MenuManager() {
+ menuItems = new SparseArray<MenuItem>();
+ }
+
+ public MenuManager(int startSize){
+ menuItems = new SparseArray<MenuItem>(startSize);
+ }
+
+ /**
+ * Adds the input menu item to the list. If the input item is the child of a submenu,
+ * also adds the item to the parent's list of children.
+ *
+ * @param item The item to add
+ */
+ public void addItem(MenuItem item){
+ log(new StringBuilder().append("Adding item: ").append(item.toString()).toString());
+ menuItems.put(item.getId(), item);
+
+ // if not a top-level menu, let's check if it needs to be added to a submenu as well
+ if(!item.isMenu()){
+ CommandButton button = (CommandButton) item;
+ int parentId = button.getParentId();
+ if(parentId != -1){
+ // button has a valid parent. let's find it and add it to the list
+ addCommandToParent(button, parentId);
+ }
+ }
+ }
+
+ /**
+ * Adds the input command button to the parent represented by the parent id.
+ *
+ * @param button The button to add
+ * @param parentId The id of the button's parent
+ */
+ private void addCommandToParent(CommandButton button, int parentId){
+ MenuItem parent = menuItems.get(parentId);
+ log(new StringBuilder().append("Adding command: ").append(button.toString()).append(" to parent: ").append(parent.toString()).toString());
+
+ if(parent.isMenu()){
+ SubmenuButton parentButton = (SubmenuButton) parent;
+ parentButton.addChild(button);
+ }
+ }
+
+ /**
+ * Removes the item with the input id. If the item to remove is a submenu which contains children,
+ * this method removes the children commands from the menu manager as well. If the item to remove is
+ * a command which belongs to a registered submenu, this method will also remove the child from its
+ * parent's list of children.
+ *
+ * @param id The id of the item to remove
+ */
+ public void removeItem(int id){
+ MenuItem itemToRemove = menuItems.get(id);
+ if(itemToRemove == null){
+ return;
+ }
+
+ log(new StringBuilder().append("Removing item: ").append(itemToRemove.toString()).toString());
+
+ if(!itemToRemove.isMenu()){
+ // command button
+ final int parentId = ((CommandButton)itemToRemove).getParentId();
+ if(parentId != -1){
+ // we have a command button with a valid parent - let's remove it from the parent list
+ SubmenuButton parent = (SubmenuButton) menuItems.get(parentId);
+ if(parent != null){
+ log(new StringBuilder().append("Removing child: ").append(itemToRemove.toString()).append(" from parent: ").append(parent.toString()).toString());
+ parent.removeChild(itemToRemove.getId());
+ }
+ }
+ }
+ else{
+ // submenu button is being deleted - remove all children as well
+ removeChildren((SubmenuButton) itemToRemove);
+ }
+ menuItems.remove(id);
+ }
+
+ /**
+ * Removes any children that belong to the input submenu.
+ *
+ * @param parent The parent whose children should be removed
+ */
+ private void removeChildren(SubmenuButton parent){
+ List<MenuItem> children = parent.getChildren();
+ if(children != null && children.size() > 0){
+ for(MenuItem child : children){
+ removeItem(child.getId());
+ }
+ }
+ }
+
+ /**
+ * Makes a copy of all registered submenus and returns it.
+ *
+ * @return A list of all registered submenus
+ */
+ public List<MenuItem> getSubmenus(){
+ if(size() == 0){
+ return Collections.emptyList();
+ }
+
+ log("Making a copy of all submenus");
+
+ List<MenuItem> result = new ArrayList<MenuItem>();
+
+ // iterate through all items
+ Iterator<MenuItem> iterator = iterator();
+ while(iterator.hasNext()){
+ MenuItem current = iterator.next();
+ if(current.isMenu()){
+ // if this item is a submenu, make a copy of the item and add it to the result list
+ result.add(new SubmenuButton((SubmenuButton) current));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Makes a copy of all registered commands and returns it.
+ *
+ * @return A list of all registered commands
+ */
+ public List<MenuItem> getCommands(){
+ if(size() == 0){
+ return Collections.emptyList();
+ }
+
+ log("Making a copy of all commands");
+
+ List<MenuItem> result = new ArrayList<MenuItem>();
+
+ // iterate through all items
+ Iterator<MenuItem> iterator = iterator();
+ while(iterator.hasNext()){
+ MenuItem current = iterator.next();
+ if(!current.isMenu()){
+ // if this item is a command, make a copy of the item and add it to the result list
+ result.add(new CommandButton((CommandButton) current));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Makes a copy of all items and returns it.
+ *
+ * @return A list of all registered menu items
+ */
+ public List<MenuItem> getAllItems(){
+ if(size() == 0){
+ return Collections.emptyList();
+ }
+
+ log("Making a copy of all menu items");
+
+ List<MenuItem> result = new ArrayList<MenuItem>(size());
+
+ // iterate through all items
+ Iterator<MenuItem> iterator = iterator();
+ while(iterator.hasNext()){
+ MenuItem current = iterator.next();
+
+ // make a copy of the item and add it to the list
+ if(current.isMenu()){
+ result.add(new SubmenuButton((SubmenuButton) current));
+ }
+ else{
+ result.add(new CommandButton((CommandButton) current));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the item with the given id.
+ *
+ * @param id The id of the item to return
+ * @return The item with the input id, or null if the id doesn't exist
+ */
+ public MenuItem get(int id){
+ return menuItems.get(id);
+ }
+
+ /**
+ * Returns the item at the input index.
+ *
+ * @param index The index of the item to return
+ * @return The item at the given index
+ */
+ public MenuItem getItemAt(int index){
+ return menuItems.valueAt(index);
+ }
+
+ /**
+ * Returns the item with the given name.
+ *
+ * @param name The name of the item to return
+ * @return The item with the given name, or null if the name doesn't exist
+ */
+ public MenuItem get(String name){
+ // iterate through all items
+ Iterator<MenuItem> iterator = iterator();
+ while(iterator.hasNext()){
+ MenuItem current = iterator.next();
+
+ // if the current item matches the input name, return it
+ if(name.equals(current.getName())){
+ return current;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the number of items being managed by the menu manager.
+ *
+ * @return The number of items being managed by the menu manager
+ */
+ public int size(){
+ return menuItems.size();
+ }
+
+ /**
+ * Removes all menu items from the menu manager.
+ */
+ public void clear(){
+ menuItems.clear();
+ }
+
+ /**
+ * Dispatches a click event to the button with the input id. If the button with the
+ * input id isn't found in the menu manager, this method does nothing.
+ *
+ * @param buttonId The id of the button that was clicked
+ */
+ public void dispatchClick(int buttonId){
+ MenuItem item = menuItems.get(buttonId);
+
+ if(item == null || item.isMenu()){
+ log("Button with selected ID hasn't been added to this menu manager.");
+ }
+ else{
+ CommandButton itemCmd = (CommandButton) item;
+ itemCmd.dispatchClickEvent();
+ }
+ }
+
+ /**
+ * Creates a new iterator object for this MenuManager object.
+ *
+ * @return The new iterator instance
+ */
+ public Iterator<MenuItem> iterator(){
+ log("Creating new iterator object");
+ Iterator<MenuItem> iterator = new MenuIterator(this);
+ return iterator;
+ }
+
+ /**
+ * Enables or disables debug mode for log messages.
+ *
+ * @param enable True to enable debug logs, false to disable
+ */
+ public static void setDebug(boolean enable){
+ debug = enable;
+ }
+
+ private static void log(String msg){
+ if(debug){
+ Log.d("MenuManager", msg);
+ }
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/SubmenuButton.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/SubmenuButton.java
new file mode 100644
index 000000000..7e2ebbe90
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/menu/SubmenuButton.java
@@ -0,0 +1,109 @@
+package com.livio.sdl.menu;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents a submenu button that can be clicked on the SDL-connected head-unit. A submenu
+ * contains all the fields contained in the MenuItem parent class, as well as maintaining a list
+ * of children commands.
+ *
+ * @author Mike Burke
+ *
+ */
+public class SubmenuButton extends MenuItem {
+
+ private List<MenuItem> children;
+
+ public SubmenuButton(SubmenuButton copy){
+ super(copy.getName(), copy.getId(), true);
+ copyChildren(copy.getChildren());
+ }
+
+ public SubmenuButton(String name, int id) {
+ super(name, id, true);
+ }
+
+ /**
+ * Creates a copy of the children to ensure there are no leaked references to the
+ * children of the object we're copying.
+ *
+ * @param children
+ */
+ private void copyChildren(List<MenuItem> children){
+ if(children == null || children.size() <= 0){
+ return;
+ }
+
+ if(this.children == null){
+ this.children = new ArrayList<MenuItem>(children.size());
+ }
+
+ for(MenuItem child : children){
+ if(child.isMenu()){
+ this.children.add(new SubmenuButton((SubmenuButton) child));
+ }
+ else{
+ this.children.add(new CommandButton((CommandButton) child));
+ }
+ }
+ }
+
+ /**
+ * Returns a copy of the list of all children associated with this menu item. If there
+ * are no children associated with this menu item, this method will return an empty list.
+ *
+ * @return A copy of the list of children
+ */
+ public List<MenuItem> getChildren(){
+ if(children == null || children.size() <= 0){
+ return Collections.emptyList();
+ }
+
+ return new ArrayList<MenuItem>(children);
+ }
+
+ /**
+ * Adds a child to this submenu object.
+ *
+ * @param item The item to add
+ */
+ public void addChild(MenuItem item){
+ if(children == null){
+ children = new ArrayList<MenuItem>();
+ }
+
+ children.add(item);
+ }
+
+ /**
+ * Removes a child from this submenu object.
+ *
+ * @param childId The id of the child to remove
+ */
+ public void removeChild(int childId){
+ if(children == null || children.size() <= 0){
+ return;
+ }
+
+ for(MenuItem child : children){
+ if(childId == child.getId()){
+ children.remove(child);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Removes all children from this submenu object.
+ */
+ public void removeAllChildren(){
+ if(children == null || children.size() <= 0){
+ return;
+ }
+
+ children.clear();
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/AndroidUtils.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/AndroidUtils.java
new file mode 100644
index 000000000..87f9a24c8
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/AndroidUtils.java
@@ -0,0 +1,158 @@
+package com.livio.sdl.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.List;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
+import android.widget.ArrayAdapter;
+
+/**
+ * Contains static methods that will help with typical Android tasks. For example,
+ * there are methods to determine internet connectivity, creating adapters for spinners
+ * and lists, etc.
+ *
+ * @author Mike Burke
+ *
+ */
+public final class AndroidUtils {
+
+ private AndroidUtils(){} // don't allow instantiation of static classes
+
+ /**
+ * Determines if the network is currently available or not.
+ *
+ * @param context The context with which to access the system connectivity service
+ * @return True if the network is available, false if not
+ */
+ public static boolean isNetworkAvailable(ContextWrapper context){
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo network = cm.getActiveNetworkInfo();
+ return ( (network != null) && (network.isConnected()) );
+ }
+
+ /**
+ * Creates a standard Android spinner adapter. Input items can be of any type.
+ *
+ * @param context Context with which to create the adapter
+ * @param items List of items to populate the adapter with
+ * @return The created adapter
+ */
+ public static <E> ArrayAdapter<E> createSpinnerAdapter(Context context, List<E> items){
+ ArrayAdapter<E> adapter = createAdapter(context, android.R.layout.select_dialog_item, items);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ return adapter;
+ }
+
+ /**
+ * Creates a standard Android spinner adapter. Input items can be of any type.
+ *
+ * @param context Context with which to create the adapter
+ * @param items Array of items to populate the adapter with
+ * @return The created adapter
+ */
+ public static <E> ArrayAdapter<E> createSpinnerAdapter(Context context, E[] items){
+ return createSpinnerAdapter(context, Arrays.asList(items));
+ }
+
+ /**
+ * Creates a standard Android ListView adapter. Input items can be of any type.
+ *
+ * @param context Context with which to create the adapter
+ * @param items List of items to populate the adapter with
+ * @return The created adapter
+ */
+ public static <E> ArrayAdapter<E> createListViewAdapter(Context context, List<E> items){
+ return createAdapter(context, android.R.layout.simple_list_item_1, items);
+ }
+
+ /**
+ * Creates a standard Android ListView adapter. Input items can be of any type.
+ *
+ * @param context Context with which to create the adapter
+ * @param items Array of items to populate the adapter with
+ * @return The created adapter
+ */
+ public static <E> ArrayAdapter<E> createListViewAdapter(Context context, E[] items){
+ return createListViewAdapter(context, Arrays.asList(items));
+ }
+
+ /**
+ * Creates a standard Android ListView multiple-choice adapter. Input items can be of any type.
+ *
+ * @param context Context with which to create the adapter
+ * @param items List of items to populate the adapter with
+ * @return The created adapter
+ */
+ public static <E> ArrayAdapter<E> createMultipleListViewAdapter(Context context, List<E> items){
+ return createAdapter(context, android.R.layout.simple_list_item_multiple_choice, items);
+ }
+
+ /**
+ * Creates a standard Android ListView multiple-choice adapter. Input items can be of any type.
+ *
+ * @param context Context with which to create the adapter
+ * @param items Array of items to populate the adapter with
+ * @return The created adapter
+ */
+ public static <E> ArrayAdapter<E> createMultipleListViewAdapter(Context context, E[] items){
+ return createMultipleListViewAdapter(context, Arrays.asList(items));
+ }
+
+ /**
+ * Creates a standard Android adapter. Input items can be of any type.
+ *
+ * @param context Context with which to create the adapter
+ * @param layoutId Android resource id to be used for a list row
+ * @param items List of items to populate the adapter with
+ * @return The created adapter
+ */
+ public static <E> ArrayAdapter<E> createAdapter(Context context, int layoutId, List<E> items){
+ return new ArrayAdapter<E>(context, layoutId, items);
+ }
+
+ /**
+ * Converts and Android bitmap file to an array of raw bytes that are ready to be sent over bluetooth,
+ * wifi, usb, etc.
+ *
+ * @param bitmap The bitmap to translate
+ * @param format The format of the bitmap
+ * @return The raw byte representation of the bitmap
+ */
+ public static byte[] bitmapToRawBytes(Bitmap bitmap, CompressFormat format){
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ bitmap.compress(format, 100, baos);
+ byte[] result = baos.toByteArray();
+ return result;
+ }
+
+ /**
+ * Enables or disables wifi. Requires CHANGE_WIFI_STATE permission.
+ *
+ * @param context A context with which to access wifi system service
+ * @param enable True if wifi should be enabled, false if it should be disabled
+ */
+ public static void enableWifi(ContextWrapper context, boolean enable){
+ WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ wifiManager.setWifiEnabled(enable);
+ }
+
+ /**
+ * Determines if the device's wifi is currently enabled or not.
+ *
+ * @param context A context with which to access wifi system service
+ * @return True if wifi is enabled or enabling, false otherwise
+ */
+ public static boolean wifiIsEnabled(ContextWrapper context){
+ WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ int wifiState = wifiManager.getWifiState();
+ return (wifiState == WifiManager.WIFI_STATE_ENABLED || wifiState == WifiManager.WIFI_STATE_ENABLING);
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/ApplicationPreferences.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/ApplicationPreferences.java
new file mode 100644
index 000000000..713759307
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/ApplicationPreferences.java
@@ -0,0 +1,188 @@
+package com.livio.sdl.utils;
+
+import java.util.Map;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * Contains static methods for saving and restoring data from Android SharedPreferences.
+ *
+ * @author Mike Burke
+ *
+ */
+public final class ApplicationPreferences {
+
+ private ApplicationPreferences(){} // don't allow instantiation of static classes
+
+ /**
+ * Determines if the input key exists as part of the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to look up
+ * @return True if the key exists, false otherwise
+ */
+ public static boolean exists(Context context, String fileName, String key){
+ SharedPreferences prefs = getSharedPreferences(context, fileName);
+ if(prefs == null){
+ return false;
+ }
+
+ Map<String, ?> mapping = prefs.getAll();
+ if(mapping == null || mapping.size() == 0){
+ return false;
+ }
+
+ return (mapping.get(key) != null);
+ }
+
+ /**
+ * Retrieves the string with the input key from the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to look up
+ * @return The string if it was found, null otherwise
+ */
+ public static String getString(Context context, String fileName, String key){
+ SharedPreferences prefs = getSharedPreferences(context, fileName);
+ String result = prefs.getString(key, null);
+ return result;
+ }
+
+ /**
+ * Retrieves the boolean with the input key from the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to look up
+ * @return The boolean if it was found, null otherwise
+ */
+ public static boolean getBoolean(Context context, String fileName, String key){
+ SharedPreferences prefs = getSharedPreferences(context, fileName);
+ boolean result = prefs.getBoolean(key, false);
+ return result;
+ }
+
+ /**
+ * Retrieves the integer with the input key from the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to look up
+ * @return The integer if it was found, null otherwise
+ */
+ public static int getInt(Context context, String fileName, String key){
+ SharedPreferences prefs = getSharedPreferences(context, fileName);
+ int result = prefs.getInt(key, -1);
+ return result;
+ }
+
+ /**
+ * Retrieves the float with the input key from the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to look up
+ * @return The float if it was found, null otherwise
+ */
+ public static float getFloat(Context context, String fileName, String key){
+ SharedPreferences prefs = getSharedPreferences(context, fileName);
+ float result = prefs.getFloat(key, -1f);
+ return result;
+ }
+
+ /**
+ * Retrieves the long with the input key from the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to look up
+ * @return The long if it was found, null otherwise
+ */
+ public static long getLong(Context context, String fileName, String key){
+ SharedPreferences prefs = getSharedPreferences(context, fileName);
+ long result = prefs.getLong(key, -1);
+ return result;
+ }
+
+ /**
+ * Saves the input value at the input key in the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to save
+ * @param value Value of the object to save
+ */
+ public static void putString(Context context, String fileName, String key, String value){
+ SharedPreferences.Editor editor = getEditor(context, fileName);
+ editor.putString(key, value);
+ editor.apply();
+ }
+
+ /**
+ * Saves the input value at the input key in the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to save
+ * @param value Value of the object to save
+ */
+ public static void putBoolean(Context context, String fileName, String key, boolean value){
+ SharedPreferences.Editor editor = getEditor(context, fileName);
+ editor.putBoolean(key, value);
+ editor.apply();
+ }
+
+ /**
+ * Saves the input value at the input key in the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to save
+ * @param value Value of the object to save
+ */
+ public static void putInt(Context context, String fileName, String key, int value){
+ SharedPreferences.Editor editor = getEditor(context, fileName);
+ editor.putInt(key, value);
+ editor.apply();
+ }
+
+ /**
+ * Saves the input value at the input key in the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to save
+ * @param value Value of the object to save
+ */
+ public static void putFloat(Context context, String fileName, String key, float value){
+ SharedPreferences.Editor editor = getEditor(context, fileName);
+ editor.putFloat(key, value);
+ editor.apply();
+ }
+
+ /**
+ * Saves the input value at the input key in the input filename.
+ *
+ * @param context Context with which to retrieve shared preferences
+ * @param fileName File name for shared preferences
+ * @param key Key of the object to save
+ * @param value Value of the object to save
+ */
+ public static void putLong(Context context, String fileName, String key, long value){
+ SharedPreferences.Editor editor = getEditor(context, fileName);
+ editor.putLong(key, value);
+ editor.apply();
+ }
+
+ private static SharedPreferences getSharedPreferences(Context context, String fileName){
+ return context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
+ }
+
+ private static SharedPreferences.Editor getEditor(Context context, String fileName){
+ return getSharedPreferences(context, fileName).edit();
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Counter.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Counter.java
new file mode 100644
index 000000000..b48690721
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Counter.java
@@ -0,0 +1,47 @@
+package com.livio.sdl.utils;
+
+/**
+ * Represents an abstract integer counter.
+ *
+ * @author Mike Burke
+ *
+ */
+public abstract class Counter {
+
+ /**
+ * Returns the current value of the counter and moves to the next one.
+ *
+ * @return Current value of the counter
+ */
+ abstract public int next();
+
+ private final int START;
+ protected int current;
+
+ public Counter(){
+ this.START = 0;
+ this.current = START;
+ }
+
+ public Counter(int start){
+ this.START = start;
+ this.current = start;
+ }
+
+ /**
+ * Resets the counter to its original starting point.
+ */
+ public void reset(){
+ this.current = START;
+ }
+
+ /**
+ * Returns the current value of the counter.
+ *
+ * @return The current value of the counter
+ */
+ public int current(){
+ return current;
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/DownCounter.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/DownCounter.java
new file mode 100644
index 000000000..036261c62
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/DownCounter.java
@@ -0,0 +1,26 @@
+package com.livio.sdl.utils;
+
+/**
+ * Represents a simple down-counter. The down-counter can be initialized with
+ * a seed value to start counting from. If initialized with no seed, the counter
+ * will start from 0.
+ *
+ * @author Mike Burke
+ *
+ */
+public class DownCounter extends Counter{
+
+ public DownCounter() {
+ super();
+ }
+
+ public DownCounter(int start){
+ super(start);
+ }
+
+ @Override
+ public int next() {
+ return current--;
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/MathUtils.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/MathUtils.java
new file mode 100644
index 000000000..0642ef2df
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/MathUtils.java
@@ -0,0 +1,21 @@
+package com.livio.sdl.utils;
+
+public final class MathUtils {
+
+ private MathUtils() {}
+
+ public static final class Conversions{
+ private Conversions(){}
+
+ public static final int S_TO_MS = 1000;
+ }
+
+ public static int convertSecsToMillisecs(int seconds){
+ return seconds * Conversions.S_TO_MS;
+ }
+
+ public static float convertSecsToMillisecs(float seconds){
+ return seconds * Conversions.S_TO_MS;
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/SdlUtils.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/SdlUtils.java
new file mode 100644
index 000000000..6393882d6
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/SdlUtils.java
@@ -0,0 +1,254 @@
+package com.livio.sdl.utils;
+
+import java.util.Vector;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+
+import com.livio.sdl.SdlConstants;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.TTSChunkFactory;
+import com.smartdevicelink.proxy.rpc.Choice;
+import com.smartdevicelink.proxy.rpc.Image;
+import com.smartdevicelink.proxy.rpc.MenuParams;
+import com.smartdevicelink.proxy.rpc.StartTime;
+import com.smartdevicelink.proxy.rpc.TTSChunk;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.ImageType;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+
+/**
+ * Contains static methods that are useful in working with SmartDeviceLink.
+ *
+ * @author Mike Burke
+ *
+ */
+public final class SdlUtils {
+ private static final int NUMBER_OF_INDENTS = 4;
+
+ private SdlUtils(){}
+
+ /**
+ * Creates and returns the raw JSON string associated with the
+ * input RPC message object.
+ *
+ * @param msg The message to retrieve raw JSON for
+ * @return The raw JSON string
+ */
+ public static String getJsonString(RPCMessage msg){
+ return getJsonString(msg, NUMBER_OF_INDENTS);
+ }
+
+ /**
+ * Creates and returns the raw JSON string associated with the input
+ * RPC message object. Allows a custom number of indent spaces.
+ *
+ * @param msg The message to retrieve raw JSON for
+ * @param numOfIndents Number of indents to be used in raw JSON
+ * @return The raw JSON string
+ */
+ public static String getJsonString(RPCMessage msg, int numOfIndents){
+ String result = "";
+ try {
+ JSONObject json = msg.serializeJSON();
+ result = json.toString(numOfIndents);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a JSON title that can be used in JSON dialogs. Input correlation ID
+ * is allowed to be null or -1 if there is no correlation ID associated with the
+ * particular message.
+ *
+ * @param correlationId The associated correlation ID for the message
+ * @return The JSON title with associated correlation ID.
+ */
+ public static String makeJsonTitle(Integer correlationId){
+ if(correlationId == null || correlationId == -1){
+ return "Raw JSON";
+ }
+
+ return new StringBuilder().append("Raw JSON (").append(correlationId).append(")").toString();
+ }
+
+ /**
+ * Converts an SDL file type to its associated CompressFormat.
+ *
+ * @param type The file type to convert
+ * @return The associated CompressFormat
+ */
+ public static CompressFormat convertImageTypeToCompressFormat(FileType type){
+ switch(type){
+ case GRAPHIC_JPEG:
+ return CompressFormat.JPEG;
+ case GRAPHIC_PNG:
+ return CompressFormat.PNG;
+ case GRAPHIC_BMP:
+ return null; // TODO what's the compression format for a bitmap object?
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Converts the input bitmap to a byte array based on the input File Type.
+ *
+ * @param image The image to convert
+ * @param type The file type of the image
+ * @return The byte array of the input bitmap
+ */
+ public static byte[] bitmapToByteArray(Bitmap image, FileType type){
+ CompressFormat format = convertImageTypeToCompressFormat(type);
+ byte[] bitmapData = AndroidUtils.bitmapToRawBytes(image, format);
+ return bitmapData;
+ }
+
+ /**
+ * Creates a Choice object to be used in Choice Interaction Sets.
+ *
+ * @param name The name of the choice
+ * @param vrCommands CSV list of voice-rec options
+ * @param imageName Image name of any associated image
+ * @return The created Choice object
+ */
+ public static Choice createChoice(String name, String vrCommands, String imageName){
+ if(name == null){
+ throw new NullPointerException();
+ }
+
+ Choice choice = new Choice();
+ choice.setMenuName(name);
+
+ if(vrCommands != null){
+ choice.setVrCommands(voiceRecognitionVector(vrCommands));
+ }
+
+ if(imageName != null){
+ choice.setImage(dynamicImage(imageName));
+ }
+
+ return choice;
+ }
+
+ /**
+ * Creates a vector of voice-rec commands based on a CSV input string.
+ *
+ * @param input CSV list of voice-rec options
+ * @return A vector of voice-rec strings
+ */
+ public static Vector<String> voiceRecognitionVector(String input){
+ if(input.trim().equals("")){
+ return null;
+ }
+
+ return StringUtils.toVector(input);
+ }
+
+ /**
+ * Creates a dynamic image object with the input image name.
+ *
+ * @param imageName The name of the image this object represents
+ * @return The created Image object
+ */
+ public static Image dynamicImage(String imageName){
+ if(imageName == null){
+ throw new NullPointerException();
+ }
+
+ Image result = new Image();
+ result.setImageType(ImageType.DYNAMIC);
+ result.setValue(imageName);
+ return result;
+ }
+
+ /**
+ * Creates a MenuParams object that can be used in other SDL requests, such as AddCommand.
+ * If there is no parent id, send -1 as the parent id.
+ *
+ * @param name The name of the menu item
+ * @param position The position of the menu item
+ * @param parentId The item's parent id
+ * @return The created MenuParams object
+ */
+ public static MenuParams menuParams(String name, int position, int parentId){
+ if(name == null){
+ throw new NullPointerException();
+ }
+ if(name.length() <= 0){
+ throw new IllegalArgumentException();
+ }
+
+ MenuParams result = new MenuParams();
+ result.setMenuName(name);
+ result.setPosition(position);
+
+ if(parentId != SdlConstants.AddCommandConstants.INVALID_PARENT_ID &&
+ parentId != SdlConstants.AddCommandConstants.ROOT_PARENT_ID){
+ result.setParentID(parentId);
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a vector of text-to-speech "chunks" that will be used to speak the input
+ * message through the vehicle's speaker system. This method assumes the input format
+ * is a normal text string (as opposed to some type of phonemes).
+ *
+ * @param input The text to create "chunks" from
+ * @return The created vector of TTSChunk objects
+ */
+ public static Vector<TTSChunk> createTextToSpeechVector(String input){
+ return createTextToSpeechVector(input, SpeechCapabilities.TEXT);
+ }
+
+ /**
+ * Creates a vector of text-to-speech "chunks" that will be used to speak the input
+ * message through the vehicle's speaker system. This method requires an input parameter
+ * detailing the format of the input string.
+ *
+ * @param input The text to create "chunks" from
+ * @param speechCapabilities The format of the input string
+ * @return The created vector of TTSChunk objects
+ */
+ public static Vector<TTSChunk> createTextToSpeechVector(String input, SpeechCapabilities speechCapabilities){
+ Vector<String> inputStrings = StringUtils.toVector(input);
+ Vector<TTSChunk> result = new Vector<TTSChunk>(inputStrings.size());
+
+ for(String str : inputStrings){
+ result.add(TTSChunkFactory.createChunk(speechCapabilities, str));
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a StartTime object from the input time in hours, minutes & seconds.
+ *
+ * @param hours Hours value for StartTime object
+ * @param minutes Minutes value for StartTime object
+ * @param seconds Seconds value for the StartTime object
+ * @return The created StartTime object
+ */
+ public static StartTime createStartTime(int hours, int minutes, int seconds){
+ if(hours < SdlConstants.SetMediaClockTimerConstants.HOURS_MINIMUM || hours > SdlConstants.SetMediaClockTimerConstants.HOURS_MAXIMUM ||
+ minutes < SdlConstants.SetMediaClockTimerConstants.MINUTES_MINIMUM || minutes > SdlConstants.SetMediaClockTimerConstants.MINUTES_MAXIMUM ||
+ seconds < SdlConstants.SetMediaClockTimerConstants.SECONDS_MINIMUM || seconds > SdlConstants.SetMediaClockTimerConstants.SECONDS_MAXIMUM ){
+ throw new IllegalArgumentException();
+ }
+
+ StartTime result = new StartTime();
+ result.setHours(hours);
+ result.setMinutes(minutes);
+ result.setSeconds(seconds);
+ return result;
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/StringUtils.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/StringUtils.java
new file mode 100644
index 000000000..9e413ce92
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/StringUtils.java
@@ -0,0 +1,66 @@
+package com.livio.sdl.utils;
+
+import java.util.Vector;
+
+/**
+ * Contains static methods useful in dealing with String objects.
+ *
+ * @author Mike Burke
+ *
+ */
+public final class StringUtils {
+
+ private static final String DEFAULT_DELIMITER = ",";
+
+ private StringUtils(){}
+
+ /**
+ * Determines if the input string is an integer or not.
+ *
+ * @param input String to analyze
+ * @return True if the string is an integer, false if not
+ */
+ public static boolean isInteger(String input){
+ try{
+ Integer.parseInt(input);
+ return true;
+ }catch(NumberFormatException e){
+ // if the string can't be parsed as an integer, it isn't a number.
+ return false;
+ }
+ }
+
+ /**
+ * Splits up a CSV string into a vector of strings.
+ *
+ * @param input The CSV string to process
+ * @return The created vector of strings
+ */
+ public static Vector<String> toVector(String input){
+ return toVector(input, DEFAULT_DELIMITER);
+ }
+
+ /**
+ * Splits up a string into a vector of strings based on the input delimiter.
+ *
+ * @param input The raw string to process
+ * @param delim The delimiter on which to split the string
+ * @return The created vector of strings
+ */
+ public static Vector<String> toVector(String input, String delim){
+ if(input == null){
+ throw new NullPointerException();
+ }
+ if(delim == null){
+ delim = DEFAULT_DELIMITER;
+ }
+ String[] inputArray = input.split(delim);
+ Vector<String> result = new Vector<String>(inputArray.length);
+
+ for(String splitStr : inputArray){
+ result.add(splitStr.trim());
+ }
+
+ return result;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Timeout.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Timeout.java
new file mode 100644
index 000000000..91b5abf55
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/Timeout.java
@@ -0,0 +1,120 @@
+package com.livio.sdl.utils;
+
+import android.os.Handler;
+
+/**
+ * Runs a simple thread that sleeps for the duration set in the constructor. When the thread
+ * is completed or interrupted, it informs the listener that the timeout has been completed.
+ *
+ * @author Mike Burke
+ *
+ */
+public class Timeout {
+
+ /**
+ * Listener interface for the Timeout class. Contains callbacks for timeout completed
+ * and timeout cancelled.
+ *
+ * @author Mike Burke
+ *
+ */
+ public interface Listener{
+ /**
+ * Called when the thread has successfully ran for the input time.
+ */
+ public void onTimeoutCompleted();
+ /**
+ * Called when the thread has been cancelled or interrupted.
+ */
+ public void onTimeoutCancelled();
+ }
+
+ protected int timeout;
+ protected Listener listener;
+ protected Thread thread;
+ protected Handler handler;
+
+ /**
+ * Creates a Timeout object.
+ *
+ * @param timeout The time to wait for (in ms)
+ * @param l A listener for when the thread completes
+ */
+ public Timeout(int timeout, Listener l) {
+ this.timeout = timeout;
+ this.listener = l;
+ handler = new Handler();
+ }
+
+ public int getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ public Listener getListener() {
+ return listener;
+ }
+
+ public void setListener(Listener listener) {
+ this.listener = listener;
+ }
+
+ /**
+ * Starts a new thread with the timeout that was set in the constructor.
+ */
+ public void start(){
+ thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(timeout);
+ onTimeoutCompleted();
+ } catch (InterruptedException e) {
+ onTimeoutCancelled();
+ }
+ }
+ });
+ thread.start();
+ }
+
+ /**
+ * Cancels the currently running thread.
+ */
+ public void cancel(){
+ if(thread != null && thread.isAlive()){
+ thread.interrupt();
+ }
+ }
+
+ /**
+ * Called by the timeout thread to inform that it has successfully completed.
+ */
+ protected void onTimeoutCompleted(){
+ if(listener != null){
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onTimeoutCompleted();
+ }
+ });
+ }
+ }
+
+ /**
+ * Called by the timeout thread to inform that it has be cancelled or interrupted.
+ */
+ protected void onTimeoutCancelled(){
+ if(listener != null){
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onTimeoutCancelled();
+ }
+ });
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/UpCounter.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/UpCounter.java
new file mode 100644
index 000000000..e1b72e026
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/UpCounter.java
@@ -0,0 +1,26 @@
+package com.livio.sdl.utils;
+
+/**
+ * Represents a simple up-counter. The up-counter can be initialized with
+ * a seed value to start counting from. If initialized with no seed, the counter
+ * will start from 0.
+ *
+ * @author Mike Burke
+ *
+ */
+public class UpCounter extends Counter{
+
+ public UpCounter(){
+ super();
+ }
+
+ public UpCounter(int start){
+ super(start);
+ }
+
+ @Override
+ public int next(){
+ return current++;
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/WifiUtils.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/WifiUtils.java
new file mode 100644
index 000000000..ceaadbef0
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/utils/WifiUtils.java
@@ -0,0 +1,61 @@
+package com.livio.sdl.utils;
+
+public final class WifiUtils {
+
+ private static final int IP_ADDRESS_MIN_LENGTH = 7;
+ private static final int IP_ADDRESS_MAX_LENGTH = 15;
+
+ private static final int TCP_PORT_MIN_VALUE = 0;
+ private static final int TCP_PORT_MAX_VALUE = 65535;
+
+ private WifiUtils(){}
+
+ /**
+ * Validates the input IP address string.
+ *
+ * @param address The IP address string to analyze
+ * @return True if this is a valid IP address, false if not
+ */
+ public static boolean validateIpAddress(String address){
+ // address should be in the form of x.x.x.x, so at least 7 characters
+ // address maximum is 255.255.255.255, so at most 15 characters
+ if(address == null || address.length() < IP_ADDRESS_MIN_LENGTH || address.length() > IP_ADDRESS_MAX_LENGTH){
+ return false;
+ }
+
+ // split the string into pieces separated by a .
+ String[] pieces = address.split("\\.");
+ // must have 4 numbers separated by .
+ if(pieces.length != 4){
+ return false;
+ }
+
+ // check each piece
+ for(String piece : pieces){
+ if(!StringUtils.isInteger(piece)){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Validates the input TCP port string.
+ *
+ * @param tcpPort The TCP port string to analyze
+ * @return True if this is a valid TCP port, false if not
+ */
+ public static boolean validateTcpPort(String tcpPort){
+ try{
+ int portNumber = Integer.parseInt(tcpPort);
+ if(portNumber < TCP_PORT_MIN_VALUE || portNumber > TCP_PORT_MAX_VALUE){
+ return false;
+ }
+ }catch(NumberFormatException e){
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/MinMaxInputFilter.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/MinMaxInputFilter.java
new file mode 100644
index 000000000..7b0d86138
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/MinMaxInputFilter.java
@@ -0,0 +1,53 @@
+package com.livio.sdl.viewhelpers;
+
+import android.text.InputFilter;
+import android.text.Spanned;
+
+/**
+ * An input filter for number-based text inputs. The filter allows the user
+ * to enter any values between the min and max values used in the constructor,
+ * but will not allow any letters or numbers outside that range to be entered into
+ * the text field. For best results, set inputType to number for any text inputs
+ * using this input filter.
+ *
+ * @author Mike Burke
+ *
+ */
+public class MinMaxInputFilter implements InputFilter {
+
+ private int min, max;
+
+ public MinMaxInputFilter(int min, int max){
+ this.min = min;
+ this.max = max;
+ }
+
+ public MinMaxInputFilter(String min, String max){
+ this.min = Integer.parseInt(min);
+ this.max = Integer.parseInt(max);
+ }
+
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
+ try{
+ int input = Integer.parseInt(dest.toString() + source.toString());
+ if(isInRange(min, max, input)){
+ return null;
+ }
+ }catch(NumberFormatException e){
+ // do nothing
+ }
+ return "";
+ }
+
+ // determines if the input is in range or not
+ private static boolean isInRange(int min, int max, int input){
+ if(max > min){
+ return (input >= min && input <= max);
+ }
+ else{
+ return (input >= max && input <= min);
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/SeekBarCalculator.java b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/SeekBarCalculator.java
new file mode 100644
index 000000000..f26701146
--- /dev/null
+++ b/SDL_Android/LivioSdlUtilities/src/com/livio/sdl/viewhelpers/SeekBarCalculator.java
@@ -0,0 +1,104 @@
+package com.livio.sdl.viewhelpers;
+
+/**
+ * Performs math operations commonly used with SeekBar views. Performs calculations
+ * to translate a progress value to a real-world value and vice-versa.
+ *
+ * @author Mike Burke
+ *
+ */
+public class SeekBarCalculator {
+
+ private static final int PROGRESS_MIN = 0; // standard Android seekbars start from 0
+
+ private int min, max;
+ private float divisor;
+
+ public SeekBarCalculator(int min, int max){
+ this(min, max, 1.0f);
+ }
+
+ public SeekBarCalculator(int min, int max, float divisor) {
+ this.min = min;
+ this.max = max;
+ this.divisor = divisor;
+ }
+
+ public int getMinValue() {
+ return min;
+ }
+
+ public void setMinValue(int min){
+ this.min = min;
+ }
+
+ public int getMaxValue() {
+ return max;
+ }
+
+ public void setMaxValue(int max){
+ this.max = max;
+ }
+
+ public float getDivisor() {
+ return divisor;
+ }
+
+ public void setDivisor(float divisor){
+ this.divisor = divisor;
+ }
+
+ /**
+ * Determines the maximum progress value based on the min and max values.
+ *
+ * @return The max value of the progress bar
+ */
+ public int getMaxProgress(){
+ return (max - min);
+ }
+
+ /**
+ * Determines the minimum progress value. A typical Android SeekBar, this value is always 0.
+ *
+ * @return The min value of the progress bar
+ */
+ public int getMinProgress(){
+ return PROGRESS_MIN;
+ }
+
+ /**
+ * Calculates the SeekBar progress value for the input real-world value.
+ *
+ * @param value Real-world value to calculate progress for
+ * @return The progress value of the input real-world value
+ */
+ public int calculateProgress(float value){
+ value *= divisor;
+
+ if(value < getMinValue() || value > getMaxValue()){
+ throw new IllegalArgumentException("Value out of seekbar range");
+ }
+
+ int result = (int) (value - getMinValue());
+
+ return result;
+ }
+
+ /**
+ * Calculates the real-world value for the input progress value.
+ *
+ * @param progress Progress value to calculate real-world value for
+ * @return The real-world value of the input progress value
+ */
+ public float calculateValue(int progress){
+ if(progress < getMinProgress() || progress > getMaxProgress()){
+ throw new IllegalArgumentException("Progress out of seekbar range");
+ }
+
+ float result = (progress + getMinValue()) / divisor;
+
+ return result;
+ }
+
+
+}
diff --git a/SDL_Android/LivioTesterApp/.classpath b/SDL_Android/LivioTesterApp/.classpath
new file mode 100644
index 000000000..0461652ec
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/SDL_Android/LivioTesterApp/.project b/SDL_Android/LivioTesterApp/.project
new file mode 100644
index 000000000..418cfd5f2
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>LivioSdlTester</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/SDL_Android/LivioTesterApp/AndroidManifest.xml b/SDL_Android/LivioTesterApp/AndroidManifest.xml
new file mode 100644
index 000000000..2fb7ec17a
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/AndroidManifest.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.livio.sdltester"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <!-- Required to pair Bluetooth devices -->
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <!-- Required to check if WiFi is enabled -->
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <!-- Required to make the device stay awake while doing XML tests -->
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <!-- Required to determine WiFi state and enable it if necessary -->
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+
+ <uses-sdk
+ android:minSdkVersion="11"
+ android:targetSdkVersion="19" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.livio.sdltester.MainActivity"
+ android:label="@string/app_name"
+ android:launchMode="singleInstance"
+ android:screenOrientation="portrait" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service android:name="com.livio.sdl.SdlService" />
+
+ <activity
+ android:name="com.livio.sdltester.HelpActivity"
+ android:label="@string/title_activity_help" >
+ </activity>
+ </application>
+
+</manifest>
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/css/style.css b/SDL_Android/LivioTesterApp/assets/help_docs/css/style.css
new file mode 100644
index 000000000..147ac09ab
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/css/style.css
@@ -0,0 +1,9 @@
+.big {font-size: 30px; line-height:50px;}
+
+h1 {font-size: 40px; text-align:center;}
+h2 {font-size: 30px; text-align:center;}
+h3 {font-size: 20px; text-align:center; font-weight:bold;}
+
+table {border:1px solid black; text-align:center; margin-left:auto; margin-right:auto;}
+td {border:1px solid black;}
+th {border:1px solid black; font-weight:bold;} \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/AddCommand.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/AddCommand.html
new file mode 100644
index 000000000..422961e7e
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/AddCommand.html
@@ -0,0 +1,50 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Add Command</h1>
+
+The AddCommand command allows developers to add a command object into the head-unit's main menu. When the item on the head-unit is clicked,
+the click event is dispatched through the onOnCommand callback in the SDLService. From there, it can be dispatched to the appropriate click-listener
+based on the clicked item's ID.
+
+<p>
+There are 4 available settings to create a command in the head-unit's main menu:
+
+<p>
+<h2>Command Name (required)</h2>
+<h3>Type: String</h3>
+</br>
+The command name represents the text to display within the body of this command on the head-unit's main menu. This text should be a short description of what this button will do when clicked.
+
+<p>
+<h2>Voice Recognition Keyword(s) (optional)</h2>
+<h3>Type: Comma-separated String</h3>
+</br>
+The voice recognition keyword input tells the vehicle what keywords are linked to this particular command when spoken through the vehicle's voice system.
+
+<p>
+<h2>Parent ID (required)</h2>
+<h3>Type: ID (integer)</h3>
+</br>
+The parent id represents where this command should be added. Commands can be added to the root-level of the head-unit's main menu, or, if a submenu has been added to the head-unit's main menu,
+commands can be added into the submenu as well. For more information, see <a href="AddSubmenu.html">AddSubmenu</a>.
+
+<p>
+<h2>Image (optional)</h2>
+<h3>Type: Image</h3>
+</br>
+The image for a particular command represents an image to be displayed alongside the command name within the body of this command on the head-unit's main menu. Images must have been added
+to the system through the <a href="PutFile.html">PutFile</a> command before they can be referenced for an AddCommand.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/AddSubmenu.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/AddSubmenu.html
new file mode 100644
index 000000000..8e325ab43
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/AddSubmenu.html
@@ -0,0 +1,33 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Add Submenu</h1>
+
+The AddSubmenu command allows developers to add a submenu object into the head-unit's main menu. When the item on the head-unit is clicked,
+the submenu's contents are displayed. As of SDL 2.0, only 1 level of submenus are permitted. In other words, submenus can only be added to
+the root-level main menu and only commands can be added to existing submenus.
+
+<p>
+Submenus that have been added to the system's main menu can be removed through the <a href="DeleteSubmenu.html">DeleteSubmenu</a> command.
+
+<p>
+An AddSubmenu command only requires a single input to create a submenu on the head-unit's main menu:
+
+<p>
+<h2>Submenu Name (required)</h2>
+<h3>Type: String</h3>
+</br>
+The submenu name represents the text to display within the body of this submenu on the head-unit's main menu. This text should be a short description of what this menu will display when clicked.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/Alert.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/Alert.html
new file mode 100644
index 000000000..d4679ebee
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/Alert.html
@@ -0,0 +1,46 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Alert</h1>
+
+The Alert command allows developers to display a pop-up alert object on the head-unit UI. The alert command has the ability to display 3 lines of text as well as speak a message to the driver. Alerts are also able to play an alert sound as the pop-up appears.
+
+<p>
+To create an alert on the head-unit, 6 inputs are available:
+
+<p>
+<h2>Text to Speak (optional)</h2>
+<h3>Type: String</h3>
+</br>
+The text to speak for an alert message will be spoken to the driver by the vehicle's text to speech engine. This text should be a concise message telling the user what the alert is about.
+
+<p>
+<h2>Alert Lines (at least 1 required)</h2>
+<h3>Type: String</h3>
+</br>
+Alert messages are permitted to have up to 3 lines of text to alert the user. Alert messages should be concise and relevant to what the user is doing.
+
+<p>
+<h2>Alert Sound Enable (required)</h2>
+<h3>Type: Boolean</h3>
+</br>
+If your alert desires the vehicle's speakers to play an alert sound, set this value to true. If no sound is desired, set this value to false.
+
+<p>
+<h2>Alert Duration (required)</h2>
+<h3>Type: Integer</h3>
+</br>
+This value represents how long the alert should be displayed to the user. The duration parameter is an integer representing how many milliseconds the alert should be displayed for.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/ChangeRegistration.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/ChangeRegistration.html
new file mode 100644
index 000000000..0f39e30ef
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/ChangeRegistration.html
@@ -0,0 +1,167 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Change Registration</h1>
+
+The ChangeRegistration command allows developers to change the language options on the connected head-unit. There are two different types of language options available.
+The "Language" input option changes the spoken language being used on the head-unit to speak to the driver while the "HMI Display Language" changes the textual display
+language being displayed on the head-unit's HMI.
+
+<p>
+If the input fields match what is already being used by the head-unit, the request will be rejected.
+
+<p>
+To create an alert on the head-unit, 2 inputs are available:
+
+<p>
+<h2>Language (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+Represents the language being spoken to the driver through the vehicle's text-to-speech engine.
+
+<p>
+<h2>HMI Display Language (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+Represents the language being displayed on the head-unit's HMI.
+
+<h3>Available Language Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>Language</th>
+</tr>
+
+<tr>
+<td>EN-US</td>
+<td>English (USA)</td>
+</tr>
+
+<tr>
+<td>EN-AU</td>
+<td>English (Australia)</td>
+</tr>
+
+<tr>
+<td>EN-GB</td>
+<td>English (Great Britain)</td>
+</tr>
+
+<tr>
+<td>ES-MX</td>
+<td>Spanish (Mexico)</td>
+</tr>
+
+<tr>
+<td>ES-ES</td>
+<td>Spanish (Spain)</td>
+</tr>
+
+<tr>
+<td>FR-FR</td>
+<td>French (France)</td>
+</tr>
+
+<tr>
+<td>FR-CA</td>
+<td>French (Canada)</td>
+</tr>
+
+<tr>
+<td>DE-DE</td>
+<td>German</td>
+</tr>
+
+<tr>
+<td>RU-RU</td>
+<td>Russian</td>
+</tr>
+
+<tr>
+<td>TR-TR</td>
+<td>Turkish</td>
+</tr>
+
+<tr>
+<td>PL-PL</td>
+<td>Polish</td>
+</tr>
+
+<tr>
+<td>IT-IT</td>
+<td>Italian</td>
+</tr>
+
+<tr>
+<td>SV-SE</td>
+<td>Swedish</td>
+</tr>
+
+<tr>
+<td>PT-PT</td>
+<td>Portuguese</td>
+</tr>
+
+<tr>
+<td>PT-BR</td>
+<td>Portuguese (Brazil)</td>
+</tr>
+
+<tr>
+<td>NL-NL</td>
+<td>Dutch</td>
+</tr>
+
+<tr>
+<td>ZH-CN</td>
+<td>Chinese (Traditional)</td>
+</tr>
+
+<tr>
+<td>ZH-TW</td>
+<td>Chinese (Thai)</td>
+</tr>
+
+<tr>
+<td>JA-JP</td>
+<td>Japanese</td>
+</tr>
+
+<tr>
+<td>AR-SA</td>
+<td>Arabic</td>
+</tr>
+
+<tr>
+<td>KO-KR</td>
+<td>Korean</td>
+</tr>
+
+<tr>
+<td>CS-CZ</td>
+<td>Czech</td>
+</tr>
+
+<tr>
+<td>DA-DK</td>
+<td>Danish</td>
+</tr>
+
+<tr>
+<td>NO-NO</td>
+<td>Norwegian</td>
+</tr>
+
+</table>
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/CreateInteractionChoiceSet.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/CreateInteractionChoiceSet.html
new file mode 100644
index 000000000..4ef19c387
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/CreateInteractionChoiceSet.html
@@ -0,0 +1,43 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Create Interaction Choice Set</h1>
+
+The Create Interaction Choice Set command allows developers to display a pop-up list of options for the user to select from on the head-unit. Interaction choice
+sets are created by creating the list of choices for the user to select from. To display the interaction choice set, developers can utilize the <a href="PerformInteraction.html">PerformInteraction</a>
+ command.
+
+<p>
+A choice can contain 3 parameters:
+
+<p>
+<h2>Choice Name (required)</h2>
+<h3>Type: String</h3>
+</br>
+The choice name represents the text to display within the body of this choice on the pop-up list. This text should be a short description of what this button will do when clicked.
+
+<p>
+<h2>Voice Recognition Keyword(s) (required)</h2>
+<h3>Type: Comma-separated String</h3>
+</br>
+The voice recognition keyword input tells the vehicle what keywords are linked to this particular choice when spoken through the vehicle's voice system.
+
+<p>
+<h2>Image (optional)</h2>
+<h3>Type: Image</h3>
+</br>
+The image for a particular choice represents an image to be displayed alongside the choice name within the body of this choice on the pop-up menu. Images must have been added to the system
+through the <a href="PutFile.html">PutFile</a> command before they can be referenced for a choice.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteCommand.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteCommand.html
new file mode 100644
index 000000000..9eace8bab
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteCommand.html
@@ -0,0 +1,29 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Delete Command</h1>
+
+The DeleteCommand command allows developers to remove commands that have been added to the system through the
+<a href="AddCommand.html">AddCommand</a> command.
+
+<p>
+In order to delete a command that has been added to the system, you simply need to supply 1 parameter:
+
+<p>
+<h2>Command ID (required)</h2>
+<h3>Type: ID (integer)</h3>
+</br>
+The ID that was assigned to the command set when it was added to the system.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteFile.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteFile.html
new file mode 100644
index 000000000..7acd3739d
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteFile.html
@@ -0,0 +1,28 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Delete File</h1>
+
+The DeleteFile command allows developers to remove files that have been added to the system through the <a href="PutFile.html">PutFile</a> command.
+
+<p>
+In order to delete a file that has been added to the system, you simply need to supply 1 parameter:
+
+<p>
+<h2>File Name (required)</h2>
+<h3>Type: String</h3>
+</br>
+The file name for a file that has been added to the system is used whenever the application needs to reference that particular file.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteInteractionChoiceSet.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteInteractionChoiceSet.html
new file mode 100644
index 000000000..706454910
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteInteractionChoiceSet.html
@@ -0,0 +1,29 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Delete Interaction Choice Set</h1>
+
+The DeleteInteractionChoiceSet command allows developers to remove choice sets that have been added to the system through the
+<a href="CreateInteractionChoiceSet.html">CreateInteractionChoiceSet</a> command.
+
+<p>
+In order to delete a choice set that has been added to the system, you simply need to supply 1 parameter:
+
+<p>
+<h2>Choice Set ID (required)</h2>
+<h3>Type: ID (integer)</h3>
+</br>
+The ID that was assigned to the interaction choice set when it was added to the system.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteSubmenu.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteSubmenu.html
new file mode 100644
index 000000000..3ac8bd3ca
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/DeleteSubmenu.html
@@ -0,0 +1,28 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Delete Submenu</h1>
+
+The DeleteSubmenu command allows developers to remove root-level submenus that have been added through the <a href="AddSubmenu.html">AddSubmenu</a> command.
+
+<p>
+A DeleteSubmenu command only requires a single input to create a submenu on the head-unit's main menu:
+
+<p>
+<h2>Submenu ID (required)</h2>
+<h3>Type: ID (integer)</h3>
+</br>
+The submenu ID that was assigned to the submenu when it was added to the system.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/GetDTCs.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/GetDTCs.html
new file mode 100644
index 000000000..1f784f80f
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/GetDTCs.html
@@ -0,0 +1,28 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Get DTCs</h1>
+
+The GetDTCs command allows developers to read diagnostic trouble codes (DTCs) from various modules in the vehicle.
+
+<p>
+In order to retrieve DTCs from a particular module, the application must know the module's specific CAN bus ID and input this ID as a parameter.
+
+<p>
+<h2>ECU ID (required)</h2>
+<h3>Type: ID (integer)</h3>
+</br>
+The CAN bus ID of the module to read DTCs from. Valid ECU IDs range from 0 to 65535.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/ListFiles.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/ListFiles.html
new file mode 100644
index 000000000..a69eeb416
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/ListFiles.html
@@ -0,0 +1,22 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>List Files</h1>
+
+The ListFiles command allows developers to read a list of files that have been added to the head-unit through SDL. This command shows any dynamic files that have been
+added through the <a href="PutFile.html">PutFile</a> command as well as any static images that are permanently stored on the head-unit.
+
+<p>
+The ListFiles command does not accept any parameters.
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/PerformInteraction.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/PerformInteraction.html
new file mode 100644
index 000000000..2ebca73e2
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/PerformInteraction.html
@@ -0,0 +1,85 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Perform Interaction</h1>
+
+The PerformInteraction command allows developers to show a pop-up list dialog that has been added through the <a href="CreateInteractionChoiceSet.html">CreateInteractionChoiceSet</a> command.
+If the user selects one of the items in the interaction choice set, the result will be returned to the onPerformInteractionResponse method in the SDL Service. From there, the click can be
+dispatched to the appropriate click-listener based on the id that was selected. The user also has the option to back out of the menu, in which case the response will show an ABORTED message.
+
+<p>
+The PerformInteraction command has capabilities for the following parameters:
+
+<p>
+<h2>Interaction Title (required)</h2>
+<h3>Type: String</h3>
+</br>
+The interaction title will show above the choices in the pop-up dialog. This should be a concise description of what the pop-up is asking for.
+
+<p>
+<h2>Interaction Voice Prompt (optional)</h2>
+<h3>Type: Comma-separated string</h3>
+</br>
+The interaction voice prompt parameter will speak the input text to the user when the pop-up dialog shows.
+
+<p>
+<h2>Choice Set Ids (required)</h2>
+<h3>Type: Vector of IDs (integers)</h3>
+</br>
+In order to perform an interaction with the user, the developer must select a set of choice set IDs that have been added to the system using the
+<a href="CreateInteractionChoiceSet.html">CreateInteractionChoiceSet</a> command.
+
+<p>
+<h2>Interaction Mode (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+The interaction mode parameter specifies how the user can perform the interaction.
+
+<h3>Available Interaction Mode Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>Interaction Mode</th>
+</tr>
+
+<tr>
+<td>MANUAL_ONLY</td>
+<td>Only click events are permitted</td>
+</tr>
+
+<tr>
+<td>VOICE_REC_ONLY</td>
+<td>Only voice events are permitted</td>
+</tr>
+
+<tr>
+<td>BOTH</td>
+<td>Both click & voice events are permitted</td>
+</tr>
+
+</table>
+
+<p>
+<h2>Interaction Timeout Enable (required)</h2>
+<h3>Type: Boolean</h3>
+</br>
+Specifies whether or not the pop-up dialog should time-out after a certain number of time or not. If this value is set to false, the pop-up will remain on the screen
+indefinitely - until the app is disconnected or until the user navigates away from it.
+
+<p>
+<h2>Interaction Timeout (optional)</h2>
+<h3>Type: Integer</h3>
+</br>
+This value represents how long the pop-up should be displayed to the user. The duration parameter is an integer representing how many milliseconds the pop-up should be displayed for.
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/PutFile.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/PutFile.html
new file mode 100644
index 000000000..09b9e6e67
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/PutFile.html
@@ -0,0 +1,80 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Put File</h1>
+
+The PutFile command allows developers to send various types of files that can be dynamically stored on the head-unit. Any files that are added through this command
+can be referenced by the associated filename.
+
+<p>
+In order to add a file to the head-unit system, your application needs to specify the following parameters:
+
+<p>
+<h2>SDL File Name (required)</h2>
+<h3>Type: String</h3>
+</br>
+This is the filename that the file will be stored as on the head-unit. This name must be unique and must be between 1-500 characters in length.
+
+<p>
+<h2>SDL File Type (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+This parameter represents what type of file is being sent to the head-unit system through SDL.
+
+<h3>Available File Type Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>File Type</th>
+</tr>
+
+<tr>
+<td>GRAPHIC_BMP</td>
+<td>A bitmap image</td>
+</tr>
+
+<tr>
+<td>GRAPHIC_JPEG</td>
+<td>A JPEG image</td>
+</tr>
+
+<tr>
+<td>GRAPHIC_PNG</td>
+<td>A PNG image</td>
+</tr>
+
+<tr>
+<td>AUDIO_WAVE</td>
+<td>A WAVE audio file</td>
+</tr>
+
+<tr>
+<td>AUDIO_MP3</td>
+<td>An MP3 audio file</td>
+</tr>
+
+</table>
+
+<p>
+<h2>Persistent (optional)</h2>
+<h3>Type: Boolean</h3>
+</br>
+If this parameter is set to true, the head-unit system will attempt to save the file across ignition cycles. If the system requires storage for higher priority files, this file may be deleted.
+When this parameter is false (or null), the application will need to send the files each session.
+
+<p>
+<h2>Bulk Data (required)</h2>
+<h3>Type: byte[]</h3>
+</br>
+The final, and most important parameter for the PutFile command is the raw byte array of the file to be sent to the system.
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/ReadDIDs.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/ReadDIDs.html
new file mode 100644
index 000000000..319da7e40
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/ReadDIDs.html
@@ -0,0 +1,35 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Read DIDs</h1>
+
+The ReadDIDs command allows developers to read specific data (DIDs) from various modules in the vehicle.
+
+<p>
+In order to retrieve DTCs from a particular module, the application must know the module's specific CAN bus ID and the ID of the DID to
+retrieve.
+
+<p>
+<h2>ECU ID (required)</h2>
+<h3>Type: ID (integer)</h3>
+</br>
+The CAN bus ID of the module to read DIDs from. Valid ECU IDs range from 0 to 65535.
+
+<p>
+<h2>DID ID (required)</h2>
+<h3>Type: ID (integer)</h3>
+</br>
+The DID ID for the module to read DIDs from. Valid DID IDs range from 0 to 65535.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/ScrollableMessage.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/ScrollableMessage.html
new file mode 100644
index 000000000..a857a2a90
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/ScrollableMessage.html
@@ -0,0 +1,43 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Scrollable Message</h1>
+
+The ScrollableMessage command allows developers to display a large portion of text to the user.
+
+<p>
+The following parameters are available to display a long, scrollable message to the user.
+
+<p>
+<h2>Message Body (required)</h2>
+<h3>Type: String</h3>
+</br>
+The body of the message to display. The message can contain line breaks and tab characters, but must be no more than 500 characters in length.
+
+<p>
+<h2>Timeout (required)</h2>
+<h3>Type: Integer</h3>
+</br>
+This parameter represents the timeout for the scrollable message in milliseconds. When the timeout expires, the scrollable message will be dismissed.
+Timeout can be anywhere from 1 to 65535 milliseconds.
+
+<p>
+<h2>Soft Buttons (optional)</h2>
+<h3>Type: Vector&#60;SoftButton&#62;</h3>
+</br>
+This parameter allows your application to add custom soft buttons to the scrollable message interface. Soft buttons represent additional user actions
+that are available for the scrollable message. Depending on the HMI, soft buttons could be along-side the scrollable message or in a separate menu available
+to the user.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/SetAppIcon.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/SetAppIcon.html
new file mode 100644
index 000000000..e0ffdb550
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/SetAppIcon.html
@@ -0,0 +1,29 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Set App Icon</h1>
+
+The SetAppIcon command allows developers to set a custom icon that will be associated with their application. Before setting the app icon through
+this message, the image must first be added through the <a href="PutFile.html">PutFile</a> command.
+
+<p>
+In order to set the app icon for your application, only one parameter is needed:
+
+<p>
+<h2>Filename (required)</h2>
+<h3>Type: String</h3>
+</br>
+The filename associated with the image that was added to the system through the <a href="PutFile.html">PutFile</a> command.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/SetMediaClockTimer.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/SetMediaClockTimer.html
new file mode 100644
index 000000000..9a842bf09
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/SetMediaClockTimer.html
@@ -0,0 +1,71 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Set Media Clock Timer</h1>
+
+The SetMediaClockTimer command allows developers to manage a clock timer on the head-unit. This command is only applicable for media applications,
+such as music players or radio applications. If the connected application is not a media application, the line of text that contains the timer text
+will be replaced with the 4th line of text in the <a href="Show.html">Show</a> command. Therefore, if the connected application is a media application,
+the 4th line of text in the <a href="Show.html">Show</a> command will be inaccessible and replaced with the clock timer.
+
+<p>
+In order for a media application to manage the clock timer, 2 parameters are needed:
+
+<p>
+<h2>Timer Mode (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+The timer mode represents what the timer should do when this command is received.
+
+<h3>Available Timer Mode Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>Timer Mode</th>
+</tr>
+
+<tr>
+<td>COUNT_UP</td>
+<td>The timer should count-up from the input start time.</td>
+</tr>
+
+<tr>
+<td>COUNT_DOWN</td>
+<td>The timer should count-down from the input start time.</td>
+</tr>
+
+<tr>
+<td>PAUSE</td>
+<td>The timer should pause. Input start time will be ignored.</td>
+</tr>
+
+<tr>
+<td>RESUME</td>
+<td>The timer should resume. Input start time will be ignored.</td>
+</tr>
+
+<tr>
+<td>CLEAR</td>
+<td>The timer should pause and reset to 00:00:00. Input start time will be ignored.</td>
+</tr>
+
+</table>
+
+<p>
+<h2>Start Time (optional)</h2>
+<h3>Type: StartTime</h3>
+</br>
+This object represents a start time with hours, minutes and seconds as parameters. The start time is mandatory for
+COUNT_UP and COUNT_DOWN timer modes, but optional for other timer modes.
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/Show.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/Show.html
new file mode 100644
index 000000000..b3c77d8a4
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/Show.html
@@ -0,0 +1,71 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Show</h1>
+
+The Show command allows developers to set lines of text on the main HMI display. For non-media applications, 4 lines of text are available. For media applications,
+the 4th line of text is replaced with a media clock that can be manipulated through the <a href="SetMediaClockTimer.html">SetMediaClockTimer</a> command. In addition,
+an image can also be displayed on the main HMI display.
+
+<p>
+The Show command useful for all applications to keep users informed as to what is happening inside the application. For example, when a music player application
+starts to play a new song, the first line of text could display the song name, the second line could display the artist name, the third line could display
+which album the song is from while the last line would show the current clock timer for the song in progress. The album art for the playing song could be
+displayed as the main artwork.
+
+<p>
+In order to make a change to the main HMI display through the show command, the following parameters are available:
+
+<p>
+<h2>Lines of Text (optional)</h2>
+<h3>Type: String</h3>
+</br>
+Each line of metadata can be represented as its own string object.
+
+<p>
+<h2>Text Alignment (optional)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+The text alignment input tells the head-unit how the lines of text should be aligned.
+
+<h3>Available Text Alignment Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>Text Alignment</th>
+</tr>
+
+<tr>
+<td>ALIGN_LEFT</td>
+<td>All lines of text should be left-aligned.</td>
+</tr>
+
+<tr>
+<td>ALIGN_RIGHT</td>
+<td>All lines of text should be right-aligned.</td>
+</tr>
+
+<tr>
+<td>ALIGN_CENTER</td>
+<td>All lines of text should be centered.</td>
+</tr>
+
+</table>
+
+<p>
+<h2>Image (optional)</h2>
+<h3>Type: Image</h3>
+</br>
+The image object represents an image to be displayed on the head-unit's main HMI display. Images must have been added
+to the system through the <a href="PutFile.html">PutFile</a> command before they can be referenced for a Show command.
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/Slider.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/Slider.html
new file mode 100644
index 000000000..0cf5cccb3
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/Slider.html
@@ -0,0 +1,54 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Slider</h1>
+
+The Slider command allows developers to display a slider widget on the head-unit's display. This could be useful for various tasks, such as
+changing the phone's ringer volume through SDL.
+
+<p>
+In order to show a slider for your application, the following parameters are available:
+
+<p>
+<h2>Slider Header (optional)</h2>
+<h3>Type: String</h3>
+</br>
+This text will be displayed above the slider widget and should be a concise description of what the slider input will change in the application.
+
+<p>
+<h2>Slider Footer (optional)</h2>
+<h3>Type: Vector&#60;String&#62;</h3>
+</br>
+This text will be displayed below the slider widget.
+
+<p>
+<h2>Number of Ticks (required)</h2>
+<h3>Type: Integer</h3>
+</br>
+This represents the maximum number of selections that will be available on the slider widget. This number must be at least 2 and cannot be greater than 26.
+
+<p>
+<h2>Start Position (required)</h2>
+<h3>Type: Integer</h3>
+</br>
+This represents the initial selection on the slider widget. This number must be at least 1 and cannot be greater than the number of ticks.
+
+<p>
+<h2>Timeout (required)</h2>
+<h3>Type: Integer</h3>
+</br>
+This represents the number of milliseconds to display the widget before it times out. The current selection of the widget will be returned through the response
+message when the timeout occurs or when the user exits the slider widget. This number must be between 0 and 65535 milliseconds.
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/Speak.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/Speak.html
new file mode 100644
index 000000000..49e075b77
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/Speak.html
@@ -0,0 +1,66 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Speak</h1>
+
+The Speak command allows developers to prompt the vehicle's text-to-speech engine to speak text or phonemes to the head-unit.
+
+<p>
+In order to speak to the driver from your application, the following parameters are needed:
+
+<p>
+<h2>Text to Speak (required)</h2>
+<h3>Type: String</h3>
+</br>
+This string represents the text (or phonemes) that should be spoken to the driver.
+
+<p>
+<h2>Speech Capability (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+This string represents the text (or phonemes) that should be spoken to the driver.
+
+<h3>Available Speech Capability Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>Speech Capability</th>
+</tr>
+
+<tr>
+<td>TEXT</td>
+<td>The input string should be interpreted as raw text.</td>
+</tr>
+
+<tr>
+<td>SAPI_PHONEMES</td>
+<td>The input string should be interpreted as phonemes from Microsoft's Speech API.</td>
+</tr>
+
+<tr>
+<td>LHPLUS_PHONEMES</td>
+<td>The input string should be interpreted as phonemes from Nuance's API.</td>
+</tr>
+
+<tr>
+<td>PRE_RECORDED</td>
+<td>The input string should be interpreted as a reference to a pre-recorded message that is supported by the head-unit. This option will vary per vehicle.</td>
+</tr>
+
+<tr>
+<td>SILENCE</td>
+<td>The input text will be ignored and any speech that is in progress will be suspended.</td>
+</tr>
+
+</table>
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/SubscribeToButtons.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/SubscribeToButtons.html
new file mode 100644
index 000000000..980da03a1
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/SubscribeToButtons.html
@@ -0,0 +1,72 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Subscribe to Buttons</h1>
+
+The ButtonSubscribe command allows the developer to subscribe to hardware buttons that are contained in the vehicle. When one of the subscribed buttons
+is pressed, the event is dispatched to the onOnButtonPress method in the SDL service.
+
+<p>
+Button subscriptions are currently handled one at a time, so if the application would like to subscribe to multiple buttons, a separate message must be
+sent for each button.
+
+<p>
+In order to subscribe the application to hardware buttons, only one parameter is required:
+
+<p>
+<h2>ButtonName (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+Represents the button to subscribe to.
+
+<h3>Available Button Name Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>Button Name</th>
+</tr>
+
+<tr>
+<td>OK</td>
+<td>Represents the OK and PLAY buttons.</td>
+</tr>
+
+<tr>
+<td>PRESET0 - PRESET9</td>
+<td>Represents the preset buttons.</td>
+</tr>
+
+<tr>
+<td>SEEK_LEFT</td>
+<td>Represents the left seek button.</td>
+</tr>
+
+<tr>
+<td>SEEK_RIGHT</td>
+<td>Represents the right seek button.</td>
+</tr>
+
+<tr>
+<td>TUNE_UP</td>
+<td>Represents the up tune button.</td>
+</tr>
+
+<tr>
+<td>TUNE_DOWN</td>
+<td>Represents the down tune button.</td>
+</tr>
+
+</table>
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/UnsubscribeFromButtons.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/UnsubscribeFromButtons.html
new file mode 100644
index 000000000..2d83dc8bd
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/UnsubscribeFromButtons.html
@@ -0,0 +1,72 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<h1>Unsubscribe from Buttons</h1>
+
+The ButtonUnsubscribe command allows the developer to unsubscribe from hardware buttons that have been subscribed to through the
+<a href="SubscribeToButtons.html">ButtonSubscribe</a> command.
+
+<p>
+Button unsubscriptions are currently handled one at a time, so if the application would like to unsubscribe from multiple buttons, a separate message must be
+sent for each button.
+
+<p>
+In order to unsubscribe the application from hardware buttons, only one parameter is required:
+
+<p>
+<h2>ButtonName (required)</h2>
+<h3>Type: Enumerated Value</h3>
+</br>
+Represents the button to subscribe to.
+
+<h3>Available Button Name Options</h3>
+<table>
+<tr>
+<th>Enumerated Value</th>
+<th>Button Name</th>
+</tr>
+
+<tr>
+<td>OK</td>
+<td>Represents the OK and PLAY buttons.</td>
+</tr>
+
+<tr>
+<td>PRESET0 - PRESET9</td>
+<td>Represents the preset buttons.</td>
+</tr>
+
+<tr>
+<td>SEEK_LEFT</td>
+<td>Represents the left seek button.</td>
+</tr>
+
+<tr>
+<td>SEEK_RIGHT</td>
+<td>Represents the right seek button.</td>
+</tr>
+
+<tr>
+<td>TUNE_UP</td>
+<td>Represents the up tune button.</td>
+</tr>
+
+<tr>
+<td>TUNE_DOWN</td>
+<td>Represents the down tune button.</td>
+</tr>
+
+</table>
+
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/assets/help_docs/html/index.html b/SDL_Android/LivioTesterApp/assets/help_docs/html/index.html
new file mode 100644
index 000000000..65633ed42
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/assets/help_docs/html/index.html
@@ -0,0 +1,43 @@
+<html>
+
+<head>
+
+<link rel="stylesheet" type="text/css" href="../css/style.css">
+
+</head>
+
+<body>
+
+<center><h1>Help</h1></center>
+
+<div class="big"><ul>
+
+<li><a href="AddCommand.html">Add Command</a></li>
+<li><a href="AddSubmenu.html">Add Submenu</a></li>
+<li><a href="Alert.html">Alert</a></li>
+<li><a href="ChangeRegistration.html">Change Registration</a></li>
+<li><a href="CreateInteractionChoiceSet.html">Create Interaction Choice Set</a></li>
+<li><a href="DeleteFile.html">Delete File</a></li>
+<li><a href="DeleteInteractionChoiceSet.html">Delete Interaction Choice Set</a></li>
+<li><a href="DeleteCommand.html">Delete Command</a></li>
+<li><a href="DeleteSubmenu.html">Delete Submenu</a></li>
+<li><a href="GetDTCs.html">Get DTCs</a></li>
+<li><a href="ListFiles.html">List Files</a></li>
+<li><a href="PerformInteraction.html">Perform Interaction</a></li>
+<li><a href="PutFile.html">Put File</a></li>
+<li><a href="ReadDIDs.html">Read DIDs</a></li>
+<li><a href="ScrollableMessage.html">Scrollable Message</a></li>
+<li><a href="SetAppIcon.html">Set App Icon</a></li>
+<li><a href="SetMediaClockTimer.html">Set Media Clock Timer</a></li>
+<li><a href="Show.html">Show</a></li>
+<li><a href="Slider.html">Slider</a></li>
+<li><a href="Speak.html">Speak</a></li>
+<li><a href="SubscribeToButtons.html">Subscribe to Buttons</a></li>
+<li><a href="UnsubscribeFromButtons.html">Unsubscribe from Buttons</a></li>
+
+</ul></div>
+
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/bin/AndroidManifest.xml b/SDL_Android/LivioTesterApp/bin/AndroidManifest.xml
new file mode 100644
index 000000000..0aae1d707
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.livio.sdltester"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <!-- Required to pair Bluetooth devices -->
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <!-- Required to check if WiFi is enabled -->
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <!-- Required to make the device stay awake while doing XML tests -->
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk
+ android:minSdkVersion="11"
+ android:targetSdkVersion="19" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.livio.sdltester.MainActivity"
+ android:label="@string/app_name"
+ android:launchMode="singleInstance"
+ android:screenOrientation="portrait" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service android:name="com.livio.sdl.SdlService" />
+
+ <activity
+ android:name="com.livio.sdltester.HelpActivity"
+ android:label="@string/title_activity_help" >
+ </activity>
+ </application>
+
+</manifest>
diff --git a/SDL_Android/LivioTesterApp/bin/LivioSdlTester.apk b/SDL_Android/LivioTesterApp/bin/LivioSdlTester.apk
new file mode 100644
index 000000000..c4c0472a2
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/LivioSdlTester.apk
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/R.txt b/SDL_Android/LivioTesterApp/bin/R.txt
new file mode 100644
index 000000000..0b36f95aa
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/R.txt
@@ -0,0 +1,214 @@
+int dimen activity_horizontal_margin 0x7f060000
+int dimen activity_vertical_margin 0x7f060001
+int drawable add 0x7f020000
+int drawable add_to_favorites 0x7f020001
+int drawable anchor 0x7f020002
+int drawable game_pad 0x7f020003
+int drawable ic_close 0x7f020004
+int drawable ic_launcher 0x7f020005
+int drawable remove 0x7f020006
+int drawable remove_from_favorites 0x7f020007
+int drawable rocket 0x7f020008
+int drawable select_an_image 0x7f020009
+int drawable undo 0x7f02000a
+int drawable zoom_in 0x7f02000b
+int drawable zoom_out 0x7f02000c
+int id btn_main_sendMessage 0x7f080019
+int id but_addItem 0x7f080014
+int id but_performInteraction_selectChoiceSets 0x7f08001d
+int id but_scrollableMessage_clear 0x7f08002b
+int id but_showImage 0x7f080047
+int id cb_putFile_addAll 0x7f080024
+int id cb_putFile_isPersistent 0x7f080027
+int id check_alert_playTone 0x7f08000b
+int id check_enable_image 0x7f080004
+int id check_performInteraction_timeoutEnabled 0x7f080020
+int id check_show1 0x7f08003b
+int id check_show2 0x7f08003d
+int id check_show3 0x7f08003f
+int id check_show4 0x7f080041
+int id check_statusBar 0x7f080045
+int id et_addCommand_commandName 0x7f080001
+int id et_addCommand_voiceRecKeyword 0x7f080002
+int id et_addSubMenu_subMenuName 0x7f080006
+int id et_alert_line1 0x7f080008
+int id et_alert_line2 0x7f080009
+int id et_alert_line3 0x7f08000a
+int id et_alert_textToSpeak 0x7f080007
+int id et_choice_imageName 0x7f080013
+int id et_choice_name 0x7f080011
+int id et_choice_vr_text 0x7f080012
+int id et_getDtcs_ecuName 0x7f080016
+int id et_imageName 0x7f080005
+int id et_ipAddress 0x7f08002e
+int id et_ipPort 0x7f08002f
+int id et_mediaClockHours 0x7f080036
+int id et_mediaClockMins 0x7f080037
+int id et_mediaClockSecs 0x7f080038
+int id et_performInteraction_title 0x7f08001b
+int id et_performInteraction_voicePrompt 0x7f08001c
+int id et_putFile_imageName 0x7f080026
+int id et_readDids_didLocation 0x7f080029
+int id et_readDids_ecuName 0x7f080028
+int id et_scrollableMessage_text 0x7f08002a
+int id et_show1 0x7f08003a
+int id et_show2 0x7f08003c
+int id et_show3 0x7f08003e
+int id et_show4 0x7f080040
+int id et_slider_footer 0x7f08004b
+int id et_slider_title 0x7f08004a
+int id et_statusBar 0x7f080046
+int id et_textToSpeak 0x7f080052
+int id ib_putFile_selectAnImage 0x7f080025
+int id iv_rowImage 0x7f080048
+int id listView 0x7f080017
+int id list_main_commandList 0x7f08001a
+int id ll_clock 0x7f080035
+int id lv_choices 0x7f080015
+int id menu_clear_list 0x7f080058
+int id menu_connect 0x7f080055
+int id menu_disconnect 0x7f080056
+int id menu_help 0x7f080059
+int id menu_reset 0x7f080057
+int id seek_alert_toneDuration 0x7f08000e
+int id seek_performInteraction_timeoutDuration 0x7f080023
+int id seek_scrollableMessage_timeout 0x7f08002d
+int id seek_slider_numOfTicks 0x7f08004d
+int id seek_slider_startPosition 0x7f08004f
+int id seek_slider_timeout 0x7f080051
+int id spin_addCommand_submenus 0x7f080003
+int id spin_changeRegistration_hmiLanguage 0x7f080010
+int id spin_changeRegistration_language 0x7f08000f
+int id spin_mediaClock_updateMode 0x7f080033
+int id spin_performInteraction_interactionMode 0x7f08001f
+int id spin_speechCapabilities 0x7f080053
+int id spin_textAlignment 0x7f080043
+int id textview 0x7f080054
+int id tv_alert_toneDuration 0x7f08000d
+int id tv_alert_toneDurationHeader 0x7f08000c
+int id tv_connectionStatus 0x7f080018
+int id tv_mediaClock_clock 0x7f080034
+int id tv_messageDetail 0x7f080032
+int id tv_messageName 0x7f080030
+int id tv_metadataLines 0x7f080039
+int id tv_performInteraction_interactionMode 0x7f08001e
+int id tv_performInteraction_timeoutDuration 0x7f080022
+int id tv_performInteraction_timeoutTitle 0x7f080021
+int id tv_rowText 0x7f080049
+int id tv_scrollableMessage_timeout 0x7f08002c
+int id tv_slider_numOfTicks 0x7f08004c
+int id tv_slider_startPosition 0x7f08004e
+int id tv_slider_timeout 0x7f080050
+int id tv_statusBar 0x7f080044
+int id tv_textAlignment 0x7f080042
+int id tv_timestamp 0x7f080031
+int id wv_help 0x7f080000
+int layout activity_help 0x7f030000
+int layout add_command 0x7f030001
+int layout add_submenu 0x7f030002
+int layout alert 0x7f030003
+int layout change_registration 0x7f030004
+int layout choice_set_item 0x7f030005
+int layout create_choice_interaction_set 0x7f030006
+int layout get_dtcs 0x7f030007
+int layout listview 0x7f030008
+int layout main 0x7f030009
+int layout offline_mode 0x7f03000a
+int layout perform_interaction 0x7f03000b
+int layout put_file 0x7f03000c
+int layout read_dids 0x7f03000d
+int layout scrollable_message 0x7f03000e
+int layout sdl_connection 0x7f03000f
+int layout sdl_message_listview_row 0x7f030010
+int layout set_media_clock_timer 0x7f030011
+int layout show 0x7f030012
+int layout simple_listview_with_image 0x7f030013
+int layout slider 0x7f030014
+int layout speak 0x7f030015
+int layout textview 0x7f030016
+int menu menu_main 0x7f070000
+int string action_settings 0x7f040050
+int string add_command_command_name 0x7f040004
+int string add_command_image_type 0x7f040007
+int string add_command_parent_menu 0x7f040006
+int string add_command_vr_keyword 0x7f040005
+int string add_image 0x7f04004e
+int string alert_duration_ms 0x7f040023
+int string alert_duration_s 0x7f040022
+int string alert_line1 0x7f04001e
+int string alert_line2 0x7f04001f
+int string alert_line3 0x7f040020
+int string alert_sound_enabled 0x7f040021
+int string app_name 0x7f040000
+int string button_subscriptions_already_subscribed 0x7f040043
+int string button_subscriptions_none_subscribed 0x7f040044
+int string choice_name 0x7f040018
+int string clock 0x7f04002d
+int string clock_hrs 0x7f04002e
+int string clock_mins 0x7f04002f
+int string clock_secs 0x7f040030
+int string colon 0x7f040031
+int string connection_status_format 0x7f04003e
+int string did_location 0x7f04000c
+int string ecu_name 0x7f04000b
+int string function_bank_root_name 0x7f040042
+int string hello_world 0x7f040051
+int string hmi_language 0x7f04001c
+int string interaction_list_none_added 0x7f04004b
+int string ip_address 0x7f04003f
+int string ip_port 0x7f040040
+int string item_number 0x7f040016
+int string item_number1 0x7f040017
+int string language 0x7f04001b
+int string max_choices 0x7f040015
+int string media 0x7f04002c
+int string media_clock_timer_mode 0x7f040032
+int string media_track 0x7f04002b
+int string menu_clear_list 0x7f04003b
+int string menu_connect 0x7f040038
+int string menu_disconnect 0x7f040039
+int string menu_help 0x7f04003c
+int string menu_reset 0x7f04003a
+int string metadata_line1 0x7f040025
+int string metadata_line2 0x7f040026
+int string metadata_line3 0x7f040027
+int string metadata_line4 0x7f040028
+int string metadata_lines 0x7f040024
+int string negative_button 0x7f040003
+int string no_commands_to_delete 0x7f040008
+int string no_selection 0x7f040035
+int string no_submenus_to_delete 0x7f040009
+int string not_an_sdl_command 0x7f040037
+int string not_implemented 0x7f040036
+int string offline_mode_text 0x7f040041
+int string perform_interaction_mode 0x7f040048
+int string perform_interaction_select_choice_set 0x7f040047
+int string perform_interaction_timeout 0x7f040049
+int string perform_interaction_timeout_enabled 0x7f04004a
+int string perform_interaction_title 0x7f040045
+int string perform_interaction_voice_prompt 0x7f040046
+int string positive_button 0x7f040002
+int string put_file_persistent_file 0x7f04004c
+int string scrollable_message_clear 0x7f040014
+int string scrollable_message_hint 0x7f040013
+int string scrollable_message_text 0x7f040012
+int string sdl_command_dialog_title 0x7f04003d
+int string sdl_disconnected 0x7f040001
+int string sdl_image_name 0x7f04004d
+int string slider_footer 0x7f04000e
+int string slider_start_position 0x7f040010
+int string slider_ticks 0x7f04000f
+int string slider_title 0x7f04000d
+int string status_bar 0x7f04002a
+int string submenu_name 0x7f04000a
+int string text_alignment 0x7f040029
+int string text_to_speak 0x7f04001d
+int string timeout 0x7f040011
+int string title_activity_help 0x7f04004f
+int string units_milliseconds 0x7f040034
+int string units_seconds 0x7f040033
+int string use_artwork 0x7f04001a
+int string voice_keyword 0x7f040019
+int style AppBaseTheme 0x7f050000
+int style AppTheme 0x7f050001
+int style small_header_text 0x7f050002
diff --git a/SDL_Android/LivioTesterApp/bin/classes.dex b/SDL_Android/LivioTesterApp/bin/classes.dex
new file mode 100644
index 000000000..72f31bc2b
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/classes.dex
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/jarlist.cache b/SDL_Android/LivioTesterApp/bin/jarlist.cache
new file mode 100644
index 000000000..0565465f2
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/jarlist.cache
@@ -0,0 +1,3 @@
+# cache for current jar dependency. DO NOT EDIT.
+# format is <lastModified> <length> <SHA-1> <path>
+# Encoding is UTF-8
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-hdpi/ic_launcher.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..a2cc99ca4
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-ldpi/ic_launcher.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..26f1f76bb
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-mdpi/ic_launcher.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..287daac09
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-xhdpi/ic_launcher.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..f5af7b060
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add.png
new file mode 100644
index 000000000..5e5c4014a
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add_to_favorites.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add_to_favorites.png
new file mode 100644
index 000000000..512e63cb4
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/add_to_favorites.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/anchor.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/anchor.png
new file mode 100644
index 000000000..242e1202d
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/anchor.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/game_pad.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/game_pad.png
new file mode 100644
index 000000000..36430d21e
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/game_pad.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_close.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_close.png
new file mode 100644
index 000000000..f19eba601
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_close.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_launcher.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_launcher.png
new file mode 100644
index 000000000..26f1f76bb
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove.png
new file mode 100644
index 000000000..079802700
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove_from_favorites.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove_from_favorites.png
new file mode 100644
index 000000000..9f9afdd73
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/remove_from_favorites.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/rocket.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/rocket.png
new file mode 100644
index 000000000..4cfb090b2
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/rocket.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/select_an_image.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/select_an_image.png
new file mode 100644
index 000000000..918b4331c
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/select_an_image.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/undo.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/undo.png
new file mode 100644
index 000000000..c8b4cc02d
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/undo.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_in.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_in.png
new file mode 100644
index 000000000..fab7aa33f
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_in.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_out.png b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_out.png
new file mode 100644
index 000000000..a777d818c
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/res/crunch/drawable/zoom_out.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/bin/resources.ap_ b/SDL_Android/LivioTesterApp/bin/resources.ap_
new file mode 100644
index 000000000..0d7de4ec2
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/bin/resources.ap_
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/gen/com/livio/sdl/R.java b/SDL_Android/LivioTesterApp/gen/com/livio/sdl/R.java
new file mode 100644
index 000000000..81d901957
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/gen/com/livio/sdl/R.java
@@ -0,0 +1,87 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+package com.livio.sdl;
+
+public final class R {
+ public static final class drawable {
+ public static final int ic_launcher = 0x7f020005;
+ }
+ public static final class id {
+ public static final int iv_rowImage = 0x7f080048;
+ public static final int listView = 0x7f080017;
+ public static final int textview = 0x7f080054;
+ public static final int tv_messageDetail = 0x7f080032;
+ public static final int tv_messageName = 0x7f080030;
+ public static final int tv_rowText = 0x7f080049;
+ public static final int tv_timestamp = 0x7f080031;
+ }
+ public static final class layout {
+ public static final int listview = 0x7f030008;
+ public static final int sdl_message_listview_row = 0x7f030010;
+ public static final int simple_listview_with_image = 0x7f030013;
+ public static final int textview = 0x7f030016;
+ }
+ public static final class string {
+ public static final int add_command_command_name = 0x7f040004;
+ public static final int add_command_image_type = 0x7f040007;
+ public static final int add_command_parent_menu = 0x7f040006;
+ public static final int add_command_vr_keyword = 0x7f040005;
+ public static final int alert_duration_ms = 0x7f040023;
+ public static final int alert_duration_s = 0x7f040022;
+ public static final int alert_line1 = 0x7f04001e;
+ public static final int alert_line2 = 0x7f04001f;
+ public static final int alert_line3 = 0x7f040020;
+ public static final int alert_sound_enabled = 0x7f040021;
+ public static final int app_name = 0x7f040000;
+ public static final int choice_name = 0x7f040018;
+ public static final int clock = 0x7f04002d;
+ public static final int clock_hrs = 0x7f04002e;
+ public static final int clock_mins = 0x7f04002f;
+ public static final int clock_secs = 0x7f040030;
+ public static final int colon = 0x7f040031;
+ public static final int did_location = 0x7f04000c;
+ public static final int ecu_name = 0x7f04000b;
+ public static final int hmi_language = 0x7f04001c;
+ public static final int item_number = 0x7f040016;
+ public static final int item_number1 = 0x7f040017;
+ public static final int language = 0x7f04001b;
+ public static final int max_choices = 0x7f040015;
+ public static final int media = 0x7f04002c;
+ public static final int media_clock_timer_mode = 0x7f040032;
+ public static final int media_track = 0x7f04002b;
+ public static final int metadata_line1 = 0x7f040025;
+ public static final int metadata_line2 = 0x7f040026;
+ public static final int metadata_line3 = 0x7f040027;
+ public static final int metadata_line4 = 0x7f040028;
+ public static final int metadata_lines = 0x7f040024;
+ public static final int negative_button = 0x7f040003;
+ public static final int no_commands_to_delete = 0x7f040008;
+ public static final int no_submenus_to_delete = 0x7f040009;
+ public static final int positive_button = 0x7f040002;
+ public static final int scrollable_message_clear = 0x7f040014;
+ public static final int scrollable_message_hint = 0x7f040013;
+ public static final int scrollable_message_text = 0x7f040012;
+ public static final int sdl_disconnected = 0x7f040001;
+ public static final int slider_footer = 0x7f04000e;
+ public static final int slider_start_position = 0x7f040010;
+ public static final int slider_ticks = 0x7f04000f;
+ public static final int slider_title = 0x7f04000d;
+ public static final int status_bar = 0x7f04002a;
+ public static final int submenu_name = 0x7f04000a;
+ public static final int text_alignment = 0x7f040029;
+ public static final int text_to_speak = 0x7f04001d;
+ public static final int timeout = 0x7f040011;
+ public static final int units_milliseconds = 0x7f040034;
+ public static final int units_seconds = 0x7f040033;
+ public static final int use_artwork = 0x7f04001a;
+ public static final int voice_keyword = 0x7f040019;
+ }
+ public static final class style {
+ public static final int AppBaseTheme = 0x7f050000;
+ public static final int AppTheme = 0x7f050001;
+ }
+}
diff --git a/SDL_Android/LivioTesterApp/gen/com/livio/sdltester/BuildConfig.java b/SDL_Android/LivioTesterApp/gen/com/livio/sdltester/BuildConfig.java
new file mode 100644
index 000000000..dc3cc9902
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/gen/com/livio/sdltester/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package com.livio.sdltester;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+} \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/gen/com/livio/sdltester/R.java b/SDL_Android/LivioTesterApp/gen/com/livio/sdltester/R.java
new file mode 100644
index 000000000..3d24a11b3
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/gen/com/livio/sdltester/R.java
@@ -0,0 +1,340 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package com.livio.sdltester;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class dimen {
+ /** Default screen margins, per the Android Design guidelines.
+
+ Customize dimensions originally defined in res/values/dimens.xml (such as
+ screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+
+ */
+ public static final int activity_horizontal_margin=0x7f060000;
+ public static final int activity_vertical_margin=0x7f060001;
+ }
+ public static final class drawable {
+ public static final int add=0x7f020000;
+ public static final int add_to_favorites=0x7f020001;
+ public static final int anchor=0x7f020002;
+ public static final int game_pad=0x7f020003;
+ public static final int ic_close=0x7f020004;
+ public static final int ic_launcher=0x7f020005;
+ public static final int remove=0x7f020006;
+ public static final int remove_from_favorites=0x7f020007;
+ public static final int rocket=0x7f020008;
+ public static final int select_an_image=0x7f020009;
+ public static final int undo=0x7f02000a;
+ public static final int zoom_in=0x7f02000b;
+ public static final int zoom_out=0x7f02000c;
+ }
+ public static final class id {
+ public static final int btn_main_sendMessage=0x7f080019;
+ public static final int but_addItem=0x7f080014;
+ public static final int but_performInteraction_selectChoiceSets=0x7f08001d;
+ public static final int but_scrollableMessage_clear=0x7f08002b;
+ public static final int but_showImage=0x7f080047;
+ public static final int cb_putFile_addAll=0x7f080024;
+ public static final int cb_putFile_isPersistent=0x7f080027;
+ public static final int check_alert_playTone=0x7f08000b;
+ public static final int check_enable_image=0x7f080004;
+ public static final int check_performInteraction_timeoutEnabled=0x7f080020;
+ public static final int check_show1=0x7f08003b;
+ public static final int check_show2=0x7f08003d;
+ public static final int check_show3=0x7f08003f;
+ public static final int check_show4=0x7f080041;
+ public static final int check_statusBar=0x7f080045;
+ public static final int et_addCommand_commandName=0x7f080001;
+ public static final int et_addCommand_voiceRecKeyword=0x7f080002;
+ public static final int et_addSubMenu_subMenuName=0x7f080006;
+ public static final int et_alert_line1=0x7f080008;
+ public static final int et_alert_line2=0x7f080009;
+ public static final int et_alert_line3=0x7f08000a;
+ public static final int et_alert_textToSpeak=0x7f080007;
+ public static final int et_choice_imageName=0x7f080013;
+ public static final int et_choice_name=0x7f080011;
+ public static final int et_choice_vr_text=0x7f080012;
+ public static final int et_getDtcs_ecuName=0x7f080016;
+ public static final int et_imageName=0x7f080005;
+ public static final int et_ipAddress=0x7f08002e;
+ public static final int et_ipPort=0x7f08002f;
+ public static final int et_mediaClockHours=0x7f080036;
+ public static final int et_mediaClockMins=0x7f080037;
+ public static final int et_mediaClockSecs=0x7f080038;
+ public static final int et_performInteraction_title=0x7f08001b;
+ public static final int et_performInteraction_voicePrompt=0x7f08001c;
+ public static final int et_putFile_imageName=0x7f080026;
+ public static final int et_readDids_didLocation=0x7f080029;
+ public static final int et_readDids_ecuName=0x7f080028;
+ public static final int et_scrollableMessage_text=0x7f08002a;
+ public static final int et_show1=0x7f08003a;
+ public static final int et_show2=0x7f08003c;
+ public static final int et_show3=0x7f08003e;
+ public static final int et_show4=0x7f080040;
+ public static final int et_slider_footer=0x7f08004b;
+ public static final int et_slider_title=0x7f08004a;
+ public static final int et_statusBar=0x7f080046;
+ public static final int et_textToSpeak=0x7f080052;
+ public static final int ib_putFile_selectAnImage=0x7f080025;
+ public static final int iv_rowImage=0x7f080048;
+ public static final int listView=0x7f080017;
+ public static final int list_main_commandList=0x7f08001a;
+ public static final int ll_clock=0x7f080035;
+ public static final int lv_choices=0x7f080015;
+ public static final int menu_clear_list=0x7f080058;
+ public static final int menu_connect=0x7f080055;
+ public static final int menu_disconnect=0x7f080056;
+ public static final int menu_help=0x7f080059;
+ public static final int menu_reset=0x7f080057;
+ public static final int seek_alert_toneDuration=0x7f08000e;
+ public static final int seek_performInteraction_timeoutDuration=0x7f080023;
+ public static final int seek_scrollableMessage_timeout=0x7f08002d;
+ public static final int seek_slider_numOfTicks=0x7f08004d;
+ public static final int seek_slider_startPosition=0x7f08004f;
+ public static final int seek_slider_timeout=0x7f080051;
+ public static final int spin_addCommand_submenus=0x7f080003;
+ public static final int spin_changeRegistration_hmiLanguage=0x7f080010;
+ public static final int spin_changeRegistration_language=0x7f08000f;
+ public static final int spin_mediaClock_updateMode=0x7f080033;
+ public static final int spin_performInteraction_interactionMode=0x7f08001f;
+ public static final int spin_speechCapabilities=0x7f080053;
+ public static final int spin_textAlignment=0x7f080043;
+ public static final int textview=0x7f080054;
+ public static final int tv_alert_toneDuration=0x7f08000d;
+ public static final int tv_alert_toneDurationHeader=0x7f08000c;
+ public static final int tv_connectionStatus=0x7f080018;
+ public static final int tv_mediaClock_clock=0x7f080034;
+ public static final int tv_messageDetail=0x7f080032;
+ public static final int tv_messageName=0x7f080030;
+ public static final int tv_metadataLines=0x7f080039;
+ public static final int tv_performInteraction_interactionMode=0x7f08001e;
+ public static final int tv_performInteraction_timeoutDuration=0x7f080022;
+ public static final int tv_performInteraction_timeoutTitle=0x7f080021;
+ public static final int tv_rowText=0x7f080049;
+ public static final int tv_scrollableMessage_timeout=0x7f08002c;
+ public static final int tv_slider_numOfTicks=0x7f08004c;
+ public static final int tv_slider_startPosition=0x7f08004e;
+ public static final int tv_slider_timeout=0x7f080050;
+ public static final int tv_statusBar=0x7f080044;
+ public static final int tv_textAlignment=0x7f080042;
+ public static final int tv_timestamp=0x7f080031;
+ public static final int wv_help=0x7f080000;
+ }
+ public static final class layout {
+ public static final int activity_help=0x7f030000;
+ public static final int add_command=0x7f030001;
+ public static final int add_submenu=0x7f030002;
+ public static final int alert=0x7f030003;
+ public static final int change_registration=0x7f030004;
+ public static final int choice_set_item=0x7f030005;
+ public static final int create_choice_interaction_set=0x7f030006;
+ public static final int get_dtcs=0x7f030007;
+ public static final int listview=0x7f030008;
+ public static final int main=0x7f030009;
+ public static final int offline_mode=0x7f03000a;
+ public static final int perform_interaction=0x7f03000b;
+ public static final int put_file=0x7f03000c;
+ public static final int read_dids=0x7f03000d;
+ public static final int scrollable_message=0x7f03000e;
+ public static final int sdl_connection=0x7f03000f;
+ public static final int sdl_message_listview_row=0x7f030010;
+ public static final int set_media_clock_timer=0x7f030011;
+ public static final int show=0x7f030012;
+ public static final int simple_listview_with_image=0x7f030013;
+ public static final int slider=0x7f030014;
+ public static final int speak=0x7f030015;
+ public static final int textview=0x7f030016;
+ }
+ public static final class menu {
+ public static final int menu_main=0x7f070000;
+ }
+ public static final class string {
+ public static final int action_settings=0x7f040050;
+ /** Add Command
+ */
+ public static final int add_command_command_name=0x7f040004;
+ public static final int add_command_image_type=0x7f040007;
+ public static final int add_command_parent_menu=0x7f040006;
+ public static final int add_command_vr_keyword=0x7f040005;
+ public static final int add_image=0x7f04004e;
+ public static final int alert_duration_ms=0x7f040023;
+ public static final int alert_duration_s=0x7f040022;
+ public static final int alert_line1=0x7f04001e;
+ public static final int alert_line2=0x7f04001f;
+ public static final int alert_line3=0x7f040020;
+ public static final int alert_sound_enabled=0x7f040021;
+ /** General Strings
+ General Strings
+ */
+ public static final int app_name=0x7f040000;
+ /** Button Subscriptions
+ */
+ public static final int button_subscriptions_already_subscribed=0x7f040043;
+ public static final int button_subscriptions_none_subscribed=0x7f040044;
+ public static final int choice_name=0x7f040018;
+ public static final int clock=0x7f04002d;
+ public static final int clock_hrs=0x7f04002e;
+ public static final int clock_mins=0x7f04002f;
+ public static final int clock_secs=0x7f040030;
+ public static final int colon=0x7f040031;
+ /** SDL Connection
+ */
+ public static final int connection_status_format=0x7f04003e;
+ public static final int did_location=0x7f04000c;
+ /** Get DTCs and Read DIDs
+ */
+ public static final int ecu_name=0x7f04000b;
+ /** Function Banks
+ */
+ public static final int function_bank_root_name=0x7f040042;
+ public static final int hello_world=0x7f040051;
+ public static final int hmi_language=0x7f04001c;
+ public static final int interaction_list_none_added=0x7f04004b;
+ public static final int ip_address=0x7f04003f;
+ public static final int ip_port=0x7f040040;
+ public static final int item_number=0x7f040016;
+ /** Choice Set Item
+ */
+ public static final int item_number1=0x7f040017;
+ /** Change Registration
+ */
+ public static final int language=0x7f04001b;
+ /** Create Interaction Choice Set
+ */
+ public static final int max_choices=0x7f040015;
+ public static final int media=0x7f04002c;
+ public static final int media_clock_timer_mode=0x7f040032;
+ public static final int media_track=0x7f04002b;
+ public static final int menu_clear_list=0x7f04003b;
+ /** Menu Strings
+ */
+ public static final int menu_connect=0x7f040038;
+ public static final int menu_disconnect=0x7f040039;
+ public static final int menu_help=0x7f04003c;
+ public static final int menu_reset=0x7f04003a;
+ public static final int metadata_line1=0x7f040025;
+ public static final int metadata_line2=0x7f040026;
+ public static final int metadata_line3=0x7f040027;
+ public static final int metadata_line4=0x7f040028;
+ /** Show Dialog
+ */
+ public static final int metadata_lines=0x7f040024;
+ public static final int negative_button=0x7f040003;
+ /** Delete Command
+ */
+ public static final int no_commands_to_delete=0x7f040008;
+ public static final int no_selection=0x7f040035;
+ /** Delete Submenu
+ */
+ public static final int no_submenus_to_delete=0x7f040009;
+ public static final int not_an_sdl_command=0x7f040037;
+ public static final int not_implemented=0x7f040036;
+ /** Offline Mode
+ */
+ public static final int offline_mode_text=0x7f040041;
+ public static final int perform_interaction_mode=0x7f040048;
+ public static final int perform_interaction_select_choice_set=0x7f040047;
+ public static final int perform_interaction_timeout=0x7f040049;
+ public static final int perform_interaction_timeout_enabled=0x7f04004a;
+ /** Perform Interaction
+ */
+ public static final int perform_interaction_title=0x7f040045;
+ public static final int perform_interaction_voice_prompt=0x7f040046;
+ /** Dialog Strings
+ */
+ public static final int positive_button=0x7f040002;
+ /** Put File
+ */
+ public static final int put_file_persistent_file=0x7f04004c;
+ public static final int scrollable_message_clear=0x7f040014;
+ public static final int scrollable_message_hint=0x7f040013;
+ /** Scrollable Message
+ */
+ public static final int scrollable_message_text=0x7f040012;
+ /** Dialog Strings
+ */
+ public static final int sdl_command_dialog_title=0x7f04003d;
+ public static final int sdl_disconnected=0x7f040001;
+ public static final int sdl_image_name=0x7f04004d;
+ public static final int slider_footer=0x7f04000e;
+ public static final int slider_start_position=0x7f040010;
+ public static final int slider_ticks=0x7f04000f;
+ /** Slider
+ */
+ public static final int slider_title=0x7f04000d;
+ public static final int status_bar=0x7f04002a;
+ /** Add Submenu
+ */
+ public static final int submenu_name=0x7f04000a;
+ public static final int text_alignment=0x7f040029;
+ /** Alert Dialog
+ */
+ public static final int text_to_speak=0x7f04001d;
+ public static final int timeout=0x7f040011;
+ public static final int title_activity_help=0x7f04004f;
+ public static final int units_milliseconds=0x7f040034;
+ /** Unit Suffixes
+ */
+ public static final int units_seconds=0x7f040033;
+ public static final int use_artwork=0x7f04001a;
+ public static final int voice_keyword=0x7f040019;
+ }
+ public static final class style {
+ /**
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+
+
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+
+
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+
+ API 11 theme customizations can go here.
+
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+
+ API 14 theme customizations can go here.
+
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+
+
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+
+
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+
+ API 11 theme customizations can go here.
+
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+
+ API 14 theme customizations can go here.
+ */
+ public static final int AppBaseTheme=0x7f050000;
+ /** Application theme.
+ All customizations that are NOT specific to a particular API-level can go here.
+ Application theme.
+ All customizations that are NOT specific to a particular API-level can go here.
+ */
+ public static final int AppTheme=0x7f050001;
+ public static final int small_header_text=0x7f050002;
+ }
+}
diff --git a/SDL_Android/LivioTesterApp/proguard-project.txt b/SDL_Android/LivioTesterApp/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/SDL_Android/LivioTesterApp/project.properties b/SDL_Android/LivioTesterApp/project.properties
new file mode 100644
index 000000000..e088af660
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/project.properties
@@ -0,0 +1,16 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-18
+android.library.reference.1=../LivioSdlUtilities
+android.library.reference.2=../../appcompat_v7
diff --git a/SDL_Android/LivioTesterApp/res/drawable-hdpi/ic_launcher.png b/SDL_Android/LivioTesterApp/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..60c1227aa
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable-ldpi/ic_launcher.png b/SDL_Android/LivioTesterApp/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..2308f49e3
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable-mdpi/ic_launcher.png b/SDL_Android/LivioTesterApp/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..1dc3a7735
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable-xhdpi/ic_launcher.png b/SDL_Android/LivioTesterApp/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..05949c460
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/add.png b/SDL_Android/LivioTesterApp/res/drawable/add.png
new file mode 100644
index 000000000..de2049056
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/add.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/add_to_favorites.png b/SDL_Android/LivioTesterApp/res/drawable/add_to_favorites.png
new file mode 100644
index 000000000..47743b515
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/add_to_favorites.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/anchor.png b/SDL_Android/LivioTesterApp/res/drawable/anchor.png
new file mode 100644
index 000000000..d680110df
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/anchor.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/game_pad.png b/SDL_Android/LivioTesterApp/res/drawable/game_pad.png
new file mode 100644
index 000000000..d07876268
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/game_pad.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/ic_close.png b/SDL_Android/LivioTesterApp/res/drawable/ic_close.png
new file mode 100644
index 000000000..2cb447347
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/ic_close.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/ic_launcher.png b/SDL_Android/LivioTesterApp/res/drawable/ic_launcher.png
new file mode 100644
index 000000000..2308f49e3
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/ic_launcher.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/remove.png b/SDL_Android/LivioTesterApp/res/drawable/remove.png
new file mode 100644
index 000000000..c45bade5b
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/remove.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/remove_from_favorites.png b/SDL_Android/LivioTesterApp/res/drawable/remove_from_favorites.png
new file mode 100644
index 000000000..e4c3d63cc
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/remove_from_favorites.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/rocket.png b/SDL_Android/LivioTesterApp/res/drawable/rocket.png
new file mode 100644
index 000000000..05d47869c
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/rocket.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/select_an_image.png b/SDL_Android/LivioTesterApp/res/drawable/select_an_image.png
new file mode 100644
index 000000000..f382f04cc
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/select_an_image.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/undo.png b/SDL_Android/LivioTesterApp/res/drawable/undo.png
new file mode 100644
index 000000000..8455136f9
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/undo.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/zoom_in.png b/SDL_Android/LivioTesterApp/res/drawable/zoom_in.png
new file mode 100644
index 000000000..cb66fc29d
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/zoom_in.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/drawable/zoom_out.png b/SDL_Android/LivioTesterApp/res/drawable/zoom_out.png
new file mode 100644
index 000000000..e72b0f436
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/drawable/zoom_out.png
Binary files differ
diff --git a/SDL_Android/LivioTesterApp/res/layout/activity_help.xml b/SDL_Android/LivioTesterApp/res/layout/activity_help.xml
new file mode 100644
index 000000000..bcf229d77
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/activity_help.xml
@@ -0,0 +1,6 @@
+<WebView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/wv_help"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".HelpActivity" />
diff --git a/SDL_Android/LivioTesterApp/res/layout/add_command.xml b/SDL_Android/LivioTesterApp/res/layout/add_command.xml
new file mode 100755
index 000000000..a2f3400af
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/add_command.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <EditText
+ android:id="@+id/et_addCommand_commandName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapWords"
+ android:hint="@string/add_command_command_name"/>
+
+ <EditText android:id="@+id/et_addCommand_voiceRecKeyword"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapSentences"
+ android:hint="@string/add_command_vr_keyword"/>
+
+ <TextView
+ style="@style/small_header_text"
+ android:text="@string/add_command_parent_menu"/>
+
+ <Spinner
+ android:id="@+id/spin_addCommand_submenus"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ </Spinner>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/check_enable_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <EditText
+ android:id="@+id/et_imageName"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textPersonName"
+ android:hint="@string/sdl_image_name"
+ android:enabled="false" />
+
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/res/layout/add_submenu.xml b/SDL_Android/LivioTesterApp/res/layout/add_submenu.xml
new file mode 100755
index 000000000..7d55626a7
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/add_submenu.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip">
+
+ <EditText android:id="@+id/et_addSubMenu_subMenuName"
+ android:hint="@string/submenu_name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textCapWords" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/res/layout/alert.xml b/SDL_Android/LivioTesterApp/res/layout/alert.xml
new file mode 100755
index 000000000..621b9f7a1
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/alert.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <EditText android:id="@+id/et_alert_textToSpeak"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapWords"
+ android:hint="@string/text_to_speak"/>
+
+ <EditText android:id="@+id/et_alert_line1"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapWords"
+ android:hint="@string/alert_line1"/>
+
+ <EditText android:id="@+id/et_alert_line2"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapWords"
+ android:hint="@string/alert_line2"/>
+
+ <EditText android:id="@+id/et_alert_line3"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapWords"
+ android:hint="@string/alert_line3"/>
+
+ <CheckBox android:id="@+id/check_alert_playTone"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="left"
+ android:text="@string/alert_sound_enabled"
+ android:checked="true"
+ />
+
+ <TextView android:id="@+id/tv_alert_toneDurationHeader"
+ style="@style/small_header_text"
+ android:text="@string/alert_duration_s"/>
+
+ <TextView android:id="@+id/tv_alert_toneDuration"
+ style="@style/small_header_text"/>
+
+ <SeekBar android:id="@+id/seek_alert_toneDuration"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:max="50"
+ android:progress="50"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/res/layout/change_registration.xml b/SDL_Android/LivioTesterApp/res/layout/change_registration.xml
new file mode 100644
index 000000000..4fc1ca17c
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/change_registration.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:gravity="center"
+ android:text="@string/language"/>
+
+ <Spinner android:id="@+id/spin_changeRegistration_language"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:textStyle="bold"
+ android:gravity="center"
+ android:text="@string/hmi_language"/>
+
+ <Spinner android:id="@+id/spin_changeRegistration_hmiLanguage"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/res/layout/choice_set_item.xml b/SDL_Android/LivioTesterApp/res/layout/choice_set_item.xml
new file mode 100644
index 000000000..21a25d059
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/choice_set_item.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <EditText
+ android:id="@+id/et_choice_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:ems="10"
+ android:inputType="textCapWords"
+ android:hint="@string/choice_name" >
+
+ <requestFocus />
+ </EditText>
+
+ <EditText
+ android:id="@+id/et_choice_vr_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/et_choice_name"
+ android:ems="10"
+ android:inputType="textCapWords"
+ android:hint="@string/voice_keyword" />
+
+ <CheckBox
+ android:id="@+id/check_enable_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/et_choice_vr_text"/>
+
+ <EditText
+ android:id="@+id/et_choice_imageName"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/et_choice_vr_text"
+ android:layout_toRightOf="@+id/check_enable_image"
+ android:ems="10"
+ android:enabled="false"
+ android:inputType="textPersonName"
+ android:hint="@string/sdl_image_name" />
+
+</RelativeLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/create_choice_interaction_set.xml b/SDL_Android/LivioTesterApp/res/layout/create_choice_interaction_set.xml
new file mode 100644
index 000000000..83b8d626f
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/create_choice_interaction_set.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip">
+
+ <Button
+ android:id="@+id/but_addItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Add an item" />
+
+ <ListView android:id="@+id/lv_choices"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/get_dtcs.xml b/SDL_Android/LivioTesterApp/res/layout/get_dtcs.xml
new file mode 100755
index 000000000..76d580153
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/get_dtcs.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip">
+
+ <EditText android:id="@+id/et_getDtcs_ecuName"
+ android:hint="@string/ecu_name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="number" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/res/layout/main.xml b/SDL_Android/LivioTesterApp/res/layout/main.xml
new file mode 100644
index 000000000..465ebe719
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/main.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <TextView android:id="@+id/tv_connectionStatus"
+ style="@style/small_header_text"
+ android:text="@string/connection_status_format" />
+
+ <Button android:id="@+id/btn_main_sendMessage"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Send Message"/>
+
+ <ListView android:id="@+id/list_main_commandList"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_width="fill_parent"
+ />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/offline_mode.xml b/SDL_Android/LivioTesterApp/res/layout/offline_mode.xml
new file mode 100644
index 000000000..bd34dda13
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/offline_mode.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/offline_mode_text" >
+
+
+</TextView>
diff --git a/SDL_Android/LivioTesterApp/res/layout/perform_interaction.xml b/SDL_Android/LivioTesterApp/res/layout/perform_interaction.xml
new file mode 100644
index 000000000..f1ce25a8d
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/perform_interaction.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <EditText android:id="@+id/et_performInteraction_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textCapSentences"
+ android:hint="@string/perform_interaction_title">
+
+ <requestFocus />
+ </EditText>
+
+ <EditText
+ android:id="@+id/et_performInteraction_voicePrompt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textCapSentences"
+ android:hint="@string/perform_interaction_voice_prompt"/>
+
+ <Button
+ android:id="@+id/but_performInteraction_selectChoiceSets"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/perform_interaction_select_choice_set"
+ />
+
+ <TextView android:id="@+id/tv_performInteraction_interactionMode"
+ style="@style/small_header_text"
+ android:text="@string/perform_interaction_mode" />
+
+ <Spinner android:id="@+id/spin_performInteraction_interactionMode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <CheckBox android:id="@+id/check_performInteraction_timeoutEnabled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/perform_interaction_timeout_enabled"
+ android:checked="true" />
+
+ <TextView android:id="@+id/tv_performInteraction_timeoutTitle"
+ style="@style/small_header_text"
+ android:text="@string/perform_interaction_timeout" />
+
+ <TextView android:id="@+id/tv_performInteraction_timeoutDuration"
+ style="@style/small_header_text" />
+
+ <SeekBar android:id="@+id/seek_performInteraction_timeoutDuration"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:max="95"
+ android:progress="95"
+ />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/put_file.xml b/SDL_Android/LivioTesterApp/res/layout/put_file.xml
new file mode 100644
index 000000000..8e0b7bb43
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/put_file.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip">
+
+ <CheckBox
+ android:id="@+id/cb_putFile_addAll"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Add all images" />
+
+ <ImageButton android:id="@+id/ib_putFile_selectAnImage"
+ android:layout_width="match_parent"
+ android:layout_height="240dp"
+ android:src="@drawable/select_an_image"
+ android:background="#00FFFFFF" />
+
+ <EditText android:id="@+id/et_putFile_imageName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapSentences"
+ android:enabled="false"
+ android:hint="@string/sdl_image_name">
+
+ <requestFocus />
+ </EditText>
+
+ <CheckBox
+ android:id="@+id/cb_putFile_isPersistent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/put_file_persistent_file" />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/read_dids.xml b/SDL_Android/LivioTesterApp/res/layout/read_dids.xml
new file mode 100644
index 000000000..00efee4a8
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/read_dids.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip">
+
+ <EditText android:id="@+id/et_readDids_ecuName"
+ android:hint="@string/ecu_name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="number" />
+
+ <EditText android:id="@+id/et_readDids_didLocation"
+ android:hint="@string/did_location"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="number" />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/scrollable_message.xml b/SDL_Android/LivioTesterApp/res/layout/scrollable_message.xml
new file mode 100644
index 000000000..d93a5d535
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/scrollable_message.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <EditText
+ android:id="@+id/et_scrollableMessage_text"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:inputType="textMultiLine"
+ android:singleLine="false"
+ android:hint="@string/scrollable_message_hint"
+ android:text="@string/scrollable_message_text">
+
+ <requestFocus />
+ </EditText>
+
+ <Button android:id="@+id/but_scrollableMessage_clear"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/scrollable_message_clear"/>
+
+ <TextView android:id="@+id/tv_scrollableMessage_timeout"
+ style="@style/small_header_text"
+ android:text="@string/timeout"/>
+
+ <SeekBar android:id="@+id/seek_scrollableMessage_timeout"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:max="64"
+ android:progress="29"
+ />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/sdl_connection.xml b/SDL_Android/LivioTesterApp/res/layout/sdl_connection.xml
new file mode 100644
index 000000000..89d002ed7
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/sdl_connection.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <EditText
+ android:id="@+id/et_ipAddress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="@string/ip_address"
+ android:inputType="number|text" >
+
+ <requestFocus />
+ </EditText>
+
+ <EditText
+ android:id="@+id/et_ipPort"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="@string/ip_port"
+ android:inputType="number|text" />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/set_media_clock_timer.xml b/SDL_Android/LivioTesterApp/res/layout/set_media_clock_timer.xml
new file mode 100644
index 000000000..ea60a7e1c
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/set_media_clock_timer.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <TextView
+ style="@style/small_header_text"
+ android:text="@string/media_clock_timer_mode" />
+
+ <Spinner android:id="@+id/spin_mediaClock_updateMode"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView android:id="@+id/tv_mediaClock_clock"
+ style="@style/small_header_text"
+ android:text="@string/clock" />
+
+ <LinearLayout android:id="@+id/ll_clock"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_horizontal">
+
+ <EditText android:id="@+id/et_mediaClockHours"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="number"
+ android:hint="@string/clock_hrs"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="@string/colon" />
+
+ <EditText android:id="@+id/et_mediaClockMins"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="number"
+ android:hint="@string/clock_mins"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="@string/colon" />
+
+ <EditText android:id="@+id/et_mediaClockSecs"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="number"
+ android:hint="@string/clock_secs"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/show.xml b/SDL_Android/LivioTesterApp/res/layout/show.xml
new file mode 100644
index 000000000..acd4a955b
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/show.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip" >
+
+
+ <TextView
+ android:id="@+id/tv_metadataLines"
+ style="@style/small_header_text"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:text="@string/metadata_lines"
+ />
+
+ <EditText
+ android:id="@+id/et_show1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/tv_metadataLines"
+ android:layout_toRightOf="@+id/check_show1"
+ android:hint="@string/metadata_line1"
+ android:inputType="textCapWords"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <EditText
+ android:id="@+id/et_show2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/et_show1"
+ android:layout_toRightOf="@+id/check_show2"
+ android:hint="@string/metadata_line2"
+ android:inputType="textCapWords"
+ android:ems="10"/>
+
+ <EditText
+ android:id="@+id/et_show3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/et_show2"
+ android:layout_toRightOf="@+id/check_show3"
+ android:hint="@string/metadata_line3"
+ android:inputType="textCapWords"
+ android:ems="10"/>
+
+ <EditText
+ android:id="@+id/et_show4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/et_show3"
+ android:layout_toRightOf="@+id/check_show4"
+ android:hint="@string/metadata_line4"
+ android:inputType="textCapWords"
+ android:ems="10"/>
+
+ <CheckBox
+ android:id="@+id/check_show1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/et_show1"
+ android:layout_alignParentLeft="true"
+ android:checked="true" />
+
+ <CheckBox
+ android:id="@+id/check_show2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/et_show2"
+ android:layout_alignBottom="@+id/et_show2"
+ android:layout_toLeftOf="@+id/et_show1"
+ android:checked="true" />
+
+ <CheckBox
+ android:id="@+id/check_show3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/et_show3"
+ android:layout_alignParentLeft="true"
+ android:checked="false" />
+
+ <CheckBox
+ android:id="@+id/check_show4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/et_show4"
+ android:layout_alignParentLeft="true"
+ android:checked="false" />
+
+ <TextView
+ android:id="@+id/tv_textAlignment"
+ style="@style/small_header_text"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/et_show4"
+ android:text="@string/text_alignment"
+ />
+
+ <Spinner
+ android:id="@+id/spin_textAlignment"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignLeft="@+id/tv_textAlignment"
+ android:layout_below="@+id/tv_textAlignment" />
+
+ <TextView
+ android:id="@+id/tv_statusBar"
+ style="@style/small_header_text"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/spin_textAlignment"
+ android:text="@string/status_bar"
+ android:visibility="gone" />
+
+ <CheckBox
+ android:id="@+id/check_statusBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/et_statusBar"
+ android:layout_alignParentLeft="true"
+ android:checked="false"
+ android:visibility="gone" />
+
+ <EditText
+ android:id="@+id/et_statusBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/tv_statusBar"
+ android:layout_toRightOf="@+id/check_statusBar"
+ android:ems="10"
+ android:hint="@string/status_bar"
+ android:inputType="textCapWords"
+ android:visibility="gone" />
+
+ <CheckBox
+ android:id="@+id/check_enable_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/spin_textAlignment"
+ android:layout_alignParentLeft="true"/>
+
+ <EditText
+ android:id="@+id/et_show_image"
+ android:layout_below="@+id/spin_textAlignment"
+ android:layout_toRightOf="@+id/check_enable_image"
+ android:layout_alignParentRight="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapSentences"
+ android:enabled="false"
+ android:hint="@string/sdl_image_name" />
+
+ </RelativeLayout>
+
+</ScrollView>
diff --git a/SDL_Android/LivioTesterApp/res/layout/slider.xml b/SDL_Android/LivioTesterApp/res/layout/slider.xml
new file mode 100644
index 000000000..93dcd6bdf
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/slider.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:orientation="vertical"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <EditText android:id="@+id/et_slider_title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapWords"
+ android:hint="@string/slider_title"/>
+
+ <EditText android:id="@+id/et_slider_footer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:freezesText="true"
+ android:inputType="textCapWords"
+ android:hint="@string/slider_footer"/>
+
+ <TextView android:id="@+id/tv_slider_numOfTicks"
+ style="@style/small_header_text"
+ android:text="@string/slider_ticks"/>
+
+ <SeekBar android:id="@+id/seek_slider_numOfTicks"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:max="24"
+ android:progress="0"
+ />
+
+ <TextView android:id="@+id/tv_slider_startPosition"
+ style="@style/small_header_text"
+ android:text="@string/slider_start_position"/>
+
+ <SeekBar android:id="@+id/seek_slider_startPosition"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:max="25"
+ android:progress="0"
+ />
+
+ <TextView android:id="@+id/tv_slider_timeout"
+ style="@style/small_header_text"
+ android:text="@string/timeout"/>
+
+ <SeekBar android:id="@+id/seek_slider_timeout"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:max="64"
+ android:progress="10"
+ />
+
+</LinearLayout>
diff --git a/SDL_Android/LivioTesterApp/res/layout/speak.xml b/SDL_Android/LivioTesterApp/res/layout/speak.xml
new file mode 100644
index 000000000..307603f2e
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/layout/speak.xml
@@ -0,0 +1,22 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <EditText
+ android:id="@+id/et_textToSpeak"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:singleLine="true"
+ android:hint="@string/text_to_speak">
+
+ <requestFocus />
+ </EditText>
+
+ <Spinner
+ android:id="@+id/spin_speechCapabilities"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/SDL_Android/LivioTesterApp/res/menu/menu_help.xml b/SDL_Android/LivioTesterApp/res/menu/menu_help.xml
new file mode 100644
index 000000000..cbc4e94b9
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/menu/menu_help.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:id="@+id/menu_help_home" android:title="@string/menu_help_home"></item>
+ <item android:id="@+id/menu_help_close" android:title="@string/menu_help_close"></item>
+
+</menu>
diff --git a/SDL_Android/LivioTesterApp/res/menu/menu_main.xml b/SDL_Android/LivioTesterApp/res/menu/menu_main.xml
new file mode 100644
index 000000000..1bd82b6e4
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/menu/menu_main.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:id="@+id/menu_connect" android:title="@string/menu_connect"></item>
+ <item android:id="@+id/menu_disconnect" android:title="@string/menu_disconnect"></item>
+ <item android:id="@+id/menu_reset" android:title="@string/menu_reset"></item>
+ <item android:id="@+id/menu_clear_list" android:title="@string/menu_clear_list"></item>
+ <item android:id="@+id/menu_help" android:title="@string/menu_help"></item>
+
+</menu>
diff --git a/SDL_Android/LivioTesterApp/res/values-sw600dp/dimens.xml b/SDL_Android/LivioTesterApp/res/values-sw600dp/dimens.xml
new file mode 100644
index 000000000..44f01db75
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/values-sw600dp/dimens.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as
+ screen margins) for sw600dp devices (e.g. 7" tablets) here.
+ -->
+
+</resources>
diff --git a/SDL_Android/LivioTesterApp/res/values-sw720dp-land/dimens.xml b/SDL_Android/LivioTesterApp/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 000000000..61e3fa8fb
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,9 @@
+<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as
+ screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+ -->
+ <dimen name="activity_horizontal_margin">128dp</dimen>
+
+</resources>
diff --git a/SDL_Android/LivioTesterApp/res/values-v11/styles.xml b/SDL_Android/LivioTesterApp/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/SDL_Android/LivioTesterApp/res/values-v14/styles.xml b/SDL_Android/LivioTesterApp/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/SDL_Android/LivioTesterApp/res/values/dimens.xml b/SDL_Android/LivioTesterApp/res/values/dimens.xml
new file mode 100644
index 000000000..55c1e5908
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/values/dimens.xml
@@ -0,0 +1,7 @@
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/SDL_Android/LivioTesterApp/res/values/strings.xml b/SDL_Android/LivioTesterApp/res/values/strings.xml
new file mode 100644
index 000000000..8fb1c32df
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/values/strings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <!-- General Strings -->
+ <string name="app_name">Livio SDL Tester</string>
+ <string name="no_selection">No selection</string>
+ <string name="not_implemented">This feature is not yet implemented.</string>
+ <string name="not_an_sdl_command">The input argument is not an SDL command.</string>
+
+ <!-- Menu Strings -->
+ <string name="menu_connect">Connect to SDL</string>
+ <string name="menu_disconnect">Disconnect from SDL</string>
+ <string name="menu_reset">Reset SDL Connection</string>
+ <string name="menu_clear_list">Clear ListView</string>
+ <string name="menu_help">Help</string>
+
+ <string name="menu_help_home">Help Home</string>
+ <string name="menu_help_close">Close Help</string>
+
+ <!-- Dialog Strings -->
+ <string name="sdl_command_dialog_title">Select an SDL Command:</string>
+
+ <!-- SDL Connection -->
+ <string name="connection_status_format">Connection Status:</string>
+ <string name="ip_address">IP Address</string>
+ <string name="ip_port">IP Port</string>
+
+ <!-- Offline Mode -->
+ <string name="offline_mode_text">Would you like to enter offline mode?</string>
+
+ <!-- Function Banks -->
+ <string name="function_bank_root_name">Root-level Menu</string>
+
+ <!-- Button Subscriptions -->
+ <string name="button_subscriptions_already_subscribed">Already subscribed to all buttons!</string>
+ <string name="button_subscriptions_none_subscribed">App hasn\'t subscribed to buttons yet!</string>
+
+ <!-- Perform Interaction -->
+ <string name="perform_interaction_title">Interaction Title</string>
+ <string name="perform_interaction_voice_prompt">Interaction Voice Prompt</string>
+ <string name="perform_interaction_select_choice_set">Select Choice Sets</string>
+ <string name="perform_interaction_mode">Interaction Mode</string>
+ <string name="perform_interaction_timeout">Interaction Timeout (in seconds)</string>
+ <string name="perform_interaction_timeout_enabled">Interaction Timeout Enabled</string>
+ <string name="interaction_list_none_added">No choice interaction sets have been added yet!</string>
+
+ <!-- Put File -->
+ <string name="put_file_persistent_file">Persistent File</string>
+ <string name="sdl_image_name">SDL Image Name</string>
+ <string name="add_image">Add Image</string>
+ <string name="title_activity_help">HelpActivity</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+
+</resources>
diff --git a/SDL_Android/LivioTesterApp/res/values/styles.xml b/SDL_Android/LivioTesterApp/res/values/styles.xml
new file mode 100644
index 000000000..7f314d2b7
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/res/values/styles.xml
@@ -0,0 +1,27 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+ <style name="small_header_text">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:layout_margin">10dip</item>
+ </style>
+
+</resources>
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/HelpActivity.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/HelpActivity.java
new file mode 100644
index 000000000..3748c90ab
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/HelpActivity.java
@@ -0,0 +1,133 @@
+package com.livio.sdltester;
+
+import java.util.Stack;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Toast;
+
+public class HelpActivity extends Activity {
+
+ public static final String EXTRA_ASSET_FILENAME = "com.livio.sdltester.HelpActivity.extraAssetFilename";
+
+ private static final String ASSET_URL_FORMAT = "file:///android_asset/help_docs/html/";
+ private static final String INDEX_FILENAME = "index.html";
+
+ private WebView webView;
+ private Stack<String> urlStack;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_help);
+
+ ActionBar actionBar = getActionBar();
+ if(actionBar != null){
+ actionBar.setTitle("SDL Help");
+ }
+
+ urlStack = new Stack<String>();
+
+ webView = (WebView) findViewById(R.id.wv_help);
+ webView.setWebViewClient(new WebViewClient(){
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ super.onPageStarted(view, url, favicon);
+ urlStack.push(url);
+ }
+
+ @Override
+ public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+ Toast.makeText(HelpActivity.this, "An error occurred.", Toast.LENGTH_LONG).show();
+
+ // the current URL didn't work, so remove it from the url stack
+ urlStack.pop();
+ }
+
+ });
+
+ Intent incomingIntent = getIntent();
+ if(incomingIntent.hasExtra(EXTRA_ASSET_FILENAME)){
+ String fileName = incomingIntent.getStringExtra(EXTRA_ASSET_FILENAME);
+ if(fileName != null && fileName.length() > 0){
+ navigateToFileName(fileName);
+ }
+ else{
+ navigateToIndex();
+ }
+ }
+ else{
+ navigateToIndex();
+ }
+ }
+
+ private void navigateToFileName(String fileName){
+ webView.loadUrl(makeUrl(fileName));
+ }
+
+ private void navigateToIndex(){
+ webView.loadUrl(makeUrl(INDEX_FILENAME));
+ }
+
+ @Override
+ public void onBackPressed() {
+ if(urlStack == null || urlStack.size() <= 1){
+ // if something went awry with the stack or if we're back at index when back is pressed, we'll just finish the activity
+ finish();
+ }
+ else{
+ // since we are going back, we don't need the current URL anymore
+ urlStack.pop();
+
+ // retrieve the last-visited page (it will be added back to the stack automatically when the page loads)
+ String urlToLoad = urlStack.pop();
+ webView.loadUrl(urlToLoad);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu_help, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ android.view.MenuItem helpHome = (android.view.MenuItem) menu.findItem(R.id.menu_help_home);
+
+ // show/hide connect/disconnect menu items
+ boolean atIndex = (urlStack == null || urlStack.size() <= 1);
+ helpHome.setVisible(!atIndex); // if we're not at the index, show the home button
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(android.view.MenuItem item) {
+ int menuItemId = item.getItemId();
+ switch(menuItemId){
+ case R.id.menu_help_home:
+ urlStack.clear();
+ navigateToIndex();
+ return true;
+ case R.id.menu_help_close:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private static String makeUrl(String fileName){
+ return new StringBuilder().append(ASSET_URL_FORMAT).append(fileName).toString();
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/LivioSdlTesterPreferences.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/LivioSdlTesterPreferences.java
new file mode 100644
index 000000000..6a3add0cf
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/LivioSdlTesterPreferences.java
@@ -0,0 +1,79 @@
+package com.livio.sdltester;
+
+import android.content.Context;
+
+import com.livio.sdl.utils.ApplicationPreferences;
+
+/**
+ * A static, uninstantiable class that allows saving application-specific information to
+ * Android's SharedPreferences. This class is a wrapper around ApplicationPreferences,
+ * allowing specific method names for the various types of data that need to be saved
+ * as part of this application.
+ *
+ * @see ApplicationPreferences
+ *
+ * @author Mike Burke
+ *
+ */
+public final class LivioSdlTesterPreferences {
+
+ private LivioSdlTesterPreferences(){}
+
+ private static final String FILENAME = "com.livio.sdltester";
+
+ private static final class Keys{
+ public static final String IP_ADDRESS = "ip_address";
+ public static final String TCP_PORT = "tcp_port";
+ }
+
+ /**
+ * Looks up the IP_ADDRESS key in Android SharedPreferences and returns the
+ * IP address if it's found.
+ *
+ * @param context Context to use for SharedPreferences
+ * @return The IP address string if it was found, null otherwise
+ */
+ public static String restoreIpAddress(Context context){
+ if(ApplicationPreferences.exists(context, FILENAME, Keys.IP_ADDRESS)){
+ return ApplicationPreferences.getString(context, FILENAME, Keys.IP_ADDRESS);
+ }
+
+ return null;
+ }
+
+ /**
+ * Saves the input IP address to Android SharedPreferences.
+ *
+ * @param context Context to use for SharedPreferences
+ * @param value The IP address string to save
+ */
+ public static void saveIpAddress(Context context, String value){
+ ApplicationPreferences.putString(context, FILENAME, Keys.IP_ADDRESS, value);
+ }
+
+ /**
+ * Looks up the TCP_PORT key in Android SharedPreferences and returns the
+ * TCP port if it's found.
+ *
+ * @param context Context to use for SharedPreferences
+ * @param value The TCP port string if it was found, null otherwise
+ */
+ public static String restoreTcpPort(Context context){
+ if(ApplicationPreferences.exists(context, FILENAME, Keys.TCP_PORT)){
+ return ApplicationPreferences.getString(context, FILENAME, Keys.TCP_PORT);
+ }
+
+ return null;
+ }
+
+ /**
+ * Saves the input TCP port to Android SharedPreferences.
+ *
+ * @param context Context to use for SharedPreferences
+ * @param value The TCP port string to save
+ */
+ public static void saveTcpPort(Context context, String value){
+ ApplicationPreferences.putString(context, FILENAME, Keys.TCP_PORT, value);
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/MainActivity.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/MainActivity.java
new file mode 100644
index 000000000..0e35a27e6
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/MainActivity.java
@@ -0,0 +1,1276 @@
+package com.livio.sdltester;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.livio.sdl.IpAddress;
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.SdlImageItem.SdlImageItemComparator;
+import com.livio.sdl.SdlLogMessage;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.SdlService;
+import com.livio.sdl.adapters.SdlMessageAdapter;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.dialogs.IndeterminateProgressDialog;
+import com.livio.sdl.dialogs.JsonFlipperDialog;
+import com.livio.sdl.dialogs.ListViewDialog;
+import com.livio.sdl.enums.EnumComparator;
+import com.livio.sdl.enums.SdlButton;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.menu.MenuItem;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdl.utils.Timeout;
+import com.livio.sdl.utils.WifiUtils;
+import com.livio.sdltester.dialogs.AddCommandDialog;
+import com.livio.sdltester.dialogs.AddSubMenuDialog;
+import com.livio.sdltester.dialogs.ButtonSubscriptionDialog;
+import com.livio.sdltester.dialogs.ButtonUnsubscriptionDialog;
+import com.livio.sdltester.dialogs.ChangeRegistrationDialog;
+import com.livio.sdltester.dialogs.CreateInteractionChoiceSetDialog;
+import com.livio.sdltester.dialogs.DeleteCommandDialog;
+import com.livio.sdltester.dialogs.DeleteFileDialog;
+import com.livio.sdltester.dialogs.DeleteInteractionDialog;
+import com.livio.sdltester.dialogs.DeleteSubmenuDialog;
+import com.livio.sdltester.dialogs.GetDtcsDialog;
+import com.livio.sdltester.dialogs.PerformInteractionDialog;
+import com.livio.sdltester.dialogs.PutFileDialog;
+import com.livio.sdltester.dialogs.ReadDidsDialog;
+import com.livio.sdltester.dialogs.ScrollableMessageDialog;
+import com.livio.sdltester.dialogs.SdlAlertDialog;
+import com.livio.sdltester.dialogs.SdlConnectionDialog;
+import com.livio.sdltester.dialogs.SetAppIconDialog;
+import com.livio.sdltester.dialogs.SetMediaClockTimerDialog;
+import com.livio.sdltester.dialogs.ShowDialog;
+import com.livio.sdltester.dialogs.SliderDialog;
+import com.livio.sdltester.dialogs.SpeakDialog;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.ListFiles;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+import com.smartdevicelink.proxy.rpc.enums.UpdateMode;
+
+
+public class MainActivity extends Activity{
+ /**
+ * Used when requesting information from the SDL service, these constants can be used
+ * to perform different tasks when the information is asynchronously returned by the service.
+ *
+ * @author Mike Burke
+ *
+ */
+ private static final class ResultCodes{
+ private static final class SubmenuResult{
+ private static final int ADD_COMMAND_DIALOG = 0;
+ private static final int DELETE_SUBMENU_DIALOG = 1;
+ }
+ private static final class CommandResult{
+ private static final int DELETE_COMMAND_DIALOG = 0;
+ }
+ private static final class ButtonSubscriptionResult{
+ private static final int BUTTON_SUBSCRIBE = 0;
+ private static final int BUTTON_UNSUBSCRIBE = 1;
+ }
+ private static final class InteractionSetResult{
+ private static final int PERFORM_INTERACTION = 0;
+ private static final int DELETE_INTERACTION_SET = 1;
+ }
+ private static final class PutFileResult{
+ private static final int PUT_FILE = 0;
+ private static final int ADD_COMMAND = 1;
+ private static final int CHOICE_INTERACTION_SET = 2;
+ private static final int DELETE_FILE = 3;
+ private static final int SET_APP_ICON = 4;
+ private static final int SHOW = 5;
+ }
+ }
+
+ private static enum ConnectionStatus{
+ CONNECTED("Connected"),
+ OFFLINE_MODE("Offline Mode"),
+ ;
+
+ private final String friendlyName;
+ private ConnectionStatus(String friendlyName){
+ this.friendlyName = friendlyName;
+ }
+
+ @Override
+ public String toString(){
+ return friendlyName;
+ }
+ }
+
+ private static final int CONNECTING_DIALOG_TIMEOUT = 10000; // duration to attempt a connection (10s)
+
+ private String connectionStatusFormat;
+
+ private ListView lv_messageLog;
+ private TextView tv_connectionStatus;
+ private SdlMessageAdapter listViewAdapter;
+
+ /* Messenger for communicating with service. */
+ private Messenger serviceMsgr = null;
+
+ private boolean isBound = false;
+
+ private BaseAlertDialog connectionDialog;
+ private IndeterminateProgressDialog connectingDialog;
+ private Timeout connectionTimeout;
+
+ // cache for all images available to send to SDL service
+ private HashMap<String, SdlImageItem> imageCache;
+ private List<MenuItem> submenuCache = null;
+
+ private List<SdlCommand> commandList = null;
+
+ /*
+ * Target we publish for clients to send messages to IncomingHandler.
+ */
+ final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+ @SuppressLint("HandlerLeak")
+ private class IncomingHandler extends Handler{
+ @SuppressWarnings("unchecked")
+ @Override
+ public void handleMessage(Message msg) {
+ // see SdlService.java in the com.livio.sdl package for details on these incoming messages
+
+ switch(msg.what){
+ case SdlService.ClientMessages.SDL_CONNECTED:
+ updateConnectionStatus(ConnectionStatus.CONNECTED);
+
+ // clear the command log since we're starting fresh from here
+ clearSdlLog();
+
+ // dismiss the connecting dialog if it's showing
+ if(connectingDialog != null && connectingDialog.isShowing()){
+ connectingDialog.dismiss();
+ }
+ if(connectionTimeout != null){
+ connectionTimeout.cancel();
+ }
+
+ // set up the app icon once we're connected
+ setAppIcon();
+ break;
+ case SdlService.ClientMessages.SDL_DISCONNECTED:
+ resetService();
+ updateConnectionStatus(ConnectionStatus.OFFLINE_MODE);
+ clearSdlLog();
+ break;
+ case SdlService.ClientMessages.SDL_HMI_FIRST_DISPLAYED:
+ setInitialHmi();
+ break;
+ case SdlService.ClientMessages.ON_MESSAGE_RESULT:
+ onMessageResponseReceived((RPCMessage) msg.obj);
+ break;
+ case SdlService.ClientMessages.SUBMENU_LIST_RECEIVED:
+ onSubmenuListReceived((List<MenuItem>) msg.obj, msg.arg1);
+ break;
+ case SdlService.ClientMessages.COMMAND_LIST_RECEIVED:
+ onCommandListReceived((List<MenuItem>) msg.obj, msg.arg1);
+ break;
+ case SdlService.ClientMessages.BUTTON_SUBSCRIPTIONS_RECEIVED:
+ onButtonSubscriptionsReceived((List<SdlButton>) msg.obj, msg.arg1);
+ break;
+ case SdlService.ClientMessages.INTERACTION_SETS_RECEIVED:
+ onInteractionListReceived((List<MenuItem>) msg.obj, msg.arg1);
+ break;
+ case SdlService.ClientMessages.PUT_FILES_RECEIVED:
+ onPutFileListReceived((List<String>) msg.obj, msg.arg1);
+ break;
+ default:
+ break;
+ }
+ super.handleMessage(msg);
+ }
+ }
+
+ /**
+ * Sends the input message to the SDL service through the service messenger.
+ *
+ * @param msg The message to send
+ */
+ private void sendMessageToService(Message msg){
+ try {
+ serviceMsgr.send(msg);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Sends an RPCRequest to the SDL service through the service messenger and adds the request to the list view.
+ *
+ * @param request The request to send
+ */
+ private void sendSdlMessageToService(RPCRequest request){
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.SEND_MESSAGE);
+ msg.obj = request;
+ sendMessageToService(msg);
+ }
+
+ /**
+ * Sends a request for the most up-to-date submenu list with a request code so this activity knows
+ * what to do when the response comes back.
+ *
+ * @param reqCode The request code to associate with the request
+ */
+ private void sendSubmenuListRequest(int reqCode){
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.REQUEST_SUBMENU_LIST);
+ msg.replyTo = mMessenger;
+ msg.arg1 = reqCode;
+ sendMessageToService(msg);
+ }
+
+ /**
+ * Sends a request for the most up-to-date command list with a request code so this activity knows
+ * what to do when the response comes back.
+ *
+ * @param reqCode The request code to associate with the request
+ */
+ private void sendCommandListRequest(int reqCode){
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.REQUEST_COMMAND_LIST);
+ msg.replyTo = mMessenger;
+ msg.arg1 = reqCode;
+ sendMessageToService(msg);
+ }
+
+ /**
+ * Sends a request for the most up-to-date list of button subscriptions with a request code so this
+ * activity knows what to do when the response comes back.
+ *
+ * @param reqCode The request code to associate with the request
+ */
+ private void sendButtonSubscriptionRequest(int reqCode){
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.REQUEST_BUTTON_SUBSCRIPTIONS);
+ msg.replyTo = mMessenger;
+ msg.arg1 = reqCode;
+ sendMessageToService(msg);
+ }
+
+ /**
+ * Sends a request for the most up-to-date list of button subscriptions with a request code so this
+ * activity knows what to do when the response comes back.
+ *
+ * @param reqCode The request code to associate with the request
+ */
+ private void sendInteractionSetRequest(int reqCode){
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.REQUEST_INTERACTION_SETS);
+ msg.replyTo = mMessenger;
+ msg.arg1 = reqCode;
+ sendMessageToService(msg);
+ }
+
+ /**
+ * Sends a request for the most up-to-date list of images added so far with a request code so this
+ * activity knows what to do when the response comes back.
+ *
+ * @param reqCode The request code to associate with the request
+ */
+ private void sendPutFileRequest(int reqCode){
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.REQUEST_PUT_FILES);
+ msg.replyTo = mMessenger;
+ msg.arg1 = reqCode;
+ sendMessageToService(msg);
+ }
+
+ /**
+ * Resets the SDL service.
+ */
+ private void resetService(){
+ Intent sdlService = new Intent(MainActivity.this, SdlService.class);
+ stopService(sdlService);
+ startService(sdlService);
+ }
+
+ /**
+ * Adds the input RPCMessage to the list view.
+ *
+ * @param request The message to log
+ */
+ private void logSdlMessage(RPCMessage request){
+ listViewAdapter.add(new SdlLogMessage(request));
+ listViewAdapter.notifyDataSetChanged();
+
+ // after adding a new item, auto-scroll to the bottom of the list
+ lv_messageLog.setSelection(listViewAdapter.getCount() - 1);
+ }
+
+ private void clearSdlLog(){
+ listViewAdapter.clear();
+ listViewAdapter.notifyDataSetChanged();
+ }
+
+ /*
+ * Sets up the App's icon using PutFile & SetAppIcon commands
+ */
+ private void setAppIcon(){
+ // first, let's send our app icon image through the PutFile command
+ SdlTesterImageResource appIcon = SdlTesterImageResource.IC_APP_ICON;
+ String appIconName = appIcon.toString();
+ FileType appIconFileType = appIcon.getFileType();
+ Bitmap appIconBitmap = imageCache.get(appIconName).getBitmap();
+ // create the image as raw bytes to send over
+ byte[] appIconBytes = AndroidUtils.bitmapToRawBytes(appIconBitmap, Bitmap.CompressFormat.PNG);
+
+ // create & send the PutFile command
+ RPCRequest putFileMsg = SdlRequestFactory.putFile(appIconName, appIconFileType, false, appIconBytes);
+ sendSdlMessageToService(putFileMsg);
+
+ // create & send the SetAppIcon command
+ RPCRequest setAppIconMsg = SdlRequestFactory.setAppIcon(appIconName);
+ sendSdlMessageToService(setAppIconMsg);
+ }
+
+ /*
+ * Sets up the initial HMI through the Show command.
+ */
+ private void setInitialHmi(){
+ // set up the main lines of text
+ String showText1 = "Livio SDL Tester";
+ String showText2 = "Send SDL Commands";
+ String showText3 = " ";
+ // showText4 is not applicable since this is set up as a media application
+
+ // set up the image to show
+ String appIconName = SdlTesterImageResource.IC_APP_ICON.toString();
+
+ // create & send the Show command
+ RPCRequest showMsg = SdlRequestFactory.show(showText1, showText2, showText3, null, null, TextAlignment.LEFT_ALIGNED, appIconName);
+ sendSdlMessageToService(showMsg);
+
+ // since this is a media app, we have to clear out the 4th line of text through the SetMediaClockTimer command
+ RPCRequest clockMsg = SdlRequestFactory.setMediaClockTimer(UpdateMode.CLEAR);
+ sendSdlMessageToService(clockMsg);
+ }
+
+ /*
+ * Class for interacting with the main interface of the service.
+ */
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ serviceMsgr = new Messenger(service);
+
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.REGISTER_CLIENT);
+ msg.replyTo = mMessenger;
+ sendMessageToService(msg);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ // process crashed - make sure nobody can use messenger instance.
+ serviceMsgr = null;
+ }
+ };
+
+ /**
+ * Binds this activity to the SDL service, using the service connection as a messenger between the two.
+ */
+ private void doBindService() {
+ if(!isBound){
+ bindService(new Intent(MainActivity.this, SdlService.class), mConnection, Context.BIND_AUTO_CREATE);
+ isBound = true;
+ }
+ }
+
+ /**
+ * Unbinds this activity from the SDL service.
+ */
+ private void doUnbindService() {
+ if (isBound) {
+ if (serviceMsgr != null) {
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.UNREGISTER_CLIENT);
+ msg.replyTo = mMessenger;
+ sendMessageToService(msg);
+ }
+
+ // Detach our existing connection.
+ unbindService(mConnection);
+ stopService(new Intent(MainActivity.this, SdlService.class));
+ isBound = false;
+ }
+ }
+
+ /* ********** Android Life-Cycle ********** */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ SdlService.setDebug(true);
+
+ createImageCache();
+ init();
+ doBindService();
+ showSdlConnectionDialog();
+ }
+
+ // create an image cache for the images that are available to send to the head-unit. this allows easy image look-up
+ // based on the filename of the image. We'll do this on a thread since processing images can be CPU intensive.
+ private void createImageCache(){
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ // grab information from the image resources enum
+ SdlTesterImageResource[] values = SdlTesterImageResource.values();
+ imageCache = new HashMap<String, SdlImageItem>(values.length);
+
+ for(SdlTesterImageResource img : values){
+ // create an SdlImageItem object for each image
+ Bitmap bitmap = BitmapFactory.decodeResource(getResources(), img.getImageId());
+ String imageName = img.toString();
+ FileType imageType = img.getFileType();
+ SdlImageItem item = new SdlImageItem(bitmap, imageName, imageType);
+
+ // map the image name to its associated SdlImageItem object
+ imageCache.put(imageName, item);
+ }
+ }
+ }).start();
+ }
+
+ // initializes views in the main activity
+ private void init(){
+ // set up the "Send Message" button
+ findViewById(R.id.btn_main_sendMessage).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Context context = MainActivity.this;
+ String dialogTitle = context.getResources().getString(R.string.sdl_command_dialog_title);
+
+ // lazily instantiate the list of commands in alphabetical order
+ if(commandList == null){
+ // grab the command list and sort the commands by name
+ commandList = Arrays.asList(SdlCommand.values());
+ Collections.sort(commandList, new EnumComparator<SdlCommand>());
+ }
+
+ // show the dialog with the commandList we created above
+ BaseAlertDialog commandDialog = new ListViewDialog<SdlCommand>(context, dialogTitle, commandList);
+ commandDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ // when a list item is clicked, dispatch the click to the showCommandDialog method
+ showCommandDialog((SdlCommand) resultData);
+ }
+ });
+ commandDialog.show();
+ }
+ });
+
+ // set up the command log
+ lv_messageLog = (ListView) findViewById(R.id.list_main_commandList);
+ listViewAdapter = new SdlMessageAdapter(this);
+ lv_messageLog.setAdapter(listViewAdapter);
+ lv_messageLog.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ // when an item is clicked, show it in the JSON flipper dialog.
+ // first, we must copy over all the messages that have been created so far.
+ int size = listViewAdapter.getCount();
+ List<SdlLogMessage> allLogs = new ArrayList<SdlLogMessage>(size);
+ for(int i=0; i<size; i++){
+ allLogs.add(listViewAdapter.getItem(i));
+ }
+
+ BaseAlertDialog jsonDialog = new JsonFlipperDialog(MainActivity.this, allLogs, position);
+ jsonDialog.show();
+ }
+ });
+
+ tv_connectionStatus = (TextView) findViewById(R.id.tv_connectionStatus);
+ }
+
+ // updates the connection status TextView
+ private void updateConnectionStatus(ConnectionStatus status){
+ if(connectionStatusFormat == null){
+ connectionStatusFormat = getResources().getString(R.string.connection_status_format);
+ }
+
+ if(status == ConnectionStatus.OFFLINE_MODE){
+ sendMessageToService(Message.obtain(null, SdlService.ServiceMessages.OFFLINE_MODE));
+ }
+
+ String text = new StringBuilder().append(connectionStatusFormat).append(" ").append(status.toString()).toString();
+ tv_connectionStatus.setText(text);
+ }
+
+ @Override
+ protected void onResume() {
+ // enable wifi if it isn't already enabled.
+ if(!AndroidUtils.wifiIsEnabled(this)){
+ Toast.makeText(this, "Enabling wifi", Toast.LENGTH_LONG).show();
+ AndroidUtils.enableWifi(this, true);
+ }
+
+ super.onResume();
+ }
+
+ @Override
+ protected void onDestroy() {
+ sendMessageToService(Message.obtain(null, SdlService.ServiceMessages.DISCONNECT));
+ doUnbindService();
+ super.onDestroy();
+ }
+
+ /**
+ * Called when a message has been received from the head-unit.
+ *
+ * @param response The response that was received
+ */
+ private void onMessageResponseReceived(RPCMessage response){
+ logSdlMessage(response);
+ }
+
+ /**
+ * Called when the up-to-date list of submenus is received. The request code can be used
+ * to perform different operations based on the request code that is sent with the initial request.
+ *
+ * @param submenuList The list of submenu items
+ * @param reqCode The request code that was sent with the request
+ */
+ private void onSubmenuListReceived(List<MenuItem> submenuList, int reqCode){
+ Collections.sort(submenuList, new MenuItem.NameComparator()); // sort submenu list by name. you can also sort by id with the MenuItem.IdComparator object
+
+ switch(reqCode){
+ case ResultCodes.SubmenuResult.ADD_COMMAND_DIALOG:
+ submenuCache = submenuList;
+ sendPutFileRequest(ResultCodes.PutFileResult.ADD_COMMAND);
+ break;
+ case ResultCodes.SubmenuResult.DELETE_SUBMENU_DIALOG:
+ if(submenuList.size() > 0){
+ createDeleteSubmenuDialog(submenuList);
+ }
+ else{
+ Toast.makeText(MainActivity.this, getResources().getString(R.string.no_submenus_to_delete), Toast.LENGTH_SHORT).show();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Called when the up-to-date list of commands is received. The request code can be used
+ * to perform different operations based on the request code that is sent with the initial request.
+ *
+ * @param commandList The list of command items
+ * @param reqCode The request code that was sent with the request
+ */
+ private void onCommandListReceived(List<MenuItem> commandList, int reqCode){
+ Collections.sort(commandList, new MenuItem.NameComparator()); // sort command list by name
+ switch(reqCode){
+ case ResultCodes.CommandResult.DELETE_COMMAND_DIALOG:
+ if(commandList.size() > 0){
+ createDeleteCommandDialog(commandList);
+ }
+ else{
+ Toast.makeText(MainActivity.this, getResources().getString(R.string.no_commands_to_delete), Toast.LENGTH_SHORT).show();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Called when the up-to-date list of button subscriptions is received. The request code can be used
+ * to perform different operations based on the request code that is sent with the initial request.
+ *
+ * @param buttonSubscriptionList The list of button subscriptions
+ * @param reqCode The request code that was sent with the request
+ */
+ private void onButtonSubscriptionsReceived(List<SdlButton> buttonSubscriptionList, int reqCode){
+ switch(reqCode){
+ case ResultCodes.ButtonSubscriptionResult.BUTTON_SUBSCRIBE:
+ if(buttonSubscriptionList.size() == SdlButton.values().length){
+ Toast.makeText(MainActivity.this, getResources().getString(R.string.button_subscriptions_already_subscribed), Toast.LENGTH_LONG).show();
+ }
+ else{
+ List<SdlButton> buttonsNotSubscribedTo = filterSubscribedButtons(buttonSubscriptionList);
+ Collections.sort(buttonsNotSubscribedTo, new EnumComparator<SdlButton>());
+ createButtonSubscribeDialog(buttonsNotSubscribedTo);
+ }
+ break;
+ case ResultCodes.ButtonSubscriptionResult.BUTTON_UNSUBSCRIBE:
+ if(buttonSubscriptionList.size() == 0){
+ Toast.makeText(MainActivity.this, getResources().getString(R.string.button_subscriptions_none_subscribed), Toast.LENGTH_LONG).show();
+ }
+ else{
+ Collections.sort(buttonSubscriptionList, new EnumComparator<SdlButton>());
+ createButtonUnsubscribeDialog(buttonSubscriptionList);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Called when the up-to-date list of interaction sets is received. The request code can be used
+ * to perform different operations based on the request code that is sent with the initial request.
+ *
+ * @param interactionSetList The list of interaction sets
+ * @param reqCode The request code that was sent with the request
+ */
+ private void onInteractionListReceived(List<MenuItem> interactionSetList, int reqCode){
+ switch(reqCode){
+ case ResultCodes.InteractionSetResult.PERFORM_INTERACTION:
+ if(interactionSetList.size() == 0){
+ Toast.makeText(MainActivity.this, getResources().getString(R.string.interaction_list_none_added), Toast.LENGTH_LONG).show();
+ }
+ else{
+ Collections.sort(interactionSetList, new MenuItem.IdComparator());
+ createPerformInteractionDialog(interactionSetList);
+ }
+ break;
+ case ResultCodes.InteractionSetResult.DELETE_INTERACTION_SET:
+ if(interactionSetList.size() == 0){
+ Toast.makeText(MainActivity.this, getResources().getString(R.string.interaction_list_none_added), Toast.LENGTH_LONG).show();
+ }
+ else{
+ Collections.sort(interactionSetList, new MenuItem.IdComparator());
+ createDeleteInteractionDialog(interactionSetList);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Called when the up-to-date list of put file images have been received. The request code can be used
+ * to perform different operations based on the request code that is sent with the initial request.
+ *
+ * @param putFileList The list of put file image names
+ * @param reqCode The request code that was sent with the request
+ */
+ private void onPutFileListReceived(List<String> putFileList, int reqCode){
+ List<SdlImageItem> availableItems;
+
+ switch(reqCode){
+ case ResultCodes.PutFileResult.PUT_FILE:
+ availableItems = filterAddedItems(putFileList);
+
+ if(availableItems.size() > 0){
+ createPutFileDialog(availableItems);
+ }
+ else{
+ Toast.makeText(this, "All images have been added!", Toast.LENGTH_LONG).show();
+ }
+ break;
+ case ResultCodes.PutFileResult.DELETE_FILE:
+ availableItems = filterUnaddedItems(putFileList);
+
+ if(availableItems.size() > 0){
+ createDeleteFileDialog(availableItems);
+ }
+ else{
+ Toast.makeText(this, "No images have been added!", Toast.LENGTH_LONG).show();
+ }
+ break;
+ case ResultCodes.PutFileResult.SET_APP_ICON:
+ availableItems = filterUnaddedItems(putFileList);
+
+ if(availableItems.size() > 0){
+ createSetAppIconDialog(availableItems);
+ }
+ else{
+ Toast.makeText(this, "No images have been added!", Toast.LENGTH_LONG).show();
+ }
+ break;
+ case ResultCodes.PutFileResult.ADD_COMMAND:
+ availableItems = filterUnaddedItems(putFileList);
+ createAddCommandDialog(submenuCache, availableItems);
+ break;
+ case ResultCodes.PutFileResult.SHOW:
+ availableItems = filterUnaddedItems(putFileList);
+ createShowDialog(availableItems);
+ break;
+ case ResultCodes.PutFileResult.CHOICE_INTERACTION_SET:
+ availableItems = filterUnaddedItems(putFileList);
+ createInteractionChoiceSetDialog(availableItems);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Filters out any images that have already been added through the PutFile command.
+ *
+ * @param putFileList The list of images that have been added through the PutFile command
+ * @return The list of images that have <b>not</b> been added through the PutFile command
+ */
+ private List<SdlImageItem> filterAddedItems(List<String> putFileList){
+ int itemsInFilteredList = imageCache.size() - putFileList.size();
+ if(itemsInFilteredList == 0){
+ return Collections.emptyList();
+ }
+
+ // first, we'll grab all image cache keys (aka image names) into a copy
+ Set<String> cacheKeys = new TreeSet<String>(imageCache.keySet());
+ // then, we'll remove all images that have been added
+ cacheKeys.removeAll(putFileList);
+
+
+ List<SdlImageItem> result = new ArrayList<SdlImageItem>(itemsInFilteredList);
+
+ // now, we'll loop through the remaining image names and create a list from them
+ for(String name : cacheKeys){
+ result.add(imageCache.get(name));
+ }
+
+ Collections.sort(result, new SdlImageItemComparator());
+
+ return result;
+ }
+
+ /**
+ * Filters out any images that have <b>not</b> been added through the PutFile command.
+ *
+ * @param putFileList The list of images that have been added through the PutFile command
+ * @return The list of images that have been added through the PutFile command
+ */
+ private List<SdlImageItem> filterUnaddedItems(List<String> putFileList){
+ List<SdlImageItem> result = new ArrayList<SdlImageItem>(putFileList.size());
+ for(String name : putFileList){
+ result.add(imageCache.get(name));
+ }
+
+ Collections.sort(result, new SdlImageItemComparator());
+ return result;
+ }
+
+ /**
+ * Finds any buttons that are <b>not</b> in the input list of button subscriptions
+ * and adds them to the listview adapter.
+ *
+ * @param buttonSubscriptions A list of buttons that have been subscribed to
+ */
+ private List<SdlButton> filterSubscribedButtons(List<SdlButton> buttonSubscriptions){
+ final SdlButton[] buttonValues = SdlButton.values();
+ final int numItems = buttonValues.length - buttonSubscriptions.size();
+ List<SdlButton> result = new ArrayList<SdlButton>(numItems);
+
+ for(SdlButton button : buttonValues){
+ if(!buttonSubscriptions.contains(button)){
+ result.add(button);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Shows the SDL connection dialog, which allows the user to enter the IP address for the core component they would like to connect to.
+ */
+ private void showSdlConnectionDialog(){
+ // restore any old IP address from preferences
+ String savedIpAddress = LivioSdlTesterPreferences.restoreIpAddress(MainActivity.this);
+ String savedTcpPort = LivioSdlTesterPreferences.restoreTcpPort(MainActivity.this);
+
+ if(savedIpAddress != null && savedTcpPort != null){
+ // if there was an old IP stored in preferences, initialize the dialog with those values
+ connectionDialog = new SdlConnectionDialog(this, savedIpAddress, savedTcpPort);
+ }
+ else{
+ // if no IP address was in preferences, initialize the dialog with no input strings
+ connectionDialog = new SdlConnectionDialog(this, "", "12345");
+ }
+
+ // set us up the dialog
+ connectionDialog.setCancelable(false);
+ connectionDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ IpAddress result = (IpAddress) resultData;
+ if(result == null){
+ updateConnectionStatus(ConnectionStatus.OFFLINE_MODE);
+ return;
+ }
+
+ String addressString = result.getIpAddress();
+ String portString = result.getTcpPort();
+
+ boolean ipAddressValid = WifiUtils.validateIpAddress(addressString);
+ boolean ipPortValid = WifiUtils.validateTcpPort(portString);
+
+ if(ipAddressValid && ipPortValid){
+ // if the user entered valid IP settings, save them to preferences so they don't have to re-enter them next time
+ LivioSdlTesterPreferences.saveIpAddress(MainActivity.this, addressString);
+ LivioSdlTesterPreferences.saveTcpPort(MainActivity.this, portString);
+
+ // show an indeterminate connecting dialog
+ connectingDialog = new IndeterminateProgressDialog(MainActivity.this, "Connecting");
+ connectingDialog.show();
+
+ // and start a timeout thread in case the connection isn't successful
+ if(connectionTimeout == null){
+ connectionTimeout = new Timeout(CONNECTING_DIALOG_TIMEOUT, new Timeout.Listener() {
+ @Override public void onTimeoutCancelled() {}
+
+ @Override
+ public void onTimeoutCompleted() {
+ if(connectingDialog != null && connectingDialog.isShowing()){
+ // if we made it here without being interrupted, the connection was unsuccessful - dismiss the dialog and enter offline mode
+ connectingDialog.dismiss();
+ }
+
+ Toast.makeText(MainActivity.this, "Connection timed out", Toast.LENGTH_SHORT).show();
+ sendMessageToService(Message.obtain(null, SdlService.ServiceMessages.OFFLINE_MODE));
+ updateConnectionStatus(ConnectionStatus.OFFLINE_MODE);
+ }
+ });
+ }
+ connectionTimeout.start();
+
+ // message the SDL service, telling it to attempt a connection with the input IP address
+ Message msg = Message.obtain(null, SdlService.ServiceMessages.CONNECT);
+ msg.obj = resultData;
+ sendMessageToService(msg);
+ }
+ else{
+ // user input was invalid
+ Toast.makeText(MainActivity.this, "Input was invalid - please try again", Toast.LENGTH_SHORT).show();
+ showSdlConnectionDialog();
+ }
+ }
+ });
+ connectionDialog.show();
+ }
+
+ /**
+ * Launches the appropriate dialog for whichever command item was clicked.
+ *
+ * @param command The command that was clicked
+ */
+ private void showCommandDialog(SdlCommand command){
+ if(command == null){
+ // shouldn't happen, but if an invalid command gets here, let's throw an exception.
+ throw new IllegalArgumentException(getResources().getString(R.string.not_an_sdl_command));
+ }
+
+ switch(command){
+ case PUT_FILE:
+ // the put file dialog needs a list of images that have been added so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onPutFileListReceived().
+ sendPutFileRequest(ResultCodes.PutFileResult.PUT_FILE);
+ break;
+ case DELETE_FILE:
+ // the delete file dialog needs a list of images that have been added so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onPutFileListReceived().
+ sendPutFileRequest(ResultCodes.PutFileResult.DELETE_FILE);
+ break;
+ case ALERT:
+ createAlertDialog();
+ break;
+ case SPEAK:
+ createSpeakDialog();
+ break;
+ case SHOW:
+ // the put file dialog needs a list of images that have been added so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onPutFileListReceived().
+ sendPutFileRequest(ResultCodes.PutFileResult.SHOW);
+ break;
+ case SUBSCRIBE_BUTTON:
+ // the subscribe button dialog needs a list of buttons that have been subscribed to so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onButtonSubscriptionsReceived().
+ sendButtonSubscriptionRequest(ResultCodes.ButtonSubscriptionResult.BUTTON_SUBSCRIBE);
+ break;
+ case UNSUBSCRIBE_BUTTON:
+ // the unsubscribe button dialog needs a list of buttons that have been subscribed to so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onButtonSubscriptionsReceived().
+ sendButtonSubscriptionRequest(ResultCodes.ButtonSubscriptionResult.BUTTON_UNSUBSCRIBE);
+ break;
+ case ADD_COMMAND:
+ // the add command dialog needs a list of submenus that the command could be added to, so let's request that list here and
+ // we'll actually show the dialog when the list gets returned by the service. See onSubmenuListReceived().
+ sendSubmenuListRequest(ResultCodes.SubmenuResult.ADD_COMMAND_DIALOG);
+ break;
+ case DELETE_COMMAND:
+ // the delete command dialog needs a list of commands that have been added so far so the user can select which command to delete,
+ // so let's request the list here and we'll show the dialog when it's returned by the service. See onCommandListReceived().
+ sendCommandListRequest(ResultCodes.CommandResult.DELETE_COMMAND_DIALOG);
+ break;
+ case ADD_SUBMENU:
+ createAddSubmenuDialog();
+ break;
+ case DELETE_SUB_MENU:
+ // the delete submenu dialog needs a list of commands that have been added so far so the user can select which submenu to delete,
+ // so let's request the list here and we'll show the dialog when it's returned by the service. See onSubmenuListReceived().
+ sendSubmenuListRequest(ResultCodes.SubmenuResult.DELETE_SUBMENU_DIALOG);
+ break;
+ case CREATE_INTERACTION_CHOICE_SET:
+ // the CreateInteractionChoiceSet dialog needs a list of images that have been added so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onPutFileListReceived().
+ sendPutFileRequest(ResultCodes.PutFileResult.CHOICE_INTERACTION_SET);
+ break;
+ case PERFORM_INTERACTION:
+ // the perform interaction dialog needs a list of interaction sets that have been added so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onInteractionListReceived().
+ sendInteractionSetRequest(ResultCodes.InteractionSetResult.PERFORM_INTERACTION);
+ break;
+ case DELETE_INTERACTION_CHOICE_SET:
+ // the delete interaction dialog needs a list of interaction sets that have been added so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onInteractionListReceived().
+ sendInteractionSetRequest(ResultCodes.InteractionSetResult.DELETE_INTERACTION_SET);
+ break;
+ case CHANGE_REGISTRATION:
+ createChangeRegistrationDialog();
+ break;
+ case GET_DTCS:
+ createGetDtcsDialog();
+ break;
+ case READ_DIDS:
+ createReadDidsDialog();
+ break;
+ case SLIDER:
+ createSliderDialog();
+ break;
+ case SCROLLABLE_MESSAGE:
+ createScrollableMessageDialog();
+ break;
+ case SET_MEDIA_CLOCK_TIMER:
+ createSetMediaClockTimerDialog();
+ break;
+ case LIST_FILES:
+ // list files command doesn't accept any parameters, so we can send it directly.
+ sendSdlMessageToService(new ListFiles());
+ break;
+ case SET_APP_ICON:
+ // the set app icon dialog needs a list of images that have been added so far, so let's request
+ // that list here and we'll actually show the dialog when it gets returned by the service. See onPutFileListReceived().
+ sendPutFileRequest(ResultCodes.PutFileResult.SET_APP_ICON);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // listener to be used when receiving a single RPCRequest from a dialog.
+ private final BaseAlertDialog.Listener singleMessageListener = new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(final Object resultData) {
+ if(resultData != null){
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ sendSdlMessageToService((RPCRequest) resultData);
+ }
+ }).start();
+ }
+ }
+ };
+
+ // listener to be used when receiving a list of RPCRequests from a dialog.
+ private final BaseAlertDialog.Listener multipleMessageListener = new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(final Object resultData) {
+ if(resultData != null){
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ @SuppressWarnings("unchecked")
+ List<RPCRequest> msgList = (List<RPCRequest>) resultData;
+ for(RPCRequest request : msgList){
+ sendSdlMessageToService(request);
+ }
+ }
+ }).start();
+ }
+ }
+ };
+
+ /**
+ * Creates an alert dialog, allowing the user to manually send an alert command.
+ */
+ private void createAlertDialog(){
+ BaseAlertDialog alertDialog = new SdlAlertDialog(this);
+ alertDialog.setListener(singleMessageListener);
+ alertDialog.show();
+ }
+
+ /**
+ * Creates a speak dialog, allowing the user to manually send a speak command.
+ */
+ private void createSpeakDialog(){
+ BaseAlertDialog speakDialog = new SpeakDialog(this);
+ speakDialog.setListener(singleMessageListener);
+ speakDialog.show();
+ }
+
+ /**
+ * Creates a show dialog, allowing the user to manually send a show command.
+ */
+ private void createShowDialog(List<SdlImageItem> images){
+ BaseAlertDialog showDialog = new ShowDialog(this, images);
+ showDialog.setListener(singleMessageListener);
+ showDialog.show();
+ }
+
+ /**
+ * Creates a button subscribe dialog, allowing the user to manually send a button subscribe command.
+ *
+ * @param buttonSubscriptions The list used to populate the dialog
+ */
+ private void createButtonSubscribeDialog(List<SdlButton> buttonSubscriptions){
+ BaseAlertDialog buttonSubscribeDialog = new ButtonSubscriptionDialog(this, buttonSubscriptions);
+ buttonSubscribeDialog.setListener(multipleMessageListener);
+ buttonSubscribeDialog.show();
+ }
+
+ /**
+ * Creates a button unsubscribe dialog, allowing the user to manually send a button unsubscribe command.
+ *
+ * @param buttonSubscriptions The list used to populate the dialog
+ */
+ private void createButtonUnsubscribeDialog(List<SdlButton> buttonSubscriptions){
+ BaseAlertDialog buttonUnsubscribeDialog = new ButtonUnsubscriptionDialog(this, buttonSubscriptions);
+ buttonUnsubscribeDialog.setListener(multipleMessageListener);
+ buttonUnsubscribeDialog.show();
+ }
+
+ /**
+ * Creates an add command dialog, allowing the user to manually send an add command command.
+ *
+ * @param allBanks The list used to populate the dialog
+ */
+ private void createAddCommandDialog(List<MenuItem> allBanks, List<SdlImageItem> availableItems){
+ BaseAlertDialog addCommandDialog = new AddCommandDialog(this, allBanks, availableItems);
+ addCommandDialog.setListener(singleMessageListener);
+ addCommandDialog.show();
+ }
+
+ /**
+ * Creates an add submenu dialog, allowing the user to manually send an add submenu command.
+ */
+ private void createAddSubmenuDialog(){
+ BaseAlertDialog submenuDialog = new AddSubMenuDialog(this);
+ submenuDialog.setListener(singleMessageListener);
+ submenuDialog.show();
+ }
+
+ /**
+ * Creates a create interaction choice set dialog, allowing the user to manually send a create interaction choice set command.
+ */
+ private void createInteractionChoiceSetDialog(List<SdlImageItem> addedImages){
+ BaseAlertDialog createInteractionChoiceSetDialog = new CreateInteractionChoiceSetDialog(this, addedImages);
+ createInteractionChoiceSetDialog.setListener(singleMessageListener);
+ createInteractionChoiceSetDialog.show();
+ }
+
+ /**
+ * Creates a change registration dialog, allowing the user to manually send a change registration command.
+ */
+ private void createChangeRegistrationDialog(){
+ BaseAlertDialog changeRegistrationDialog = new ChangeRegistrationDialog(this);
+ changeRegistrationDialog.setListener(singleMessageListener);
+ changeRegistrationDialog.show();
+ }
+
+ /**
+ * Creates a delete command dialog, allowing the user to manually send a delete command command.
+ *
+ * @param commandList The list used to populate the dialog
+ */
+ private void createDeleteCommandDialog(List<MenuItem> commandList){
+ BaseAlertDialog deleteCommandDialog = new DeleteCommandDialog(this, commandList);
+ deleteCommandDialog.setListener(singleMessageListener);
+ deleteCommandDialog.show();
+ }
+
+ /**
+ * Creates a delete submenu dialog, allowing the user to manually send a delete submenu command.
+ *
+ * @param submenuList The list used to populate the dialog
+ */
+ private void createDeleteSubmenuDialog(List<MenuItem> submenuList){
+ BaseAlertDialog deleteCommandDialog = new DeleteSubmenuDialog(this, submenuList);
+ deleteCommandDialog.setListener(singleMessageListener);
+ deleteCommandDialog.show();
+ }
+
+ /**
+ * Creates a perform interaction dialog, allowing the user to manually send a PerformInteraction command.
+ *
+ * @param interactionList The list used to populate the dialog
+ */
+ private void createPerformInteractionDialog(List<MenuItem> interactionList){
+ BaseAlertDialog performInteractionDialog = new PerformInteractionDialog(this, interactionList);
+ performInteractionDialog.setListener(singleMessageListener);
+ performInteractionDialog.show();
+ }
+
+ /**
+ * Creates a delete interaction dialog, allowing the user to manually send a DeleteInteractionChoiceSet command.
+ *
+ * @param interactionList The list used to populate the dialog
+ */
+ private void createDeleteInteractionDialog(List<MenuItem> interactionList){
+ BaseAlertDialog deleteInteractionDialog = new DeleteInteractionDialog(this, interactionList);
+ deleteInteractionDialog.setListener(singleMessageListener);
+ deleteInteractionDialog.show();
+ }
+
+ /**
+ * Creates a get DTCs dialog, allowing the user to manually send a GetDTCs command.
+ */
+ private void createGetDtcsDialog(){
+ BaseAlertDialog getDtcsDialog = new GetDtcsDialog(this);
+ getDtcsDialog.setListener(singleMessageListener);
+ getDtcsDialog.show();
+ }
+
+ /**
+ * Creates a read DIDs dialog, allowing the user to manually send a ReadDID command.
+ */
+ private void createReadDidsDialog(){
+ BaseAlertDialog getDtcsDialog = new ReadDidsDialog(this);
+ getDtcsDialog.setListener(singleMessageListener);
+ getDtcsDialog.show();
+ }
+
+ /**
+ * Creates a slider dialog, allowing the user to manually send a Slider command.
+ */
+ private void createSliderDialog(){
+ BaseAlertDialog getDtcsDialog = new SliderDialog(this);
+ getDtcsDialog.setListener(singleMessageListener);
+ getDtcsDialog.show();
+ }
+
+ /**
+ * Creates a scrollable message dialog, allowing the user to manually send a ScrollableMessage command.
+ */
+ private void createScrollableMessageDialog(){
+ BaseAlertDialog scrollableMessageDialog = new ScrollableMessageDialog(this);
+ scrollableMessageDialog.setListener(singleMessageListener);
+ scrollableMessageDialog.show();
+ }
+
+ /**
+ * Creates a set media clock timer dialog, allowing the user to manually send a SetMediaClockTimer command.
+ */
+ private void createSetMediaClockTimerDialog(){
+ BaseAlertDialog setMediaClockTimerDialog = new SetMediaClockTimerDialog(this);
+ setMediaClockTimerDialog.setListener(singleMessageListener);
+ setMediaClockTimerDialog.show();
+ }
+
+ /**
+ * Creates a put file dialog, allowing the user to manually send images through the PutFile command.
+ *
+ * @param imagesAddedSoFar Images that have <b>not</b> already been added
+ */
+ private void createPutFileDialog(List<SdlImageItem> imagesAddedSoFar){
+ BaseAlertDialog putFileDialog = new PutFileDialog(this, imagesAddedSoFar);
+ putFileDialog.setListener(multipleMessageListener);
+ putFileDialog.show();
+ }
+
+ /**
+ * Creates a delete file dialog, allowing the user to manually delete files that have been added through the PutFile command.
+ *
+ * @param imagesAddedSoFar The list of images that have been added so far
+ */
+ private void createDeleteFileDialog(List<SdlImageItem> imagesAddedSoFar){
+ BaseAlertDialog deleteFileDialog = new DeleteFileDialog(this, imagesAddedSoFar);
+ deleteFileDialog.setListener(multipleMessageListener);
+ deleteFileDialog.show();
+ }
+
+ /**
+ * Creates a set app icon dialog, allowing the user to manually set their app icon based on images that have been added through the PutFile command.
+ *
+ * @param imagesAddedSoFar The list of images that have been added so far
+ */
+ private void createSetAppIconDialog(List<SdlImageItem> imagesAddedSoFar){
+ BaseAlertDialog setAppIconDialog = new SetAppIconDialog(this, imagesAddedSoFar);
+ setAppIconDialog.setListener(singleMessageListener);
+ setAppIconDialog.show();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ android.view.MenuItem connectToSdl = (android.view.MenuItem) menu.findItem(R.id.menu_connect);
+ android.view.MenuItem disconnectFromSdl = (android.view.MenuItem) menu.findItem(R.id.menu_disconnect);
+ android.view.MenuItem resetSdl = (android.view.MenuItem) menu.findItem(R.id.menu_reset);
+ android.view.MenuItem clearListView = (android.view.MenuItem) menu.findItem(R.id.menu_clear_list);
+
+ // show/hide connect/disconnect menu items
+ boolean connected = tv_connectionStatus.getText().toString().contains(ConnectionStatus.CONNECTED.friendlyName);
+ connectToSdl.setVisible(!connected); // if we are not connected, show the connect item
+ disconnectFromSdl.setVisible(connected); // if we are connected, show the disconnect item
+ resetSdl.setVisible(connected); // if we are connected, show reset SDL item
+
+ boolean listHasItems = (listViewAdapter != null && listViewAdapter.getCount() > 0);
+ clearListView.setVisible(listHasItems); // if the list has items, show the clear list item
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(android.view.MenuItem item) {
+ int menuItemId = item.getItemId();
+ switch(menuItemId){
+ case R.id.menu_connect:
+ showSdlConnectionDialog();
+ return true;
+ case R.id.menu_disconnect:
+ sendMessageToService(Message.obtain(null, SdlService.ServiceMessages.DISCONNECT));
+ return true;
+ case R.id.menu_reset:
+ sendMessageToService(Message.obtain(null, SdlService.ServiceMessages.RESET));
+ return true;
+ case R.id.menu_clear_list:
+ clearSdlLog();
+ return true;
+ case R.id.menu_help:
+ Intent helpIntent = new Intent(MainActivity.this, HelpActivity.class);
+ startActivity(helpIntent);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/SdlTesterImageResource.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/SdlTesterImageResource.java
new file mode 100644
index 000000000..b88b73415
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/SdlTesterImageResource.java
@@ -0,0 +1,56 @@
+package com.livio.sdltester;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+
+public enum SdlTesterImageResource {
+ IC_APP_ICON("App Icon", FileType.GRAPHIC_PNG, R.drawable.ic_launcher),
+ IC_ADD("Add Item", FileType.GRAPHIC_PNG, R.drawable.add),
+ IC_ADD_TO_FAVORITES("Add to Favorites", FileType.GRAPHIC_PNG, R.drawable.add_to_favorites),
+ IC_ANCHOR("Anchor", FileType.GRAPHIC_PNG, R.drawable.anchor),
+ IC_GAME_PAD("Game Pad", FileType.GRAPHIC_PNG, R.drawable.game_pad),
+ IC_REMOVE_FROM_FAVORITES("Remove from Favorites", FileType.GRAPHIC_PNG, R.drawable.remove_from_favorites),
+ IC_REMOVE("Remove Item", FileType.GRAPHIC_PNG, R.drawable.remove),
+ IC_ROCKET("Rocket", FileType.GRAPHIC_PNG, R.drawable.rocket),
+ IC_UNDO("Undo", FileType.GRAPHIC_PNG, R.drawable.undo),
+ IC_ZOOM_IN("Zoom In", FileType.GRAPHIC_PNG, R.drawable.zoom_in),
+ IC_ZOOM_OUT("Zoom Out", FileType.GRAPHIC_PNG, R.drawable.zoom_out),
+
+ ;
+
+ private String friendlyName;
+ private int imageId;
+ private FileType fileType;
+ private SdlTesterImageResource(String friendlyName, FileType type, int imageId){
+ this.friendlyName = friendlyName;
+ this.imageId = imageId;
+ this.fileType = type;
+ }
+
+ @Override
+ public String toString(){
+ return friendlyName;
+ }
+
+ public int getImageId(){
+ return imageId;
+ }
+ public FileType getFileType() {
+ return fileType;
+ }
+
+ public Bitmap getBitmap(Resources res){
+ Bitmap result = BitmapFactory.decodeResource(res, imageId);
+ return result;
+ }
+
+ public static Bitmap getBitmap(Resources res, SdlTesterImageResource image){
+ int resId = image.getImageId();
+ Bitmap result = BitmapFactory.decodeResource(res, resId);
+ return result;
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddCommandDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddCommandDialog.java
new file mode 100644
index 000000000..5c2302bbb
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddCommandDialog.java
@@ -0,0 +1,121 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.dialogs.ImageListDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.menu.MenuItem;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class AddCommandDialog extends BaseOkCancelDialog{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.ADD_COMMAND;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private EditText et_newCommand;
+ private EditText et_voiceRecKeyword;
+ private EditText et_imageName;
+
+ private Spinner spin_addCommand_submenus;
+ private CheckBox cb_image;
+
+ private SdlImageItem selectedImage;
+
+ public AddCommandDialog(Context context, List<MenuItem> availableBanks, List<SdlImageItem> images) {
+ super(context, DIALOG_TITLE, R.layout.add_command);
+ setupViews(availableBanks, images);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ private void setupViews(List<MenuItem> availableSubmenus, final List<SdlImageItem> images){
+ List<MenuItem> submenuList = new ArrayList<MenuItem>(availableSubmenus);
+ submenuList.add(0, new MenuItem("Root-level menu", SdlConstants.AddCommandConstants.ROOT_PARENT_ID, true));
+ spin_addCommand_submenus.setAdapter(AndroidUtils.createSpinnerAdapter(context, submenuList));
+
+ if(images == null || images.size() <= 0){
+ cb_image.setVisibility(View.GONE);
+ et_imageName.setVisibility(View.GONE);
+ }
+
+ cb_image.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(isChecked){
+ if(images == null || images.size() <= 0){
+ Toast.makeText(context, "No images have been added to the system yet.", Toast.LENGTH_LONG).show();
+ }
+ else{
+ BaseAlertDialog selectImageDialog = new ImageListDialog(context, images);
+ selectImageDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ selectedImage = (SdlImageItem) resultData;
+ et_imageName.setText(selectedImage.getImageName());
+ }
+ });
+ selectImageDialog.show();
+ }
+ }
+ else{
+ et_imageName.setText("");
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ et_newCommand = (EditText) parent.findViewById(R.id.et_addCommand_commandName);
+ et_voiceRecKeyword = (EditText) parent.findViewById(R.id.et_addCommand_voiceRecKeyword);
+ et_imageName = (EditText) parent.findViewById(R.id.et_imageName);
+ spin_addCommand_submenus = (Spinner) parent.findViewById(R.id.spin_addCommand_submenus);
+ cb_image = (CheckBox) parent.findViewById(R.id.check_enable_image);
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if(listener != null){
+ // grab the data from the views
+ final String commandName = et_newCommand.getText().toString();
+ final int position = SdlConstants.AddCommandConstants.DEFAULT_POSITION;
+ final String voiceRecKeyword = et_voiceRecKeyword.getText().toString();
+ final MenuItem parentBank = (MenuItem) spin_addCommand_submenus.getSelectedItem();
+ final int parentId = (parentBank != null) ? parentBank.getId() : SdlConstants.AddCommandConstants.INVALID_PARENT_ID;
+ final String imageName = (selectedImage != null) ? selectedImage.getImageName() : null;
+
+ // all we really need is a valid name
+ if(commandName.length() > 0){
+ // if we have it, let's create our RPC object
+ RPCRequest result = SdlRequestFactory.addCommand(commandName, position, parentId, voiceRecKeyword, imageName);
+ notifyListener(result);
+ }
+ else{
+ // if we don't have a valid name, inform the user.
+ Toast.makeText(context, "Must enter command name.", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddSubMenuDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddSubMenuDialog.java
new file mode 100644
index 000000000..adbc297d5
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/AddSubMenuDialog.java
@@ -0,0 +1,55 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class AddSubMenuDialog extends BaseOkCancelDialog{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.ADD_SUBMENU;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private EditText et_submenuName;
+
+ public AddSubMenuDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.add_submenu);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ et_submenuName = (EditText) view.findViewById(R.id.et_addSubMenu_subMenuName);
+ }
+
+ //dialog button click listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String submenuName = et_submenuName.getText().toString();
+ final int position = SdlConstants.AddSubmenuConstants.DEFAULT_POSITION;
+
+ // all we need is a valid name
+ if(submenuName.length() > 0){
+ // if we have it, create the request and send it to the listener
+ RPCRequest rpcCommand = SdlRequestFactory.addSubmenu(submenuName, position);
+ notifyListener(rpcCommand);
+ }
+ else{
+ // if we don't have a valid name, inform the user
+ Toast.makeText(context, "Must enter a submenu name.", Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonSubscriptionDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonSubscriptionDialog.java
new file mode 100644
index 000000000..8e526dd23
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonSubscriptionDialog.java
@@ -0,0 +1,60 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import android.content.Context;
+import android.content.DialogInterface;
+
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseMultipleListViewDialog;
+import com.livio.sdl.enums.SdlButton;
+import com.livio.sdl.enums.SdlCommand;
+import com.smartdevicelink.proxy.RPCRequest;
+
+/**
+ * Shows a dialog allowing the user to subscribe to buttons that haven't been subscribed to yet. This class
+ * requires an input list of SDL buttons that have been subscribed to so far. Button subscriptions should
+ * be queried from the SDL service prior to showing this dialog to be sure information is up to date.
+ *
+ * The result of this dialog is a list of RPC requests, one request per selected item since SDL can't do
+ * these as a batch as of version 2.0.
+ *
+ * @author Mike Burke
+ *
+ */
+public class ButtonSubscriptionDialog extends BaseMultipleListViewDialog<SdlButton>{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.SUBSCRIBE_BUTTON;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ public ButtonSubscriptionDialog(Context context, List<SdlButton> buttonSubscriptions) {
+ super(context, DIALOG_TITLE, buttonSubscriptions);
+ setPositiveButton(positiveButton);
+ createDialog();
+ }
+
+ //dialog button click listeners
+ private final DialogInterface.OnClickListener positiveButton = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // called when the OK button is clicked.
+
+ if(selectedItems == null || selectedItems.size() == 0){
+ // if no items were selected, send an empty list as the result
+ notifyListener(Collections.emptyList());
+ }
+ else{
+ List<RPCRequest> buttonSubscribeMessages = new ArrayList<RPCRequest>(selectedItems.size());
+
+ // loop through the selected items and create RPC requests for each one since they can't be done in a batch.
+ for(SdlButton button : selectedItems){
+ RPCRequest temp = SdlRequestFactory.subscribeButton(SdlButton.translateToLegacy(button));
+ buttonSubscribeMessages.add(temp);
+ }
+ notifyListener(buttonSubscribeMessages);
+ }
+ }
+ };
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonUnsubscriptionDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonUnsubscriptionDialog.java
new file mode 100644
index 000000000..1824dc032
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ButtonUnsubscriptionDialog.java
@@ -0,0 +1,60 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import android.content.Context;
+import android.content.DialogInterface;
+
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseMultipleListViewDialog;
+import com.livio.sdl.enums.SdlButton;
+import com.livio.sdl.enums.SdlCommand;
+import com.smartdevicelink.proxy.RPCRequest;
+
+/**
+ * Shows a dialog allowing the user to unsubscribe from buttons that have been subscribed to. This class
+ * requires an input list of SDL buttons that have been subscribed to so far. Button subscriptions should
+ * be queried from the SDL service prior to showing this dialog to be sure information is up to date.
+ *
+ * The result of this dialog is a list of RPC requests, one request per selected item since SDL can't do
+ * these as a batch as of version 2.0.
+ *
+ * @author Mike Burke
+ *
+ */
+public class ButtonUnsubscriptionDialog extends BaseMultipleListViewDialog<SdlButton> {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.UNSUBSCRIBE_BUTTON;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ public ButtonUnsubscriptionDialog(Context context, List<SdlButton> buttonSubscriptions) {
+ super(context, DIALOG_TITLE, buttonSubscriptions);
+ setPositiveButton(positiveButton);
+ createDialog();
+ }
+
+ //dialog button click listeners
+ private final DialogInterface.OnClickListener positiveButton = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // called when the OK button is clicked.
+
+ if(selectedItems == null || selectedItems.size() == 0){
+ // if no items were selected, send an empty list as the result
+ notifyListener(Collections.emptyList());
+ }
+ else{
+ List<RPCRequest> buttonUnsubscribeMessages = new ArrayList<RPCRequest>(selectedItems.size());
+
+ // loop through the selected items and create RPC requests for each one since they can't be done in a batch.
+ for(SdlButton button : selectedItems){
+ RPCRequest temp = SdlRequestFactory.unsubscribeButton(SdlButton.translateToLegacy(button));
+ buttonUnsubscribeMessages.add(temp);
+ }
+ notifyListener(buttonUnsubscribeMessages);
+ }
+ }
+ };
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChangeRegistrationDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChangeRegistrationDialog.java
new file mode 100644
index 000000000..bc328a608
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChangeRegistrationDialog.java
@@ -0,0 +1,55 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.enums.SdlLanguage;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.enums.Language;
+
+public class ChangeRegistrationDialog extends BaseOkCancelDialog{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.CHANGE_REGISTRATION;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private Spinner spin_changeRegistration_language;
+ private Spinner spin_changeRegistration_hmiLanguage;
+
+ public ChangeRegistrationDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.change_registration);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ ArrayAdapter<SdlLanguage> adapter = AndroidUtils.createSpinnerAdapter(context, SdlLanguage.values());
+
+ spin_changeRegistration_language = (Spinner) view.findViewById(R.id.spin_changeRegistration_language);
+ spin_changeRegistration_language.setAdapter(adapter);
+ spin_changeRegistration_hmiLanguage = (Spinner) view.findViewById(R.id.spin_changeRegistration_hmiLanguage);
+ spin_changeRegistration_hmiLanguage.setAdapter(adapter);
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String language = spin_changeRegistration_language.getSelectedItem().toString();
+ final String hmiLanguage = spin_changeRegistration_hmiLanguage.getSelectedItem().toString();
+
+ RPCRequest result = SdlRequestFactory.changeRegistration(Language.valueForString(language), Language.valueForString(hmiLanguage));
+ notifyListener(result);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChoiceItemDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChoiceItemDialog.java
new file mode 100644
index 000000000..1c18c2edb
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ChoiceItemDialog.java
@@ -0,0 +1,105 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.Toast;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.dialogs.ImageListDialog;
+import com.livio.sdl.utils.SdlUtils;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.rpc.Choice;
+
+public class ChoiceItemDialog extends BaseOkCancelDialog {
+
+ private static final String DIALOG_TITLE = "Create a Choice";
+ private List<SdlImageItem> availableImages;
+
+ private EditText et_choiceName, et_choiceVr, et_imageName;
+ private CheckBox cb_hasImage;
+ private BaseAlertDialog imagesDialog;
+
+ public ChoiceItemDialog(Context context, List<SdlImageItem> availableImages) {
+ super(context, DIALOG_TITLE, R.layout.choice_set_item);
+ setPositiveButton(okButton);
+ this.availableImages = availableImages;
+ if(availableImages == null || availableImages.size() <= 0){
+ cb_hasImage.setVisibility(View.GONE);
+ et_imageName.setVisibility(View.GONE);
+ }
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ et_choiceName = (EditText) parent.findViewById(R.id.et_choice_name);
+ et_choiceVr = (EditText) parent.findViewById(R.id.et_choice_vr_text);
+ et_imageName = (EditText) parent.findViewById(R.id.et_choice_imageName);
+ cb_hasImage = (CheckBox) parent.findViewById(R.id.check_enable_image);
+ cb_hasImage.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(isChecked){
+ showImagesDialog();
+ }
+ else{
+ et_imageName.setText("");
+ }
+ }
+ });
+ }
+
+ private void showImagesDialog(){
+ if(imagesDialog == null){
+ imagesDialog = new ImageListDialog(context, availableImages);
+ imagesDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ SdlImageItem selectedItem = (SdlImageItem) resultData;
+ if(selectedItem != null){
+ et_imageName.setText(selectedItem.getImageName());
+ }
+ }
+ });
+ }
+
+ imagesDialog.show();
+ }
+
+ private DialogInterface.OnClickListener okButton = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String choiceName = et_choiceName.getText().toString();
+ String choiceVr = et_choiceVr.getText().toString();
+ String imageName = et_imageName.getText().toString();
+
+ if(choiceName.length() > 0){
+ if(imageName.length() <= 0){
+ imageName = null;
+ }
+
+ if(choiceVr.length() > 0){
+ Choice choice = SdlUtils.createChoice(choiceName, choiceVr, imageName);
+ notifyListener(choice);
+ }
+ else{
+ Toast.makeText(context, "Choice must have a voice-rec keyword.", Toast.LENGTH_LONG).show();
+ }
+
+ }
+ else{
+ Toast.makeText(context, "Choice must have a name.", Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/CreateInteractionChoiceSetDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/CreateInteractionChoiceSetDialog.java
new file mode 100644
index 000000000..6ba95b95b
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/CreateInteractionChoiceSetDialog.java
@@ -0,0 +1,136 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Vector;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Bitmap;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.SdlImageItem.SdlImageItemComparator;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.adapters.SdlImageAdapter;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.dialogs.TextViewOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.Choice;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+
+public class CreateInteractionChoiceSetDialog extends BaseOkCancelDialog{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.CREATE_INTERACTION_CHOICE_SET;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private static final int MAX_CHOICES = 10;
+
+ private List<Choice> choiceItemList = new ArrayList<Choice>(MAX_CHOICES);
+
+ private Button but_addItem;
+ private ListView lv_choiceItems;
+ private ArrayAdapter<SdlImageItem> adapter;
+ private List<SdlImageItem> adapterList = new ArrayList<SdlImageItem>(MAX_CHOICES);
+
+ private BaseAlertDialog choiceDialog = null;
+ private List<SdlImageItem> allImages;
+
+ public CreateInteractionChoiceSetDialog(Context context, List<SdlImageItem> images){
+ super(context, DIALOG_TITLE, R.layout.create_choice_interaction_set);
+ setPositiveButton(okButtonListener);
+ adapter = new SdlImageAdapter(context, adapterList);
+ lv_choiceItems.setAdapter(adapter);
+ this.allImages = images;
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ lv_choiceItems = (ListView) view.findViewById(R.id.lv_choices);
+ lv_choiceItems.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
+ BaseAlertDialog deleteDialog = new TextViewOkCancelDialog(context, "Delete", "Do you want to delete this item?");
+ deleteDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ boolean delete = (Boolean) resultData;
+ if(delete){
+ adapterList.remove(position);
+ adapter.notifyDataSetChanged();
+ }
+ }
+ });
+ deleteDialog.show();
+ }
+ });
+ but_addItem = (Button) view.findViewById(R.id.but_addItem);
+ but_addItem.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showChoiceDialog();
+ }
+ });
+ }
+
+ private void showChoiceDialog(){
+ choiceDialog = new ChoiceItemDialog(context, allImages);
+ choiceDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ Choice choice = (Choice) resultData;
+ addChoiceToList(choice);
+ }
+ });
+ choiceDialog.show();
+ }
+
+ private void addChoiceToList(Choice choice){
+ choiceItemList.add(choice);
+
+ if(choice.getImage() != null){
+ // if user selected an image, figure out which one they selected
+ int imageIndex = Collections.binarySearch(allImages, new SdlImageItem(null, choice.getImage().getValue(), null), new SdlImageItemComparator());
+ if(imageIndex >= 0 && imageIndex < allImages.size()){
+ SdlImageItem item = allImages.get(imageIndex);
+ adapter.add(new SdlImageItem(item.getBitmap(), choice.getMenuName(), FileType.GRAPHIC_JPEG));
+ adapter.notifyDataSetChanged();
+ }
+ }
+ else{
+ // if the user didn't select an image, we'll create an empty one to display in the adapter
+ Bitmap image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+ adapter.add(new SdlImageItem(image, choice.getMenuName(), FileType.GRAPHIC_JPEG));
+ adapter.notifyDataSetChanged();
+ }
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Vector<Choice> choiceItems = new Vector<Choice>(choiceItemList);
+
+ if(choiceItems.size() > 0){
+ RPCRequest result = SdlRequestFactory.createInteractionChoiceSet(choiceItems);
+ notifyListener(result);
+ }
+ else{
+ Toast.makeText(context, "Must enter at least 1 choice name.", Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteCommandDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteCommandDialog.java
new file mode 100644
index 000000000..de4e7086b
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteCommandDialog.java
@@ -0,0 +1,51 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.menu.MenuItem;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class DeleteCommandDialog extends BaseAlertDialog{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.DELETE_COMMAND;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private ListView listView;
+
+ public DeleteCommandDialog(Context context, List<MenuItem> commandList) {
+ super(context, DIALOG_TITLE, R.layout.listview);
+ listView.setAdapter(AndroidUtils.createListViewAdapter(context, commandList));
+ setCancelable(true);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listView = (ListView) parent.findViewById(R.id.listView);
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ @SuppressWarnings("unchecked")
+ final MenuItem selectedButton = ((ArrayAdapter<MenuItem>) parent.getAdapter()).getItem(position);
+ final int commandId = selectedButton.getId();
+
+ RPCRequest result = SdlRequestFactory.deleteCommand(commandId);
+ notifyListener(result);
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteFileDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteFileDialog.java
new file mode 100644
index 000000000..e182c69f1
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteFileDialog.java
@@ -0,0 +1,65 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.adapters.SdlImageAdapter;
+import com.livio.sdl.dialogs.BaseImageListDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class DeleteFileDialog extends BaseImageListDialog {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.DELETE_FILE;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private static final String DELETE_ALL = "Delete All";
+ private List<SdlImageItem> imageList;
+
+ public DeleteFileDialog(Context context, List<SdlImageItem> imageList) {
+ super(context, DIALOG_TITLE, imageList);
+ this.imageList = new ArrayList<SdlImageItem>(imageList);
+ Bitmap deleteAllImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_close);
+ ((SdlImageAdapter)listview.getAdapter()).insert(new SdlImageItem(deleteAllImage, DELETE_ALL, null), 0);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listview = (ListView) parent.findViewById(R.id.listView);
+ listview.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ List<RPCRequest> messages = new ArrayList<RPCRequest>(imageList.size());
+ SdlImageItem selectedItem = ((SdlImageAdapter) parent.getAdapter()).getItem(position);
+ String selectedName = selectedItem.getImageName();
+
+ if(selectedName.equals(DELETE_ALL)){
+ for(SdlImageItem item : imageList){
+ RPCRequest request = SdlRequestFactory.deleteFile(item.getImageName());
+ messages.add(request);
+ }
+ }
+ else{
+ RPCRequest request = SdlRequestFactory.deleteFile(selectedItem.getImageName());
+ messages.add(request);
+ }
+
+ notifyListener(messages);
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteInteractionDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteInteractionDialog.java
new file mode 100644
index 000000000..66f2ae7f7
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteInteractionDialog.java
@@ -0,0 +1,45 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.livio.sdl.R;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseSingleListViewDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.menu.MenuItem;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class DeleteInteractionDialog extends BaseSingleListViewDialog<MenuItem> {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.DELETE_INTERACTION_CHOICE_SET;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ public DeleteInteractionDialog(Context context, List<MenuItem> items) {
+ super(context, DIALOG_TITLE, items);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listView = (ListView) parent.findViewById(R.id.listView);
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ selectedItem = ((ArrayAdapter<MenuItem>) parent.getAdapter()).getItem(position);
+
+ RPCRequest result = SdlRequestFactory.deleteInteractionChoiceSet(selectedItem.getId());
+ notifyListener(result);
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteSubmenuDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteSubmenuDialog.java
new file mode 100644
index 000000000..fad53a528
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/DeleteSubmenuDialog.java
@@ -0,0 +1,48 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseSingleListViewDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.menu.MenuItem;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class DeleteSubmenuDialog extends BaseSingleListViewDialog<MenuItem>{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.DELETE_SUB_MENU;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ public DeleteSubmenuDialog(Context context, List<MenuItem> submenuList) {
+ super(context, DIALOG_TITLE, submenuList);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listView = (ListView) parent.findViewById(R.id.listView);
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ @SuppressWarnings("unchecked")
+ final MenuItem selectedButton = ((ArrayAdapter<MenuItem>) parent.getAdapter()).getItem(position);
+ final int menuId = selectedButton.getId();
+
+ RPCRequest request = SdlRequestFactory.deleteSubmenu(menuId);
+ notifyListener(request);
+
+ // since this isn't an ok/cancel dialog, we must dismiss the dialog when an item is selected
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/GetDtcsDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/GetDtcsDialog.java
new file mode 100644
index 000000000..05cb15f2f
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/GetDtcsDialog.java
@@ -0,0 +1,54 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.InputFilter;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.viewhelpers.MinMaxInputFilter;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class GetDtcsDialog extends BaseOkCancelDialog {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.GET_DTCS;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private EditText et_ecuName;
+
+ public GetDtcsDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.get_dtcs);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ et_ecuName = (EditText) parent.findViewById(R.id.et_getDtcs_ecuName);
+
+ // set an input filter on the edit text so the user can only enter a valid input
+ et_ecuName.setFilters(new InputFilter[]{new MinMaxInputFilter(SdlConstants.GetDtcsConstants.MINIMUM_ECU_ID, SdlConstants.GetDtcsConstants.MAXIMUM_ECU_ID)});
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String ecuName = et_ecuName.getText().toString();
+ if(ecuName.length() > 0){
+ RPCRequest result = SdlRequestFactory.getDtcs(ecuName);
+ notifyListener(result);
+ }
+ else{
+ Toast.makeText(context, "Must enter an ECU name.", Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PerformInteractionDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PerformInteractionDialog.java
new file mode 100644
index 000000000..241fda8c8
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PerformInteractionDialog.java
@@ -0,0 +1,181 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.List;
+import java.util.Vector;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.dialogs.MultipleListViewDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.enums.SdlInteractionMode;
+import com.livio.sdl.menu.MenuItem;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdl.utils.MathUtils;
+import com.livio.sdl.viewhelpers.SeekBarCalculator;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
+
+public class PerformInteractionDialog extends BaseOkCancelDialog implements OnCheckedChangeListener, OnSeekBarChangeListener{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.PERFORM_INTERACTION;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ //set up your min & max time allowed here.
+ private static final int MINIMUM_TIMEOUT = SdlConstants.PerformInteractionConstants.MINIMUM_TIMEOUT;
+ private static final int MAXIMUM_TIMEOUT = SdlConstants.PerformInteractionConstants.MAXIMUM_TIMEOUT;
+
+ //this is your default selection for tone duration. again, divide by 10 for the actual time in seconds.
+ private static final int DEFAULT_TIMEOUT = 30; // 30.0 seconds
+
+ private EditText et_title, et_voicePrompt;
+ private Button but_choiceSet;
+ private Spinner spin_interactionMode;
+ private TextView tv_interactionTimeout, tv_interactionTimeoutDuration;
+ private CheckBox check_timeoutEnabled;
+ private SeekBar seek_timeoutDuration;
+ private SeekBarCalculator progressCalculator;
+ private String secondsStr;
+
+ private List<MenuItem> selectedChoiceSets;
+
+ //TODO - leaving these out for now - MRB
+ //private CheckBox check_alert_includeSoftButtons;
+ //private Button but_alert_includeSoftButtons;
+
+ public PerformInteractionDialog(Context context, List<MenuItem> interactionSets) {
+ super(context, DIALOG_TITLE, R.layout.perform_interaction);
+ setPositiveButton(okButtonListener);
+ setupViews(interactionSets);
+ createDialog();
+ }
+
+ private void setupViews(final List<MenuItem> interactionSets){
+ // setup the button click event, which shows another dialog to select which choice sets to show
+ but_choiceSet.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ MultipleListViewDialog<MenuItem> dialog = new MultipleListViewDialog<MenuItem>(context, "Select Choice Sets to show", interactionSets);
+ dialog.setListener(new BaseAlertDialog.Listener() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onResult(Object resultData) {
+ selectedChoiceSets = (List<MenuItem>) resultData;
+ }
+ });
+ dialog.show();
+ }
+ });
+
+ // setup the spinner
+ spin_interactionMode.setAdapter(AndroidUtils.createSpinnerAdapter(context, SdlInteractionMode.values()));
+
+ check_timeoutEnabled.setOnCheckedChangeListener(this);
+
+ seek_timeoutDuration.setOnSeekBarChangeListener(this);
+ seek_timeoutDuration.setProgress(progressCalculator.calculateProgress(DEFAULT_TIMEOUT));
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ progressCalculator = new SeekBarCalculator(MINIMUM_TIMEOUT, MAXIMUM_TIMEOUT);
+ secondsStr = context.getResources().getString(R.string.units_seconds);
+
+ et_title = (EditText) parent.findViewById(R.id.et_performInteraction_title);
+ et_voicePrompt = (EditText) parent.findViewById(R.id.et_performInteraction_voicePrompt);
+ tv_interactionTimeout = (TextView) parent.findViewById(R.id.tv_performInteraction_timeoutTitle);
+ tv_interactionTimeoutDuration = (TextView) parent.findViewById(R.id.tv_performInteraction_timeoutDuration);
+
+ but_choiceSet = (Button) parent.findViewById(R.id.but_performInteraction_selectChoiceSets);
+ spin_interactionMode = (Spinner) parent.findViewById(R.id.spin_performInteraction_interactionMode);
+ check_timeoutEnabled = (CheckBox) parent.findViewById(R.id.check_performInteraction_timeoutEnabled);
+ seek_timeoutDuration = (SeekBar) parent.findViewById(R.id.seek_performInteraction_timeoutDuration);
+
+ //make initial updates to the UI using default values
+ updateProgressText(DEFAULT_TIMEOUT);
+ enableDuration(check_timeoutEnabled.isChecked());
+ }
+
+ private void updateProgressText(float progress){
+ StringBuilder strBuilder = new StringBuilder();
+ strBuilder.append(progress);
+ strBuilder.append(secondsStr);
+ tv_interactionTimeoutDuration.setText(strBuilder.toString());
+ }
+
+ private void enableDuration(boolean enabled){
+ int visibility = (enabled) ? View.VISIBLE : View.GONE;
+ tv_interactionTimeout.setVisibility(visibility);
+ tv_interactionTimeoutDuration.setVisibility(visibility);
+ seek_timeoutDuration.setVisibility(visibility);
+ }
+
+ // dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if(selectedChoiceSets == null || selectedChoiceSets.size() == 0){
+ Toast.makeText(context, "Must select an interaction set in order to perform an interaction", Toast.LENGTH_LONG).show();
+ }
+ else{
+ String title = et_title.getText().toString();
+ String voicePrompt = et_voicePrompt.getText().toString();
+
+ boolean timeoutEnabled = check_timeoutEnabled.isChecked();
+ int timeout = SdlConstants.PerformInteractionConstants.INVALID_TIMEOUT;
+ if(timeoutEnabled){
+ timeout = progressCalculator.calculateProgress(seek_timeoutDuration.getProgress());
+ timeout = MathUtils.convertSecsToMillisecs(timeout);
+ }
+
+ SdlInteractionMode sdlInteractionMode = (SdlInteractionMode) spin_interactionMode.getAdapter().getItem(spin_interactionMode.getSelectedItemPosition());
+ InteractionMode interactionMode = SdlInteractionMode.translateToLegacy(sdlInteractionMode);
+
+ Vector<Integer> choiceSetIds = new Vector<Integer>(selectedChoiceSets.size());
+ for(MenuItem item : selectedChoiceSets){
+ choiceSetIds.add(item.getId());
+ }
+
+ RPCRequest result = SdlRequestFactory.performInteraction(title, voicePrompt, choiceSetIds, interactionMode, timeout);
+ notifyListener(result);
+ }
+ }
+ };
+
+ /*
+ * On Check Changed Listener Methods
+ */
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ enableDuration(isChecked);
+ }
+
+ /*
+ * On Seek Bar Changed Listener Methods
+ */
+ @Override public void onStartTrackingTouch(SeekBar seekBar) {}
+ @Override public void onStopTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ updateProgressText(progressCalculator.calculateValue(progress));
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PutFileDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PutFileDialog.java
new file mode 100644
index 000000000..5cb54555d
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/PutFileDialog.java
@@ -0,0 +1,172 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.SdlImageItem.SdlImageItemComparator;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.dialogs.ImageListDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.utils.SdlUtils;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class PutFileDialog extends BaseOkCancelDialog {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.PUT_FILE;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private ImageButton ib_putFile_selectAnImage;
+ private EditText et_putFile_imageName;
+ private CheckBox cb_putFile_isPersistent;
+ private CheckBox cb_putFile_addAll;
+
+ private SdlImageItem selectedImage = null;
+ private List<SdlImageItem> availableImages;
+
+ private HashMap<SdlImageItem, byte[]> bitmapRawByteMap;
+
+ public PutFileDialog(Context context, List<SdlImageItem> availableImages) {
+ super(context, DIALOG_TITLE, R.layout.put_file);
+ this.availableImages = availableImages;
+ Collections.sort(this.availableImages, new SdlImageItemComparator());
+ startImageProcessing();
+ setupViews();
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ /**
+ * Preemptively processes all available images in the background so the calculations
+ * don't have to be done when the user makes a selection. By the time the user
+ * clicks on something, the calculations should be complete.
+ */
+ private void startImageProcessing(){
+ final int size = availableImages.size();
+ bitmapRawByteMap = new HashMap<SdlImageItem, byte[]>(size);
+
+ // loop through all images and start a thread to process them
+ for(final SdlImageItem item : availableImages){
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ byte[] bitmapData = SdlUtils.bitmapToByteArray(item.getBitmap(), item.getImageType());
+ addDataToMap(item, bitmapData);
+ }
+ }).start();
+ }
+ }
+
+ /**
+ * Allows synchronized editting of the raw byte hash map. HashMaps are not thread safe,
+ * so we must protect the data structure through a synchronized method.
+ *
+ * @param key The key to add
+ * @param value The value to add
+ */
+ private synchronized void addDataToMap(SdlImageItem key, byte[] value){
+ bitmapRawByteMap.put(key, value);
+ }
+
+ private void setupViews(){
+ ib_putFile_selectAnImage.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ BaseAlertDialog selectImageDialog = new ImageListDialog(context, availableImages);
+ selectImageDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ selectedImage = (SdlImageItem) resultData;
+ onItemSelected(selectedImage);
+ }
+ });
+ selectImageDialog.show();
+ }
+ });
+ }
+
+ private void onItemSelected(SdlImageItem item){
+ ib_putFile_selectAnImage.setImageBitmap(item.getBitmap());
+ et_putFile_imageName.setText(item.getImageName());
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ et_putFile_imageName = (EditText) parent.findViewById(R.id.et_putFile_imageName);
+ cb_putFile_isPersistent = (CheckBox) parent.findViewById(R.id.cb_putFile_isPersistent);
+ ib_putFile_selectAnImage = (ImageButton) parent.findViewById(R.id.ib_putFile_selectAnImage);
+
+ cb_putFile_addAll = (CheckBox) parent.findViewById(R.id.cb_putFile_addAll);
+ cb_putFile_addAll.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ enableViews(!isChecked);
+ }
+ });
+ }
+
+ private void enableViews(boolean enable){
+ int visibility = (enable) ? View.VISIBLE : View.GONE;
+ et_putFile_imageName.setVisibility(visibility);
+ cb_putFile_isPersistent.setVisibility(visibility);
+ ib_putFile_selectAnImage.setVisibility(visibility);
+ }
+
+ //dialog button click listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ List<RPCMessage> messages = new ArrayList<RPCMessage>(availableImages.size());
+ boolean persistentFile = cb_putFile_isPersistent.isChecked();
+
+ if(cb_putFile_addAll.isChecked()){
+ Set<SdlImageItem> keySet = bitmapRawByteMap.keySet();
+ Iterator<SdlImageItem> iterator = keySet.iterator();
+
+ while(iterator.hasNext()){
+ SdlImageItem item = iterator.next();
+ RPCRequest result = SdlRequestFactory.putFile(item.getImageName(), item.getImageType(), persistentFile, bitmapRawByteMap.get(item));
+ messages.add(result);
+ }
+
+ notifyListener(messages);
+ }
+ else if(selectedImage != null){
+ String name = et_putFile_imageName.getText().toString();
+ if(name.length() > 0){
+ RPCRequest result = SdlRequestFactory.putFile(name, selectedImage.getImageType(), persistentFile, bitmapRawByteMap.get(selectedImage));
+ messages.add(result);
+
+ notifyListener(messages);
+ }
+ else{
+ Toast.makeText(context, "Must enter a name for the image.", Toast.LENGTH_LONG).show();
+ }
+ }
+ else{
+ Toast.makeText(context, "Must select an image to add.", Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ReadDidsDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ReadDidsDialog.java
new file mode 100644
index 000000000..3a9fb33be
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ReadDidsDialog.java
@@ -0,0 +1,60 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.InputFilter;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.viewhelpers.MinMaxInputFilter;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class ReadDidsDialog extends BaseOkCancelDialog {
+
+ // TODO didLocation should be comma-separated.
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.READ_DIDS;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private static final int MIN_ECU_NUMBER = SdlConstants.ReadDidsConstants.MINIMUM_ECU_ID;
+ private static final int MAX_ECU_NUMBER = SdlConstants.ReadDidsConstants.MAXIMUM_ECU_ID;
+
+ private EditText et_ecuName, et_didLocation;
+
+ public ReadDidsDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.read_dids);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ et_ecuName = (EditText) parent.findViewById(R.id.et_readDids_ecuName);
+ et_ecuName.setFilters(new InputFilter[]{new MinMaxInputFilter(MIN_ECU_NUMBER, MAX_ECU_NUMBER)});
+ et_didLocation = (EditText) parent.findViewById(R.id.et_readDids_didLocation);
+ et_didLocation.setFilters(new InputFilter[]{new MinMaxInputFilter(MIN_ECU_NUMBER, MAX_ECU_NUMBER)});
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String ecuName = et_ecuName.getText().toString();
+ final String didLocation = et_didLocation.getText().toString();
+ if(ecuName.length() > 0 && didLocation.length() > 0){
+ RPCRequest result = SdlRequestFactory.readDid(Integer.parseInt(ecuName), Integer.parseInt(didLocation));
+ notifyListener(result);
+ }
+ else{
+ Toast.makeText(context, "Must enter ECU name & DID location.", Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ScrollableMessageDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ScrollableMessageDialog.java
new file mode 100644
index 000000000..f15c00251
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ScrollableMessageDialog.java
@@ -0,0 +1,103 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.utils.MathUtils;
+import com.livio.sdl.viewhelpers.SeekBarCalculator;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class ScrollableMessageDialog extends BaseOkCancelDialog {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.SCROLLABLE_MESSAGE;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private static final int TIMEOUT_DEFAULT = 30;
+ private static final int TIMEOUT_MIN = SdlConstants.ScrollableMessageConstants.TIMEOUT_MINIMUM;
+ private static final int TIMEOUT_MAX = SdlConstants.ScrollableMessageConstants.TIMEOUT_MAXIMUM;
+ private static final int LENGTH_MAX = SdlConstants.ScrollableMessageConstants.MESSAGE_LENGTH_MAX;
+
+ private EditText et_scrollableMessage_text;
+ private TextView tv_timeout;
+ private SeekBar seek_timeout;
+ private String timeoutBaseStr;
+ private SeekBarCalculator progressCalculator;
+
+ public ScrollableMessageDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.scrollable_message);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ timeoutBaseStr = context.getResources().getString(R.string.timeout);
+ progressCalculator = new SeekBarCalculator(TIMEOUT_MIN, TIMEOUT_MAX);
+
+ et_scrollableMessage_text = (EditText) parent.findViewById(R.id.et_scrollableMessage_text);
+
+ Button clearButton = (Button) parent.findViewById(R.id.but_scrollableMessage_clear);
+ clearButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ et_scrollableMessage_text.setText("");
+ }
+ });
+
+ tv_timeout = (TextView) parent.findViewById(R.id.tv_scrollableMessage_timeout);
+ updateTimeoutText(TIMEOUT_DEFAULT);
+
+ seek_timeout = (SeekBar) parent.findViewById(R.id.seek_scrollableMessage_timeout);
+ seek_timeout.setMax(progressCalculator.getMaxProgress());
+ seek_timeout.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override public void onStopTrackingTouch(SeekBar seekBar) {}
+ @Override public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ updateTimeoutText((int) progressCalculator.calculateValue(progress));
+ }
+ });
+ seek_timeout.setProgress(progressCalculator.calculateProgress(TIMEOUT_DEFAULT));
+ }
+
+ private void updateTimeoutText(int timeout){
+ tv_timeout.setText(new StringBuilder().append(timeoutBaseStr).append(timeout).append(" s").toString());
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String message = et_scrollableMessage_text.getText().toString();
+ int timeout = (int) progressCalculator.calculateValue(seek_timeout.getProgress());
+ timeout = MathUtils.convertSecsToMillisecs(timeout);
+
+ if(message.length() <= 0){
+ message = " ";
+ }
+ // if the message is too long, we'll just chop off the end
+ else if(message.length() > LENGTH_MAX){
+ Toast.makeText(context, "Text was too long, extra characters are being dropped.", Toast.LENGTH_LONG).show();
+ message = message.substring(0, LENGTH_MAX);
+ }
+
+ RPCRequest result = SdlRequestFactory.scrollableMessage(message, timeout);
+ notifyListener(result);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlAlertDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlAlertDialog.java
new file mode 100644
index 000000000..d341c7a99
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlAlertDialog.java
@@ -0,0 +1,137 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.utils.MathUtils;
+import com.livio.sdl.viewhelpers.SeekBarCalculator;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class SdlAlertDialog extends BaseOkCancelDialog implements OnSeekBarChangeListener{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.ALERT;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ // a seekbar cannot do decimal points, so it currently ranges 0-50, which is then
+ // divided by 10.0f to give us a number of seconds, rounded to 1/10 of a second.
+ private static final float TENS_PLACE_DENOMINATOR = 10.0f;
+
+ //set up your min & max time allowed here. we multiply by 10 since we're allowing 1/10th of a second selections
+ private static final int MINIMUM_ALERT_TONE_TIME = (int) (SdlConstants.AlertConstants.ALERT_TIME_MINIMUM * TENS_PLACE_DENOMINATOR);
+ private static final int MAXIMUM_ALERT_TONE_TIME = (int) (SdlConstants.AlertConstants.ALERT_TIME_MAXIMUM * TENS_PLACE_DENOMINATOR);
+
+ //this is your default selection for tone duration. again, divide by 10 for the actual time in seconds.
+ private static final float DEFAULT_TONE_DURATION = 5.0f; // 5.0 seconds
+
+ private EditText et_alert_textToSpeak;
+ private EditText et_alert_line1;
+ private EditText et_alert_line2;
+ private EditText et_alert_line3;
+
+ private TextView tv_alert_toneDuration;
+
+ private CheckBox check_alert_playTone;
+
+ private SeekBar seek_alert_toneDuration;
+ private SeekBarCalculator progressCalculator;
+
+ private String secondsStr;
+
+ //TODO - leaving these out for now - MRB
+ //private CheckBox check_alert_includeSoftButtons;
+ //private Button but_alert_includeSoftButtons;
+
+ public SdlAlertDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.alert);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ secondsStr = context.getResources().getString(R.string.units_seconds);
+ progressCalculator = new SeekBarCalculator(MINIMUM_ALERT_TONE_TIME, MAXIMUM_ALERT_TONE_TIME, TENS_PLACE_DENOMINATOR);
+
+ et_alert_textToSpeak = (EditText) view.findViewById(R.id.et_alert_textToSpeak);
+ et_alert_line1 = (EditText) view.findViewById(R.id.et_alert_line1);
+ et_alert_line2 = (EditText) view.findViewById(R.id.et_alert_line2);
+ et_alert_line3 = (EditText) view.findViewById(R.id.et_alert_line3);
+
+ tv_alert_toneDuration = (TextView) view.findViewById(R.id.tv_alert_toneDuration);
+
+ seek_alert_toneDuration = (SeekBar) view.findViewById(R.id.seek_alert_toneDuration);
+ seek_alert_toneDuration.setMax(progressCalculator.getMaxProgress());
+ seek_alert_toneDuration.setProgress(progressCalculator.calculateProgress(DEFAULT_TONE_DURATION));
+ seek_alert_toneDuration.setOnSeekBarChangeListener(this);
+
+ check_alert_playTone = (CheckBox) view.findViewById(R.id.check_alert_playTone);
+
+ //make initial updates to the UI using default values
+ updateProgressText(DEFAULT_TONE_DURATION);
+
+ //TODO - leaving these out for now - MRB
+ //check_alert_includeSoftButtons = (CheckBox) view.findViewById(R.id.check_alert_includeSoftButtons);
+ //
+ //but_alert_includeSoftButtons = (Button) view.findViewById(R.id.but_alert_includeSoftButtons);
+ //TODO - add on click listener to this button.
+ }
+
+ private void updateProgressText(float progress){
+ StringBuilder strBuilder = new StringBuilder();
+ strBuilder.append(progress);
+ strBuilder.append(secondsStr);
+ tv_alert_toneDuration.setText(strBuilder.toString());
+ }
+
+ // dialog button listsners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String textToSpeak = et_alert_textToSpeak.getText().toString();
+ String line1 = et_alert_line1.getText().toString();
+ String line2 = et_alert_line2.getText().toString();
+ String line3 = et_alert_line3.getText().toString();
+ int toneDurationInS = (int) progressCalculator.calculateValue(seek_alert_toneDuration.getProgress());
+ int toneDurationInMs = MathUtils.convertSecsToMillisecs(toneDurationInS);
+ boolean playTone = check_alert_playTone.isChecked();
+
+ if(textToSpeak.equals("")){
+ textToSpeak = null;
+ }
+ if(line1.equals("")){
+ line1 = " ";
+ }
+ if(line2.equals("")){
+ line2 = " ";
+ }
+ if(line3.equals("")){
+ line3 = " ";
+ }
+
+ RPCRequest result = SdlRequestFactory.alert(textToSpeak, line1, line2, line3, playTone, toneDurationInMs);
+ notifyListener(result);
+ }
+ };
+
+ /*
+ * On Seek Bar Changed Listener Methods
+ */
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ updateProgressText(progressCalculator.calculateValue(progress));
+ }
+
+ @Override public void onStartTrackingTouch(SeekBar seekBar) {}
+ @Override public void onStopTrackingTouch(SeekBar seekBar) {}
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlConnectionDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlConnectionDialog.java
new file mode 100644
index 000000000..56ad25d59
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SdlConnectionDialog.java
@@ -0,0 +1,61 @@
+package com.livio.sdltester.dialogs;
+
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.EditText;
+
+import com.livio.sdl.IpAddress;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdltester.R;
+
+public class SdlConnectionDialog extends BaseOkCancelDialog {
+
+ private static final String DIALOG_TITLE = "SDL Connection";
+
+ private EditText et_ipAddress, et_ipPort;
+
+ public SdlConnectionDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.sdl_connection);
+ setPositiveButton(okButtonListener);
+ setNegativeButton(cancelListener);
+ createDialog();
+ }
+
+ public SdlConnectionDialog(Context context, String initIpAddress, String initPort){
+ this(context);
+ setEditTextStrings(initIpAddress, initPort);
+ }
+
+ private void setEditTextStrings(String ipAddress, String ipPort){
+ et_ipAddress.setText(ipAddress);
+ et_ipPort.setText(ipPort);
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ et_ipAddress = (EditText) parent.findViewById(R.id.et_ipAddress);
+ et_ipPort = (EditText) parent.findViewById(R.id.et_ipPort);
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String ipAddress = et_ipAddress.getText().toString();
+ final String ipPort = et_ipPort.getText().toString();
+
+ IpAddress result = new IpAddress(ipAddress, ipPort);
+ notifyListener(result);
+ }
+ };
+
+ private final DialogInterface.OnClickListener cancelListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ notifyListener(null);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetAppIconDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetAppIconDialog.java
new file mode 100644
index 000000000..e0937b097
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetAppIconDialog.java
@@ -0,0 +1,45 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.adapters.SdlImageAdapter;
+import com.livio.sdl.dialogs.BaseImageListDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class SetAppIconDialog extends BaseImageListDialog {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.SET_APP_ICON;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ public SetAppIconDialog(Context context, List<SdlImageItem> imageList) {
+ super(context, DIALOG_TITLE, imageList);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ listview = (ListView) parent.findViewById(R.id.listView);
+ listview.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ SdlImageItem selectedItem = ((SdlImageAdapter) parent.getAdapter()).getItem(position);
+
+ RPCRequest result = SdlRequestFactory.setAppIcon(selectedItem.getImageName());
+ notifyListener(result);
+
+ dismiss();
+ }
+ });
+ }
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetMediaClockTimerDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetMediaClockTimerDialog.java
new file mode 100644
index 000000000..63c245fb4
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SetMediaClockTimerDialog.java
@@ -0,0 +1,112 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.InputFilter;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.enums.SdlUpdateMode;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdl.viewhelpers.MinMaxInputFilter;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.enums.UpdateMode;
+
+public class SetMediaClockTimerDialog extends BaseOkCancelDialog {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.SET_MEDIA_CLOCK_TIMER;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private static final int HOURS_MIN = SdlConstants.SetMediaClockTimerConstants.HOURS_MINIMUM;
+ private static final int HOURS_MAX = SdlConstants.SetMediaClockTimerConstants.HOURS_MAXIMUM;
+ private static final int MINS_MIN = SdlConstants.SetMediaClockTimerConstants.MINUTES_MINIMUM;
+ private static final int MINS_MAX = SdlConstants.SetMediaClockTimerConstants.MINUTES_MAXIMUM;
+ private static final int SECS_MIN = SdlConstants.SetMediaClockTimerConstants.SECONDS_MINIMUM;
+ private static final int SECS_MAX = SdlConstants.SetMediaClockTimerConstants.SECONDS_MAXIMUM;
+
+ private Spinner spin_mediaClock_type;
+ private EditText et_mediaClock_hrs, et_mediaClock_mins, et_mediaClock_secs;
+ private TextView tv_mediaClock_clock;
+ private LinearLayout ll_mediaClock_clockContainer;
+
+ public SetMediaClockTimerDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.set_media_clock_timer);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ spin_mediaClock_type = (Spinner) parent.findViewById(R.id.spin_mediaClock_updateMode);
+ spin_mediaClock_type.setAdapter(AndroidUtils.createSpinnerAdapter(context, SdlUpdateMode.values()));
+ spin_mediaClock_type.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override public void onNothingSelected(AdapterView<?> arg0) {}
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ SdlUpdateMode mode = (SdlUpdateMode) parent.getAdapter().getItem(position);
+ onModeUpdated(mode);
+ }
+ });
+
+ et_mediaClock_hrs = (EditText) parent.findViewById(R.id.et_mediaClockHours);
+ et_mediaClock_hrs.setFilters(new InputFilter[]{new MinMaxInputFilter(HOURS_MIN, HOURS_MAX)});
+ et_mediaClock_mins = (EditText) parent.findViewById(R.id.et_mediaClockMins);
+ et_mediaClock_mins.setFilters(new InputFilter[]{new MinMaxInputFilter(MINS_MIN, MINS_MAX)});
+ et_mediaClock_secs = (EditText) parent.findViewById(R.id.et_mediaClockSecs);
+ et_mediaClock_secs.setFilters(new InputFilter[]{new MinMaxInputFilter(SECS_MIN, SECS_MAX)});
+
+ tv_mediaClock_clock = (TextView) parent.findViewById(R.id.tv_mediaClock_clock);
+
+ ll_mediaClock_clockContainer = (LinearLayout) parent.findViewById(R.id.ll_clock);
+ }
+
+ private void onModeUpdated(SdlUpdateMode mode){
+ enableClockView( (mode == SdlUpdateMode.COUNT_DOWN || mode == SdlUpdateMode.COUNT_UP) );
+ }
+
+ private void enableClockView(boolean enable){
+ int visibility = (enable) ? View.VISIBLE : View.GONE;
+ tv_mediaClock_clock.setVisibility(visibility);
+ ll_mediaClock_clockContainer.setVisibility(visibility);
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ SdlUpdateMode mode = (SdlUpdateMode) spin_mediaClock_type.getSelectedItem();
+ UpdateMode legacyMode = SdlUpdateMode.translateToLegacy(mode);
+ RPCRequest result;
+
+ if(mode == SdlUpdateMode.COUNT_UP || mode == SdlUpdateMode.COUNT_DOWN){
+ String hours = et_mediaClock_hrs.getText().toString();
+ String mins = et_mediaClock_mins.getText().toString();
+ String secs = et_mediaClock_secs.getText().toString();
+
+ // if user left a field blank, let's assume it's a 0
+ hours = (hours.length() > 0) ? hours : "0";
+ mins = (mins.length() > 0) ? mins : "0";
+ secs = (secs.length() > 0) ? secs : "0";
+
+ result = SdlRequestFactory.setMediaClockTimer(legacyMode, Integer.parseInt(hours), Integer.parseInt(mins), Integer.parseInt(secs));
+ }
+ else{
+ result = SdlRequestFactory.setMediaClockTimer(legacyMode);
+ }
+
+ notifyListener(result);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ShowDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ShowDialog.java
new file mode 100644
index 000000000..cf0964cbe
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/ShowDialog.java
@@ -0,0 +1,186 @@
+package com.livio.sdltester.dialogs;
+
+import java.util.List;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlImageItem;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseAlertDialog;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.dialogs.ImageListDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.enums.SdlTextAlignment;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+
+public class ShowDialog extends BaseOkCancelDialog{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.SHOW;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private CheckBox check_show1, check_show2, check_show3, check_show4, check_statusBar, check_imageName;
+ private EditText et_show1, et_show2, et_show3, et_show4, et_statusBar, et_imageName;
+ private Spinner spin_textAlignment;
+ private SdlImageItem selectedImage;
+
+ public ShowDialog(final Context context, final List<SdlImageItem> images){
+ super(context, DIALOG_TITLE, R.layout.show);
+ setPositiveButton(okButtonListener);
+
+ if(images == null || images.size() <= 0){
+ check_imageName.setVisibility(View.GONE);
+ et_imageName.setVisibility(View.GONE);
+ }
+
+ check_imageName.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(isChecked){
+ if(images == null || images.size() <= 0){
+ Toast.makeText(context, "No images have been added to the system yet.", Toast.LENGTH_LONG).show();
+ }
+ else{
+ BaseAlertDialog selectImageDialog = new ImageListDialog(context, images);
+ selectImageDialog.setListener(new BaseAlertDialog.Listener() {
+ @Override
+ public void onResult(Object resultData) {
+ selectedImage = (SdlImageItem) resultData;
+ et_imageName.setText(selectedImage.getImageName());
+ }
+ });
+ selectImageDialog.show();
+ }
+ }
+ else{
+ et_imageName.setText("");
+ }
+ }
+ });
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View view){
+ check_show1 = (CheckBox) view.findViewById(R.id.check_show1);
+ check_show1.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ et_show1.setEnabled(isChecked);
+ }
+ });
+ check_show2 = (CheckBox) view.findViewById(R.id.check_show2);
+ check_show2.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ et_show2.setEnabled(isChecked);
+ }
+ });
+ check_show3 = (CheckBox) view.findViewById(R.id.check_show3);
+ check_show3.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ et_show3.setEnabled(isChecked);
+ }
+ });
+ check_show4 = (CheckBox) view.findViewById(R.id.check_show4);
+ check_show4.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ et_show4.setEnabled(isChecked);
+ }
+ });
+ check_statusBar = (CheckBox) view.findViewById(R.id.check_statusBar);
+ check_statusBar.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ et_statusBar.setEnabled(isChecked);
+ }
+ });
+ check_imageName = (CheckBox) view.findViewById(R.id.check_enable_image);
+
+ et_show1 = (EditText) view.findViewById(R.id.et_show1);
+ et_show1.setEnabled(check_show1.isChecked());
+ et_show2 = (EditText) view.findViewById(R.id.et_show2);
+ et_show2.setEnabled(check_show2.isChecked());
+ et_show3 = (EditText) view.findViewById(R.id.et_show3);
+ et_show3.setEnabled(check_show3.isChecked());
+ et_show4 = (EditText) view.findViewById(R.id.et_show4);
+ et_show4.setEnabled(check_show4.isChecked());
+ et_statusBar = (EditText) view.findViewById(R.id.et_statusBar);
+ et_statusBar.setEnabled(check_statusBar.isChecked());
+ et_imageName = (EditText) view.findViewById(R.id.et_show_image);
+
+
+ spin_textAlignment = (Spinner) view.findViewById(R.id.spin_textAlignment);
+ spin_textAlignment.setAdapter(AndroidUtils.createSpinnerAdapter(context, SdlTextAlignment.values()));
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String line1 = null, line2 = null, line3 = null, line4 = null, statusBar = null, imageName = null;
+ TextAlignment alignment = null;
+
+ if(et_show1.isEnabled()){
+ line1 = et_show1.getText().toString();
+ if(line1.length() <= 0){
+ line1 = " ";
+ }
+ }
+
+ if(et_show2.isEnabled()){
+ line2 = et_show2.getText().toString();
+ if(line2.length() <= 0){
+ line2 = " ";
+ }
+ }
+
+ if(et_show3.isEnabled()){
+ line3 = et_show3.getText().toString();
+ if(line3.length() <= 0){
+ line3 = " ";
+ }
+ }
+
+ if(et_show4.isEnabled()){
+ line4 = et_show4.getText().toString();
+ if(line4.length() <= 0){
+ line4 = " ";
+ }
+ }
+
+ if(et_statusBar.isEnabled()){
+ statusBar = et_statusBar.getText().toString();
+ if(statusBar.length() <= 0){
+ statusBar = " ";
+ }
+ }
+
+ if(spin_textAlignment.getSelectedItemPosition() != 0){
+ SdlTextAlignment sdlAlignment = (SdlTextAlignment) spin_textAlignment.getSelectedItem();
+ alignment = SdlTextAlignment.translateToLegacy(sdlAlignment);
+ }
+
+ if(selectedImage != null){
+ imageName = selectedImage.getImageName();
+ }
+
+ RPCRequest result = SdlRequestFactory.show(line1, line2, line3, line4, statusBar, alignment, imageName);
+ notifyListener(result);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SliderDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SliderDialog.java
new file mode 100644
index 000000000..981379dbb
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SliderDialog.java
@@ -0,0 +1,155 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.livio.sdl.SdlConstants;
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.utils.MathUtils;
+import com.livio.sdl.viewhelpers.SeekBarCalculator;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+
+public class SliderDialog extends BaseOkCancelDialog {
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.SLIDER;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private static final int NUM_OF_TICKS_MIN = SdlConstants.SliderConstants.NUM_OF_TICKS_MIN;
+ private static final int NUM_OF_TICKS_MAX = SdlConstants.SliderConstants.NUM_OF_TICKS_MAX;
+ private static final int START_POSITION_MIN = SdlConstants.SliderConstants.START_POSITION_MIN;
+ private static final int TIMEOUT_MIN = SdlConstants.SliderConstants.TIMEOUT_MIN;
+ private static final int TIMEOUT_MAX = SdlConstants.SliderConstants.TIMEOUT_MAX;
+
+ private static final int NUM_OF_TICKS_DEFAULT = 10;
+ private static final int START_POSITION_DEFAULT = 1;
+ private static final int TIMEOUT_DEFAULT = 10;
+
+ private EditText et_slider_title, et_slider_footer;
+ private SeekBar seek_slider_numOfTicks, seek_slider_startPosition, seek_slider_timeout;
+ private TextView tv_slider_numOfTicks, tv_slider_startPosition, tv_slider_timeout;
+ private SeekBarCalculator numOfTicksCalculator, startPositionCalculator, timeoutCalculator;
+
+ private String numOfTicks, startPosition, timeout;
+
+ public SliderDialog(Context context) {
+ super(context, DIALOG_TITLE, R.layout.slider);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View parent) {
+ numOfTicksCalculator = new SeekBarCalculator(NUM_OF_TICKS_MIN, NUM_OF_TICKS_MAX);
+ startPositionCalculator = new SeekBarCalculator(START_POSITION_MIN, NUM_OF_TICKS_MAX);
+ timeoutCalculator = new SeekBarCalculator(TIMEOUT_MIN, TIMEOUT_MAX);
+
+ Resources res = context.getResources();
+ numOfTicks = res.getString(R.string.slider_ticks);
+ startPosition= res.getString(R.string.slider_start_position);
+ timeout = res.getString(R.string.timeout);
+
+ et_slider_title = (EditText) parent.findViewById(R.id.et_slider_title);
+ et_slider_footer = (EditText) parent.findViewById(R.id.et_slider_footer);
+ tv_slider_numOfTicks = (TextView) parent.findViewById(R.id.tv_slider_numOfTicks);
+ tv_slider_startPosition = (TextView) parent.findViewById(R.id.tv_slider_startPosition);
+ tv_slider_timeout = (TextView) parent.findViewById(R.id.tv_slider_timeout);
+
+ updateTicks(NUM_OF_TICKS_DEFAULT);
+ updateStartPosition(START_POSITION_DEFAULT);
+ updateTimeout(TIMEOUT_DEFAULT);
+
+ seek_slider_numOfTicks = (SeekBar) parent.findViewById(R.id.seek_slider_numOfTicks);
+ seek_slider_numOfTicks.setMax(numOfTicksCalculator.getMaxProgress());
+ seek_slider_numOfTicks.setProgress(numOfTicksCalculator.calculateProgress(NUM_OF_TICKS_DEFAULT));
+ seek_slider_numOfTicks.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override public void onStopTrackingTouch(SeekBar seekBar) {}
+ @Override public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ int adjustedProgress = (int) numOfTicksCalculator.calculateValue(progress);
+ updateTicks(adjustedProgress);
+ updateStartPositionMax(adjustedProgress);
+ }
+ });
+
+ seek_slider_startPosition = (SeekBar) parent.findViewById(R.id.seek_slider_startPosition);
+ seek_slider_startPosition.setProgress(startPositionCalculator.calculateProgress(START_POSITION_DEFAULT));
+ seek_slider_startPosition.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override public void onStopTrackingTouch(SeekBar seekBar) {}
+ @Override public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ updateStartPosition((int) startPositionCalculator.calculateValue(progress));
+ }
+ });
+
+ seek_slider_timeout = (SeekBar) parent.findViewById(R.id.seek_slider_timeout);
+ seek_slider_timeout.setMax(timeoutCalculator.getMaxProgress());
+ seek_slider_timeout.setProgress(timeoutCalculator.calculateProgress(TIMEOUT_DEFAULT));
+ seek_slider_timeout.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override public void onStopTrackingTouch(SeekBar seekBar) {}
+ @Override public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ updateTimeout((int) timeoutCalculator.calculateValue(progress));
+ }
+ });
+
+ updateStartPositionMax(NUM_OF_TICKS_DEFAULT);
+ }
+
+ private void updateTicks(int ticks){
+ tv_slider_numOfTicks.setText(new StringBuilder().append(numOfTicks).append(ticks).toString());
+ }
+
+ private void updateStartPosition(int start){
+ tv_slider_startPosition.setText(new StringBuilder().append(startPosition).append(start).toString());
+ }
+
+ private void updateStartPositionMax(int numOfTicks){
+ startPositionCalculator.setMaxValue(numOfTicks);
+ seek_slider_startPosition.setMax(startPositionCalculator.getMaxProgress());
+ }
+
+ private void updateTimeout(int newTimeout){
+ tv_slider_timeout.setText(new StringBuilder().append(timeout).append(newTimeout).append(" s").toString());
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String sliderTitle = et_slider_title.getText().toString();
+ String sliderFooter = et_slider_footer.getText().toString();
+
+ int numOfTicks = (int) numOfTicksCalculator.calculateValue(seek_slider_numOfTicks.getProgress());
+ int startPosition = (int) startPositionCalculator.calculateValue(seek_slider_startPosition.getProgress());
+
+ int timeout = (int) timeoutCalculator.calculateValue(seek_slider_timeout.getProgress());
+ timeout = MathUtils.convertSecsToMillisecs(timeout);
+
+ if(sliderTitle.length() <= 0){
+ sliderTitle = " ";
+ }
+ if(sliderFooter.length() <= 0){
+ sliderFooter = " ";
+ }
+
+ RPCRequest result = SdlRequestFactory.slider(sliderTitle, sliderFooter, numOfTicks, startPosition, timeout);
+ notifyListener(result);
+ }
+ };
+
+}
diff --git a/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SpeakDialog.java b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SpeakDialog.java
new file mode 100644
index 000000000..0aa44fb03
--- /dev/null
+++ b/SDL_Android/LivioTesterApp/src/com/livio/sdltester/dialogs/SpeakDialog.java
@@ -0,0 +1,67 @@
+package com.livio.sdltester.dialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+import com.livio.sdl.SdlRequestFactory;
+import com.livio.sdl.dialogs.BaseOkCancelDialog;
+import com.livio.sdl.enums.SdlCommand;
+import com.livio.sdl.enums.SdlSpeechCapability;
+import com.livio.sdl.utils.AndroidUtils;
+import com.livio.sdltester.R;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+
+public class SpeakDialog extends BaseOkCancelDialog{
+
+ private static final SdlCommand SYNC_COMMAND = SdlCommand.SPEAK;
+ private static final String DIALOG_TITLE = SYNC_COMMAND.toString();
+
+ private EditText et_textToSpeak;
+ private Spinner spin_speechCapabilities;
+
+ public SpeakDialog(Context context){
+ super(context, DIALOG_TITLE, R.layout.speak);
+ setPositiveButton(okButtonListener);
+ createDialog();
+ }
+
+ @Override
+ protected void findViews(View view){
+ et_textToSpeak = (EditText) view.findViewById(R.id.et_textToSpeak);
+ spin_speechCapabilities = (Spinner) view.findViewById(R.id.spin_speechCapabilities);
+ spin_speechCapabilities.setAdapter(AndroidUtils.createSpinnerAdapter(context, SdlSpeechCapability.values()));
+ }
+
+ //dialog button listeners
+ private final DialogInterface.OnClickListener okButtonListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String ttsText = et_textToSpeak.getText().toString();
+ final SdlSpeechCapability sdlSpeechCapabilities = (SdlSpeechCapability) spin_speechCapabilities.getSelectedItem();
+ final SpeechCapabilities speechCapabilities = SdlSpeechCapability.translateToLegacy(sdlSpeechCapabilities);
+
+ if(speechCapabilities != SpeechCapabilities.SILENCE){
+ if(ttsText.length() > 0){
+ RPCRequest result = SdlRequestFactory.speak(ttsText, speechCapabilities);
+ notifyListener(result);
+ }
+ else{
+ Toast.makeText(context, "Must enter text to speak.", Toast.LENGTH_LONG).show();
+ }
+ }
+ else{
+ RPCRequest result = SdlRequestFactory.speak(ttsText, speechCapabilities);
+ notifyListener(result);
+ }
+
+
+
+ }
+ };
+
+}