├── Makefile
├── README.md
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── follower
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── stop.png
│ │ │ └── follow.png
│ │ ├── drawable-hdpi
│ │ │ └── icon.png
│ │ ├── drawable-ldpi
│ │ │ └── icon.png
│ │ ├── drawable-mdpi
│ │ │ └── icon.png
│ │ ├── drawable-xhdpi
│ │ │ └── icon.png
│ │ ├── values
│ │ │ └── strings.xml
│ │ ├── layout-land
│ │ │ └── main.xml
│ │ └── layout
│ │ │ └── main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── turtlebot
│ │ └── turtlebot_android
│ │ └── follower
│ │ └── FollowerActivity.java
└── build.gradle
├── panorama
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ └── icon.png
│ │ ├── drawable-ldpi
│ │ │ └── icon.png
│ │ ├── drawable-mdpi
│ │ │ └── icon.png
│ │ ├── drawable-xhdpi
│ │ │ └── icon.png
│ │ ├── drawable
│ │ │ └── default_image.jpg
│ │ ├── values
│ │ │ └── strings.xml
│ │ ├── layout-port
│ │ │ └── main.xml
│ │ └── layout-land
│ │ │ └── main.xml
│ │ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── turtlebot
│ │ │ └── turtlebot_android
│ │ │ └── panorama
│ │ │ ├── ScaledBitmapFromCompressedImage.java
│ │ │ └── PanoramaActivity.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── CMakeLists.txt
├── .gitignore
├── turtlebot_android.rosinstall
├── package.xml
├── gradlew.bat
└── gradlew
/Makefile:
--------------------------------------------------------------------------------
1 | include $(shell rospack find mk)/cmake_stack.mk
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | turtlebot_android
2 | =================
3 |
4 | Android app development for the turtlebot.
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | *include 'panorama'
3 | *include 'follower'
4 | */
5 |
6 | include 'panorama'
7 | include 'follower'
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/follower/src/main/res/drawable/stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/follower/src/main/res/drawable/stop.png
--------------------------------------------------------------------------------
/follower/src/main/res/drawable/follow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/follower/src/main/res/drawable/follow.png
--------------------------------------------------------------------------------
/follower/src/main/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/follower/src/main/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/follower/src/main/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/follower/src/main/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/follower/src/main/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/follower/src/main/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/follower/src/main/res/drawable-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/follower/src/main/res/drawable-xhdpi/icon.png
--------------------------------------------------------------------------------
/panorama/src/main/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/panorama/src/main/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/panorama/src/main/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/panorama/src/main/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/panorama/src/main/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/panorama/src/main/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/panorama/src/main/res/drawable-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/panorama/src/main/res/drawable-xhdpi/icon.png
--------------------------------------------------------------------------------
/panorama/src/main/res/drawable/default_image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turtlebot/turtlebot_android/HEAD/panorama/src/main/res/drawable/default_image.jpg
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.3)
2 | project(turtlebot_android)
3 |
4 | find_package(catkin REQUIRED rosjava_build_tools)
5 | ####find_package(catkin REQUIRED uploadArchives)
6 |
7 | catkin_android_setup(assemble uploadArchives)
8 |
9 | catkin_package()
10 |
11 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jul 02 16:34:33 KST 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | gen
3 | libs
4 | build.xml
5 | local.properties
6 | proguard-project.txt
7 | .gradle
8 | build
9 | .project
10 | .classpath
11 |
12 | # These are Android Studio files, might be worth including these later.
13 | .idea
14 | *.iml
15 | build.log
16 | build-log.xml
17 | docs.iml
18 |
19 |
--------------------------------------------------------------------------------
/follower/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Turtlebot Follower
4 | Kill
5 |
6 | turtlebot_core_apps/follower
7 |
8 |
--------------------------------------------------------------------------------
/panorama/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Turtlebot Panorama
4 | Kill
5 |
6 | turtlebot_core_apps/panorama
7 |
8 |
--------------------------------------------------------------------------------
/turtlebot_android.rosinstall:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Source installation instructions at:
4 | #
5 | # http://www.ros.org/wiki/turtlebot_android/Tutorials/hydro/Installation
6 | #
7 | ##############################################################################
8 |
9 | - git:
10 | uri: https://github.com/turtlebot/turtlebot_android.git
11 | local-name: turtlebot_android
12 | version: hydro-devel
13 |
--------------------------------------------------------------------------------
/panorama/src/main/java/com/github/turtlebot/turtlebot_android/panorama/ScaledBitmapFromCompressedImage.java:
--------------------------------------------------------------------------------
1 | package com.github.turtlebot.turtlebot_android.panorama;
2 |
3 | import org.jboss.netty.buffer.ChannelBuffer;
4 | import org.ros.android.BitmapFromCompressedImage;
5 |
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 |
9 | public class ScaledBitmapFromCompressedImage extends BitmapFromCompressedImage
10 | {
11 | private int scaleFactor = 1;
12 |
13 | public ScaledBitmapFromCompressedImage(int scale)
14 | {
15 | scaleFactor = scale;
16 | }
17 |
18 | @Override
19 | public Bitmap call(sensor_msgs.CompressedImage message)
20 | {
21 | BitmapFactory.Options opt = new BitmapFactory.Options();
22 | opt.inSampleSize = scaleFactor;
23 |
24 | ChannelBuffer buffer = message.getData();
25 | byte[] data = buffer.array();
26 |
27 | return BitmapFactory.decodeByteArray(data, buffer.arrayOffset(), buffer.readableBytes(), opt);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | turtlebot_android
4 | 0.1.0
5 |
6 | Android applications and libraries for turtlebot.
7 |
8 | http://ros.org/wiki/turtlebot_android
9 | https://github.com/turtlebot/turtlebot_android
10 | https://github.com/turtlebot/turtlebot_android/issues
11 | Daniel Stonier
12 | Apache 2.0
13 |
14 | catkin
15 | rosjava_build_tools
16 | rosjava_bootstrap
17 | android_core
18 | android_apps
19 | android_extras
20 | rosjava_messages
21 |
26 |
27 |
--------------------------------------------------------------------------------
/follower/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Yujin Robot.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy of
6 | * the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations under
14 | * the License.
15 | */
16 |
17 | dependencies {
18 | compile 'com.github.rosjava.android_remocons:common_tools:[0.2,0.3)'
19 | compile 'org.ros.android_core:android_10:[0.2,0.3)]'
20 | compile 'org.ros.rosjava_core:rosjava:0.1.+'
21 | compile 'org.ros.rosjava_messages:turtlebot_msgs:2.2.+'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 |
26 | android {
27 | compileSdkVersion 15
28 |
29 | defaultConfig {
30 | minSdkVersion 15
31 | targetSdkVersion 15
32 | versionCode 1
33 | versionName "1.1.1"
34 | }
35 | productFlavors {
36 | indigo {
37 | applicationId "com.github.turtlebot.turtlebot_android.follower.indigo"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/panorama/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Yujin Robot.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy of
6 | * the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations under
14 | * the License.
15 | */
16 |
17 | dependencies {
18 | compile 'com.github.rosjava.android_remocons:common_tools:[0.2,0.3)'
19 | compile 'org.ros.android_core:android_10:[0.2,0.3)]'
20 | compile 'org.ros.rosjava_core:rosjava:0.1.+'
21 | compile 'org.ros.rosjava_messages:turtlebot_msgs:2.2.+'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 |
26 | android {
27 | compileSdkVersion 15
28 |
29 | defaultConfig {
30 | minSdkVersion 15
31 | targetSdkVersion 15
32 | versionCode 1
33 | versionName "1.1.0"
34 | }
35 | productFlavors {
36 | indigo {
37 | applicationId "com.github.turtlebot.turtlebot_android.panorama.indigo"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/panorama/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/follower/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/follower/src/main/res/layout-land/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
29 |
30 |
31 |
37 |
38 |
44 |
45 |
57 |
58 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/follower/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
29 |
30 |
31 |
36 |
37 |
43 |
44 |
56 |
57 |
68 |
69 |
73 |
74 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/panorama/src/main/res/layout-port/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
24 |
25 |
32 |
33 |
34 |
40 |
41 |
47 |
48 |
54 |
55 |
63 |
64 |
70 |
71 |
78 |
79 |
85 |
86 |
93 |
94 |
101 |
102 |
109 |
110 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/panorama/src/main/res/layout-land/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
21 |
22 |
28 |
29 |
36 |
37 |
38 |
43 |
44 |
55 |
56 |
67 |
68 |
80 |
81 |
94 |
95 |
108 |
109 |
122 |
123 |
136 |
137 |
150 |
151 |
165 |
166 |
167 |
168 |
175 |
176 |
--------------------------------------------------------------------------------
/follower/src/main/java/com/github/turtlebot/turtlebot_android/follower/FollowerActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.turtlebot.turtlebot_android.follower;
2 |
3 | import org.ros.address.InetAddressFactory;
4 | import org.ros.android.BitmapFromCompressedImage;
5 | import com.github.rosjava.android_remocons.common_tools.apps.RosAppActivity;
6 | import org.ros.android.view.RosImageView;
7 | import org.ros.exception.RemoteException;
8 | import org.ros.exception.ServiceNotFoundException;
9 | import org.ros.namespace.GraphName;
10 | import org.ros.namespace.NameResolver;
11 | import org.ros.node.AbstractNodeMain;
12 | import org.ros.node.ConnectedNode;
13 | import org.ros.node.NodeConfiguration;
14 | import org.ros.node.NodeMainExecutor;
15 | import org.ros.node.service.ServiceClient;
16 | import org.ros.node.service.ServiceResponseListener;
17 |
18 | import android.content.res.Configuration;
19 | import android.os.Bundle;
20 | import android.os.StrictMode;
21 | import android.util.Log;
22 | import android.view.Menu;
23 | import android.view.MenuItem;
24 | import android.view.View;
25 | import android.view.View.OnClickListener;
26 | import android.widget.Button;
27 | import android.widget.ImageButton;
28 | import android.widget.Toast;
29 |
30 | public class FollowerActivity extends RosAppActivity
31 | {
32 | private Toast lastToast;
33 | private ConnectedNode node;
34 | private RosImageView cameraView;
35 | private static final String cameraTopic = "camera/rgb/image_rect_color/compressed";
36 |
37 |
38 |
39 | public FollowerActivity()
40 | {
41 | super("FollowerActivity", "FollowerActivity");
42 | }
43 |
44 | /** Called when the activity is first created. */
45 | @Override
46 | public void onCreate(Bundle savedInstanceState)
47 | {
48 | setDefaultMasterName(getString(R.string.default_robot));
49 | setDefaultAppName(getString(R.string.default_app));
50 | setDashboardResource(R.id.top_bar);
51 | setMainWindowResource(R.layout.main);
52 |
53 | super.onCreate(savedInstanceState);
54 | cameraView = (RosImageView)findViewById(R.id.image);
55 | cameraView.setMessageType(sensor_msgs.CompressedImage._TYPE);
56 | cameraView.setMessageToBitmapCallable(new BitmapFromCompressedImage());
57 |
58 | buildView(cameraView, false);
59 |
60 | // TODO Tricky solution to the StrictMode; the recommended way is by using AsyncTask
61 | if (android.os.Build.VERSION.SDK_INT > 9) {
62 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
63 | StrictMode.setThreadPolicy(policy);
64 | }
65 | }
66 |
67 | @Override
68 | public void onConfigurationChanged(Configuration newConfig)
69 | {
70 | // TODO this is not called now, so we cannot flip the screen
71 | Log.e("FollowerActivity", "onConfigurationChanged");
72 | super.onConfigurationChanged(newConfig);
73 | setContentView(R.layout.main);
74 |
75 | buildView(cameraView, true);
76 | }
77 |
78 | private void buildView(RosImageView prevCamImage, boolean rebuild)
79 | {
80 | if(rebuild == true)
81 | {
82 | cameraView = prevCamImage;
83 | // Register input control callbacks
84 | Button backButton = (Button) findViewById(R.id.back_button);
85 | backButton.setOnClickListener(backButtonListener);
86 |
87 | ImageButton startButton = (ImageButton)findViewById(R.id.button_start);
88 | startButton.setOnClickListener(startButtonListener);
89 |
90 | ImageButton stopButton = (ImageButton)findViewById(R.id.button_stop);
91 | stopButton.setOnClickListener(stopButtonListener);
92 | }
93 | else
94 | {
95 | // Register input control callbacks
96 | Button backButton = (Button) findViewById(R.id.back_button);
97 | backButton.setOnClickListener(backButtonListener);
98 |
99 | ImageButton startButton = (ImageButton)findViewById(R.id.button_start);
100 | startButton.setOnClickListener(startButtonListener);
101 |
102 | ImageButton stopButton = (ImageButton)findViewById(R.id.button_stop);
103 | stopButton.setOnClickListener(stopButtonListener);
104 | }
105 | }
106 |
107 | @Override
108 | protected void init(NodeMainExecutor nodeMainExecutor)
109 | {
110 | super.init(nodeMainExecutor);
111 |
112 | NodeConfiguration nodeConfiguration =
113 | NodeConfiguration.newPublic(InetAddressFactory.newNonLoopback().getHostAddress(), getMasterUri());
114 |
115 | // Execute camera view node
116 | cameraView.setTopicName(getMasterNameSpace().resolve(cameraTopic).toString());
117 | nodeMainExecutor.execute(cameraView, nodeConfiguration.setNodeName("android/camera_view"));
118 |
119 | // Execute another node just to allow calling services; I suppose there will be a shortcut for this in the future
120 | nodeMainExecutor.execute(new AbstractNodeMain()
121 | {
122 | @Override
123 | public GraphName getDefaultNodeName()
124 | {
125 | return GraphName.of("android/follower");
126 | }
127 |
128 | @Override
129 | public void onStart(final ConnectedNode connectedNode)
130 | {
131 | node = connectedNode;
132 | }
133 | }, nodeConfiguration.setNodeName("android/follower"));
134 | }
135 |
136 | @Override
137 | public boolean onCreateOptionsMenu(Menu menu)
138 | {
139 | menu.add(0,0,0,R.string.stop_app);
140 | return super.onCreateOptionsMenu(menu);
141 | }
142 |
143 | @Override
144 | public boolean onOptionsItemSelected(MenuItem item)
145 | {
146 | super.onOptionsItemSelected(item);
147 | switch (item.getItemId()){
148 | case 0:
149 | onDestroy();
150 | break;
151 | }
152 | return true;
153 | }
154 |
155 | private void callService(byte newState)
156 | {
157 | if (node == null)
158 | {
159 | Log.e("FollowerActivity", "Still doesn't have a connected node");
160 | return;
161 | }
162 |
163 | ServiceClient serviceClient;
164 | try
165 | {
166 | NameResolver appNameSpace = getMasterNameSpace();
167 | String srvTopic = appNameSpace.resolve("turtlebot_follower/change_state").toString();
168 | serviceClient = node.newServiceClient(srvTopic, turtlebot_msgs.SetFollowState._TYPE);
169 | }
170 | catch (ServiceNotFoundException e)
171 | {
172 | Log.e("FollowerActivity", "Service not found: " + e.getMessage());
173 | Toast.makeText(getBaseContext(), "Change follower state service not found", Toast.LENGTH_LONG).show();
174 | return;
175 | }
176 | final turtlebot_msgs.SetFollowStateRequest request = serviceClient.newMessage();
177 | request.setState(newState);
178 |
179 | serviceClient.call(request, new ServiceResponseListener() {
180 | @Override
181 | public void onSuccess(turtlebot_msgs.SetFollowStateResponse response) {
182 | Log.i("FollowerActivity", "Service result " + response.getResult());
183 | if (request.getState() == turtlebot_msgs.SetFollowStateRequest.STOPPED)
184 | showToast("Follower stopped");
185 | else
186 | showToast("Follower started");
187 | }
188 |
189 | @Override
190 | public void onFailure(RemoteException e) {
191 | Log.e("FollowerActivity", "Service result: failure (" + e.getMessage() + ")");
192 | showToast("Change follower state failed");
193 | }
194 | });
195 | }
196 |
197 | /**
198 | * Call Toast on UI thread.
199 | * @param message Message to show on toast.
200 | */
201 | private void showToast(final String message)
202 | {
203 | runOnUiThread(new Runnable()
204 | {
205 | @Override
206 | public void run() {
207 | if (lastToast != null)
208 | lastToast.cancel();
209 |
210 | lastToast = Toast.makeText(getBaseContext(), message, Toast.LENGTH_LONG);
211 | lastToast.show();
212 | }
213 | });
214 | }
215 |
216 | private final OnClickListener backButtonListener = new OnClickListener()
217 | {
218 | @Override
219 | public void onClick(View v)
220 | {
221 | onBackPressed();
222 | }
223 | };
224 |
225 | private final OnClickListener startButtonListener = new OnClickListener()
226 | {
227 | @Override
228 | public void onClick(View v)
229 | {
230 | callService(turtlebot_msgs.SetFollowStateRequest.FOLLOW);
231 | }
232 | };
233 |
234 | private final OnClickListener stopButtonListener = new OnClickListener()
235 | {
236 | @Override
237 | public void onClick(View v)
238 | {
239 | callService(turtlebot_msgs.SetFollowStateRequest.STOPPED);
240 | }
241 | };
242 | }
243 |
--------------------------------------------------------------------------------
/panorama/src/main/java/com/github/turtlebot/turtlebot_android/panorama/PanoramaActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.turtlebot.turtlebot_android.panorama;
2 |
3 | import org.ros.address.InetAddressFactory;
4 | import org.ros.android.MessageCallable;
5 | import com.github.rosjava.android_remocons.common_tools.apps.RosAppActivity;
6 | import org.ros.exception.RemoteException;
7 | import org.ros.exception.ServiceNotFoundException;
8 | import org.ros.message.MessageListener;
9 | import org.ros.namespace.GraphName;
10 | import org.ros.namespace.NameResolver;
11 | import org.ros.node.ConnectedNode;
12 | import org.ros.node.Node;
13 | import org.ros.node.NodeConfiguration;
14 | import org.ros.node.NodeMain;
15 | import org.ros.node.NodeMainExecutor;
16 | import org.ros.node.service.ServiceClient;
17 | import org.ros.node.service.ServiceResponseListener;
18 | import org.ros.node.topic.Subscriber;
19 |
20 | import android.content.res.Configuration;
21 | import android.graphics.Bitmap;
22 | import android.graphics.drawable.Drawable;
23 | import android.os.Bundle;
24 | import android.os.StrictMode;
25 | import android.util.Log;
26 | import android.view.View;
27 | import android.view.View.OnClickListener;
28 | import android.widget.Button;
29 | import android.widget.CheckBox;
30 | import android.widget.CompoundButton;
31 | import android.widget.CompoundButton.OnCheckedChangeListener;
32 | import android.widget.ImageView;
33 | import android.widget.SeekBar;
34 | import android.widget.SeekBar.OnSeekBarChangeListener;
35 | import android.widget.Toast;
36 |
37 |
38 | public class PanoramaActivity extends RosAppActivity implements NodeMain
39 | {
40 | private ImageView imgView;
41 | private Toast lastToast;
42 | private ConnectedNode node;
43 | private final MessageCallable callable =
44 | new ScaledBitmapFromCompressedImage(2);
45 |
46 |
47 | public PanoramaActivity()
48 | {
49 | super("PanoramaActivity", "PanoramaActivity");
50 | }
51 |
52 | /************************************************************
53 | Android code:
54 | Activity life cycle and GUI management
55 | ************************************************************/
56 |
57 | /** Called when the activity is first created. */
58 | @Override
59 | public void onCreate(Bundle savedInstanceState)
60 | {
61 | setDefaultMasterName(getString(R.string.default_robot));
62 | setDefaultAppName(getString(R.string.default_app));
63 | setDashboardResource(R.id.top_bar);
64 | setMainWindowResource(R.layout.main);
65 |
66 | super.onCreate(savedInstanceState);
67 | buildView(false);
68 |
69 | // TODO Tricky solution to the StrictMode; the recommended way is by using AsyncTask
70 | if (android.os.Build.VERSION.SDK_INT > 9) {
71 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
72 | StrictMode.setThreadPolicy(policy);
73 | }
74 | }
75 |
76 | @Override
77 | protected void onStop()
78 | {
79 | super.onStop();
80 | }
81 |
82 | @Override
83 | protected void onRestart()
84 | {
85 | super.onRestart();
86 | }
87 |
88 | @Override
89 | protected void onPause()
90 | {
91 | super.onPause();
92 | }
93 |
94 | @Override
95 | protected void onResume()
96 | {
97 | super.onResume();
98 | }
99 |
100 | @Override
101 | public void onConfigurationChanged(Configuration newConfig)
102 | {
103 | // TODO this is not called now, so we cannot flip the screen
104 | Log.e("PanoramaActivity", "onConfigurationChanged");
105 | super.onConfigurationChanged(newConfig);
106 |
107 | buildView(true);
108 | }
109 |
110 | private void buildView(boolean rebuild)
111 | {
112 | CheckBox prevContCheck = null;
113 | SeekBar prevSpeedBar = null;
114 | SeekBar prevAngleBar = null;
115 | SeekBar prevIntervBar = null;
116 | Drawable prevDrawable = null;
117 |
118 | if (rebuild == true)
119 | {
120 | // If we are rebuilding GUI (probably because the screen was rotated) we must save widgets'
121 | // previous content, as setContentView will destroy and replace them with new instances
122 | prevContCheck = (CheckBox)findViewById(R.id.checkBox_continuous);
123 | prevSpeedBar = (SeekBar)findViewById(R.id.seekBar_speed);
124 | prevAngleBar = (SeekBar)findViewById(R.id.seekBar_angle);
125 | prevIntervBar = (SeekBar)findViewById(R.id.seekBar_interval);
126 | prevDrawable = imgView.getDrawable();
127 | }
128 |
129 | // Register input controls callbacks
130 | Button backButton = (Button) findViewById(R.id.back_button);
131 | backButton.setOnClickListener(backButtonListener);
132 |
133 | Button startButton = (Button)findViewById(R.id.button_start);
134 | startButton.setOnClickListener(startButtonListener);
135 |
136 | Button stopButton = (Button)findViewById(R.id.button_stop);
137 | stopButton.setOnClickListener(stopButtonListener);
138 |
139 | CheckBox contCheck = (CheckBox)findViewById(R.id.checkBox_continuous);
140 | contCheck.setOnCheckedChangeListener(contCheckListener);
141 | if (rebuild == true)
142 | contCheck.setChecked(prevContCheck.isChecked());
143 |
144 | SeekBar speedBar = (SeekBar)findViewById(R.id.seekBar_speed);
145 | speedBar.setOnSeekBarChangeListener(speedBarListener);
146 | if (rebuild == true)
147 | speedBar.setProgress(prevSpeedBar.getProgress());
148 |
149 | SeekBar angleBar = (SeekBar)findViewById(R.id.seekBar_angle);
150 | angleBar.setOnSeekBarChangeListener(angleBarListener);
151 | if (rebuild == true)
152 | angleBar.setProgress(prevAngleBar.getProgress());
153 |
154 | SeekBar intervBar = (SeekBar)findViewById(R.id.seekBar_interval);
155 | intervBar.setOnSeekBarChangeListener(intervalBarListener);
156 | if (rebuild == true)
157 | intervBar.setProgress(prevIntervBar.getProgress());
158 |
159 | // Take a reference to the image view to show incoming panoramic pictures
160 | imgView = (ImageView)findViewById(R.id.imageView_panorama);
161 | if (rebuild == true)
162 | imgView.setImageDrawable(prevDrawable);
163 | }
164 |
165 | /**
166 | * Call Toast on UI thread.
167 | * @param message Message to show on toast.
168 | */
169 | public void showToast(final String message)
170 | {
171 | runOnUiThread(new Runnable()
172 | {
173 | @Override
174 | public void run() {
175 | Toast.makeText(getBaseContext(), message, Toast.LENGTH_LONG).show();
176 | }
177 | });
178 | }
179 |
180 | /************************************************************
181 | ROS code:
182 | NodeMain implementation and service call code
183 | ************************************************************/
184 |
185 | @Override
186 | protected void init(NodeMainExecutor nodeMainExecutor)
187 | {
188 | super.init(nodeMainExecutor);
189 |
190 | NodeConfiguration nodeConfiguration =
191 | NodeConfiguration.newPublic(InetAddressFactory.newNonLoopback().getHostAddress(), getMasterUri());
192 |
193 | nodeMainExecutor.execute(this, nodeConfiguration.setNodeName("android/video_view"));
194 | }
195 |
196 | protected void callService(byte mode)
197 | {
198 | if (node == null)
199 | {
200 | Log.e("PanoramaActivity", "Still doesn't have a connected node");
201 | return;
202 | }
203 |
204 | ServiceClient serviceClient;
205 | try
206 | {
207 | NameResolver appNameSpace = getMasterNameSpace();
208 | String srvTopic = appNameSpace.resolve("turtlebot_panorama/take_pano").toString();
209 | serviceClient = node.newServiceClient(srvTopic, turtlebot_msgs.TakePanorama._TYPE);
210 | }
211 | catch (ServiceNotFoundException e)
212 | {
213 | Log.e("PanoramaActivity", "Service not found: " + e.getMessage());
214 | Toast.makeText(getBaseContext(), "Panorama service not found", Toast.LENGTH_LONG).show();
215 | return;
216 | }
217 | final turtlebot_msgs.TakePanoramaRequest request = serviceClient.newMessage();
218 |
219 | SeekBar ang_speed = (SeekBar)findViewById(R.id.seekBar_speed);
220 | SeekBar pano_angle = (SeekBar)findViewById(R.id.seekBar_angle);
221 | SeekBar snap_interv = (SeekBar)findViewById(R.id.seekBar_interval);
222 | CheckBox continuous = (CheckBox)findViewById(R.id.checkBox_continuous);
223 | if (continuous.isChecked() == true)
224 | request.setSnapInterval(snap_interv.getProgress()/100.0f); // convert to seconds
225 | else
226 | request.setSnapInterval(snap_interv.getProgress());
227 |
228 | request.setRotVel((float)((ang_speed.getProgress()*Math.PI)/180.0)); // convert to radians/s
229 | request.setPanoAngle(pano_angle.getProgress());
230 | request.setMode(mode);
231 |
232 | serviceClient.call(request, new ServiceResponseListener() {
233 | @Override
234 | public void onSuccess(turtlebot_msgs.TakePanoramaResponse response) {
235 | Log.i("PanoramaActivity", "Service result: success (status " + response.getStatus() + ")");
236 | node.getLog().info(String.format("Service result %d", response.getStatus()));
237 | if (request.getMode() == turtlebot_msgs.TakePanoramaRequest.STOP)
238 | showToast("Take panorama stopped.");
239 | else
240 | showToast("Take panorama started.");
241 | }
242 |
243 | @Override
244 | public void onFailure(RemoteException e) {
245 | Log.e("PanoramaActivity", "Service result: failure (" + e.getMessage() + ")");
246 | node.getLog().info(String.format("Service result: failure (%s)", e.getMessage()));
247 | showToast("Take panorama failed");
248 | }
249 | });
250 | }
251 |
252 | @Override
253 | public void onStart(ConnectedNode connectedNode)
254 | {
255 | Log.e("PanoramaActivity", connectedNode.getName() + " node started");
256 | node = connectedNode;
257 |
258 | NameResolver appNameSpace = getMasterNameSpace();
259 | String panoImgTopic = appNameSpace.resolve("turtlebot_panorama/panorama/compressed").toString();
260 |
261 | Subscriber subscriber =
262 | connectedNode.newSubscriber(panoImgTopic, sensor_msgs.CompressedImage._TYPE);
263 | subscriber.addMessageListener(new MessageListener() {
264 | @Override
265 | public void onNewMessage(final sensor_msgs.CompressedImage message) {
266 | imgView.post(new Runnable() {
267 | @Override
268 | public void run() {
269 | imgView.setImageBitmap(callable.call(message));
270 | }
271 | });
272 | imgView.postInvalidate();
273 | }
274 | });
275 | }
276 |
277 | @Override
278 | public void onError(Node n, Throwable e)
279 | {
280 | Log.d("PanoramaActivity", n.getName() + " node error: " + e.getMessage());
281 | }
282 |
283 | @Override
284 | public void onShutdown(Node n)
285 | {
286 | Log.d("PanoramaActivity", n.getName() + " node shuting down...");
287 | }
288 |
289 | @Override
290 | public void onShutdownComplete(Node n)
291 | {
292 | Log.d("PanoramaActivity", n.getName() + " node shutdown completed");
293 | }
294 |
295 | @Override
296 | public GraphName getDefaultNodeName()
297 | {
298 | return GraphName.of("android/panorama");
299 | }
300 |
301 | /************************************************************
302 | Android code:
303 | Anonymous implementation for input controls callbacks
304 | ************************************************************/
305 |
306 | private final OnClickListener backButtonListener = new OnClickListener()
307 | {
308 | @Override
309 | public void onClick(View v)
310 | {
311 | onBackPressed();
312 | }
313 | };
314 |
315 | private final OnClickListener startButtonListener = new OnClickListener()
316 | {
317 | @Override
318 | public void onClick(View v)
319 | {
320 | CheckBox continuous = (CheckBox)findViewById(R.id.checkBox_continuous);
321 |
322 | if (continuous.isChecked() == true)
323 | callService(turtlebot_msgs.TakePanoramaRequest.CONTINUOUS);
324 | else
325 | callService(turtlebot_msgs.TakePanoramaRequest.SNAPANDROTATE);
326 | }
327 | };
328 |
329 | private final OnClickListener stopButtonListener = new OnClickListener()
330 | {
331 | @Override
332 | public void onClick(View v)
333 | {
334 | callService(turtlebot_msgs.TakePanoramaRequest.STOP);
335 | }
336 | };
337 |
338 | private final OnCheckedChangeListener contCheckListener = new OnCheckedChangeListener()
339 | {
340 | @Override
341 | public void onCheckedChanged(CompoundButton cb, boolean checked)
342 | {
343 | SeekBar snap_interv = (SeekBar)findViewById(R.id.seekBar_interval);
344 | if (checked == true)
345 | {
346 | // continuous rotation; snap interval represents milliseconds between snaps
347 | snap_interv.setMax(5000);
348 | snap_interv.setProgress(Math.round((snap_interv.getProgress()*5000)/90));
349 | }
350 | else
351 | {
352 | // Snap and rotate; snap interval represents degrees between snaps
353 | snap_interv.setProgress(Math.round((snap_interv.getProgress()*90)/5000));
354 | snap_interv.setMax(90);
355 | }
356 | }
357 | };
358 |
359 | private final OnSeekBarChangeListener speedBarListener = new OnSeekBarChangeListener()
360 | {
361 | @Override
362 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
363 | {
364 | if (lastToast == null)
365 | lastToast = Toast.makeText(getBaseContext(), progress + " deg/s", Toast.LENGTH_SHORT);
366 | else
367 | lastToast.setText(progress + " deg/s");
368 |
369 | lastToast.show();
370 | }
371 |
372 | @Override public void onStartTrackingTouch(SeekBar seekBar) { }
373 | @Override public void onStopTrackingTouch(SeekBar seekBar) { }
374 | };
375 |
376 | private final OnSeekBarChangeListener angleBarListener = new OnSeekBarChangeListener()
377 | {
378 | @Override
379 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
380 | {
381 | if (lastToast == null)
382 | lastToast = Toast.makeText(getBaseContext(), progress + " degrees", Toast.LENGTH_SHORT);
383 | else
384 | lastToast.setText(progress + " degrees");
385 |
386 | lastToast.show();
387 | }
388 |
389 | @Override public void onStartTrackingTouch(SeekBar seekBar) { }
390 | @Override public void onStopTrackingTouch(SeekBar seekBar) { }
391 | };
392 |
393 | private final OnSeekBarChangeListener intervalBarListener = new OnSeekBarChangeListener()
394 | {
395 | @Override
396 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
397 | {
398 | if (seekBar.getMax() > 360) // continuous rotation; snap interval represents milliseconds between snaps
399 | {
400 | if (lastToast == null)
401 | lastToast = Toast.makeText(getBaseContext(), (progress/100)/10.0 + " seconds", Toast.LENGTH_SHORT);
402 | else
403 | lastToast.setText((progress/100)/10.0 + " seconds");
404 | }
405 | else // snap and rotate; snap interval represents degrees between snaps
406 | {
407 | if (lastToast == null)
408 | lastToast = Toast.makeText(getBaseContext(), progress + " degrees", Toast.LENGTH_SHORT);
409 | else
410 | lastToast.setText(progress + " degrees");
411 | }
412 | lastToast.show();
413 | }
414 |
415 | @Override public void onStartTrackingTouch(SeekBar seekBar) { }
416 | @Override public void onStopTrackingTouch(SeekBar seekBar) { }
417 | };
418 | }
419 |
--------------------------------------------------------------------------------