Merge branch 'android' into revo-mini
Conflicts: androidgcs/AndroidManifest.xml androidgcs/res/layout/gcs_home.xml androidgcs/res/values/strings.xml androidgcs/src/org/openpilot/androidgcs/AttitudeView.java androidgcs/src/org/openpilot/androidgcs/HomePage.java
@ -1,96 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.openpilot.androidgcs" android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<uses-sdk android:minSdkVersion="14" />
|
||||
|
||||
package="org.openpilot.androidgcs"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-feature android:name="android.hardware.usb.host" />
|
||||
|
||||
<application android:icon="@drawable/ic_logo" android:label="@string/app_name" android:theme="@android:style/Theme.Holo">
|
||||
<!-- for map overlay -->
|
||||
<uses-library android:name="com.google.android.maps" />
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" /> <!-- OpenGL min requierements (2.0) -->
|
||||
<!-- Object browser - main activity at the moment -->
|
||||
<activity android:name="HomePage" android:label="@string/app_name">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- <intent-filter> -->
|
||||
<!-- <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> -->
|
||||
<!-- </intent-filter> -->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<!-- <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" -->
|
||||
<!-- android:resource="@xml/device_filter" /> -->
|
||||
</activity>
|
||||
|
||||
<uses-feature android:name="android.hardware.usb.host" />
|
||||
<activity android:name="ObjectBrowser" android:label="@string/object_browser_name" />
|
||||
<activity android:name="PfdActivity" android:label="PFD" />
|
||||
<activity android:name="Controller" android:label="@string/controller_name" />
|
||||
<activity android:name="Preferences" android:label="@string/preference_title" />
|
||||
<activity android:name="UAVLocation" android:label="@string/location_name" />
|
||||
<activity android:name="SystemAlarmActivity" android:label="System Alarms" />
|
||||
<activity android:name="TuningActivity" android:label="Tuning" />
|
||||
<activity android:name="ObjectEditor" android:label="ObjectEditor"
|
||||
android:theme="@android:style/Theme.Dialog" />
|
||||
<activity android:name="Logger" android:label="Logger"
|
||||
android:theme="@android:style/Theme.Dialog" />
|
||||
|
||||
|
||||
|
||||
<activity
|
||||
android:name="FragmentTester"
|
||||
android:label="FragmentTester" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/ic_logo"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.Holo" >
|
||||
|
||||
<!-- for map overlay -->
|
||||
<uses-library android:name="com.google.android.maps" />
|
||||
|
||||
<!-- Object browser - main activity at the moment -->
|
||||
<activity
|
||||
android:name="org.openpilot.androidgcs.HomePage"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- <intent-filter> -->
|
||||
<!-- <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> -->
|
||||
<!-- </intent-filter> -->
|
||||
|
||||
|
||||
<!-- <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" -->
|
||||
<!-- android:resource="@xml/device_filter" /> -->
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="ObjectBrowser"
|
||||
android:label="@string/object_browser_name" />
|
||||
<activity
|
||||
android:name="PfdActivity"
|
||||
android:label="PFD" />
|
||||
<activity
|
||||
android:name="Controller"
|
||||
android:label="@string/controller_name" />
|
||||
<activity
|
||||
android:name="Preferences"
|
||||
android:label="@string/preference_title" />
|
||||
<activity
|
||||
android:name="UAVLocation"
|
||||
android:label="@string/location_name" />
|
||||
<activity
|
||||
android:name="SystemAlarmActivity"
|
||||
android:label="System Alarms" />
|
||||
<activity
|
||||
android:name="ObjectEditor"
|
||||
android:label="ObjectEditor"
|
||||
android:theme="@android:style/Theme.Dialog" />
|
||||
<activity
|
||||
android:name="Logger"
|
||||
android:label="Logger"
|
||||
android:theme="@android:style/Theme.Dialog" />
|
||||
<activity android:name="FragmentTester" android:label="FragmentTester" />
|
||||
<activity
|
||||
android:name="OsgViewer"
|
||||
android:label="3DView" />
|
||||
|
||||
<receiver android:name="TelemetryWidget" >
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.openpilot.intent.action.CONNECTED" />
|
||||
<action android:name="org.openpilot.intent.action.DISCONNECTED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/telemetry_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<service android:name="org.openpilot.androidgcs.telemetry.OPTelemetryService" >
|
||||
</service>
|
||||
</application>
|
||||
|
||||
<receiver android:name="TelemetryWidget">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.openpilot.intent.action.CONNECTED" />
|
||||
<action android:name="org.openpilot.intent.action.DISCONNECTED" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.appwidget.provider"
|
||||
android:resource="@xml/telemetry_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<service android:name="org.openpilot.androidgcs.telemetry.OPTelemetryService"></service>
|
||||
</application>
|
||||
</manifest>
|
Before Width: | Height: | Size: 823 B After Width: | Height: | Size: 849 B |
BIN
androidgcs/res/drawable-hdpi/ic_tuning.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 800 B After Width: | Height: | Size: 797 B |
BIN
androidgcs/res/drawable-hdpi/im_pfd_fixed.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 9.7 KiB |
BIN
androidgcs/res/drawable-hdpi/im_pfd_reticule.png
Normal file
After Width: | Height: | Size: 18 KiB |
138
androidgcs/res/layout-w600dp/gcs_home.xml
Normal file
@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_gravity="center_horizontal" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_controller"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="3"
|
||||
android:layout_gravity="right"
|
||||
android:layout_row="1"
|
||||
android:layout_rowSpan="4"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_controller"
|
||||
android:text="@string/controller_name" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_object_browser"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="0"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_row="2"
|
||||
android:layout_rowSpan="2"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_browser"
|
||||
android:text="@string/object_browser_name" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_location"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_row="2"
|
||||
android:layout_rowSpan="2"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_map"
|
||||
android:text="@string/location_name" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_tuning"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="4"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_row="2"
|
||||
android:layout_rowSpan="2"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_tuning"
|
||||
android:text="@string/tuning" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_logger"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="3"
|
||||
android:layout_gravity="left|bottom"
|
||||
android:layout_row="3"
|
||||
android:layout_rowSpan="3"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_logging"
|
||||
android:text="@string/logger_name" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_alarms"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="0"
|
||||
android:layout_gravity="left"
|
||||
android:layout_row="5"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_alarms"
|
||||
android:text="@string/alarms" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_pfd"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:layout_gravity="left"
|
||||
android:layout_row="5"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_pfd"
|
||||
android:text="@string/pfd_name" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_osgViewer"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="0"
|
||||
android:layout_gravity="right"
|
||||
android:layout_row="9"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_alarms"
|
||||
android:text="@string/_3dview" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_tester"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:layout_row="9"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_alarms"
|
||||
android:text="@string/tester" />
|
||||
|
||||
<Space
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_column="0"
|
||||
android:layout_row="0" />
|
||||
|
||||
<Space
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="47dp"
|
||||
android:layout_column="0"
|
||||
android:layout_row="4" />
|
||||
|
||||
<Space
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="47dp"
|
||||
android:layout_column="0"
|
||||
android:layout_row="8" />
|
||||
|
||||
<Space
|
||||
android:layout_height="15dp"
|
||||
android:layout_column="1" />
|
||||
|
||||
<Space
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="2dp"
|
||||
android:layout_row="6" />
|
||||
|
||||
</GridLayout>
|
103
androidgcs/res/layout-w600dp/tuning.xml
Normal file
@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:setting_attributes="http://schemas.android.com/apk/res/org.openpilot.androidgcs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/saveBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text="@string/save" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/applyBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_toLeftOf="@+id/saveBtn"
|
||||
android:text="@string/apply" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true" >
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/rollRateKp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
setting_attributes:max_value="0.01"
|
||||
setting_attributes:setting_name="Roll Rate Kp" >
|
||||
</org.openpilot.androidgcs.views.ScrollBarView>
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/pitchRateKp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
setting_attributes:max_value="0.01"
|
||||
setting_attributes:setting_name="Pitch Rate Kp" >
|
||||
</org.openpilot.androidgcs.views.ScrollBarView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_below="@+id/linearLayout3" >
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/rollRateKi"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
setting_attributes:max_value="0.05"
|
||||
setting_attributes:setting_name="Roll Rate Ki" >
|
||||
</org.openpilot.androidgcs.views.ScrollBarView>
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/pitchRateKi"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
setting_attributes:max_value="0.05"
|
||||
setting_attributes:setting_name="Pitch Rate Ki" >
|
||||
</org.openpilot.androidgcs.views.ScrollBarView>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_below="@+id/linearLayout1" >
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/rollKp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
setting_attributes:max_value="5"
|
||||
setting_attributes:setting_name="Roll Kp" >
|
||||
</org.openpilot.androidgcs.views.ScrollBarView>
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/pitchKp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
setting_attributes:max_value="5"
|
||||
setting_attributes:setting_name="Pitch Kp" >
|
||||
</org.openpilot.androidgcs.views.ScrollBarView>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -81,25 +81,37 @@
|
||||
android:id="@+id/launch_tester"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_row="6"
|
||||
android:layout_row="8"
|
||||
android:layout_column="0"
|
||||
android:layout_rowSpan="2"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_alarms"
|
||||
android:text="@string/tester" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_osgViewer"
|
||||
<Button
|
||||
android:id="@+id/launch_osgViewer"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="1"
|
||||
android:layout_gravity="right"
|
||||
android:layout_row="8"
|
||||
android:layout_rowSpan="2"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_alarms"
|
||||
android:text="@string/_3dview" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/launch_tuning"
|
||||
android:layout_width="132dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="1"
|
||||
android:layout_column="0"
|
||||
android:layout_gravity="right"
|
||||
android:layout_row="6"
|
||||
android:layout_rowSpan="2"
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableTop="@drawable/ic_alarms"
|
||||
android:text="@string/_3dview" />
|
||||
|
||||
android:drawableTop="@drawable/ic_tuning"
|
||||
android:text="@string/tuning" />
|
||||
|
||||
<Space
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="32dp"
|
||||
|
@ -23,14 +23,14 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="Settings" />
|
||||
android:text="@string/settings" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/dataCheck"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="Data" />
|
||||
android:text="@string/data" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:background="#FFFFFFFF" />
|
||||
|
||||
<LinearLayout
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
@ -66,40 +66,29 @@
|
||||
android:id="@+id/object_information"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Button
|
||||
android:id="@+id/editButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text="@string/edit" />
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Button
|
||||
android:id="@+id/object_load_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_toLeftOf="@+id/editButton"
|
||||
android:text="@string/load" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
</RelativeLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/object_load_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Load" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/editButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:text="Edit" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
83
androidgcs/res/layout/tuning.xml
Normal file
@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:setting_attributes="http://schemas.android.com/apk/res/org.openpilot.androidgcs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/rollRateKp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
setting_attributes:setting_name="Roll Rate Kp"
|
||||
setting_attributes:max_value="0.01" />
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/pitchRateKp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
setting_attributes:setting_name="Pitch Rate Kp"
|
||||
setting_attributes:max_value="0.01" />
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/rollRateKi"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
setting_attributes:setting_name="Roll Rate Ki"
|
||||
setting_attributes:max_value="0.05" />
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/pitchRateKi"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
setting_attributes:setting_name="Pitch Rate Ki"
|
||||
setting_attributes:max_value="0.05" />
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/rollKp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
setting_attributes:setting_name="Roll Kp"
|
||||
setting_attributes:max_value="5" />
|
||||
|
||||
<org.openpilot.androidgcs.views.ScrollBarView
|
||||
android:id="@+id/pitchKp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
setting_attributes:setting_name="Pitch Kp"
|
||||
setting_attributes:max_value="5" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/applyBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/apply" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/saveBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/save" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -14,4 +14,12 @@
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
<string-array name="controllerTypeArray">
|
||||
<item>Mode 1</item>
|
||||
<item>Mode 2</item>
|
||||
</string-array>
|
||||
<string-array name="controllerTypeValues">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
</resources>
|
7
androidgcs/res/values/attrs.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="setting_attributes">
|
||||
<attr name="setting_name" format="string"/>
|
||||
<attr name="max_value" format="float"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
@ -34,4 +34,11 @@
|
||||
<string name="rxrate">RxRate: </string>
|
||||
<string name="tester">Tester</string>
|
||||
<string name="_3dview">3DView</string>
|
||||
<string name="tuning">Tuning</string>
|
||||
<string name="apply">Apply</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="data">Data</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="load">Load</string>
|
||||
</resources>
|
||||
|
10
androidgcs/res/xml/controller_preferences.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<PreferenceCategory android:title="Controller Settings">
|
||||
<ListPreference android:title="Controller Mode"
|
||||
android:key="controller_type" android:summary="Select whether you want the virtual controller to use mode 1 or mode 2 convention"
|
||||
android:defaultValue="Mode 1"
|
||||
android:entries="@array/controllerTypeArray"
|
||||
android:entryValues="@array/controllerTypeValues"/>
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
@ -56,29 +56,64 @@ public class AttitudeView extends View {
|
||||
|
||||
}
|
||||
|
||||
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int measuredWidth = measure(widthMeasureSpec);
|
||||
int measuredHeight = measure(heightMeasureSpec);
|
||||
int d = Math.min(measuredWidth, measuredHeight);
|
||||
setMeasuredDimension(d/2, d/2);
|
||||
}
|
||||
/**
|
||||
* @see android.view.View#measure(int, int)
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(measureWidth(widthMeasureSpec),
|
||||
measureHeight(heightMeasureSpec));
|
||||
}
|
||||
|
||||
private int measure(int measureSpec) {
|
||||
int result = 0;
|
||||
// Decode the measurement specifications.
|
||||
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
/**
|
||||
* Determines the height of this view
|
||||
* @param measureSpec A measureSpec packed into an int
|
||||
* @return The height of the view, honoring constraints from measureSpec
|
||||
*/
|
||||
private int measureHeight(int measureSpec) {
|
||||
int result = 0;
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
|
||||
if (specMode == MeasureSpec.UNSPECIFIED) { // Return a default size of 200 if no bounds are specified.
|
||||
result = 200;
|
||||
} else {
|
||||
// As you want to fill the available space
|
||||
// always return the full available bounds.
|
||||
result = specSize;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (specMode == MeasureSpec.EXACTLY) {
|
||||
// We were told how big to be
|
||||
result = specSize;
|
||||
} else {
|
||||
// Measure the text (beware: ascent is a negative number)
|
||||
result = 1600;
|
||||
if (specMode == MeasureSpec.AT_MOST) {
|
||||
// Respect AT_MOST value if that was what is called for by measureSpec
|
||||
result = Math.min(result, specSize);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the width of this view
|
||||
* @param measureSpec A measureSpec packed into an int
|
||||
* @return The width of the view, honoring constraints from measureSpec
|
||||
*/
|
||||
private int measureWidth(int measureSpec) {
|
||||
int result = 0;
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
|
||||
if (specMode == MeasureSpec.EXACTLY) {
|
||||
// We were told how big to be
|
||||
result = specSize;
|
||||
} else {
|
||||
// Measure the text
|
||||
result = 800;
|
||||
if (specMode == MeasureSpec.AT_MOST) {
|
||||
// Respect AT_MOST value if that was what is called for by measureSpec
|
||||
result = Math.min(result, specSize);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private float roll;
|
||||
public void setRoll(double roll) {
|
||||
@ -101,25 +136,35 @@ public class AttitudeView extends View {
|
||||
|
||||
canvas.save();
|
||||
canvas.rotate(-roll, PX / 2, PY / 2);
|
||||
canvas.save();
|
||||
|
||||
canvas.translate(0, pitch * DEG_TO_PX);
|
||||
Drawable horizon = getContext().getResources().getDrawable(
|
||||
R.drawable.im_pfd_horizon);
|
||||
Drawable reticule = getContext().getResources().getDrawable(
|
||||
R.drawable.im_pfd_reticule);
|
||||
Drawable fixed = getContext().getResources().getDrawable(
|
||||
R.drawable.im_pfd_fixed);
|
||||
|
||||
// Starting with a square image, want to size it equally
|
||||
double margin = 0.2;
|
||||
int screenSize = Math.min(PX, PY);
|
||||
int imageHalfSize = (int) ((screenSize + screenSize * margin) / 2);
|
||||
|
||||
// This puts the image at the center of the PFD canvas (after it was
|
||||
// translated)
|
||||
double margin = 0.5;
|
||||
horizon.setBounds( (int) (-margin * PX), (int) (-margin * PY), (int) ((1 + margin) * PX), (int) ((1+margin) *PY));
|
||||
horizon.setBounds( PX/2 - imageHalfSize, PY/2 - imageHalfSize, PX/2 + imageHalfSize, PY/2 + imageHalfSize);
|
||||
horizon.draw(canvas);
|
||||
canvas.restore();
|
||||
|
||||
canvas.drawLine(0, 0, PX, 0, markerPaint);
|
||||
canvas.drawLine(0, 0, 0, PY, markerPaint);
|
||||
canvas.drawLine(PX, 0, PX, PY, markerPaint);
|
||||
canvas.drawLine(0, PY, PX, PY, markerPaint);
|
||||
// Draw the overlay that only rolls
|
||||
reticule.setBounds( PX/2 - imageHalfSize, PY/2 - imageHalfSize, PX/2 + imageHalfSize, PY/2 + imageHalfSize);
|
||||
reticule.draw(canvas);
|
||||
canvas.restore();
|
||||
|
||||
canvas.drawLine(0,PY/2,PX,PY/2,markerPaint);
|
||||
canvas.drawLine(PX/2,0,PX/2,PY,markerPaint);
|
||||
|
||||
}
|
||||
// Draw the overlay that never moves
|
||||
fixed.setBounds( PX/2 - imageHalfSize, PY/2 - imageHalfSize, PX/2 + imageHalfSize, PY/2 + imageHalfSize);
|
||||
fixed.draw(canvas);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,10 +44,7 @@ public class BluetoothDevicePreference extends ListPreference {
|
||||
Set<BluetoothDevice> pairedDevices = bta.getBondedDevices();
|
||||
CharSequence[] entries = new CharSequence[pairedDevices.size()];
|
||||
CharSequence[] entryValues = new CharSequence[pairedDevices.size()];
|
||||
if (pairedDevices.size() == 0) {
|
||||
entries[0] = "No Devices";
|
||||
entryValues[0] = "";
|
||||
} else {
|
||||
if (pairedDevices.size() > 0) {
|
||||
int i = 0;
|
||||
for (BluetoothDevice dev : pairedDevices) {
|
||||
entries[i] = dev.getName();
|
||||
|
@ -34,7 +34,9 @@ import org.openpilot.uavtalk.UAVDataObject;
|
||||
import org.openpilot.uavtalk.UAVObject;
|
||||
import org.openpilot.uavtalk.UAVObjectField;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@ -76,7 +78,7 @@ public class Controller extends ObjectManagerActivity {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
// Once we have updated settings we can active the GCS receiver mode
|
||||
Log.d(TAG,"Got update from settings");
|
||||
if (DEBUG) Log.d(TAG,"Got update from settings");
|
||||
UAVDataObject manualControlSettings = (UAVDataObject) objMngr.getObject("ManualControlSettings");
|
||||
if(manualControlSettings != null) {
|
||||
manualControlSettings.removeUpdatedObserver(this);
|
||||
@ -89,7 +91,27 @@ public class Controller extends ObjectManagerActivity {
|
||||
void onOPConnected() {
|
||||
super.onOPConnected();
|
||||
|
||||
Log.d(TAG, "onOPConnected()");
|
||||
if (DEBUG) Log.d(TAG, "onOPConnected()");
|
||||
|
||||
DualJoystickView joystick = (DualJoystickView) findViewById(R.id.dualjoystickView);
|
||||
joystick.setMovementConstraint(JoystickView.CONSTRAIN_BOX);
|
||||
joystick.setMovementRange((int)MOVEMENT_RANGE, (int)MOVEMENT_RANGE);
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
int mode = Integer.decode(prefs.getString("controller_type", "1"));
|
||||
switch(mode) {
|
||||
case 1:
|
||||
if (DEBUG) Log.d(TAG, "Mode1 connected");
|
||||
joystick.setOnJostickMovedListener(mode1_left, mode1_right);
|
||||
break;
|
||||
case 2:
|
||||
if (DEBUG) Log.d(TAG, "Mode2 connected");
|
||||
joystick.setOnJostickMovedListener(mode2_left, mode2_right);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown controller type");
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to updates from ManualControlCommand and show the values for crude feedback
|
||||
UAVDataObject manualControl = (UAVDataObject) objMngr.getObject("ManualControlCommand");
|
||||
@ -100,39 +122,6 @@ public class Controller extends ObjectManagerActivity {
|
||||
manualSettings.addUpdatedObserver(settingsUpdated);
|
||||
manualSettings.updateRequested();
|
||||
|
||||
final double MOVEMENT_RANGE = 50.0;
|
||||
DualJoystickView joystick = (DualJoystickView) findViewById(R.id.dualjoystickView);
|
||||
joystick.setMovementConstraint(JoystickView.CONSTRAIN_BOX);
|
||||
joystick.setMovementRange((int)MOVEMENT_RANGE, (int)MOVEMENT_RANGE);
|
||||
|
||||
// Hardcode a Mode 1 listener for now
|
||||
joystick.setOnJostickMovedListener(new JoystickMovedListener() {
|
||||
@Override
|
||||
public void OnMoved(int pan, int tilt) {
|
||||
pitch = tilt / MOVEMENT_RANGE;
|
||||
yaw = pan / MOVEMENT_RANGE;
|
||||
leftJoystickHeld = true;
|
||||
}
|
||||
@Override
|
||||
public void OnReleased() { leftJoystickHeld = false; throttle = -1; updated = true; }
|
||||
@Override
|
||||
public void OnReturnedToCenter() { }
|
||||
}, new JoystickMovedListener() {
|
||||
@Override
|
||||
public void OnMoved(int pan, int tilt) {
|
||||
throttle = (-tilt + (MOVEMENT_RANGE -5)) / (MOVEMENT_RANGE - 5);
|
||||
throttle *= 0.5;
|
||||
if (throttle < 0)
|
||||
throttle = -1;
|
||||
roll = pan / MOVEMENT_RANGE;
|
||||
rightJoystickHeld = true;
|
||||
}
|
||||
@Override
|
||||
public void OnReleased() { rightJoystickHeld = false; throttle = -1; updated = true; }
|
||||
@Override
|
||||
public void OnReturnedToCenter() { }
|
||||
});
|
||||
|
||||
//! This timer task actually periodically sends updates to the UAV
|
||||
TimerTask controllerTask = new TimerTask() {
|
||||
@Override
|
||||
@ -248,4 +237,64 @@ public class Controller extends ObjectManagerActivity {
|
||||
return (float) (neutral + (neutral - CHANNEL_MIN) * in);
|
||||
}
|
||||
|
||||
private final JoystickMovedListener mode1_left = new JoystickMovedListener() {
|
||||
@Override
|
||||
public void OnMoved(int pan, int tilt) {
|
||||
pitch = tilt / MOVEMENT_RANGE;
|
||||
yaw = pan / MOVEMENT_RANGE;
|
||||
leftJoystickHeld = true;
|
||||
}
|
||||
@Override
|
||||
public void OnReleased() { leftJoystickHeld = false; throttle = -1; updated = true; }
|
||||
@Override
|
||||
public void OnReturnedToCenter() { }
|
||||
};
|
||||
|
||||
private final JoystickMovedListener mode1_right = new JoystickMovedListener() {
|
||||
@Override
|
||||
public void OnMoved(int pan, int tilt) {
|
||||
throttle = (-tilt + (MOVEMENT_RANGE -5)) / (MOVEMENT_RANGE - 5);
|
||||
throttle *= 0.5;
|
||||
if (throttle < 0)
|
||||
throttle = -1;
|
||||
roll = pan / MOVEMENT_RANGE;
|
||||
rightJoystickHeld = true;
|
||||
}
|
||||
@Override
|
||||
public void OnReleased() { rightJoystickHeld = false; throttle = -1; updated = true; }
|
||||
@Override
|
||||
public void OnReturnedToCenter() { }
|
||||
};
|
||||
|
||||
private final JoystickMovedListener mode2_left = new JoystickMovedListener() {
|
||||
@Override
|
||||
public void OnMoved(int pan, int tilt) {
|
||||
throttle = (-tilt + (MOVEMENT_RANGE -5)) / (MOVEMENT_RANGE - 5);
|
||||
throttle *= 0.5;
|
||||
if (throttle < 0)
|
||||
throttle = -1;
|
||||
yaw = pan / MOVEMENT_RANGE;
|
||||
leftJoystickHeld = true;
|
||||
}
|
||||
@Override
|
||||
public void OnReleased() { leftJoystickHeld = false; throttle = -1; updated = true; }
|
||||
@Override
|
||||
public void OnReturnedToCenter() { }
|
||||
};
|
||||
|
||||
private final JoystickMovedListener mode2_right = new JoystickMovedListener() {
|
||||
@Override
|
||||
public void OnMoved(int pan, int tilt) {
|
||||
pitch = tilt / MOVEMENT_RANGE;
|
||||
roll = pan / MOVEMENT_RANGE;
|
||||
rightJoystickHeld = true;
|
||||
}
|
||||
@Override
|
||||
public void OnReleased() { rightJoystickHeld = false; throttle = -1; updated = true; }
|
||||
@Override
|
||||
public void OnReturnedToCenter() { }
|
||||
};
|
||||
|
||||
final double MOVEMENT_RANGE = 50.0;
|
||||
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ public class HomePage extends ObjectManagerActivity {
|
||||
startActivity(new Intent(HomePage.this, FragmentTester.class));
|
||||
}
|
||||
});
|
||||
|
||||
Button osgViewer = (Button) findViewById(R.id.launch_osgViewer);
|
||||
osgViewer.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
@ -99,6 +100,14 @@ public class HomePage extends ObjectManagerActivity {
|
||||
}
|
||||
});
|
||||
|
||||
Button tuning = (Button) findViewById(R.id.launch_tuning);
|
||||
tuning.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
startActivity(new Intent(HomePage.this, TuningActivity.class));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ public class Preferences extends PreferenceActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
// Load the preferences from an XML resource
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
addPreferencesFromResource(R.xml.controller_preferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
47
androidgcs/src/org/openpilot/androidgcs/TuningActivity.java
Normal file
@ -0,0 +1,47 @@
|
||||
package org.openpilot.androidgcs;
|
||||
|
||||
import org.openpilot.androidgcs.util.SmartSave;
|
||||
import org.openpilot.androidgcs.views.ScrollBarView;
|
||||
import org.openpilot.uavtalk.UAVDataObject;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
|
||||
public class TuningActivity extends ObjectManagerActivity {
|
||||
private final String TAG = TuningActivity.class.getSimpleName();
|
||||
|
||||
private final boolean DEBUG = false;
|
||||
|
||||
private SmartSave smartSave;
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.tuning);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onOPConnected() {
|
||||
super.onOPConnected();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "onOPConnected()");
|
||||
|
||||
// Subscribe to updates from ManualControlCommand and show the values for crude feedback
|
||||
UAVDataObject stabilizationSettings = (UAVDataObject) objMngr.getObject("StabilizationSettings");
|
||||
|
||||
smartSave = new SmartSave(objMngr, stabilizationSettings,
|
||||
(Button) findViewById(R.id.saveBtn),
|
||||
(Button) findViewById(R.id.applyBtn));
|
||||
|
||||
smartSave.addControlMapping((ScrollBarView) findViewById(R.id.rollRateKp), "RollRatePID", 0);
|
||||
smartSave.addControlMapping((ScrollBarView) findViewById(R.id.rollRateKi), "RollRatePID", 1);
|
||||
smartSave.addControlMapping((ScrollBarView) findViewById(R.id.pitchRateKp), "PitchRatePID", 0);
|
||||
smartSave.addControlMapping((ScrollBarView) findViewById(R.id.pitchRateKi), "PitchRatePID", 1);
|
||||
smartSave.addControlMapping((ScrollBarView) findViewById(R.id.rollKp), "RollPI", 0);
|
||||
smartSave.addControlMapping((ScrollBarView) findViewById(R.id.pitchKp), "PitchPI", 0);
|
||||
smartSave.refreshSettingsDisplay();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package org.openpilot.androidgcs.util;
|
||||
|
||||
public interface ObjectFieldMappable {
|
||||
public double getValue();
|
||||
public void setValue(double val);
|
||||
}
|
256
androidgcs/src/org/openpilot/androidgcs/util/SmartSave.java
Normal file
@ -0,0 +1,256 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file SmartSave.java
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
|
||||
* @brief Provides a handler to provide robust apply and save for settings
|
||||
* and updating of the UI when the object changes.
|
||||
* @see The GNU Public License (GPL) Version 3
|
||||
*
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package org.openpilot.androidgcs.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.openpilot.uavtalk.UAVObject;
|
||||
import org.openpilot.uavtalk.UAVObjectManager;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
|
||||
public class SmartSave {
|
||||
|
||||
private final static String TAG = SmartSave.class.getSimpleName();
|
||||
private final static boolean DEBUG = false;
|
||||
|
||||
//! Create a smart save button attached to the object manager and an apply and ave button
|
||||
public SmartSave(UAVObjectManager objMngr, UAVObject obj, Button saveButton, Button applyButton) {
|
||||
Assert.assertNotNull(objMngr);
|
||||
this.objMngr = objMngr;
|
||||
this.applyBtn = applyButton;
|
||||
this.obj = obj;
|
||||
|
||||
Assert.assertNotNull(objMngr);
|
||||
Assert.assertNotNull(obj);
|
||||
|
||||
obj.addUpdatedObserver(ObjectUpdated);
|
||||
|
||||
controlFieldMapping = new HashMap<ObjectFieldMappable,FieldPairing>();
|
||||
|
||||
if (saveButton != null) {
|
||||
saveBtn = saveButton;
|
||||
saveBtn.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
saveSettings();
|
||||
}
|
||||
});
|
||||
} else
|
||||
saveBtn = null;
|
||||
|
||||
if (applyButton != null) {
|
||||
applyBtn = applyButton;
|
||||
applyBtn.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
applySettings();
|
||||
}
|
||||
});
|
||||
} else
|
||||
applyBtn = null;
|
||||
}
|
||||
|
||||
//! Disconnect any listeners when this object is destroyed
|
||||
public void disconnect() {
|
||||
obj.removeUpdatedObserver(ObjectUpdated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a control to this SmartSave object which maps between a particular control
|
||||
* and a UAVO field.
|
||||
* @param control The control to associate with
|
||||
* @param fieldName The name of the UAVO field
|
||||
* @param fieldIndex The index of the UAVO field
|
||||
*/
|
||||
public void addControlMapping(ObjectFieldMappable control, String fieldName, int fieldIndex) {
|
||||
FieldPairing pairing = new FieldPairing(fieldName, fieldIndex);
|
||||
controlFieldMapping.put(control, pairing);
|
||||
}
|
||||
|
||||
//! Update the settings in the UI from the mappings
|
||||
public void refreshSettingsDisplay() {
|
||||
if (DEBUG) Log.d(TAG, "Refreshing display");
|
||||
|
||||
Set<ObjectFieldMappable> keys = controlFieldMapping.keySet();
|
||||
Iterator<ObjectFieldMappable> iter = keys.iterator();
|
||||
while(iter.hasNext()) {
|
||||
ObjectFieldMappable mappable = iter.next();
|
||||
FieldPairing field = controlFieldMapping.get(mappable);
|
||||
mappable.setValue(field.getValue(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call applySettings() and then save them
|
||||
* @return True if the save succeeded or false otherwise
|
||||
*/
|
||||
private boolean saveSettings() {
|
||||
/*
|
||||
* 1. Update object
|
||||
* 2. Install listener on object persistence
|
||||
* 3. Send save operation
|
||||
* 4. Wait for completed
|
||||
* 5. Remove listener
|
||||
*/
|
||||
|
||||
// 1. Update object
|
||||
if(!applySettings())
|
||||
return false;
|
||||
|
||||
UAVObject persistence = objMngr.getObject("ObjectPersistence");
|
||||
Assert.assertNotNull(persistence);
|
||||
|
||||
// 2. Install listener
|
||||
persistence.addUpdatedObserver(ObjectPersistenceUpdated);
|
||||
|
||||
// 3. Send save operation
|
||||
Long objId = obj.getObjID();
|
||||
if (DEBUG) Log.d(TAG, "Saving object ID: " + objId);
|
||||
persistence.getField("ObjectID").setValue(objId);
|
||||
persistence.getField("Operation").setValue("Save");
|
||||
persistence.getField("Selection").setValue("SingleObject");
|
||||
persistence.updated();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Robustly apply the settings to the UAV
|
||||
* @return True if the apply is ack'd, False if not
|
||||
*/
|
||||
private boolean applySettings() {
|
||||
/*
|
||||
* 1. Set the values from the fields into the object
|
||||
* 2. Install the listener on the object
|
||||
* 3. Update object
|
||||
* 4. Wait for the acknowledgment or timeout
|
||||
* 5. Uninstall the listener
|
||||
*/
|
||||
|
||||
// 1. Set the fields in the object from the UI
|
||||
Set<ObjectFieldMappable> keys = controlFieldMapping.keySet();
|
||||
Iterator<ObjectFieldMappable> iter = keys.iterator();
|
||||
while(iter.hasNext()) {
|
||||
ObjectFieldMappable mappable = iter.next();
|
||||
FieldPairing field = controlFieldMapping.get(mappable);
|
||||
field.setValue(obj,mappable.getValue());
|
||||
}
|
||||
|
||||
// 2. Install the listener on the object
|
||||
obj.addTransactionCompleted(ApplyCompleted);
|
||||
|
||||
// 3. Update the object
|
||||
obj.updated();
|
||||
|
||||
// 4. Wait for acknowledgment
|
||||
// TODO: Set up some semaphore with timeout
|
||||
// sem.wait(1000);
|
||||
|
||||
// 5. Uninstall the listener
|
||||
//obj.removeTransactionCompleted(ApplyCompleted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Private class to store the field mapping information
|
||||
private class FieldPairing {
|
||||
FieldPairing(String fieldName, int fieldIndex) {
|
||||
this.fieldName = fieldName;
|
||||
this.fieldIndex = fieldIndex;
|
||||
}
|
||||
|
||||
//! Update the field in the UAVO
|
||||
void setValue(UAVObject obj, double value) {
|
||||
Assert.assertNotNull(obj);
|
||||
obj.getField(fieldName).setDouble(value,fieldIndex);
|
||||
}
|
||||
|
||||
//! Get the value from the UAVO field
|
||||
double getValue(UAVObject obj) {
|
||||
double val = obj.getField(fieldName).getDouble(fieldIndex);
|
||||
if (DEBUG) Log.d(TAG, "Getting value from: " + fieldName + " " + val);
|
||||
return obj.getField(fieldName).getDouble(fieldIndex);
|
||||
}
|
||||
|
||||
//! Cache the name of the field
|
||||
private final String fieldName;
|
||||
|
||||
//! Cache the field index
|
||||
private final int fieldIndex;
|
||||
}
|
||||
|
||||
//! Installed on monitored object to know when an object is updated
|
||||
private final Observer ApplyCompleted = new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
if (DEBUG) Log.d(TAG, "Apply called");
|
||||
}
|
||||
};
|
||||
|
||||
//! Installed on monitored object to know when an object is updated
|
||||
private final Observer ObjectUpdated = new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
if (DEBUG) Log.d(TAG, "Object updated");
|
||||
refreshSettingsDisplay();
|
||||
}
|
||||
};
|
||||
|
||||
//! Installed on object persistence to know when save completes
|
||||
private final Observer ObjectPersistenceUpdated = new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
if (DEBUG) Log.d(TAG, "Object persistence updated");
|
||||
}
|
||||
};
|
||||
|
||||
//! Map of all the UAVO field <-> control mappings. The key is the control.
|
||||
private final Map<ObjectFieldMappable,FieldPairing> controlFieldMapping;
|
||||
|
||||
//! Handle to the object manager
|
||||
private final UAVObjectManager objMngr;
|
||||
|
||||
//! Handle to the apply button
|
||||
private Button applyBtn;
|
||||
|
||||
//! Handle to the save button
|
||||
private Button saveBtn;
|
||||
|
||||
//! Handle to the UAVO this class works with
|
||||
private final UAVObject obj;
|
||||
|
||||
}
|
129
androidgcs/src/org/openpilot/androidgcs/views/ScrollBarView.java
Normal file
@ -0,0 +1,129 @@
|
||||
package org.openpilot.androidgcs.views;
|
||||
|
||||
import org.openpilot.androidgcs.R;
|
||||
import org.openpilot.androidgcs.util.ObjectFieldMappable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.EditText;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ScrollBarView extends GridLayout implements ObjectFieldMappable {
|
||||
|
||||
private final static String TAG = ScrollBarView.class.getSimpleName();
|
||||
|
||||
private final TextView lbl;
|
||||
private final EditText edit;
|
||||
private final SeekBar bar;
|
||||
private double value;
|
||||
private String name;
|
||||
|
||||
private final double SCALE = 1000000;
|
||||
|
||||
public ScrollBarView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
Log.d(TAG, "Scroll bar init called");
|
||||
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
setColumnCount(2);
|
||||
|
||||
lbl = new TextView(context);
|
||||
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.setting_attributes, 0, 0);
|
||||
lbl.setText(ta.getString(R.styleable.setting_attributes_setting_name));
|
||||
addView(lbl, new GridLayout.LayoutParams(spec(0), spec(0)));
|
||||
|
||||
edit = new EditText(context);
|
||||
addView(edit, new GridLayout.LayoutParams(spec(0), spec(1)));
|
||||
|
||||
bar = new SeekBar(context);
|
||||
addView(bar, new GridLayout.LayoutParams(spec(1), spec(0,2)));
|
||||
|
||||
ta = context.obtainStyledAttributes(attrs, R.styleable.setting_attributes, 0, 0);
|
||||
final double max = ta.getFloat(R.styleable.setting_attributes_max_value,0);
|
||||
bar.setMax((int) (SCALE * max));
|
||||
|
||||
// Update the value when the progress bar changes
|
||||
bar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress,
|
||||
boolean fromUser) {
|
||||
value = progress / SCALE;
|
||||
edit.setText(Double.toString(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
|
||||
// Update the value when the edit box changes
|
||||
edit.addTextChangedListener(new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
value = Double.parseDouble(s.toString());
|
||||
bar.setProgress((int) (SCALE * value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
}
|
||||
});
|
||||
|
||||
setPadding(5,5,5,5);
|
||||
|
||||
setMinimumWidth(300);
|
||||
setValue(0.0035);
|
||||
}
|
||||
|
||||
public void setName(String n)
|
||||
{
|
||||
name = n;
|
||||
lbl.setText(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
// This shouldn't be needed if I could make this scroll bar
|
||||
// automagically span both columns
|
||||
android.view.ViewGroup.LayoutParams param = bar.getLayoutParams();
|
||||
param.width = (int) (getMeasuredWidth() * 0.9);
|
||||
|
||||
// Force the label to half the page width
|
||||
param = lbl.getLayoutParams();
|
||||
param.width = getMeasuredWidth() / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(double val) {
|
||||
value = val;
|
||||
edit.setText(Double.toString(value));
|
||||
bar.setProgress((int) (SCALE * value));
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 823 B After Width: | Height: | Size: 849 B |
BIN
artwork/Android/hdpi/ic_tuning.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
artwork/Android/hdpi/im_pfd_fixed.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 9.7 KiB |
BIN
artwork/Android/hdpi/im_pfd_reticule.png
Normal file
After Width: | Height: | Size: 18 KiB |