47 |
48 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/layout/activity_backgroundservice.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
17 |
27 |
28 |
44 |
45 |
62 |
63 |
64 |
65 |
66 |
76 |
77 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
47 |
52 |
57 |
62 |
67 |
68 |
71 |
72 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/layout/fragment_rating.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
20 |
29 |
30 |
37 |
38 |
48 |
49 |
61 |
62 |
76 |
77 |
93 |
94 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/java/com/ilm/sandwich/sensors/OrientationProvider.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | */
4 | package com.ilm.sandwich.sensors;
5 |
6 | import android.hardware.Sensor;
7 | import android.hardware.SensorEventListener;
8 | import android.hardware.SensorManager;
9 |
10 | import com.ilm.sandwich.representation.EulerAngles;
11 | import com.ilm.sandwich.representation.Matrixf4x4;
12 | import com.ilm.sandwich.representation.Quaternion;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /**
18 | * Classes implementing this interface provide an orientation of the device
19 | * either by directly accessing hardware, using Android sensor fusion or fusing
20 | * sensors itself.
21 | *
22 | * The orientation can be provided as rotation matrix or quaternion.
23 | *
24 | * @author Alexander Pacha
25 | */
26 | public abstract class OrientationProvider implements SensorEventListener {
27 | /**
28 | * Sync-token for syncing read/write to sensor-data from sensor manager and
29 | * fusion algorithm
30 | */
31 | protected final Object syncToken = new Object();
32 | /**
33 | * The matrix that holds the current rotation
34 | */
35 | protected final Matrixf4x4 currentOrientationRotationMatrix;
36 | /**
37 | * The quaternion that holds the current rotation
38 | */
39 | protected final Quaternion currentOrientationQuaternion;
40 | /**
41 | * The list of sensors used by this provider
42 | */
43 | protected List sensorList = new ArrayList();
44 | /**
45 | * The sensor manager for accessing android sensors
46 | */
47 | protected SensorManager sensorManager;
48 |
49 | /**
50 | * Initialises a new OrientationProvider
51 | *
52 | * @param sensorManager The android sensor manager
53 | */
54 | public OrientationProvider(SensorManager sensorManager) {
55 | this.sensorManager = sensorManager;
56 |
57 | // Initialise with identity
58 | currentOrientationRotationMatrix = new Matrixf4x4();
59 |
60 | // Initialise with identity
61 | currentOrientationQuaternion = new Quaternion();
62 | }
63 |
64 | /**
65 | * Starts the sensor fusion (e.g. when resuming the activity)
66 | */
67 | public void start() {
68 | // enable our sensor when the activity is resumed, ask for
69 | // 10 ms updates.
70 | for (Sensor sensor : sensorList) {
71 | // enable our sensors when the activity is resumed, ask for
72 | // 20 ms updates (Sensor_delay_game)
73 | sensorManager.registerListener(this, sensor,
74 | SensorManager.SENSOR_DELAY_GAME);
75 | }
76 | }
77 |
78 | /**
79 | * Stops the sensor fusion (e.g. when pausing/suspending the activity)
80 | */
81 | public void stop() {
82 | // make sure to turn our sensors off when the activity is paused
83 | for (Sensor sensor : sensorList) {
84 | sensorManager.unregisterListener(this, sensor);
85 | }
86 | }
87 |
88 | @Override
89 | public void onAccuracyChanged(Sensor sensor, int accuracy) {
90 | // Not doing anything
91 | }
92 |
93 | /**
94 | * @return Returns the current rotation of the device in the rotation matrix
95 | * format (4x4 matrix)
96 | */
97 | public Matrixf4x4 getRotationMatrix() {
98 | synchronized (syncToken) {
99 | return currentOrientationRotationMatrix;
100 | }
101 | }
102 |
103 | /**
104 | * @return Returns the current rotation of the device in the quaternion
105 | * format (vector4f)
106 | */
107 | public Quaternion getQuaternion() {
108 | synchronized (syncToken) {
109 | return currentOrientationQuaternion.clone();
110 | }
111 | }
112 |
113 | /**
114 | * @return Returns the current rotation of the device in the Euler-Angles
115 | */
116 | public EulerAngles getEulerAngles() {
117 | synchronized (syncToken) {
118 |
119 | float[] angles = new float[3];
120 | SensorManager.getOrientation(currentOrientationRotationMatrix.matrix, angles);
121 | return new EulerAngles(angles[0], angles[1], angles[2]);
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartNavi
8 | 开始步骤基于导航
9 | 搜索
10 | 载入路线,请稍候…
11 | 请将设备保持在您的身前,便于SmartNavi识别步幅和方向。
12 | SmartNavi was not able to get an initial position.\nPlease enable internet or GPS and restart SmartNavi,\nor contact our support: info@smartnavi.app
13 | SmartNavi是第一款不依赖于GPS信号,而是根据步伐进行定位的安卓应用。
14 | 如何工作:
15 | SmartNavi仍然在不断改进。\n请联系我们:\ninfo@smartnavi.app
16 | 请输入您的身高,以方便我们确定您的步幅。
17 |
18 |
19 | 輕觸螢幕並將手指停留在地圖上以便得到額外操作選項. 利用額外操作選項, 您可以設定您自己額外的出發位置
20 | 请输入您的身高
21 | 请勾选\n允许模拟位置\n路径:\n>设置\n> 开发者选项
22 | 设置
23 | 長按
24 | 更多信息请访问:\nhttps://smartnavi.app
25 |
26 |
27 | 所以它
28 | 的工作原理
29 | 无结果。请检查网络连接并重试。
30 | 停用
31 | Could not get route information. Please try again later.
32 |
33 |
34 | 您似乎喜歡使用SmartNavi,您給予SmartNavi的評分就是對我們最大的支持 :)
35 | 无效值。
36 | 开始
37 | 請給SmartNavi評分
38 | 取消
39 | 打开设置
40 | 好
41 | 暫時不進行評分
42 | 現在就去PlayStore 進行評分
43 |
44 |
45 | SmartNavi利用加速度传感器和磁场传感器的数据识别您的步伐。
46 | 每一步都会在相应的方向上改变位置。内部的校准器将保证持久的精度。 出发位置将会使用GPS,Wifi或者手机网络进行确定。SmartNavi通过使用内部传感器,与使用GPS相比可以节省80%的电量,从而延长电池使用时间。
47 | 很抱歉,您的设备没有磁场传感器(指南针)。SmartNavi无法工作。\n更多信息请访问我们的主页:\nhttps://smartnavi-app.com\n
48 | 请调整设置:
49 | 请检查您的网络连接。
50 | 注意!请激活GPS!
51 | 指南
52 |
53 |
54 | 调整身高
55 | 将依据您的身高计算出您的步长
56 | 一般
57 | 振动
58 | 卫星视图
59 | 语音输出
60 | 在给出下一步指示时振动。
61 | 导出您的路线至SD卡中(GPX格式)
62 | 导出路线
63 |
64 |
65 | 自动更正
66 | 后台服务
67 | 关于
68 | 通过GPS自动更正
69 | SmartNavi可以为其他的应用提供位置数据。
70 | 使用这个功能为其他应用进行步伐导航并节省电量。
71 | 停止后台服务
72 | 后台服务已停止。
73 |
74 |
75 | 杂项
76 | SmartNavi正在后台运行。
77 | 位置数据已提供
78 | 启用后台服务
79 | 电池寿命长
80 | 均衡
81 | No results were found for your request.
82 | 精度高
83 |
84 | Enable GPS to get a better initial position.\nAfter getting your initial position SmartNavi works independent from GPS! :)
85 | Initial positioning stopped.
86 | For navigation instructions
87 | Corrects your position regularly with GPS
88 | GPX-File will be created in Folder \"smartnavi\"
89 | Offline Maps
90 |
91 | Welcome!
92 | SmartNavi recognizes your steps by using internal sensors
93 | Be independent from GPS and save up to 80% battery
94 | SmartNavi is open source and respects your privacy
95 | Next
96 | SmartNavi needs permission for location services.
97 |
98 | SmartNavi needs permission to save map data to storage.
99 |
100 |
101 |
102 | 确定 位置
103 | 确定 目的地
104 |
105 |
106 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values-ja/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartNavi
8 | 歩行ベース のナビゲーションを開始
9 | 検索
10 | 経路の読み込み中。しばらくお待ちください…
11 | 体の前にデバイスを持ってください
12 | SmartNavi が初期の場所を取得できませんでした。\nインターネットまたは GPS を有効にして、SmartNaviを再起動してください。\nまたは、サポート: info@smartnavi.app にご連絡ください
13 | SmartNavi はオープンソースで、\n歩行ベースの GPS から独立したナビゲーションを提供します。
14 | 動作方法:
15 | お気軽にご連絡ください:\ninfo@smartnavi.app
16 | あなたの身長を入力してください。歩幅を決定します。
17 |
18 |
19 | 地図を長押しすると追加のオプションを表示します。そこで、あなたの正確な場所を設定することができます。
20 | 身長\nを入力してください
21 | \> 設定\n> 開発 から、\nモックの位置\nを有効にしてください
22 | 設定
23 | 長押し
24 | 詳細な情報は Web サイト:\nhttps://smartnavi.app
25 |
26 |
27 | 動作
28 | 方法
29 | 結果はありません。インターネット接続を確認してもう一度試してください。
30 | 無効にしました
31 | 経路情報を取得できません。後でもう一度試してください。
32 |
33 |
34 | SmartNavi を気に入っていただきましたか。 :) \nあなたの評価が私たちにとって大きな支援になります!
35 | 数字が正しくありません。
36 | 開始
37 | SmartNavi を評価
38 | キャンセル
39 | 設定を開く
40 | OK
41 | 後で
42 | Play ストアで今すぐ評価
43 |
44 |
45 | SmartNavi は、加速度計と磁気センサーのデータを使用して、歩数を認識します。
46 | 歩行により、対応する方向で場所を変更します。内部エラー訂正により、しっかりした精度を保証します。
47 | 開始場所は GPS または UMTS/Wifi/GSM いずれかの位置情報を使用して位置決めします。GPS の代わりに、内部センサーを使用すると SmartNavi は、80 %までエネルギーと、したがってバッテリー寿命を節約することができます。
48 | 残念ながら、お使いのデバイスは磁気センサーがないようです (コンパス)。 SmartNavi はそれがないと動作しません。\n詳細な情報は Web サイト:\nhttps://smartnavi-app.com\n
49 | 設定の調整:
50 | インターネット接続を確認してください。
51 | ご注意ください! GPS を有効にしてください!
52 | チュートリアル
53 |
54 |
55 | 身長
56 | 身長を使用して歩幅を計算します
57 | 全般
58 | 振動
59 | 衛星表示
60 | 音声出力
61 | 次のナビゲーション ステップで振動します。
62 | SD カードの GPX ファイルに歩行経路をエクスポートします
63 | 経路をエクスポート
64 |
65 |
66 | 自動修正
67 | バックグランド サービス
68 | アプリについて
69 | GPS 経由で自動修正
70 | SmartNavi は他のアプリに位置データを提供することができます。
71 | この機能を使用して、他のアプリで歩行ベースのナビゲーションと、エネルギーを節約することができます。
72 | バックグラウンド サービスを停止
73 | バックグラウンド サービスが停止しました。
74 |
75 |
76 | その他
77 | SmartNavi はバックグラウンドで実行します
78 | 位置データを提供します
79 | バックグラウンド サービスの開始
80 | 長寿命のバッテリー
81 | 均衡のとれた
82 | リクエストの結果が見つかりませんでした。
83 | 高精度
84 |
85 | 正確な初期の場所を取得するには GPS を有効にしてください。\n初期の場所を取得した後は SmartNavi は GPS から独立して動作します! :)
86 | 初期の場所決めを停止しました。
87 | ナビゲーション手順用
88 | GPS で定期的に自分の位置を補正する
89 | フォルダー \"smartnavi\" に GPX ファイルを作成します
90 | オフライン地図
91 |
92 | ようこそ!
93 | SmartNavi は内部センサーを使用して 歩行 を認識します
94 | GPS から 独立 し、バッテリーを 80% まで節約します
95 | SmartNavi はオープンソースで、あなたのプライバシーを尊重します
96 | 次へ
97 | SmartNavi は、位置情報サービスのアクセス許可が必要です。
98 |
99 | SmartNavi は、データをストレージに保存するアクセス許可が必要です。
100 |
101 |
102 |
103 | 場所を設定
104 | 目的地を設定
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/java/com/ilm/sandwich/tools/HttpRequests.java:
--------------------------------------------------------------------------------
1 | package com.ilm.sandwich.tools;
2 |
3 | import org.apache.http.HttpEntity;
4 | import org.apache.http.HttpResponse;
5 | import org.apache.http.client.entity.UrlEncodedFormEntity;
6 | import org.apache.http.client.methods.HttpGet;
7 | import org.apache.http.client.methods.HttpPost;
8 | import org.apache.http.impl.client.DefaultHttpClient;
9 | import org.apache.http.message.BasicNameValuePair;
10 | import org.apache.http.util.EntityUtils;
11 |
12 | import java.net.URI;
13 | import java.net.URISyntaxException;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /**
18 | * @author Markus Kniep | 2imba
19 | *
20 | * This class is the basis for HTTP requests within an asynchroneous task. UI changes resulting from the AsyncTask have to be made on the UI Thread.
21 | * Create a intern class which extends AsyncTask and do UI changes in the onPostExecute()
22 | * @usage public class SendData extends AsyncTask { ... }
23 | */
24 | public class HttpRequests {
25 |
26 | private List postingData;
27 | private String response = null;
28 | private URI uri;
29 | private String header = null;
30 | private String requestMethod;
31 |
32 | public HttpRequests() {
33 | postingData = new ArrayList(1);
34 | // standard method post
35 | requestMethod = "POST";
36 | }
37 |
38 | /**
39 | * specifies the adress of a server/webpage/script where the request will be executed
40 | *
41 | * @param _uri URL adress
42 | */
43 | public void setURL(String _uri) {
44 |
45 | try {
46 | uri = new URI(_uri);
47 | } catch (URISyntaxException e) {
48 | //e.printStackTrace();
49 | }
50 | }
51 |
52 | /**
53 | * set the user agent header for the HTTP request
54 | *
55 | * @param _header User Agent
56 | */
57 | public void setHeader(String _header) {
58 | header = _header;
59 | }
60 |
61 | /**
62 | * set the request method to "GET" or "POST"
63 | *
64 | * @param _requestMethod Request Type
65 | */
66 | public void setMethod(String _requestMethod) {
67 | requestMethod = _requestMethod;
68 | }
69 |
70 | /**
71 | * set post variables and values
72 | *
73 | * @param varName name of variable
74 | * @param value value of variable
75 | */
76 | public void addValue(String varName, String value) {
77 | postingData.add(new BasicNameValuePair(varName, value));
78 | }
79 |
80 | public void setBasicNameValuePairs(List _data) {
81 | postingData = _data;
82 | }
83 |
84 | /**
85 | * execute the http server request
86 | *
87 | * @return response contains the server message
88 | */
89 | public String doRequest() {
90 |
91 | DefaultHttpClient httpclient = new DefaultHttpClient();
92 |
93 | response = null;
94 |
95 | if (requestMethod.equals("GET")) {
96 |
97 | // build typical GET url from postingData
98 | StringBuilder urlString = new StringBuilder();
99 |
100 | for (int i = 0; i < postingData.size(); i++) {
101 | urlString.append("" + postingData.get(i).getName() + "=" + postingData.get(i).getValue());
102 | if (i < postingData.size() - 1)
103 | urlString.append("&");
104 | }
105 |
106 | String getURI = uri + "?" + urlString;
107 |
108 | HttpGet httpReq = new HttpGet(getURI);
109 |
110 | try {
111 |
112 | if (header != null)
113 | httpReq.setHeader("User-Agent", header);
114 |
115 | // execute HTTP GET request
116 | HttpResponse httpResponse = httpclient.execute(httpReq);
117 | HttpEntity httpEntity = httpResponse.getEntity();
118 | response = EntityUtils.toString(httpEntity);
119 |
120 | } catch (Exception e) {
121 | // e.printStackTrace();
122 | response = "Error: " + e;
123 | }
124 |
125 | } else if (requestMethod.equals("POST")) {
126 | HttpPost httpReq = new HttpPost(uri);
127 |
128 | try {
129 |
130 | httpReq.setEntity(new UrlEncodedFormEntity(postingData));
131 | if (header != null)
132 | httpReq.setHeader("User-Agent", header);
133 |
134 | // execute HTTP POST request
135 | HttpResponse httpResponse = httpclient.execute(httpReq);
136 | HttpEntity httpEntity = httpResponse.getEntity();
137 | response = EntityUtils.toString(httpEntity);
138 |
139 | } catch (Exception e) {
140 | // e.printStackTrace();
141 | response = "Error: " + e;
142 | }
143 | }
144 |
145 | return response;
146 | }
147 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/java/com/ilm/sandwich/fragments/RatingFragment.java:
--------------------------------------------------------------------------------
1 | package com.ilm.sandwich.fragments;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.util.Log;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.Button;
13 | import android.widget.ImageView;
14 |
15 | import androidx.annotation.Nullable;
16 | import androidx.fragment.app.Fragment;
17 |
18 | import com.google.firebase.analytics.FirebaseAnalytics;
19 | import com.ilm.sandwich.BuildConfig;
20 | import com.ilm.sandwich.R;
21 | import com.ilm.sandwich.tools.Config;
22 |
23 | /**
24 | * Fragment to show Rating Dialog.
25 | *
26 | * @author Christian
27 | */
28 | public class RatingFragment extends Fragment {
29 |
30 | private onRatingFinishedListener mListener;
31 | private View fragmentView;
32 | private FirebaseAnalytics mFirebaseAnalytics;
33 |
34 | public RatingFragment() {
35 | }
36 |
37 | @Override
38 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
39 | super.onViewCreated(view, savedInstanceState);
40 | fragmentView = view;
41 |
42 | // Obtain the FirebaseAnalytics instance.
43 | mFirebaseAnalytics = FirebaseAnalytics.getInstance(view.getContext());
44 | mFirebaseAnalytics.logEvent("Rating_Popup_Shown", null);
45 |
46 | showRateDialog();
47 | }
48 |
49 | @Override
50 | public void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | }
53 |
54 | @Override
55 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
56 | Bundle savedInstanceState) {
57 | // Inflate the layout for this fragment
58 | return inflater.inflate(R.layout.fragment_rating, container, false);
59 | }
60 |
61 | @Override
62 | public void onAttach(Context context) {
63 | super.onAttach(context);
64 | if (context instanceof onRatingFinishedListener) {
65 | mListener = (onRatingFinishedListener) context;
66 | } else {
67 | throw new RuntimeException(context.toString()
68 | + " must implement OnFragmentInteractionListener");
69 | }
70 | }
71 |
72 | @Override
73 | public void onDetach() {
74 | super.onDetach();
75 | mListener = null;
76 | }
77 |
78 | private void showRateDialog() {
79 | Button rateButton1 = fragmentView.findViewById(R.id.rateButton);
80 | if (rateButton1 != null) {
81 | rateButton1.setOnClickListener(new View.OnClickListener() {
82 | @Override
83 | public void onClick(View v) {
84 | mFirebaseAnalytics.logEvent("Rating_Popup_No", null);
85 | SharedPreferences prefs = RatingFragment.this.getActivity().getSharedPreferences(RatingFragment.this.getActivity().getPackageName() + "_preferences", 0);
86 | int notRated = prefs.getInt("ratingDenied", 0) + 1;
87 | if (BuildConfig.debug)
88 | Log.i("RateDialog", "Not Rated: " + notRated);
89 | prefs.edit().putInt("ratingDenied", notRated).apply();
90 |
91 | if (notRated == 1) {
92 | prefs.edit().putInt("appLaunchCounter", -2).apply();
93 | } else if (notRated == 2) {
94 | prefs.edit().putInt("appLaunchCounter", -5).apply();
95 | } else if (notRated == 3) {
96 | prefs.edit().putInt("appLaunchCounter", -5).apply();
97 | } else if (notRated >= 4) {
98 | prefs.edit().putBoolean("neverShowRatingAgain", true).apply();
99 | }
100 | if (mListener != null) {
101 | mListener.onRatingFinished();
102 | }
103 | }
104 | });
105 | }
106 | Button rateButton3 = fragmentView.findViewById(R.id.rateButton2);
107 | if (rateButton3 != null) {
108 | rateButton3.setOnClickListener(new View.OnClickListener() {
109 | @Override
110 | public void onClick(View v) {
111 | SharedPreferences prefs = RatingFragment.this.getActivity().getSharedPreferences(RatingFragment.this.getActivity().getPackageName() + "_preferences", 0);
112 | mFirebaseAnalytics.logEvent("Rating_Popup_Yes", null);
113 | prefs.edit().putInt("ratingDenied", 100).apply();
114 | prefs.edit().putBoolean("neverShowRatingAgain", true).apply();
115 | if (mListener != null) {
116 | mListener.onRatingFinished();
117 | }
118 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + Config.APP_PNAME)));
119 | }
120 | });
121 | }
122 |
123 | ImageView stars = fragmentView.findViewById(R.id.stars);
124 | if (stars != null) {
125 | stars.setOnClickListener(new View.OnClickListener() {
126 | @Override
127 | public void onClick(View v) {
128 | SharedPreferences prefs = RatingFragment.this.getActivity().getSharedPreferences(RatingFragment.this.getActivity().getPackageName() + "_preferences", 0);
129 | prefs.edit().putInt("ratingDenied", 100).apply();
130 | mFirebaseAnalytics.logEvent("Rating_Popup_Yes", null);
131 | prefs.edit().putBoolean("neverShowRatingAgain", true).apply();
132 | if (mListener != null) {
133 | mListener.onRatingFinished();
134 | }
135 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + Config.APP_PNAME)));
136 | }
137 | });
138 | }
139 | }
140 |
141 | public interface onRatingFinishedListener {
142 | void onRatingFinished();
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartNavi
8 | Inizia la navigazione basata sui passi
9 | Ricerca
10 | Caricamento percorso. Attendere…
11 | Tieni sempre il dispositivo davanti al tuo corpo
12 | SmartNavi non ha localizzato la posizione di partenza.\nAttiva internet o GPS e fai ripartire SmartNavi,\naltrimenti contatta il nostro supporto: info@smartnavi.app
13 | SmartNavi è OpenSource e offre\nuna navigazione GPS indipendente e basata sui passi.
14 | Come funziona:
15 | Suggerimenti sono sempre accetti:\ninfo@smartnavi.app
16 | Inserisci per favore la tua altezza, per determinare la lunghezza del tuo passo.
17 |
18 |
19 | Tieni premuto sulla mappa per avere più opzioni. Ad esempio, puoi impostare la tua posizione esatta di partenza.
20 | Inserisci la tua\naltezza
21 | Please select SmartNavi as mock location app at Developer options!
22 | Opzioni
23 | Tieni premuto
24 | Per maggiori informazioni:\nhttps://smartnavi.app
25 |
26 |
27 | COME
28 | FUNZIONA
29 | Nessun risultato. Verifica la connessione internet e riprova.
30 | disattivato
31 | Nessuna informazione di percorso ottenuta. Riprova più tardi.
32 |
33 |
34 | Pare che SmartNavi ti piaccia. :) \nPotresti aiutarci con la tua valutazione!
35 | Valore non valido.
36 | Inizia
37 | Valuta SmartNavi
38 | Annulla
39 | Opzioni
40 | OK
41 | Non ora
42 | Valuta ora in PlayStore
43 |
44 |
45 | SmartNavi riconosce i passi tramite i dati di accelerometro e sensore magnetico. Ogni passo cambia la posizione nella direzione corrispondente. La correzione interna d\'errore favorisce una precisione stabile. La posizione è localizzata tramite GPS o localizzazione UMTS/Wifi/GSM. L\'uso di sensori interni al posto del GPS permette di risparmiare l\'80% dell\'energia e quindi della batteria.
46 | Purtroppo il tuo dispositivo non dispone di alcun sensore magnetico (bussola). Senza di esso SmartNavi non può funzionare.\nPer maggiori informazioni:\nhttps://smartnavi-app.com\n
47 | Modifica opzioni:
48 | Per favore controlla la tua connessione internet.
49 | Attenzione! Attiva il GPS!
50 | Tutorial
51 |
52 |
53 | Altezza corporea
54 | La lunghezza del passo è calcolata con la tua altezza
55 | Generale
56 | Vibrazione
57 | Vista satellite
58 | Sintesi vocale
59 | Vibrazione al prossimo passo.
60 | Esporta il tuo percorso come file GPX sulla scheda SD
61 | Esporta percorso
62 |
63 |
64 | Autocorreggi
65 | Servizio in background
66 | Info
67 | Autocorrezione tramite GPS
68 | SmartNavi può fornire posizioni ad altre applicazioni.
69 | Usa questa funzione per navigare con altre app step-based e salva-energia.
70 | Ferma servizio in background
71 | Servizio in background fermato.
72 |
73 |
74 | Altro
75 | SmartNavi ora funziona in background
76 | Inviando posizioni
77 | Inizia servizio in background
78 | Allunga durata batteria
79 | Bilanciato
80 | Nessun risultato trovato per la tua ricerca.
81 | Alta precisione
82 |
83 | Attiva il GPS per ottenere una migliore posizione iniziale.\nDopo la prima posizione SmartNavi lavora indipendente dal GPS! :)
84 | Posizionamento iniziale fermato.
85 | Per istruzioni di navigazione
86 | Corregge la tua posizione regolarmente col GPS
87 | Il file GPX sarà memorizzato nella cartella \"smartnavi\"
88 | Mappe non in linea
89 |
90 | Welcome!
91 | SmartNavi recognizes your steps by using internal sensors
92 | Be independent from GPS and save up to 80% battery
93 | SmartNavi is open source and respects your privacy
94 | Next
95 | SmartNavi needs permission for location services.
96 |
97 | SmartNavi needs permission to save map data to storage.
98 |
99 |
100 |
101 | Imposta posizione
102 | Imposta destinazione
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartNavi
8 | Comenzar la navegación basada en pasos
9 | Búsquedas
10 | Cargando ruta. Por favor espere…
11 | Mantenga el dispositivo frente su cuerpo
12 | SmartNavi no pudo encontrar su posición inicial. Por favor active internet o el GPS y reinicie SmartNavi o contacte a nuestro servicio de atención: info@smartnavi.app
13 | SmartNavi es el primer programa o aplicación GPS de navegación basado en pasos para Android.
14 | Como funciona:
15 | SmartNavi sigue en desarrollo.\nSiéntase libre de contactarnos:\ninfo@smartnavi.app
16 | Por favor Introduzca su altura. Para poder determinar la longitud de sus pasos.
17 |
18 |
19 | Toca y mantén presionado el mapa para obtener opciones extra. Así podrás determinar tu exacta poción de inicio.
20 | Ingrese su peso de\naltura
21 | Por favor active\nMock Locations (locaciones falsas)\nunder:\> Settings\n> Development
22 | Ajustes
23 | Mantén presionado
24 | Mas información en nuestra pagina:\nhttps://smartnavi.app
25 |
26 |
27 | COMO
28 | FUNCIONA
29 | No hay resultados. Compruebe su conexión a internet e intente de nuevo.
30 | Desactivado
31 | No se pudo obtener información de la ruta deseada. Por favor intente de nuevo mas tarde.
32 |
33 |
34 | Parece que te gusta SmartNavi. Su clasificación nos será de gran apoyo para nosotros! :)
35 | Numero invalido.
36 | Empezar
37 | Clasifica SmartNavi
38 | Cancelar
39 | Abrir ajustes.
40 | OK
41 | No clasificar
42 | Clasifica ahora en Playstore
43 |
44 |
45 | SmartNavi reconoce los pasos usando el acelerómetro y la información de los sensores magnéticos. Cada paso cambia la posición y dirección. La corrección interna de errores garantiza una precisión durable. Una vez se haya realizado el posicionamiento inicial via GPS o UMTS/WiFi/GSM, el programa usará los sensores internos del teléfono para ahorrar más de un 80% de energia y vida de la batería.
46 | Desafortunadamente su aparato no tiene sensores de campo magnéticos (brújula). SmartNavi no puede funcionar sin eso.\npara mas información visita nuestro sitio web:\nhttps://smartnavi-app.com\n
47 | Por favor adapte los ajustes del sistema:
48 | Por favor confirme su conexión a internet.
49 | Atención! Por favor active su GPS!
50 | Tutorial
51 |
52 |
53 | Altura
54 | La longitud de tus pasos es calculada con tu altura
55 | General
56 | Vibración
57 | Vista desde Satélite
58 | Sonido
59 | Vibrar al siguiente paso de navegación.
60 | Exportar la ruta en formato GPX a tu tarjeta de memoria
61 | Exporta ruta
62 |
63 |
64 | Autocorrección
65 | Servicio en segundo plano
66 | Acerca de
67 | Autocorregir via GPS
68 | SmartNavi puede proveer datos de localización para otras aplicaciones.
69 | Usa esta característica para navegar con otras aplicaciones basándose en la tecnología de pasos y ahorro de energía
70 | Detener servicio en segundo plano
71 | El servicio en segundo plano se detuvo.
72 |
73 |
74 | Misceláneos
75 | SmartNavi ahora corre en segundo plano
76 | Proveyendo datos de localización
77 | Iniciar servicio en segundo plano
78 | Prolonga la vida de la batería
79 | Balanceado
80 | No se encontraron los resultados de tu búsqueda.
81 | Alta precisión
82 |
83 | Enable GPS to get a better initial position.\nAfter getting your initial position SmartNavi works independent from GPS! :)
84 | Initial positioning stopped.
85 | For navigation instructions
86 | Corrects your position regularly with GPS
87 | GPX-File will be created in Folder \"smartnavi\"
88 | Offline Maps
89 |
90 | Welcome!
91 | SmartNavi recognizes your steps by using internal sensors
92 | Be independent from GPS and save up to 80% battery
93 | SmartNavi is open source and respects your privacy
94 | Next
95 | SmartNavi needs permission for location services.
96 |
97 | SmartNavi needs permission to save map data to storage.
98 |
99 |
100 | Ajustar la posición
101 | Ajustar el destino
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartNavi
8 | Uruchom nawigację opartą kroku
9 | Wyszukiwanie
10 | Ładowanie trasy. Proszę czekać…
11 | Trzymaj swoje urządzenie zawsze przed sobą, wtedy SmartNavi rozpoznaje kroki i kierunki.
12 | SmartNavi nie była w stanie ustalić pozycji początkowej.\nProszę włączyć internet lub GPS i zrestartować SmartNavi,\nalbo skontaktować się z naszym centrum wsparcia: info@smartnavi.app
13 | SmartNavi jest pierwszą niezależną od GPS i bazującą na krokach aplikacją nawigującą na Androida.
14 | Jak to działa:
15 | SmartNavi jest ciągle w rozwoju.\nSkontaktuj się z nami:\ninfo@smartnavi.app
16 | Proszę wprowadź wysokość twego ciała, aby można było określić długość twego kroku.
17 |
18 |
19 | Dotknij i przytrzymaj na mapie, aby uzyskać dodatkowe opcje. Tak możesz ustawić dokładną pozycję startową.
20 | Wprowadź wysokość twojego ciała
21 | Please select SmartNavi as mock location app at Developer options!
22 | Ustawienia
23 | Dłuższe wiśnięcie
24 | Więcej informacji na naszej stronie internetowej:\nhttps://smartnavi.app
25 |
26 |
27 | Tak to
28 | działa
29 | Brak wyników. Sprawdź połączenie z internetem i spróbuj ponownie.
30 | wyłączona
31 | Nie udało się uzyskać informacji o trasie. Spróbuj ponownie później.
32 |
33 |
34 | Wygląda na to, że lubisz SmartNavi. Twoja ocena byłaby ogromnym wsparciem dla nas. :)
35 | Niewłaściwy numer.
36 | Start
37 | Oceń SmartNavi
38 | Anuluj
39 | Otwórz ustawienia
40 | OK
41 | Nie teraz
42 | Oceń teraz w sklepie Play
43 |
44 |
45 | SmartNavi rozpoznaje kroki przez użycie akcelerometra i danych czujnika pola magnetycznego.
46 | Każdy krok zmienia pozycję w odpowiednim kierunku. Wewnętrzna korekcja błędów gwarantuje trwałą dokładność. Pozycja startowa
47 | zostanie zlokalizowana za pomocą GPS lub lokalizowania UMTS/Wifi/GSM. Przez używanie wewnętrznych czujników zamiast GPS, SmartNavi może oszczędzić nawet do 80% energii i przedłużyć życie baterii.
48 | Niestety wygląda na to, że twoje urządzenie nie posiada czujnika pola magnetycznego (kompasu) SmartNavi nie może działać bez tego.\nWięcej informacji na naszej stronie internetowej:\nhttps://smartnavi-app.com\n
49 | Proszę zmienić ustawienia:
50 | Proszę sprawdż twoje połączenie z internetem.
51 | Uwaga! Prosimy aktywować GPS!
52 | Tutorial
53 |
54 |
55 | Wysokość twojego ciała
56 | Długość twojego kroku jest wyliczana na podstawie wysokości ciała
57 | Ogólny
58 | Wibracja
59 | Widok z satelity
60 | Tryb mówiący
61 | Wibruje w następnym kroku nawigacji.
62 | Eksportuje trasę, po której szedłeś w pliku GPX na twoją kartę SD
63 | Eksport trasy
64 |
65 |
66 | Autokorekta
67 | Usługa w tle
68 | Informacje
69 | Autokorekta przez GPS
70 | SmartNavi może dostarczyć dane o lokalizacji dla innych aplikacji.
71 | Użyj tej funkcji do nawigowania z wykorzystaniem innych bazujących na krokach i energooszczędnych aplikacji.
72 | Zatrzymaj usługę w tle
73 | Usługa w tle zatrzymana.
74 |
75 |
76 | Rozmaity
77 | SmartNavi jest uruchomiona w tle
78 | Trwa dostarczanie danych o lokalizacji
79 | Uruchom usługę w tle
80 | Długi czas pracy baterii
81 | Zrównoważony
82 | Nie znaleziono wyników dla Twojego zapytania.
83 | Wysoka dokładność
84 |
85 | Enable GPS to get a better initial position.\nAfter getting your initial position SmartNavi works independent from GPS! :)
86 | Initial positioning stopped.
87 | For navigation instructions
88 | Corrects your position regularly with GPS
89 | GPX-File will be created in Folder \"smartnavi\"
90 | Offline Maps
91 |
92 | Welcome!
93 | SmartNavi recognizes your steps by using internal sensors
94 | Be independent from GPS and save up to 80% battery
95 | SmartNavi is open source and respects your privacy
96 | Next
97 | SmartNavi needs permission for location services.
98 |
99 | SmartNavi needs permission to save map data to storage.
100 |
101 |
102 |
103 | Określać Pozycja
104 | Określać cel
105 |
106 |
107 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | SmartNavi
7 | Start step based navigation
8 | Search
9 | Loading Route. Please wait…
10 | Hold your device always in front of your body
11 | SmartNavi was not able to get an initial position.\nPlease enable internet or GPS and restart SmartNavi,\nor contact our support: info@smartnavi.app
12 | SmartNavi is OpenSource and offers\nstep-based and GPS independent navigation.
13 | How it works:
14 | Contact us:\ninfo@smartnavi.app
15 | Please enter your body height, so that your step length can be determined.
16 |
17 |
18 | Touch and hold the map to get extra options. So you can set your exact start position.
19 | Enter your\nbody height
20 | Nearly there!\n\nPlease select SmartNavi as mock location app in the Developer options.
21 | Settings
22 | Long Press
23 | More information on our website:\nhttps://smartnavi.app
24 |
25 |
26 | HOW IT
27 | WORKS
28 | No Result. Check internet connection and try again.
29 | deactivated
30 | Could not get route information. Please try again later.
31 |
32 |
33 | It seems that you like SmartNavi. :) \nYour rating would be great support for us!
34 | Invalid number.
35 | Start
36 | Rate SmartNavi
37 | Cancel
38 | Open Settings
39 | OK
40 | Not now
41 | Rate now in the PlayStore
42 |
43 |
44 | SmartNavi recognizes steps by using accelerometer and magnetic-field-sensor data.
45 | Every step changes the position in the corresponding direction. Internal error correction guarantees a durable accuracy. The startposition
46 | will be located by using either GPS or UMTS/Wifi/GSM locationing. By using internal sensors instead of GPS, SmartNavi can save up to 80% energy and therefore battery life.
47 | Unfortunately your device does not seem to have a magnetic field sensor (compass). SmartNavi can not work without that.\nMore information on our website:\nwww.smartnavi.mobi\n
48 | Adjust settings:
49 | Please check your internet connection.
50 | Attention! Please activate GPS!
51 | Tutorial
52 |
53 |
54 | Body Height
55 | Your step length is calculated by using your height
56 | General
57 | Vibration
58 | Satellite View
59 | Speech Output
60 | Vibrates at next navigation step.
61 | Exports the route you walk in a GPX-file on your SD-Card
62 | Export Route
63 |
64 |
65 | AutoCorrect
66 | Background Service
67 | About
68 | AutoCorrect via GPS
69 | SmartNavi can provide location data for other apps.
70 | Use this feature to navigate with other apps stepbased and energy-saving.
71 | Stop background service
72 | Background service stopped.
73 |
74 |
75 | Miscellaneous
76 | SmartNavi is running in background
77 | Now providing location data
78 | Start background service
79 | Long battery life
80 | Balanced
81 | No results were found for your request.
82 | High accuracy
83 |
84 | Enable GPS to get a better initial position.\nAfter getting your initial position SmartNavi works independent from GPS! :)
85 | Initial positioning stopped.
86 | For navigation instructions
87 | Corrects your position regularly with GPS
88 | GPX-File will be created in Folder \"smartnavi\"
89 | Offline Maps
90 |
91 | Welcome!
92 | SmartNavi recognizes your steps by using internal sensors
93 | Be independent from GPS and save up to 80% battery
94 | SmartNavi is open source and respects your privacy
95 | Next
96 | SmartNavi needs permission for location services.
97 |
98 | SmartNavi needs permission to save map data to storage.
99 | Privacy Policy \nhttps://smartnavi.app/privacy
100 | An error occurred. Please try again later.
101 | Enable Developer Options
102 | In order to proceed you have to enable Developer Options on your device.\n\nTo do this tap 7 times on the Build Number in your settings.\n\nOn some devices the Build Number can be found under Software Information.\n\nThen come back and try again.
103 |
104 |
105 | cm
106 | foot\'inch
107 |
108 |
109 |
110 | Set Position
111 | Set Destination
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartNavi
8 | Démarrer la navigation basée sur vos pas
9 | Rechercher
10 | Chargement. Veuillez patienter…
11 | Tenez toujours votre appareil en face de vous
12 | SmartNavi n\'a pas été capable d\'établir votre position initiale.\nVeuillez activer l\'internet ou le GPS, puis redémarrez SmartNavi,\nou contactez notre support: info@smartnavi.app
13 | SmartNavi est open source et permet\nune navigation basée sur vos pas et indépendante du GPS.
14 | Fonctionnement
15 | N\'hésitez pas à nous contacter à :\ninfo@smartnavi.app
16 | Afin de déterminer la longueur de vos pas, merci d\'indiquer votre taille.
17 |
18 |
19 | Restez appuyer sur la carte pour obtenir des options supplémentaires. Vous pouvez par exemple de cette façon fournir de façon précise votre position initiale.
20 | Entrez votre\ntaille
21 | Please select SmartNavi as mock location app at Developer options!
22 | Paramètres
23 | Maintenir appuyer
24 | Plus d\'informations sur notre site internet :\nhttps://smartnavi.app
25 |
26 |
27 | Comment cela
28 | fonctionne
29 | Aucun résultat. Vérifiez votre connexion internet puis réessayez.
30 | Désactivé
31 | Impossible de récupérer les informations sur le parcours. Veuillez réessayer plus tard.
32 |
33 |
34 | Il semblerait que vous aimiez SmartNavi. :)\nVotre évaluation serait un vrai encouragement pour nous !
35 | Nombre invalide.
36 | Démarrer
37 | Évaluer SmartNavi
38 | Annuler
39 | Paramètres
40 | OK
41 | Pas maintenant
42 | Évaluer maintenant sur le Play Store
43 |
44 |
45 | SmartNavi reconnait vos pas en utilisant l\'accéléromètre et des capteurs de champ magnétique. Chaque pas change votre position dans la direction correspondante. Des corrections internes à l\'application garantissent une précision dans la durée. La position de départ sera localisée en utilisant le GPS, le Wi-Fi ou le réseau mobile. En utilisant des capteurs internes plutôt que le GPS, SmartNavi peut vous faire économiser jusque 80% d\'énergie et donc améliorer le temps d\'utilisation de votre batterie.
46 | Malheureusement votre appareil ne semble pas avoir de capteur de champ magnétique (boussole). SmartNavi ne peut pas fonctionner sans cela.\nPlus d\'information sur notre site internet :\nhttps://smartnavi-app.com\n
47 | Ajuster les paramètres :
48 | Veuillez vérifier votre connexion internet.
49 | Attention ! Activez votre GPS !
50 | Tutoriel
51 |
52 |
53 | Taille
54 | La longueur de vos pas est calculée à partir de votre taille
55 | Général
56 | Vibration
57 | Vue satellite
58 | Sortie vocale
59 | Vibrer à la prochaine étape de navigation.
60 | Exporter votre chemin dans un fichier GPX sur votre carte SD
61 | Exporter le chemin
62 |
63 |
64 | Auto correction
65 | Service d\'arrière-plan
66 | À propos
67 | Auto correction via le GPS
68 | SmartNavi peut fournir les données de localisation à d\'autres applications
69 | Utilisez cette fonction pour naviguer à partir de vos pas dans d\'autres applications tout en économisant de l\'énergie.
70 | Arrêter le service d\'arrière-plan
71 | Service d\'arrière-plan arrêté.
72 |
73 |
74 | Divers
75 | SmartNavi fonctionne maintenant en arrière-plan
76 | Données de localisation fournies
77 | Démarrer le service d\'arrière-plan
78 | Longue durée de vie de la batterie
79 | Équilibré
80 | Aucun résultat n\'a été trouvé pour votre requête.
81 | Haute précision
82 |
83 |
84 | Activez le GPS pour récupérer une meilleure position initiale.\nAprès la récupération de votre position initiale, SmartNavi fonctionne indépendamment du GPS ! :)
85 | Position initiale arrêtée.
86 | Pour les instructions de navigation
87 | Corriger régulièrement votre position avec le GPS
88 | Le fichier GPX sera créé dans le dossier \"smartnavi\"
89 | Carte hors-ligne
90 |
91 |
92 | Bienvenue !
93 | SmartNavi reconnaît vos pas en utilisant les capteurs internes
94 | Être indépendant du GPS et économiser jusqu\'à 80% de batterie
95 | SmartNavi est open source et respecte votre vie privée
96 | Suivant
97 | SmartNavi needs permission for location services.
98 |
99 | SmartNavi needs permission to save map data to storage.
100 |
101 |
102 |
103 | Définir la position
104 | Définir la destination
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartNavi
8 | Navigiere schrittbasiert
9 | Suche
10 | Lade Route. Bitte warten…
11 | Halte das Gerät stets vor deinen Körper
12 | SmartNavi konnte keine Startposition bestimmen.\nBitte aktiviere eine Internetverbindung oder GPS und starte SmartNavi neu.\nBei Problemen helfen wir Dir gerne unter: info@smartnavi.app
13 | SmartNavi ist OpenSource und bietet\nschrittbasierte und GPS unabhängige Navigation.
14 | Funktionsweise:
15 | Anregungen sind immer willkommen:\ninfo@smartnavi.app
16 | Bitte gib deine Körpergröße ein, damit deine Schrittlänge bestimmt werden kann.
17 |
18 |
19 | Halte lange auf der Karte gedrückt um zusätzliche Funktionen zu bekommen.So kannst du z.B. deinen eigenen Standort exakt setzen.
20 | Körpergröße eingeben
21 | Fast fertig!\n\nSetze SmartNavi als App für simulierte Standorte in den Entwickleroptionen.
22 | Einstellungen
23 | Gedrückt halten
24 | Mehr Informationen unter:\nhttps://smartnavi.app
25 |
26 |
27 | So einfach
28 | geht´s
29 | Kein Ergebnis. Internet-Verbindung prüfen und nochmal versuchen.
30 | deaktiviert
31 | Kann keine Routeninformationen abrufen. Bitte später erneut versuchen.
32 |
33 |
34 | SmartNavi scheint Dir zu gefallen. \nDurch eine Bewertung würdest Du uns sehr unterstützen! :)
35 | Ungültiger Wert.
36 | Start
37 | Bewerte SmartNavi
38 | Abbrechen
39 | Einstellungen
40 | OK
41 | Nicht jetzt
42 | Jetzt im PlayStore bewerten
43 |
44 |
45 | Die App erkennt Schritte durch Auswertung von Beschleunigungs- und Magnetfeld-Sensoren.
46 | Jeder Schritt verändert die Position in die entsprechende Richtung. Eingebaute Fehlerkorrekturen sorgen für dauerhafte Genauigkeit.
47 | Zur Bestimmung der StartPosition wird GPS oder W-LAN- bzw. Mobilfunk-Ortung genutzt. Durch die Nutzung interner Sensoren kann somit im Vergleich zur GPS-Navigation eine Menge Energie und
48 | somit Akkulaufzeit gespart werden.
49 | Leider verfügt dein Gerät über keinen Magnetfeld-Sensor (Kompass). Ohne diesen kann SmartNavi nicht funktionieren.\n\nMehr Informationen unter:\nhttps://smartnavi-app.com\n
50 | Einstellungen anpassen:
51 | Bitte überprüfe deine Internetverbindung.
52 | Achtung! Bitte aktivieren Sie GPS!
53 | Tutorial
54 |
55 |
56 | Körpergröße einstellen
57 | Aus deiner Körpergröße wird die Schrittlänge berechnet
58 | Allgemein
59 | Vibration
60 | Satelliten-Ansicht
61 | Sprachausgabe
62 | Vibration bei nächstem Navigationshinweis.
63 | Exportiert deinen Weg in eine GPX-Datei auf die SD-Karte
64 | Exportiere Strecke
65 |
66 |
67 | Autokorrektur
68 | Hintergrunddienst
69 | Info
70 | Autokorrektur mit GPS
71 | SmartNavi kann Positionen für andere Apps bereitstellen.
72 | Nutze diese Funktion, um auch mit anderen Apps schrittbasiert und energiesparend zu navigieren.
73 | Beende Hintergrunddienst
74 | Hintergrunddienst beendet.
75 |
76 |
77 | Sonstiges
78 | SmartNavi läuft im Hintergrund
79 | Positionen werden bereitgestellt
80 | Starte Hintergrunddienst
81 | Hohe Akkulebensdauer
82 | Ausgewogen
83 | Ihre Suchanfrage konnte leider keine Ergebnisse liefern.
84 | Hohe Genauigkeit
85 |
86 | Erlaube GPS um eine bessere Startposition zu erhalten.\nNach der ersten Position arbeitet SmartNavi unabhängig von GPS! :)
87 | Bestimmung der Startposition gestoppt.
88 | Für Navigations-Anweisungen
89 | Korrigiert deine Position regelmäßig durch GPS
90 | GPX-Datei wird in Ordner \"smartnavi\" geschrieben.
91 | Offline Karten
92 |
93 | Willkommen!
94 | SmartNavi erkennt deine Schritte mittels Smartphone Sensoren
95 | Werde unabhängig von GPS und spare bis zu 80% Akku
96 | SmartNavi ist Open Source und respektiert deine Privatsphäre
97 | Weiter
98 | SmartNavi benötigt Rechte zur Standortbestimmung.
99 |
100 | SmartNavi benötigt Rechte um Kartenmaterial zu speichern.
101 | Privacy Policy \nhttps://smartnavi.app/privacy
102 | Ein Fehler ist aufgetreten.
103 | Enable Developer Options
104 | Um fortzufahren müssen die Entwickleroptionen eingeschaltet sein.\n\nTippe dazu 7 mal auf die Build Number unter Einstellungen.\n\nAuf manchen Geräten befindet sich die Build Number unter Software Information.\n\nDann komm zurück und schließe die Einrichtung ab.
105 |
106 |
107 |
108 | Standort setzen
109 | Ziel setzen
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/layout/activity_googlemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
17 |
18 |
34 |
35 |
41 |
42 |
48 |
49 |
58 |
59 |
69 |
70 |
71 |
72 |
73 |
74 |
82 |
83 |
88 |
89 |
104 |
105 |
106 |
112 |
113 |
123 |
124 |
139 |
140 |
155 |
156 |
161 |
162 |
163 |
164 |
167 |
168 |
169 |
170 |
171 |
185 |
186 |
187 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/java/com/ilm/sandwich/representation/Vector4f.java:
--------------------------------------------------------------------------------
1 | package com.ilm.sandwich.representation;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Representation of a four-dimensional float-vector
7 | */
8 | public class Vector4f extends Renderable implements Serializable {
9 |
10 | /**
11 | * ID for Serialisation
12 | */
13 | private static final long serialVersionUID = 1L;
14 | /**
15 | * The points.
16 | */
17 | protected float points[] = {0, 0, 0, 0};
18 |
19 | /**
20 | * Instantiates a new vector4f.
21 | *
22 | * @param x the x
23 | * @param y the y
24 | * @param z the z
25 | * @param w the w
26 | */
27 | public Vector4f(float x, float y, float z, float w) {
28 | this.points[0] = x;
29 | this.points[1] = y;
30 | this.points[2] = z;
31 | this.points[3] = w;
32 | }
33 |
34 | /**
35 | * Instantiates a new vector4f.
36 | */
37 | public Vector4f() {
38 | this.points[0] = 0;
39 | this.points[1] = 0;
40 | this.points[2] = 0;
41 | this.points[3] = 0;
42 | }
43 |
44 | public Vector4f(Vector3f vector3f, float w) {
45 | this.points[0] = vector3f.x();
46 | this.points[1] = vector3f.y();
47 | this.points[2] = vector3f.z();
48 | this.points[3] = w;
49 | }
50 |
51 | /**
52 | * To array.
53 | *
54 | * @return the float[]
55 | */
56 | public float[] ToArray() {
57 | return points;
58 | }
59 |
60 | public void copyVec4(Vector4f vec) {
61 | this.points[0] = vec.points[0];
62 | this.points[1] = vec.points[1];
63 | this.points[2] = vec.points[2];
64 | this.points[3] = vec.points[3];
65 | }
66 |
67 | /**
68 | * Adds the.
69 | *
70 | * @param vector the vector
71 | */
72 | public void add(Vector4f vector) {
73 | this.points[0] += vector.points[0];
74 | this.points[1] += vector.points[1];
75 | this.points[2] += vector.points[2];
76 | this.points[3] += vector.points[3];
77 | }
78 |
79 | public void add(Vector3f vector, float w) {
80 | this.points[0] += vector.x();
81 | this.points[1] += vector.y();
82 | this.points[2] += vector.z();
83 | this.points[3] += w;
84 | }
85 |
86 | public void subtract(Vector4f vector) {
87 | this.points[0] -= vector.points[0];
88 | this.points[1] -= vector.points[1];
89 | this.points[2] -= vector.points[2];
90 | this.points[3] -= vector.points[3];
91 | }
92 |
93 | public void subtract(Vector4f vector, Vector4f output) {
94 | output.setXYZW(this.points[0] - vector.points[0], this.points[1] - vector.points[1], this.points[2]
95 | - vector.points[2], this.points[3] - vector.points[3]);
96 | }
97 |
98 | public void subdivide(Vector4f vector) {
99 | this.points[0] /= vector.points[0];
100 | this.points[1] /= vector.points[1];
101 | this.points[2] /= vector.points[2];
102 | this.points[3] /= vector.points[3];
103 | }
104 |
105 | /**
106 | * Multiply by scalar.
107 | *
108 | * @param scalar the scalar
109 | */
110 | public void multiplyByScalar(float scalar) {
111 | this.points[0] *= scalar;
112 | this.points[1] *= scalar;
113 | this.points[2] *= scalar;
114 | this.points[3] *= scalar;
115 | }
116 |
117 | public float dotProduct(Vector4f input) {
118 | return this.points[0] * input.points[0] + this.points[1] * input.points[1] + this.points[2] * input.points[2]
119 | + this.points[3] * input.points[3];
120 | }
121 |
122 | /**
123 | * Linear interpolation between two vectors storing the result in the output variable.
124 | *
125 | * @param input
126 | * @param output
127 | * @param t
128 | */
129 | public void lerp(Vector4f input, Vector4f output, float t) {
130 | output.points[0] = (points[0] * (1.0f * t) + input.points[0] * t);
131 | output.points[1] = (points[1] * (1.0f * t) + input.points[1] * t);
132 | output.points[2] = (points[2] * (1.0f * t) + input.points[2] * t);
133 | output.points[3] = (points[3] * (1.0f * t) + input.points[3] * t);
134 |
135 | }
136 |
137 | /**
138 | * Normalize.
139 | */
140 | public void normalize() {
141 | if (points[3] == 0)
142 | return;
143 |
144 | points[0] /= points[3];
145 | points[1] /= points[3];
146 | points[2] /= points[3];
147 |
148 | double a = Math.sqrt(this.points[0] * this.points[0] + this.points[1] * this.points[1] + this.points[2]
149 | * this.points[2]);
150 | points[0] = (float) (this.points[0] / a);
151 | points[1] = (float) (this.points[1] / a);
152 | points[2] = (float) (this.points[2] / a);
153 | }
154 |
155 | /**
156 | * Gets the x.
157 | *
158 | * @return the x
159 | */
160 | public float getX() {
161 | return this.points[0];
162 | }
163 |
164 | /**
165 | * Sets the x.
166 | *
167 | * @param x the new x
168 | */
169 | public void setX(float x) {
170 | this.points[0] = x;
171 | }
172 |
173 | /**
174 | * Gets the y.
175 | *
176 | * @return the y
177 | */
178 | public float getY() {
179 | return this.points[1];
180 | }
181 |
182 | /**
183 | * Sets the y.
184 | *
185 | * @param y the new y
186 | */
187 | public void setY(float y) {
188 | this.points[1] = y;
189 | }
190 |
191 | /**
192 | * Gets the z.
193 | *
194 | * @return the z
195 | */
196 | public float getZ() {
197 | return this.points[2];
198 | }
199 |
200 | /**
201 | * Sets the z.
202 | *
203 | * @param z the new z
204 | */
205 | public void setZ(float z) {
206 | this.points[2] = z;
207 | }
208 |
209 | /**
210 | * Gets the w.
211 | *
212 | * @return the w
213 | */
214 | public float getW() {
215 | return this.points[3];
216 | }
217 |
218 | /**
219 | * Sets the w.
220 | *
221 | * @param w the new w
222 | */
223 | public void setW(float w) {
224 | this.points[3] = w;
225 | }
226 |
227 | public float x() {
228 | return this.points[0];
229 | }
230 |
231 | public float y() {
232 | return this.points[1];
233 | }
234 |
235 | public float z() {
236 | return this.points[2];
237 | }
238 |
239 | public float w() {
240 | return this.points[3];
241 | }
242 |
243 | public void x(float x) {
244 | this.points[0] = x;
245 | }
246 |
247 | public void y(float y) {
248 | this.points[1] = y;
249 | }
250 |
251 | public void z(float z) {
252 | this.points[2] = z;
253 | }
254 |
255 | public void w(float w) {
256 | this.points[3] = w;
257 | }
258 |
259 | public void setXYZW(float x, float y, float z, float w) {
260 | this.points[0] = x;
261 | this.points[1] = y;
262 | this.points[2] = z;
263 | this.points[3] = w;
264 | }
265 |
266 | /**
267 | * Compare this vector4f to the supplied one
268 | *
269 | * @param rhs True if they match, false other wise.
270 | * @return
271 | */
272 | public boolean compareTo(Vector4f rhs) {
273 | boolean ret = false;
274 | if (this.points[0] == rhs.points[0] && this.points[1] == rhs.points[1] && this.points[2] == rhs.points[2]
275 | && this.points[3] == rhs.points[3])
276 | ret = true;
277 | return ret;
278 | }
279 |
280 | /**
281 | * Copies the data from the supplied vec3 into this vec4 plus the supplied w.
282 | *
283 | * @param input The x y z values to copy in.
284 | * @param w The extra w element to copy in
285 | */
286 | public void copyFromV3f(Vector3f input, float w) {
287 | points[0] = (input.x());
288 | points[1] = (input.y());
289 | points[2] = (input.z());
290 | points[3] = (w);
291 | }
292 |
293 | @Override
294 | public String toString() {
295 | return "X:" + points[0] + " Y:" + points[1] + " Z:" + points[2] + " W:" + points[3];
296 | }
297 |
298 | }
--------------------------------------------------------------------------------
/smartnaviapp/src/main/java/com/ilm/sandwich/representation/Vector3f.java:
--------------------------------------------------------------------------------
1 | package com.ilm.sandwich.representation;
2 |
3 | /**
4 | * 3-dimensional vector with conventient getters and setters. Additionally this class is serializable and
5 | */
6 | public class Vector3f extends Renderable {
7 |
8 | /**
9 | * ID for serialisation
10 | */
11 | private static final long serialVersionUID = -4565578579900616220L;
12 |
13 | /**
14 | * A float array was chosen instead of individual variables due to performance concerns. Converting the points into
15 | * an array at run time can cause slowness so instead we use one array and extract the individual variables with get
16 | * methods.
17 | */
18 | protected float[] points = new float[3];
19 |
20 | /**
21 | * Initialises the vector with the given values
22 | *
23 | * @param x the x-component
24 | * @param y the y-component
25 | * @param z the z-component
26 | */
27 | public Vector3f(float x, float y, float z) {
28 | this.points[0] = x;
29 | this.points[1] = y;
30 | this.points[2] = z;
31 | }
32 |
33 | /**
34 | * Initialises all components of this vector with the given same value.
35 | *
36 | * @param value Initialisation value for all components
37 | */
38 | public Vector3f(float value) {
39 | this.points[0] = value;
40 | this.points[1] = value;
41 | this.points[2] = value;
42 | }
43 |
44 | /**
45 | * Instantiates a new vector3f.
46 | */
47 | public Vector3f() {
48 | }
49 |
50 | /**
51 | * Copy constructor
52 | */
53 | public Vector3f(Vector3f vector) {
54 | this.points[0] = vector.points[0];
55 | this.points[1] = vector.points[1];
56 | this.points[2] = vector.points[2];
57 | }
58 |
59 | /**
60 | * Initialises this vector from a 4-dimensional vector. If the fourth component is not zero, a normalisation of all
61 | * components will be performed.
62 | *
63 | * @param vector The 4-dimensional vector that should be used for initialisation
64 | */
65 | public Vector3f(Vector4f vector) {
66 | if (vector.w() != 0) {
67 | this.points[0] = vector.x() / vector.w();
68 | this.points[1] = vector.y() / vector.w();
69 | this.points[2] = vector.z() / vector.w();
70 | } else {
71 | this.points[0] = vector.x();
72 | this.points[1] = vector.y();
73 | this.points[2] = vector.z();
74 | }
75 | }
76 |
77 | /**
78 | * Returns this vector as float-array.
79 | *
80 | * @return the float[]
81 | */
82 | public float[] toArray() {
83 | return this.points;
84 | }
85 |
86 | /**
87 | * Adds a vector to this vector
88 | *
89 | * @param summand the vector that should be added component-wise
90 | */
91 | public void add(Vector3f summand) {
92 | this.points[0] += summand.points[0];
93 | this.points[1] += summand.points[1];
94 | this.points[2] += summand.points[2];
95 | }
96 |
97 | /**
98 | * Adds the value to all components of this vector
99 | *
100 | * @param summand The value that should be added to all components
101 | */
102 | public void add(float summand) {
103 | this.points[0] += summand;
104 | this.points[1] += summand;
105 | this.points[2] += summand;
106 | }
107 |
108 | /**
109 | * @param subtrahend
110 | */
111 | public void subtract(Vector3f subtrahend) {
112 | this.points[0] -= subtrahend.points[0];
113 | this.points[1] -= subtrahend.points[1];
114 | this.points[2] -= subtrahend.points[2];
115 | }
116 |
117 | /**
118 | * Multiply by scalar.
119 | *
120 | * @param scalar the scalar
121 | */
122 | public void multiplyByScalar(float scalar) {
123 | this.points[0] *= scalar;
124 | this.points[1] *= scalar;
125 | this.points[2] *= scalar;
126 | }
127 |
128 | /**
129 | * Normalize.
130 | */
131 | public void normalize() {
132 |
133 | double a = Math.sqrt(points[0] * points[0] + points[1] * points[1] + points[2] * points[2]);
134 | this.points[0] = (float) (this.points[0] / a);
135 | this.points[1] = (float) (this.points[1] / a);
136 | this.points[2] = (float) (this.points[2] / a);
137 |
138 | }
139 |
140 | /**
141 | * Gets the x.
142 | *
143 | * @return the x
144 | */
145 | public float getX() {
146 | return points[0];
147 | }
148 |
149 | /**
150 | * Sets the x.
151 | *
152 | * @param x the new x
153 | */
154 | public void setX(float x) {
155 | this.points[0] = x;
156 | }
157 |
158 | /**
159 | * Gets the y.
160 | *
161 | * @return the y
162 | */
163 | public float getY() {
164 | return points[1];
165 | }
166 |
167 | /**
168 | * Sets the y.
169 | *
170 | * @param y the new y
171 | */
172 | public void setY(float y) {
173 | this.points[1] = y;
174 | }
175 |
176 | /**
177 | * Gets the z.
178 | *
179 | * @return the z
180 | */
181 | public float getZ() {
182 | return points[2];
183 | }
184 |
185 | /**
186 | * Sets the z.
187 | *
188 | * @param z the new z
189 | */
190 | public void setZ(float z) {
191 | this.points[2] = z;
192 | }
193 |
194 | /**
195 | * Functions for convenience
196 | */
197 |
198 | public float x() {
199 | return this.points[0];
200 | }
201 |
202 | public float y() {
203 | return this.points[1];
204 | }
205 |
206 | public float z() {
207 | return this.points[2];
208 | }
209 |
210 | public void x(float x) {
211 | this.points[0] = x;
212 | }
213 |
214 | public void y(float y) {
215 | this.points[1] = y;
216 | }
217 |
218 | public void z(float z) {
219 | this.points[2] = z;
220 | }
221 |
222 | public void setXYZ(float x, float y, float z) {
223 | this.points[0] = x;
224 | this.points[1] = y;
225 | this.points[2] = z;
226 | }
227 |
228 | /**
229 | * Return the dot product of this vector with the input vector
230 | *
231 | * @param inputVec The vector you want to do the dot product with against this vector.
232 | * @return Float value representing the scalar of the dot product operation
233 | */
234 | public float dotProduct(Vector3f inputVec) {
235 | return points[0] * inputVec.points[0] + points[1] * inputVec.points[1] + points[2] * inputVec.points[2];
236 |
237 | }
238 |
239 | /**
240 | * Get the cross product of this vector and another vector. The result will be stored in the output vector.
241 | *
242 | * @param inputVec The vector you want to get the dot product of against this vector.
243 | * @param outputVec The vector to store the result in.
244 | */
245 | public void crossProduct(Vector3f inputVec, Vector3f outputVec) {
246 | outputVec.setX(points[1] * inputVec.points[2] - points[2] * inputVec.points[1]);
247 | outputVec.setY(points[2] * inputVec.points[0] - points[0] * inputVec.points[2]);
248 | outputVec.setZ(points[0] * inputVec.points[1] - points[1] * inputVec.points[0]);
249 | }
250 |
251 | public Vector3f crossProduct(Vector3f in) {
252 | Vector3f out = new Vector3f();
253 | crossProduct(in, out);
254 | return out;
255 | }
256 |
257 | /**
258 | * If you need to get the length of a vector then use this function.
259 | *
260 | * @return The length of the vector
261 | */
262 | public float getLength() {
263 | return (float) Math.sqrt(points[0] * points[0] + points[1] * points[1] + points[2] * points[2]);
264 | }
265 |
266 | @Override
267 | public String toString() {
268 | return "X:" + points[0] + " Y:" + points[1] + " Z:" + points[2];
269 | }
270 |
271 | /**
272 | * Clone the input vector so that this vector has the same values.
273 | *
274 | * @param source The vector you want to clone.
275 | */
276 | public void clone(Vector3f source) {
277 | // this.points[0] = source.points[0];
278 | // this.points[1] = source.points[1];
279 | // this.points[2] = source.points[2];
280 | System.arraycopy(source.points, 0, points, 0, 3);
281 | }
282 |
283 | /**
284 | * Clone the input vector so that this vector has the same values.
285 | *
286 | * @param source The vector you want to clone.
287 | */
288 | public void clone(float[] source) {
289 | // this.points[0] = source[0];
290 | // this.points[1] = source[1];
291 | // this.points[2] = source[2];
292 | System.arraycopy(source, 0, points, 0, 3);
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/res/layout/activity_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
21 |
22 |
31 |
32 |
41 |
42 |
54 |
55 |
68 |
69 |
70 |
83 |
84 |
85 |
86 |
96 |
97 |
111 |
112 |
127 |
128 |
143 |
144 |
145 |
146 |
157 |
158 |
173 |
174 |
187 |
188 |
204 |
205 |
206 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | **SmartNavi - Copyright 2011-2016 Christian Henke**
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
15 | Images
16 | ==============
17 | Images, if not stated otherwise, are licensed under
18 | Creative Commons Attribution-ShareAlike 4.0 International License
19 | http://creativecommons.org/licenses/by-sa/4.0/
20 |
21 | Libraries
22 | ==============
23 | SmartNavi makes use of the following libraries and classes under their corresponding licenses:
24 | * Android DirectoryChooser - Apache License 2.0
25 | * The class ImprovedOrientationSensor2Provider and connected classes are developed by Alexander Pacha under the MIT License
26 |
27 |
28 | Apache License
29 | ==============
30 |
31 | Apache License
32 | Version 2.0, January 2004
33 | http://www.apache.org/licenses/
34 |
35 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
36 |
37 | 1. Definitions.
38 |
39 | "License" shall mean the terms and conditions for use, reproduction,
40 | and distribution as defined by Sections 1 through 9 of this document.
41 |
42 | "Licensor" shall mean the copyright owner or entity authorized by
43 | the copyright owner that is granting the License.
44 |
45 | "Legal Entity" shall mean the union of the acting entity and all
46 | other entities that control, are controlled by, or are under common
47 | control with that entity. For the purposes of this definition,
48 | "control" means (i) the power, direct or indirect, to cause the
49 | direction or management of such entity, whether by contract or
50 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
51 | outstanding shares, or (iii) beneficial ownership of such entity.
52 |
53 | "You" (or "Your") shall mean an individual or Legal Entity
54 | exercising permissions granted by this License.
55 |
56 | "Source" form shall mean the preferred form for making modifications,
57 | including but not limited to software source code, documentation
58 | source, and configuration files.
59 |
60 | "Object" form shall mean any form resulting from mechanical
61 | transformation or translation of a Source form, including but
62 | not limited to compiled object code, generated documentation,
63 | and conversions to other media types.
64 |
65 | "Work" shall mean the work of authorship, whether in Source or
66 | Object form, made available under the License, as indicated by a
67 | copyright notice that is included in or attached to the work
68 | (an example is provided in the Appendix below).
69 |
70 | "Derivative Works" shall mean any work, whether in Source or Object
71 | form, that is based on (or derived from) the Work and for which the
72 | editorial revisions, annotations, elaborations, or other modifications
73 | represent, as a whole, an original work of authorship. For the purposes
74 | of this License, Derivative Works shall not include works that remain
75 | separable from, or merely link (or bind by name) to the interfaces of,
76 | the Work and Derivative Works thereof.
77 |
78 | "Contribution" shall mean any work of authorship, including
79 | the original version of the Work and any modifications or additions
80 | to that Work or Derivative Works thereof, that is intentionally
81 | submitted to Licensor for inclusion in the Work by the copyright owner
82 | or by an individual or Legal Entity authorized to submit on behalf of
83 | the copyright owner. For the purposes of this definition, "submitted"
84 | means any form of electronic, verbal, or written communication sent
85 | to the Licensor or its representatives, including but not limited to
86 | communication on electronic mailing lists, source code control systems,
87 | and issue tracking systems that are managed by, or on behalf of, the
88 | Licensor for the purpose of discussing and improving the Work, but
89 | excluding communication that is conspicuously marked or otherwise
90 | designated in writing by the copyright owner as "Not a Contribution."
91 |
92 | "Contributor" shall mean Licensor and any individual or Legal Entity
93 | on behalf of whom a Contribution has been received by Licensor and
94 | subsequently incorporated within the Work.
95 |
96 | 2. Grant of Copyright License. Subject to the terms and conditions of
97 | this License, each Contributor hereby grants to You a perpetual,
98 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
99 | copyright license to reproduce, prepare Derivative Works of,
100 | publicly display, publicly perform, sublicense, and distribute the
101 | Work and such Derivative Works in Source or Object form.
102 |
103 | 3. Grant of Patent License. Subject to the terms and conditions of
104 | this License, each Contributor hereby grants to You a perpetual,
105 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
106 | (except as stated in this section) patent license to make, have made,
107 | use, offer to sell, sell, import, and otherwise transfer the Work,
108 | where such license applies only to those patent claims licensable
109 | by such Contributor that are necessarily infringed by their
110 | Contribution(s) alone or by combination of their Contribution(s)
111 | with the Work to which such Contribution(s) was submitted. If You
112 | institute patent litigation against any entity (including a
113 | cross-claim or counterclaim in a lawsuit) alleging that the Work
114 | or a Contribution incorporated within the Work constitutes direct
115 | or contributory patent infringement, then any patent licenses
116 | granted to You under this License for that Work shall terminate
117 | as of the date such litigation is filed.
118 |
119 | 4. Redistribution. You may reproduce and distribute copies of the
120 | Work or Derivative Works thereof in any medium, with or without
121 | modifications, and in Source or Object form, provided that You
122 | meet the following conditions:
123 |
124 | (a) You must give any other recipients of the Work or
125 | Derivative Works a copy of this License; and
126 |
127 | (b) You must cause any modified files to carry prominent notices
128 | stating that You changed the files; and
129 |
130 | (c) You must retain, in the Source form of any Derivative Works
131 | that You distribute, all copyright, patent, trademark, and
132 | attribution notices from the Source form of the Work,
133 | excluding those notices that do not pertain to any part of
134 | the Derivative Works; and
135 |
136 | (d) If the Work includes a "NOTICE" text file as part of its
137 | distribution, then any Derivative Works that You distribute must
138 | include a readable copy of the attribution notices contained
139 | within such NOTICE file, excluding those notices that do not
140 | pertain to any part of the Derivative Works, in at least one
141 | of the following places: within a NOTICE text file distributed
142 | as part of the Derivative Works; within the Source form or
143 | documentation, if provided along with the Derivative Works; or,
144 | within a display generated by the Derivative Works, if and
145 | wherever such third-party notices normally appear. The contents
146 | of the NOTICE file are for informational purposes only and
147 | do not modify the License. You may add Your own attribution
148 | notices within Derivative Works that You distribute, alongside
149 | or as an addendum to the NOTICE text from the Work, provided
150 | that such additional attribution notices cannot be construed
151 | as modifying the License.
152 |
153 | You may add Your own copyright statement to Your modifications and
154 | may provide additional or different license terms and conditions
155 | for use, reproduction, or distribution of Your modifications, or
156 | for any such Derivative Works as a whole, provided Your use,
157 | reproduction, and distribution of the Work otherwise complies with
158 | the conditions stated in this License.
159 |
160 | 5. Submission of Contributions. Unless You explicitly state otherwise,
161 | any Contribution intentionally submitted for inclusion in the Work
162 | by You to the Licensor shall be under the terms and conditions of
163 | this License, without any additional terms or conditions.
164 | Notwithstanding the above, nothing herein shall supersede or modify
165 | the terms of any separate license agreement you may have executed
166 | with Licensor regarding such Contributions.
167 |
168 | 6. Trademarks. This License does not grant permission to use the trade
169 | names, trademarks, service marks, or product names of the Licensor,
170 | except as required for reasonable and customary use in describing the
171 | origin of the Work and reproducing the content of the NOTICE file.
172 |
173 | 7. Disclaimer of Warranty. Unless required by applicable law or
174 | agreed to in writing, Licensor provides the Work (and each
175 | Contributor provides its Contributions) on an "AS IS" BASIS,
176 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
177 | implied, including, without limitation, any warranties or conditions
178 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
179 | PARTICULAR PURPOSE. You are solely responsible for determining the
180 | appropriateness of using or redistributing the Work and assume any
181 | risks associated with Your exercise of permissions under this License.
182 |
183 | 8. Limitation of Liability. In no event and under no legal theory,
184 | whether in tort (including negligence), contract, or otherwise,
185 | unless required by applicable law (such as deliberate and grossly
186 | negligent acts) or agreed to in writing, shall any Contributor be
187 | liable to You for damages, including any direct, indirect, special,
188 | incidental, or consequential damages of any character arising as a
189 | result of this License or out of the use or inability to use the
190 | Work (including but not limited to damages for loss of goodwill,
191 | work stoppage, computer failure or malfunction, or any and all
192 | other commercial damages or losses), even if such Contributor
193 | has been advised of the possibility of such damages.
194 |
195 | 9. Accepting Warranty or Additional Liability. While redistributing
196 | the Work or Derivative Works thereof, You may choose to offer,
197 | and charge a fee for, acceptance of support, warranty, indemnity,
198 | or other liability obligations and/or rights consistent with this
199 | License. However, in accepting such obligations, You may act only
200 | on Your own behalf and on Your sole responsibility, not on behalf
201 | of any other Contributor, and only if You agree to indemnify,
202 | defend, and hold each Contributor harmless for any liability
203 | incurred by, or claims asserted against, such Contributor by reason
204 | of your accepting any such warranty or additional liability.
205 |
206 | END OF TERMS AND CONDITIONS
207 |
208 |
209 | MIT License
210 | ===============
211 |
212 | The MIT License (MIT)
213 | Copyright (c)
214 |
215 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
216 |
217 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
218 |
219 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
220 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/java/com/ilm/sandwich/sensors/ImprovedOrientationSensor2Provider.java:
--------------------------------------------------------------------------------
1 | package com.ilm.sandwich.sensors;
2 |
3 | import android.hardware.Sensor;
4 | import android.hardware.SensorEvent;
5 | import android.hardware.SensorManager;
6 | import android.util.Log;
7 |
8 | import com.ilm.sandwich.representation.Quaternion;
9 |
10 | /**
11 | * The orientation provider that delivers the absolute orientation from the {@link Sensor#TYPE_GYROSCOPE
12 | * Gyroscope} and {@link Sensor#TYPE_ROTATION_VECTOR Android Rotation Vector sensor}.
13 | *
14 | * It mainly relies on the gyroscope, but corrects with the Android Rotation Vector which also provides an absolute
15 | * estimation of current orientation. The correction is a static weight.
16 | *
17 | * @author Alexander Pacha
18 | */
19 | public class ImprovedOrientationSensor2Provider extends OrientationProvider {
20 |
21 | /**
22 | * Constant specifying the factor between a Nano-second and a second
23 | */
24 | private static final float NS2S = 1.0f / 1000000000.0f;
25 | /**
26 | * This is a filter-threshold for discarding Gyroscope measurements that are below a certain level and
27 | * potentially are only noise and not real motion. Values from the gyroscope are usually between 0 (stop) and
28 | * 10 (rapid rotation), so 0.1 seems to be a reasonable threshold to filter noise (usually smaller than 0.1) and
29 | * real motion (usually > 0.1). Note that there is a chance of missing real motion, if the use is turning the
30 | * device really slowly, so this value has to find a balance between accepting noise (threshold = 0) and missing
31 | * slow user-action (threshold > 0.5). 0.1 seems to work fine for most applications.
32 | */
33 | private static final double EPSILON = 0.1f;
34 | /**
35 | * This weight determines indirectly how much the rotation sensor will be used to correct. This weight will be
36 | * multiplied by the velocity to obtain the actual weight. (in sensor-fusion-scenario 2 -
37 | * SensorSelection.GyroscopeAndRotationVector2).
38 | * Must be a value between 0 and approx. 0.04 (because, if multiplied with a velocity of up to 25, should be still
39 | * less than 1, otherwise the SLERP will not correctly interpolate). Should be close to zero.
40 | */
41 | private static final float INDIRECT_INTERPOLATION_WEIGHT = 0.01f;
42 | /**
43 | * The threshold that indicates an outlier of the rotation vector. If the dot-product between the two vectors
44 | * (gyroscope orientation and rotationVector orientation) falls below this threshold (ideally it should be 1,
45 | * if they are exactly the same) the system falls back to the gyroscope values only and just ignores the
46 | * rotation vector.
47 | *
48 | * This value should be quite high (> 0.7) to filter even the slightest discrepancies that causes jumps when
49 | * tiling the device. Possible values are between 0 and 1, where a value close to 1 means that even a very small
50 | * difference between the two sensors will be treated as outlier, whereas a value close to zero means that the
51 | * almost any discrepancy between the two sensors is tolerated.
52 | */
53 | private static final float OUTLIER_THRESHOLD = 0.85f;
54 | /**
55 | * The threshold that indicates a massive discrepancy between the rotation vector and the gyroscope orientation.
56 | * If the dot-product between the two vectors
57 | * (gyroscope orientation and rotationVector orientation) falls below this threshold (ideally it should be 1, if
58 | * they are exactly the same), the system will start increasing the panic counter (that probably indicates a
59 | * gyroscope failure).
60 | *
61 | * This value should be lower than OUTLIER_THRESHOLD (0.5 - 0.7) to only start increasing the panic counter,
62 | * when there is a huge discrepancy between the two fused sensors.
63 | */
64 | private static final float OUTLIER_PANIC_THRESHOLD = 0.75f;
65 | /**
66 | * The threshold that indicates that a chaos state has been established rather than just a temporary peak in the
67 | * rotation vector (caused by exploding angled during fast tilting).
68 | *
69 | * If the chaosCounter is bigger than this threshold, the current position will be reset to whatever the
70 | * rotation vector indicates.
71 | */
72 | private static final int PANIC_THRESHOLD = 60;
73 | private static float[] RMatrixRemapped = new float[16];
74 | private static float[] orientation = new float[3];
75 | /**
76 | * The quaternion that stores the difference that is obtained by the gyroscope.
77 | * Basically it contains a rotational difference encoded into a quaternion.
78 | *
79 | * To obtain the absolute orientation one must add this into an initial position by
80 | * multiplying it with another quaternion
81 | */
82 | private final Quaternion deltaQuaternion = new Quaternion();
83 | /**
84 | * The Quaternions that contain the current rotation (Angle and axis in Quaternion format) of the Gyroscope
85 | */
86 | private Quaternion quaternionGyroscope = new Quaternion();
87 | /**
88 | * The quaternion that contains the absolute orientation as obtained by the rotationVector sensor.
89 | */
90 | private Quaternion quaternionRotationVector = new Quaternion();
91 | /**
92 | * The time-stamp being used to record the time when the last gyroscope event occurred.
93 | */
94 | private long timestamp;
95 | /**
96 | * Value giving the total velocity of the gyroscope (will be high, when the device is moving fast and low when
97 | * the device is standing still). This is usually a value between 0 and 10 for normal motion. Heavy shaking can
98 | * increase it to about 25. Keep in mind, that these values are time-depended, so changing the sampling rate of
99 | * the sensor will affect this value!
100 | */
101 | private double gyroscopeRotationVelocity = 0;
102 | /**
103 | * Flag indicating, whether the orientations were initialised from the rotation vector or not. If false, the
104 | * gyroscope can not be used (since it's only meaningful to calculateAzimuth differences from an initial state). If
105 | * true,
106 | * the gyroscope can be used normally.
107 | */
108 | private boolean positionInitialised = false;
109 | /**
110 | * Counter that sums the number of consecutive frames, where the rotationVector and the gyroscope were
111 | * significantly different (and the dot-product was smaller than 0.7). This event can either happen when the
112 | * angles of the rotation vector explode (e.g. during fast tilting) or when the device was shaken heavily and
113 | * the gyroscope is now completely off.
114 | */
115 | private int panicCounter;
116 |
117 |
118 | /**
119 | * Initialises a new ImprovedOrientationSensor2Provider
120 | *
121 | * @param sensorManager The android sensor manager
122 | */
123 | public ImprovedOrientationSensor2Provider(SensorManager sensorManager) {
124 | super(sensorManager);
125 |
126 | //Add the gyroscope and rotation Vector
127 | sensorList.add(sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE));
128 | sensorList.add(sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR));
129 | }
130 |
131 | @Override
132 | public void onSensorChanged(SensorEvent event) {
133 |
134 | if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
135 | // Process rotation vector (just safe it)
136 |
137 | float[] q = new float[4];
138 | // Calculate angle. Starting with API_18, Android will provide this value as event.values[3], but if not, we have to calculateAzimuth it manually.
139 | SensorManager.getQuaternionFromVector(q, event.values);
140 |
141 | // Store in quaternion
142 | quaternionRotationVector.setXYZW(q[1], q[2], q[3], -q[0]);
143 | if (!positionInitialised) {
144 | // Override
145 | quaternionGyroscope.set(quaternionRotationVector);
146 | positionInitialised = true;
147 | }
148 |
149 | } else if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
150 | // Process Gyroscope and perform fusion
151 |
152 | // This timestep's delta rotation to be multiplied by the current rotation
153 | // after computing it from the gyro sample data.
154 | if (timestamp != 0) {
155 | final float dT = (event.timestamp - timestamp) * NS2S;
156 | // Axis of the rotation sample, not normalized yet.
157 | float axisX = event.values[0];
158 | float axisY = event.values[1];
159 | float axisZ = event.values[2];
160 |
161 | // Calculate the angular speed of the sample
162 | gyroscopeRotationVelocity = Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);
163 |
164 | // Normalize the rotation vector if it's big enough to get the axis
165 | if (gyroscopeRotationVelocity > EPSILON) {
166 | axisX /= gyroscopeRotationVelocity;
167 | axisY /= gyroscopeRotationVelocity;
168 | axisZ /= gyroscopeRotationVelocity;
169 | }
170 |
171 | // Integrate around this axis with the angular speed by the timestep
172 | // in order to get a delta rotation from this sample over the timestep
173 | // We will convert this axis-angle representation of the delta rotation
174 | // into a quaternion before turning it into the rotation matrix.
175 | double thetaOverTwo = gyroscopeRotationVelocity * dT / 2.0f;
176 | double sinThetaOverTwo = Math.sin(thetaOverTwo);
177 | double cosThetaOverTwo = Math.cos(thetaOverTwo);
178 | deltaQuaternion.setX((float) (sinThetaOverTwo * axisX));
179 | deltaQuaternion.setY((float) (sinThetaOverTwo * axisY));
180 | deltaQuaternion.setZ((float) (sinThetaOverTwo * axisZ));
181 | deltaQuaternion.setW(-(float) cosThetaOverTwo);
182 |
183 | // Move current gyro orientation
184 | deltaQuaternion.multiplyByQuat(quaternionGyroscope, quaternionGyroscope);
185 |
186 | // Calculate dot-product to calculateAzimuth whether the two orientation sensors have diverged
187 | // (if the dot-product is closer to 0 than to 1), because it should be close to 1 if both are the same.
188 | float dotProd = quaternionGyroscope.dotProduct(quaternionRotationVector);
189 |
190 | // If they have diverged, rely on gyroscope only (this happens on some devices when the rotation vector "jumps").
191 | if (Math.abs(dotProd) < OUTLIER_THRESHOLD) {
192 | // Increase panic counter
193 | if (Math.abs(dotProd) < OUTLIER_PANIC_THRESHOLD) {
194 | panicCounter++;
195 | }
196 |
197 | // Directly use Gyro
198 | setOrientationQuaternionAndMatrix(quaternionGyroscope);
199 |
200 | } else {
201 | // Both are nearly saying the same. Perform normal fusion.
202 |
203 | // Interpolate with a fixed weight between the two absolute quaternions obtained from gyro and rotation vector sensors
204 | // The weight should be quite low, so the rotation vector corrects the gyro only slowly, and the output keeps responsive.
205 | Quaternion interpolate = new Quaternion();
206 | quaternionGyroscope.slerp(quaternionRotationVector, interpolate,
207 | (float) (INDIRECT_INTERPOLATION_WEIGHT * gyroscopeRotationVelocity));
208 |
209 | // Use the interpolated value between gyro and rotationVector
210 | setOrientationQuaternionAndMatrix(interpolate);
211 | // Override current gyroscope-orientation
212 | quaternionGyroscope.copyVec4(interpolate);
213 |
214 | // Reset the panic counter because both sensors are saying the same again
215 | panicCounter = 0;
216 | }
217 |
218 | if (panicCounter > PANIC_THRESHOLD) {
219 | Log.d("Rotation Vector",
220 | "Panic counter is bigger than threshold; this indicates a Gyroscope failure. Panic reset is imminent.");
221 |
222 | if (gyroscopeRotationVelocity < 3) {
223 | Log.d("Rotation Vector",
224 | "Performing Panic-reset. Resetting orientation to rotation-vector value.");
225 |
226 | // Manually set position to whatever rotation vector says.
227 | setOrientationQuaternionAndMatrix(quaternionRotationVector);
228 | // Override current gyroscope-orientation with corrected value
229 | quaternionGyroscope.copyVec4(quaternionRotationVector);
230 |
231 | panicCounter = 0;
232 | } else {
233 | Log.d("Rotation Vector",
234 | String.format(
235 | "Panic reset delayed due to ongoing motion (user is still shaking the device). Gyroscope Velocity: %.2f > 3",
236 | gyroscopeRotationVelocity));
237 | }
238 | }
239 | }
240 | timestamp = event.timestamp;
241 | }
242 | }
243 |
244 | /**
245 | * Sets the output quaternion and matrix with the provided quaternion and synchronises the setting
246 | *
247 | * @param quaternion The Quaternion to set (the result of the sensor fusion)
248 | */
249 | private void setOrientationQuaternionAndMatrix(Quaternion quaternion) {
250 | Quaternion correctedQuat = quaternion.clone();
251 | // We inverted w in the deltaQuaternion, because currentOrientationQuaternion required it.
252 | // Before converting it back to matrix representation, we need to revert this process
253 | correctedQuat.w(-correctedQuat.w());
254 |
255 | synchronized (syncToken) {
256 | // Use gyro only
257 | currentOrientationQuaternion.copyVec4(quaternion);
258 |
259 | // Set the rotation matrix as well to have both representations
260 | SensorManager.getRotationMatrixFromVector(currentOrientationRotationMatrix.matrix, correctedQuat.ToArray());
261 | }
262 | }
263 |
264 | public float getAzimuth(float decl) {
265 | SensorManager.remapCoordinateSystem(currentOrientationRotationMatrix.matrix, SensorManager.AXIS_X, SensorManager.AXIS_Y, RMatrixRemapped);
266 | SensorManager.getOrientation(RMatrixRemapped, orientation);
267 | if (orientation[0] >= 0) {
268 | // Azimuth-Calculation (rad in degree) + difference to true north (decl)
269 | return (orientation[0] * 57.29577951f + decl);
270 | } else {
271 | // Azimuth-Calculation (rad in degree) +360 + difference to true north (decl)
272 | return (orientation[0] * 57.29577951f + 360 + decl);
273 | }
274 |
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/smartnaviapp/src/main/java/com/ilm/sandwich/fragments/TutorialFragment.java:
--------------------------------------------------------------------------------
1 | package com.ilm.sandwich.fragments;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 | import android.view.KeyEvent;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.view.inputmethod.EditorInfo;
12 | import android.view.inputmethod.InputMethodManager;
13 | import android.widget.AdapterView;
14 | import android.widget.ArrayAdapter;
15 | import android.widget.Button;
16 | import android.widget.EditText;
17 | import android.widget.Spinner;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import androidx.annotation.Nullable;
22 | import androidx.fragment.app.Fragment;
23 |
24 | import com.google.firebase.analytics.FirebaseAnalytics;
25 | import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
26 | import com.ilm.sandwich.BuildConfig;
27 | import com.ilm.sandwich.R;
28 | import com.ilm.sandwich.sensors.Core;
29 |
30 | import java.text.DecimalFormat;
31 | import java.util.Locale;
32 |
33 | /**
34 | * Fragment to show TutorialFragment for first app start or if requested by user.
35 | */
36 | public class TutorialFragment extends Fragment {
37 |
38 | static DecimalFormat df0 = new DecimalFormat("0");
39 | private onTutorialFinishedListener mListener;
40 | private View tutorialOverlay;
41 | private View welcomeView;
42 | private boolean metricUnits = true;
43 | private View fragmentView;
44 | private FirebaseAnalytics mFirebaseAnalytics;
45 |
46 | public TutorialFragment() {
47 | }
48 |
49 | @Override
50 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
51 | super.onViewCreated(view, savedInstanceState);
52 | fragmentView = view;
53 |
54 | // Obtain the FirebaseAnalytics instance.
55 | mFirebaseAnalytics = FirebaseAnalytics.getInstance(view.getContext());
56 | mFirebaseAnalytics.logEvent("Tutorial_Start", null);
57 |
58 | startTutorial(view);
59 | }
60 |
61 | @Override
62 | public void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | }
65 |
66 | @Override
67 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
68 | Bundle savedInstanceState) {
69 | // Inflate the layout for this fragment
70 | return inflater.inflate(R.layout.fragment_tutorial, container, false);
71 | }
72 |
73 | @Override
74 | public void onAttach(Context context) {
75 | super.onAttach(context);
76 | if (context instanceof onTutorialFinishedListener) {
77 | mListener = (onTutorialFinishedListener) context;
78 | } else {
79 | throw new RuntimeException(context.toString()
80 | + " must implement OnFragmentInteractionListener");
81 | }
82 | }
83 |
84 | @Override
85 | public void onDetach() {
86 | super.onDetach();
87 | mListener = null;
88 | }
89 |
90 | private void startTutorial(View view) {
91 | //Remote Config for AB Testing for Tutorial Wording
92 | FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
93 | boolean showTutorialPage1 = mFirebaseRemoteConfig.getBoolean("tutorial_page1_shown");
94 | Log.i("Tutorial AB Test", "showTutorialPage1 = " + showTutorialPage1);
95 | if (showTutorialPage1) {
96 | //AB TEst about hiding the first page, proceed normally and show page 1
97 | welcomeView = view.findViewById(R.id.welcomeView);
98 | welcomeView.setVisibility(View.VISIBLE);
99 |
100 | mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.TUTORIAL_BEGIN, null);
101 |
102 | Button welcomeButton = view.findViewById(R.id.welcomeButton);
103 | welcomeButton.setOnClickListener(new View.OnClickListener() {
104 | @Override
105 | public void onClick(View view) {
106 | mFirebaseAnalytics.logEvent("Tutorial_Button1_pressed", null);
107 | welcomeView.setVisibility(View.INVISIBLE);
108 | tutorialOverlay = fragmentView.findViewById(R.id.tutorialOverlay);
109 | tutorialOverlay.setVisibility(View.VISIBLE);
110 | }
111 | });
112 |
113 | } else {
114 | //AB TEst with hiding the first page, just show page 2
115 | tutorialOverlay = fragmentView.findViewById(R.id.tutorialOverlay);
116 | tutorialOverlay.setVisibility(View.VISIBLE);
117 | }
118 |
119 |
120 | SharedPreferences settings = this.getActivity().getSharedPreferences(this.getActivity().getPackageName() + "_preferences", Context.MODE_PRIVATE);
121 | String stepLengthString = settings.getString("step_length", null);
122 | Spinner spinner = view.findViewById(R.id.tutorialSpinner);
123 | // Create an ArrayAdapter using the string array and a default spinner layout
124 | ArrayAdapter adapter = ArrayAdapter.createFromResource(this.getActivity(), R.array.dimension, android.R.layout.simple_spinner_item);
125 | // Specify the layout to use when the list of choices appears
126 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
127 | // Apply the adapter to the spinner
128 | spinner.setAdapter(adapter);
129 | if (stepLengthString != null) {
130 | try {
131 | if (stepLengthString.contains("'")) {
132 | EditText editText = view.findViewById(R.id.tutorialEditText);
133 | editText.setText(stepLengthString, TextView.BufferType.EDITABLE);
134 | spinner.setSelection(1);
135 | } else {
136 | stepLengthString = stepLengthString.replace(",", ".");
137 | int savedBodyHeight = Integer.parseInt(stepLengthString);
138 | String savedBodyHeightString = "" + savedBodyHeight;
139 | EditText editText = view.findViewById(R.id.tutorialEditText);
140 | editText.setText(savedBodyHeightString, TextView.BufferType.EDITABLE);
141 | spinner.setSelection(0);
142 | }
143 | } catch (Exception e) {
144 | e.printStackTrace();
145 | }
146 | } else {
147 | //For US users switch to foot inch bodyheight
148 | Log.i("Language", "" + Locale.getDefault().getISO3Country());
149 | if (Locale.getDefault().getISO3Country().contains("USA")) {
150 | spinner.setSelection(1, false);
151 | metricUnits = false;
152 | }
153 | }
154 | spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
155 | @Override
156 | public void onItemSelected(AdapterView> arg0, View arg1, int arg2, long arg3) {
157 | if (arg2 == 0) metricUnits = true;
158 | else metricUnits = false;
159 | }
160 | @Override
161 | public void onNothingSelected(AdapterView> arg0) {
162 | }
163 | });
164 |
165 | Button startButton = view.findViewById(R.id.startbutton);
166 | startButton.setOnClickListener(new View.OnClickListener() {
167 |
168 | @Override
169 | public void onClick(View v) {
170 |
171 |
172 | mFirebaseAnalytics.logEvent("Tutorial_Button2_pressed", null);
173 | boolean tutorialDone = false;
174 | final EditText heightField = fragmentView.findViewById(R.id.tutorialEditText);
175 | int op = heightField.length();
176 | float number;
177 | if (op != 0) {
178 | if (metricUnits) {
179 | try {
180 | number = Float.valueOf(heightField.getText().toString());
181 | if (number < 241 && number > 49) {
182 | String numberString = df0.format(number);
183 | fragmentView.getContext().getSharedPreferences(fragmentView.getContext().getPackageName() + "_preferences", Context.MODE_PRIVATE).edit().putString("step_length", numberString).commit();
184 | Core.stepLength = (number / 222);
185 | tutorialDone = true;
186 | } else {
187 | Toast.makeText(fragmentView.getContext(), fragmentView.getContext().getResources().getString(R.string.tx_10), Toast.LENGTH_LONG).show();
188 | }
189 | } catch (NumberFormatException e) {
190 | if (BuildConfig.debug)
191 | Toast.makeText(fragmentView.getContext(), fragmentView.getContext().getResources().getString(R.string.tx_32), Toast.LENGTH_LONG).show();
192 | }
193 | } else {
194 | try {
195 | String numberString = heightField.getText().toString();
196 | fragmentView.getContext().getSharedPreferences(TutorialFragment.this.getActivity().getPackageName() + "_preferences", Context.MODE_PRIVATE).edit().putString("step_length", numberString).apply();
197 | String[] feetInchString = numberString.split("'");
198 | String feetString = feetInchString[0];
199 | float feet = Float.valueOf(feetString);
200 |
201 | //Check if user provided inch, if so set that. If not assume 0
202 | float inch = 0;
203 | if (feetInchString.length > 1) {
204 | String inchString = feetInchString[1];
205 | inch = Float.valueOf(inchString);
206 | } else {
207 | inch = 0;
208 | }
209 | float totalInch = 12 * feet + inch;
210 | Core.stepLength = (float) (totalInch * 2.54 / 222);
211 | tutorialDone = true;
212 | } catch (NumberFormatException e) {
213 | if (BuildConfig.debug)
214 | Toast.makeText(fragmentView.getContext(), fragmentView.getContext().getResources().getString(R.string.tx_32), Toast.LENGTH_LONG).show();
215 | }
216 | }
217 | if (BuildConfig.DEBUG) Log.i("Step length", "Step length = " + Core.stepLength);
218 | } else {
219 | Toast.makeText(fragmentView.getContext(), fragmentView.getContext().getResources().getString(R.string.tx_10), Toast.LENGTH_LONG).show();
220 | }
221 |
222 | if (tutorialDone) {
223 | // TutorialFragment finished
224 | mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.TUTORIAL_COMPLETE, null);
225 | if (mListener != null) {
226 | mListener.onTutorialFinished();
227 | }
228 | }
229 | }
230 | });
231 |
232 | final EditText bodyHeightField = view.findViewById(R.id.tutorialEditText);
233 | bodyHeightField.setOnEditorActionListener(new TextView.OnEditorActionListener() {
234 | @Override
235 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
236 | EditText bodyHeightField = fragmentView.findViewById(R.id.tutorialEditText);
237 | if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_SEND || actionId == EditorInfo.IME_ACTION_NEXT) {
238 | try {
239 | InputMethodManager inputManager = (InputMethodManager) fragmentView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
240 | try {
241 | inputManager.hideSoftInputFromWindow(fragmentView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
242 | } catch (Exception e) {
243 | e.printStackTrace();
244 | }
245 | //Workaround Coursor out off textfield
246 | bodyHeightField.setFocusable(false);
247 | bodyHeightField.setFocusableInTouchMode(true);
248 | bodyHeightField.setFocusable(true);
249 | } catch (Exception e) {
250 | e.printStackTrace();
251 | }
252 | }
253 | return false;
254 | }
255 | });
256 |
257 | bodyHeightField.setOnKeyListener(new View.OnKeyListener() {
258 | @Override
259 | public boolean onKey(View v, int keyCode, KeyEvent event) {
260 | if (!metricUnits) {
261 | if (event.getKeyCode() != KeyEvent.KEYCODE_DEL) {
262 | String input = bodyHeightField.getText().toString();
263 | if (input.length() == 1) {
264 | input = input + "'";
265 | bodyHeightField.setText(input);
266 | int pos = bodyHeightField.getText().length();
267 | bodyHeightField.setSelection(pos);
268 | }
269 | }
270 | }
271 | return false;
272 | }
273 | });
274 |
275 | //If remoteConfig String is NOT EMPTY, then use it.
276 | //Page1
277 | if (!mFirebaseRemoteConfig.getString("step1_text1").equalsIgnoreCase("")) {
278 | TextView s1t1 = view.findViewById(R.id.welcomeNumber1Text);
279 | s1t1.setText(mFirebaseRemoteConfig.getString("step1_text1"));
280 | }
281 | if (!mFirebaseRemoteConfig.getString("step1_text2").equalsIgnoreCase("")) {
282 | TextView s1t1 = view.findViewById(R.id.welcomeNumber2TextBarText);
283 | s1t1.setText(mFirebaseRemoteConfig.getString("step1_text2"));
284 | }
285 | if (!mFirebaseRemoteConfig.getString("step1_text3").equalsIgnoreCase("")) {
286 | TextView s1t1 = view.findViewById(R.id.welcomeNumber4Text);
287 | s1t1.setText(mFirebaseRemoteConfig.getString("step1_text3"));
288 | }
289 | //Page 2
290 | if (!mFirebaseRemoteConfig.getString("step2_text1").equalsIgnoreCase("")) {
291 | TextView s1t1 = view.findViewById(R.id.tutorialNumber1Text);
292 | s1t1.setText(mFirebaseRemoteConfig.getString("step2_text1"));
293 | }
294 | if (!mFirebaseRemoteConfig.getString("step2_text2").equalsIgnoreCase("")) {
295 | TextView s1t1 = view.findViewById(R.id.tutorialNumber2TextBarText);
296 | s1t1.setText(mFirebaseRemoteConfig.getString("step2_text2"));
297 | }
298 | if (!mFirebaseRemoteConfig.getString("step2_text3").equalsIgnoreCase("")) {
299 | TextView s1t1 = view.findViewById(R.id.tutorialNumber4Text);
300 | s1t1.setText(mFirebaseRemoteConfig.getString("step2_text3"));
301 | }
302 | }
303 |
304 | public interface onTutorialFinishedListener {
305 | void onTutorialFinished();
306 | }
307 | }
308 |
--------------------------------------------------------------------------------