Refactor periodic tasks.

This commit is contained in:
Sandor Dornbush
2011-04-05 21:34:47 -07:00
parent 9616084769
commit 8d3ab8f93b
24 changed files with 767 additions and 1773 deletions
+7 -5
View File
@@ -112,9 +112,9 @@ limitations under the License.
<item>3.1 miles (senyal GPS pobre)</item> <item>3.1 miles (senyal GPS pobre)</item>
</string-array> </string-array>
<!-- Split frequency --> <!-- Task frequency -->
<string-array name="split_frequency_options"> <string-array name="task_frequency_options">
<item>Sense divisió</item> <item>Inactiu</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -130,8 +130,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>No splitting</item> <item>Inactiu</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
@@ -286,6 +286,8 @@ limitations under the License.
<array name="sensor_type_options"> <array name="sensor_type_options">
<item>Cap</item> <item>Cap</item>
<item>ANT+ Sensors</item>
<item>SRM ANT+ Bridge</item>
<item>Zephyr Bluetooth</item> <item>Zephyr Bluetooth</item>
</array> </array>
+4 -5
View File
@@ -48,7 +48,6 @@ limitations under the License.
<item>328 Fuß</item> <item>328 Fuß</item>
</string-array> </string-array>
<!-- Maximum recording distance --> <!-- Maximum recording distance -->
<string-array name="max_recording_distance_options"> <string-array name="max_recording_distance_options">
<item>50 m</item> <item>50 m</item>
@@ -113,9 +112,9 @@ limitations under the License.
<item>3,1 Meilen (sehr schlechtes GPS-Signal)</item> <item>3,1 Meilen (sehr schlechtes GPS-Signal)</item>
</string-array> </string-array>
<!-- Split frequency --> <!-- Task frequency -->
<string-array name="split_frequency_options"> <string-array name="task_frequency_options">
<item>Nicht zerteilen</item> <item>Aus</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -132,7 +131,7 @@ limitations under the License.
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="split_frequency_options_ft">
<item>Nicht zerteilen</item> <item>Aus</item>
<item>100 Meilen</item> <item>100 Meilen</item>
<item>50 Meilen</item> <item>50 Meilen</item>
<item>25 Meilen</item> <item>25 Meilen</item>
+5 -17
View File
@@ -15,18 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>Apagado</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</item>
</string-array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
@@ -112,9 +100,9 @@ limitations under the License.
<item>3.1 miles (señal GPS débil)</item> <item>3.1 miles (señal GPS débil)</item>
</string-array> </string-array>
<!-- Split frequency --> <!-- Task frequency -->
<string-array name="split_frequency_options"> <string-array name="task_frequency_options">
<item>No dividir</item> <item>Apagado</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -130,8 +118,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>No dividir</item> <item>Apagado</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
+6 -19
View File
@@ -15,18 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>Absent</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</item>
</string-array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
@@ -112,10 +100,9 @@ limitations under the License.
<item>3.1 miles (mauvais signal GPS)</item> <item>3.1 miles (mauvais signal GPS)</item>
</string-array> </string-array>
<!-- Task frequency -->
<!-- Split frequency --> <string-array name="task_frequency_options">
<string-array name="split_frequency_options"> <item>Absent</item>
<item>Pas de séparation</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -131,8 +118,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>Pas de séparation</item> <item>Absent</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
@@ -310,5 +297,5 @@ limitations under the License.
<item>Pont SRM ANT+</item> <item>Pont SRM ANT+</item>
<item>Bluetooth Zephyr</item> <item>Bluetooth Zephyr</item>
</array> </array>
</resources> </resources>
+5 -17
View File
@@ -15,18 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>Disattivato</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</item>
</string-array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
@@ -112,9 +100,9 @@ limitations under the License.
<item>3.1 mi (segnale GPS debole)</item> <item>3.1 mi (segnale GPS debole)</item>
</string-array> </string-array>
<!-- Split frequency --> <!-- Task frequency -->
<string-array name="split_frequency_options"> <string-array name="task_frequency_options">
<item>Non dividere</item> <item>Disattivato</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -130,8 +118,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>Non dividere</item> <item>Disattivato</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
+7 -22
View File
@@ -15,19 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency --> <!-- Minimum recording distance -->
<string-array name="task_frequency_options">
<item>不使用</item>
<item>1 分</item>
<item>2 分</item>
<item>5 分</item>
<item>10 分</item>
<item>15 分</item>
<item>30 分</item>
<item>60 分</item>
</string-array>
<!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
<item>2 m</item> <item>2 m</item>
@@ -48,7 +36,6 @@ limitations under the License.
<item>328 ft</item> <item>328 ft</item>
</string-array> </string-array>
<!-- Maximum recording distance --> <!-- Maximum recording distance -->
<string-array name="max_recording_distance_options"> <string-array name="max_recording_distance_options">
<item>50 m</item> <item>50 m</item>
@@ -68,7 +55,6 @@ limitations under the License.
<item>3.1 mi</item> <item>3.1 mi</item>
</string-array> </string-array>
<!-- Minimum recording interval --> <!-- Minimum recording interval -->
<string-array name="min_recording_interval_options"> <string-array name="min_recording_interval_options">
<item>電源節約 (動的制御)</item> <item>電源節約 (動的制御)</item>
@@ -89,7 +75,6 @@ limitations under the License.
<item>30 分</item> <item>30 分</item>
</string-array> </string-array>
<!-- Minimum required accuracy --> <!-- Minimum required accuracy -->
<string-array name="min_required_accuracy_options"> <string-array name="min_required_accuracy_options">
<item>10 m (GPS信号強度良好)</item> <item>10 m (GPS信号強度良好)</item>
@@ -115,10 +100,9 @@ limitations under the License.
<item>3.1 mi (GPS信号強度不良)</item> <item>3.1 mi (GPS信号強度不良)</item>
</string-array> </string-array>
<!-- Task frequency -->
<!-- Split frequency --> <string-array name="task_frequency_options">
<string-array name="split_frequency_options"> <item>不使用</item>
<item>分割なし</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -134,8 +118,8 @@ limitations under the License.
<item>1 分</item> <item>1 分</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>分割なし</item> <item>不使用</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
@@ -294,4 +278,5 @@ limitations under the License.
<item>SRM ANT+ ブリッジ</item> <item>SRM ANT+ ブリッジ</item>
<item>Zephyr Bluetooth</item> <item>Zephyr Bluetooth</item>
</array> </array>
</resources> </resources>
+6 -18
View File
@@ -15,18 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>Brak</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</item>
</string-array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
@@ -112,9 +100,9 @@ limitations under the License.
<item>3.1 mili (słaby sygnał GPS)</item> <item>3.1 mili (słaby sygnał GPS)</item>
</string-array> </string-array>
<!-- Split frequency --> <!-- Task frequency -->
<string-array name="split_frequency_options"> <string-array name="task_frequency_options">
<item>Brak dzielenia</item> <item>Brak</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -130,8 +118,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>Brak dzielenia</item> <item>Brak</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
@@ -146,7 +134,7 @@ limitations under the License.
<item>2 min</item> <item>2 min</item>
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<!-- Auto-resume track timeout --> <!-- Auto-resume track timeout -->
<string-array name="auto_resume_track_timeout_options"> <string-array name="auto_resume_track_timeout_options">
<item>Nigdy</item> <item>Nigdy</item>
+19 -36
View File
@@ -15,21 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<array name="task_frequency_options">
<item>Desligado</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</item>
</array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
<item>2 m</item> <item>2 m</item>
<item>3 m</item> <item>3 m</item>
@@ -37,9 +24,9 @@ limitations under the License.
<item>5 m (recomendada)</item> <item>5 m (recomendada)</item>
<item>10 m</item> <item>10 m</item>
<item>100 m</item> <item>100 m</item>
</array> </string-array>
<array name="min_recording_distance_options_ft"> <string-array name="min_recording_distance_options_ft">
<item>3 pés</item> <item>3 pés</item>
<item>6 pés</item> <item>6 pés</item>
<item>10 pés</item> <item>10 pés</item>
@@ -47,31 +34,29 @@ limitations under the License.
<item>16 pés (recomendado)</item> <item>16 pés (recomendado)</item>
<item>33 pés</item> <item>33 pés</item>
<item>328 pés</item> <item>328 pés</item>
</array> </string-array>
<!-- Maximum recording distance --> <!-- Maximum recording distance -->
<array name="max_recording_distance_options"> <string-array name="max_recording_distance_options">
<item>50 m</item> <item>50 m</item>
<item>100 m</item> <item>100 m</item>
<item>200 m (recomendado)</item> <item>200 m (recomendado)</item>
<item>500 m</item> <item>500 m</item>
<item>1000 m</item> <item>1000 m</item>
<item>5000 m</item> <item>5000 m</item>
</array> </string-array>
<array name="max_recording_distance_options_ft"> <string-array name="max_recording_distance_options_ft">
<item>164 pés</item> <item>164 pés</item>
<item>328 pés</item> <item>328 pés</item>
<item>656 pés (recomendado)</item> <item>656 pés (recomendado)</item>
<item>1640 pés</item> <item>1640 pés</item>
<item>0.6 milhas</item> <item>0.6 milhas</item>
<item>3.1 milhas</item> <item>3.1 milhas</item>
</array> </string-array>
<!-- Minimum recording interval --> <!-- Minimum recording interval -->
<array name="min_recording_interval_options"> <string-array name="min_recording_interval_options">
<item>economia de bateria (adaptativo)</item> <item>economia de bateria (adaptativo)</item>
<item>alta precisão (adaptativo)</item> <item>alta precisão (adaptativo)</item>
<item>máxima (recomendada)</item> <item>máxima (recomendada)</item>
@@ -88,11 +73,10 @@ limitations under the License.
<item>10 min</item> <item>10 min</item>
<item>15 min</item> <item>15 min</item>
<item>30 min</item> <item>30 min</item>
</array> </string-array>
<!-- Minimum required accuracy --> <!-- Minimum required accuracy -->
<array name="min_required_accuracy_options"> <string-array name="min_required_accuracy_options">
<item>10 m (sinal de GPS excelente)</item> <item>10 m (sinal de GPS excelente)</item>
<item>20 m</item> <item>20 m</item>
<item>50 m</item> <item>50 m</item>
@@ -102,9 +86,9 @@ limitations under the License.
<item>1000 m</item> <item>1000 m</item>
<item>2000 m</item> <item>2000 m</item>
<item>5000 m (sinal de GPS ruim)</item> <item>5000 m (sinal de GPS ruim)</item>
</array> </string-array>
<array name="min_required_accuracy_options_ft"> <string-array name="min_required_accuracy_options_ft">
<item>33 pés (sinal de GPS excelente)</item> <item>33 pés (sinal de GPS excelente)</item>
<item>66 pés</item> <item>66 pés</item>
<item>165 pés</item> <item>165 pés</item>
@@ -114,12 +98,11 @@ limitations under the License.
<item>0.6 milhas</item> <item>0.6 milhas</item>
<item>1.2 milhas</item> <item>1.2 milhas</item>
<item>3.1 milhas (sinal de GPS ruim)</item> <item>3.1 milhas (sinal de GPS ruim)</item>
</array> </string-array>
<!-- Task frequency -->
<!-- Split frequency --> <string-array name="task_frequency_options">
<string-array name="split_frequency_options"> <item>Desligado</item>
<item>Sem divisões</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -135,8 +118,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>Sem divisões</item> <item>Desligado</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
+5 -18
View File
@@ -15,18 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>Av</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</item>
</string-array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
@@ -67,7 +55,6 @@ limitations under the License.
<item>3.1 miles</item> <item>3.1 miles</item>
</string-array> </string-array>
<!-- Minimum recording interval --> <!-- Minimum recording interval -->
<string-array name="min_recording_interval_options"> <string-array name="min_recording_interval_options">
<item>batterioptimerad (anpassningsbar)</item> <item>batterioptimerad (anpassningsbar)</item>
@@ -113,9 +100,9 @@ limitations under the License.
<item>3.1 miles (dålig GPS-signal)</item> <item>3.1 miles (dålig GPS-signal)</item>
</string-array> </string-array>
<!-- Split frequency --> <!-- Task frequency -->
<string-array name="split_frequency_options"> <string-array name="task_frequency_options">
<item>Ingen delning</item> <item>Av</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -131,8 +118,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>Ingen delning</item> <item>Av</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
+6 -17
View File
@@ -15,18 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>关闭</item>
<item>1 分钟</item>
<item>2 分钟</item>
<item>5 分钟</item>
<item>10 分钟</item>
<item>15 分钟</item>
<item>30 分钟</item>
<item>60 分钟</item>
</string-array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 米</item> <item>1 米</item>
@@ -112,9 +100,9 @@ limitations under the License.
<item>3.1 英里 (糟糕的 GPS 信号)</item> <item>3.1 英里 (糟糕的 GPS 信号)</item>
</string-array> </string-array>
<!-- Split frequency --> <!-- Task frequency -->
<string-array name="split_frequency_options"> <string-array name="task_frequency_options">
<item>不分割</item> <item>关闭</item>
<item>100 千米</item> <item>100 千米</item>
<item>50 千米</item> <item>50 千米</item>
<item>25 千米</item> <item>25 千米</item>
@@ -130,8 +118,8 @@ limitations under the License.
<item>1 分钟</item> <item>1 分钟</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>不分割</item> <item>关闭</item>
<item>100 英里</item> <item>100 英里</item>
<item>50 英里</item> <item>50 英里</item>
<item>25 英里</item> <item>25 英里</item>
@@ -155,6 +143,7 @@ limitations under the License.
<item>15 分钟</item> <item>15 分钟</item>
<item>总是</item> <item>总是</item>
</string-array> </string-array>
<!-- Other non-preference arrays --> <!-- Other non-preference arrays -->
<array name="activity_types"> <array name="activity_types">
<item>未知</item> <item>未知</item>
+5 -138
View File
@@ -15,31 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>Off</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</item>
</string-array>
<!-- DO NOT TRANSLATE -->
<string-array name="task_frequency_values">
<item>-1</item>
<item>1</item>
<item>2</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>30</item>
<item>60</item>
</string-array>
<!-- Minimum recording distance --> <!-- Minimum recording distance -->
<string-array name="min_recording_distance_options"> <string-array name="min_recording_distance_options">
<item>1 m</item> <item>1 m</item>
@@ -61,18 +36,6 @@ limitations under the License.
<item>328 ft</item> <item>328 ft</item>
</string-array> </string-array>
<!-- DO NOT TRANSLATE -->
<string-array name="min_recording_distance_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>10</item>
<item>100</item>
</string-array>
<!-- Maximum recording distance --> <!-- Maximum recording distance -->
<string-array name="max_recording_distance_options"> <string-array name="max_recording_distance_options">
<item>50 m</item> <item>50 m</item>
@@ -92,17 +55,6 @@ limitations under the License.
<item>3.1 miles</item> <item>3.1 miles</item>
</string-array> </string-array>
<!-- DO NOT TRANSLATE -->
<string-array name="max_recording_distance_values">
<item>50</item>
<item>100</item>
<item>200</item>
<item>500</item>
<item>1000</item>
<item>5000</item>
</string-array>
<!-- Minimum recording interval --> <!-- Minimum recording interval -->
<string-array name="min_recording_interval_options"> <string-array name="min_recording_interval_options">
<item>battery miser (adaptive)</item> <item>battery miser (adaptive)</item>
@@ -123,27 +75,6 @@ limitations under the License.
<item>30 min</item> <item>30 min</item>
</string-array> </string-array>
<!-- DO NOT TRANSLATE -->
<string-array name="min_recording_interval_values">
<item>-2</item>
<item>-1</item>
<item>0</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>10</item>
<item>20</item>
<item>30</item>
<item>60</item>
<item>120</item>
<item>300</item>
<item>480</item>
<item>900</item>
<item>1800</item>
</string-array>
<!-- Minimum required accuracy --> <!-- Minimum required accuracy -->
<string-array name="min_required_accuracy_options"> <string-array name="min_required_accuracy_options">
<item>10 m (excellent GPS signal)</item> <item>10 m (excellent GPS signal)</item>
@@ -169,23 +100,9 @@ limitations under the License.
<item>3.1 miles (poor GPS signal)</item> <item>3.1 miles (poor GPS signal)</item>
</string-array> </string-array>
<!-- DO NOT TRANSLATE --> <!-- Task frequency -->
<string-array name="min_required_accuracy_values"> <string-array name="task_frequency_options">
<item>10</item> <item>Off</item>
<item>20</item>
<item>50</item>
<item>100</item>
<item>200</item>
<item>500</item>
<item>1000</item>
<item>2000</item>
<item>5000</item>
</string-array>
<!-- Split frequency -->
<string-array name="split_frequency_options">
<item>No splitting</item>
<item>100 km</item> <item>100 km</item>
<item>50 km</item> <item>50 km</item>
<item>25 km</item> <item>25 km</item>
@@ -201,8 +118,8 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<string-array name="split_frequency_options_ft"> <string-array name="task_frequency_options_ft">
<item>No splitting</item> <item>Off</item>
<item>100 mi</item> <item>100 mi</item>
<item>50 mi</item> <item>50 mi</item>
<item>25 mi</item> <item>25 mi</item>
@@ -218,24 +135,6 @@ limitations under the License.
<item>1 min</item> <item>1 min</item>
</string-array> </string-array>
<!-- DO NOT TRANSLATE -->
<string-array name="split_frequency_values">
<item>0</item>
<item>-100</item>
<item>-50</item>
<item>-25</item>
<item>-10</item>
<item>-5</item>
<item>-1</item>
<item>60</item>
<item>30</item>
<item>15</item>
<item>10</item>
<item>5</item>
<item>2</item>
<item>1</item>
</string-array>
<!-- Auto-resume track timeout --> <!-- Auto-resume track timeout -->
<string-array name="auto_resume_track_timeout_options"> <string-array name="auto_resume_track_timeout_options">
<item>Never</item> <item>Never</item>
@@ -245,16 +144,6 @@ limitations under the License.
<item>Always</item> <item>Always</item>
</string-array> </string-array>
<!-- DO NOT TRANSLATE -->
<string-array name="auto_resume_track_timeout_values">
<item>0</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>-1</item>
</string-array>
<!-- Other non-preference arrays --> <!-- Other non-preference arrays -->
<array name="activity_types"> <array name="activity_types">
<item>unknown</item> <item>unknown</item>
@@ -383,14 +272,6 @@ limitations under the License.
<item>way point</item> <item>way point</item>
</array> </array>
<!-- DO NOT TRANSLATE -->
<array name="export_formats">
<item>GPX</item>
<item>KML</item>
<item>CSV</item>
<item>TCX</item>
</array>
<array name="sensor_type_options"> <array name="sensor_type_options">
<item>None</item> <item>None</item>
<item>ANT+ Sensors</item> <item>ANT+ Sensors</item>
@@ -398,18 +279,4 @@ limitations under the License.
<item>Zephyr Bluetooth</item> <item>Zephyr Bluetooth</item>
</array> </array>
<!-- DO NOT TRANSLATE -->
<array name="sensor_type_values">
<item>@string/none</item>
<item>@string/ant_sensor_type</item>
<item>@string/srm_ant_bridge_sensor_type</item>
<item>@string/zephyr_sensor_type</item>
</array>
<!-- DO NOT TRANSLATE -->
<string-array name="ant_sensor_type_values">
<item>@string/ant_sensor_type</item>
<item>@string/srm_ant_bridge_sensor_type</item>
</string-array>
</resources> </resources>
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Do not translate any arrays in this file. -->
<string-array name="min_required_accuracy_values">
<item>10</item>
<item>20</item>
<item>50</item>
<item>100</item>
<item>200</item>
<item>500</item>
<item>1000</item>
<item>2000</item>
<item>5000</item>
</string-array>
<string-array name="min_recording_distance_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>10</item>
<item>100</item>
</string-array>
<string-array name="min_recording_distance_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>10</item>
<item>100</item>
</string-array>
<string-array name="max_recording_distance_values">
<item>50</item>
<item>100</item>
<item>200</item>
<item>500</item>
<item>1000</item>
<item>5000</item>
</string-array>
<string-array name="min_recording_interval_values">
<item>-2</item>
<item>-1</item>
<item>0</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>10</item>
<item>20</item>
<item>30</item>
<item>60</item>
<item>120</item>
<item>300</item>
<item>480</item>
<item>900</item>
<item>1800</item>
</string-array>
<string-array name="task_frequency_values">
<item>0</item>
<item>-100</item>
<item>-50</item>
<item>-25</item>
<item>-10</item>
<item>-5</item>
<item>-1</item>
<item>60</item>
<item>30</item>
<item>15</item>
<item>10</item>
<item>5</item>
<item>2</item>
<item>1</item>
</string-array>
<string-array name="auto_resume_track_timeout_values">
<item>0</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>-1</item>
</string-array>
<array name="export_formats">
<item>GPX</item>
<item>KML</item>
<item>CSV</item>
<item>TCX</item>
</array>
<array name="sensor_type_values">
<item>@string/none</item>
<item>@string/ant_sensor_type</item>
<item>@string/srm_ant_bridge_sensor_type</item>
<item>@string/zephyr_sensor_type</item>
</array>
<string-array name="ant_sensor_type_values">
<item>@string/ant_sensor_type</item>
<item>@string/srm_ant_bridge_sensor_type</item>
</string-array>
</resources>
+2 -2
View File
@@ -48,8 +48,8 @@ limitations under the License.
android:key="@string/split_frequency_key" android:key="@string/split_frequency_key"
android:title="@string/settings_split_frequency" android:title="@string/settings_split_frequency"
android:summary="@string/settings_split_frequency_summary" android:summary="@string/settings_split_frequency_summary"
android:entries="@array/split_frequency_options" android:entries="@array/task_frequency_options"
android:entryValues="@array/split_frequency_values" android:entryValues="@array/task_frequency_values"
android:defaultValue="0" /> android:defaultValue="0" />
<com.google.android.apps.mytracks.IntegerListPreference <com.google.android.apps.mytracks.IntegerListPreference
@@ -19,29 +19,21 @@ import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.ChartView.Mode; import com.google.android.apps.mytracks.ChartView.Mode;
import com.google.android.apps.mytracks.content.MyTracksLocation; import com.google.android.apps.mytracks.content.MyTracksLocation;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackPointsColumns;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet; import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory; import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.stats.DoubleBuffer; import com.google.android.apps.mytracks.stats.DoubleBuffer;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder; import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.apps.mytracks.util.MyTracksUtils; import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.UnitConversions; import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.database.Cursor;
import android.location.Location; import android.location.Location;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@@ -58,48 +50,28 @@ import java.util.ArrayList;
* An activity that displays a chart from the track point provider. * An activity that displays a chart from the track point provider.
* *
* @author Sandor Dornbush * @author Sandor Dornbush
* @author Rodrigo Damazio
*/ */
public class ChartActivity extends Activity implements public class ChartActivity extends Activity implements TrackDataListener {
SharedPreferences.OnSharedPreferenceChangeListener {
private final static int BUFFER_SIZE = 1024;
private double profileLength = 0;
private boolean metricUnits = true;
private boolean reportSpeed = true;
/**
* The track id that is displayed.
*/
private long selectedTrackId = -1;
/**
* Id of the last location that was seen when reading tracks from the
* provider. This is used to determine which locations are new compared to the
* last time the chart was updated.
*/
private long lastSeenLocationId = -1;
private long startTime = -1;
private Location lastLocation;
/**
* The id of the track currently being recorded.
*/
private long recordingTrackId = -1;
private final DoubleBuffer elevationBuffer = private final DoubleBuffer elevationBuffer =
new DoubleBuffer(Constants.ELEVATION_SMOOTHING_FACTOR); new DoubleBuffer(Constants.ELEVATION_SMOOTHING_FACTOR);
private final DoubleBuffer speedBuffer = private final DoubleBuffer speedBuffer =
new DoubleBuffer(Constants.SPEED_SMOOTHING_FACTOR); new DoubleBuffer(Constants.SPEED_SMOOTHING_FACTOR);
private final ArrayList<double[]> pendingPoints = new ArrayList<double[]>();
private TrackDataHub dataHub;
// Stats gathered from received data.
private double profileLength = 0;
private long startTime = -1;
private Location lastLocation;
private double trackMaxSpeed;
// Modes of operation
private Mode mode = Mode.BY_DISTANCE; private Mode mode = Mode.BY_DISTANCE;
private boolean metricUnits;
/** private boolean reportSpeed;
* Utilities to deal with the database.
*/
private MyTracksProviderUtils providerUtils;
/* /*
* UI elements: * UI elements:
@@ -109,9 +81,6 @@ public class ChartActivity extends Activity implements
private LinearLayout busyPane; private LinearLayout busyPane;
private ZoomControls zoomControls; private ZoomControls zoomControls;
/** Handler for callbacks to the UI thread */
private final Handler uiHandler = new Handler();
/** /**
* A runnable that can be posted to the UI thread. It will remove the spinner * A runnable that can be posted to the UI thread. It will remove the spinner
* (if any), enable/disable zoom controls and orange pointer as appropriate * (if any), enable/disable zoom controls and orange pointer as appropriate
@@ -123,89 +92,17 @@ public class ChartActivity extends Activity implements
busyPane.setVisibility(View.GONE); busyPane.setVisibility(View.GONE);
zoomControls.setIsZoomInEnabled(chartView.canZoomIn()); zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut()); zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
chartView.setShowPointer(selectedTrackIsRecording()); chartView.setShowPointer(dataHub.isRecordingSelected());
chartView.invalidate(); chartView.invalidate();
} }
}; };
/**
* A runnable that can be posted to the UI thread. It will show the spinner.
*/
private final Runnable showSpinner = new Runnable() {
@Override
public void run() {
busyPane.setVisibility(View.VISIBLE);
}
};
/**
* An observer for the tracks provider. Will listen to new track points being
* added and update the chart if necessary.
*/
private ContentObserver observer;
/**
* An observer for the waypoints provider. Will listen to new way points being
* added and update the chart if necessary.
*/
private ContentObserver waypointObserver;
/**
* A thread with a looper. Post to updateTrackHandler to execute Runnables on
* this thread.
*/
private final HandlerThread updateTrackThread =
new HandlerThread("updateTrackThread");
/** Handler for updateTrackThread */
private Handler updateTrackHandler;
/**
* A runnable that updates the profile from the provider.
*/
private final Runnable updateTrackRunnable = new Runnable() {
@Override
public void run() {
readNewTrackPoints();
}
};
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key != null) {
if (key.equals(getString(R.string.selected_track_key))) {
selectedTrackId =
sharedPreferences.getLong(getString(R.string.selected_track_key),
-1);
readProfileAsync();
} else if (key.equals(getString(R.string.metric_units_key))) {
metricUnits =
sharedPreferences.getBoolean(getString(R.string.metric_units_key),
true);
chartView.setMetricUnits(metricUnits);
readProfileAsync();
} else if (key.equals(getString(R.string.report_speed_key))) {
reportSpeed =
sharedPreferences.getBoolean(getString(R.string.report_speed_key),
true);
chartView.setReportSpeed(reportSpeed, this);
readProfileAsync();
} else if (key.equals(getString(R.string.recording_track_key))) {
recordingTrackId =
sharedPreferences.getLong(getString(R.string.recording_track_key),
-1);
runOnUiThread(updateChart);
}
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Log.w(TAG, "ChartActivity.onCreate"); Log.w(TAG, "ChartActivity.onCreate");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
MyTracks.getInstance().setChartActivity(this); MyTracks.getInstance().setChartActivity(this);
providerUtils = MyTracksProviderUtils.Factory.get(this); dataHub = MyTracks.getInstance().getDataHub();
// The volume we want to control is the Text-To-Speech volume // The volume we want to control is the Text-To-Speech volume
int volumeStream = int volumeStream =
@@ -221,22 +118,6 @@ public class ChartActivity extends Activity implements
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
layout.addView(chartView, params); layout.addView(chartView, params);
SharedPreferences preferences =
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
selectedTrackId =
preferences.getLong(getString(R.string.selected_track_key), -1);
recordingTrackId =
preferences.getLong(getString(R.string.recording_track_key), -1);
metricUnits = preferences.getBoolean(getString(R.string.metric_units_key),
true);
chartView.setMetricUnits(metricUnits);
reportSpeed = preferences.getBoolean(getString(R.string.report_speed_key),
true);
chartView.setReportSpeed(reportSpeed, this);
preferences.registerOnSharedPreferenceChangeListener(this);
}
busyPane = (LinearLayout) findViewById(R.id.elevation_busypane); busyPane = (LinearLayout) findViewById(R.id.elevation_busypane);
zoomControls = (ZoomControls) findViewById(R.id.elevation_zoom); zoomControls = (ZoomControls) findViewById(R.id.elevation_zoom);
zoomControls.setOnZoomInClickListener(new View.OnClickListener() { zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
@@ -251,97 +132,20 @@ public class ChartActivity extends Activity implements
zoomOut(); zoomOut();
} }
}); });
updateTrackThread.start();
updateTrackHandler = new Handler(updateTrackThread.getLooper());
// Register observer for the track point provider:
Handler contentHandler = new Handler();
observer = new ContentObserver(contentHandler) {
@Override
public void onChange(boolean selfChange) {
Log.d(TAG, "ChartActivity: ContentObserver.onChange");
// Check for any new locations and append them to the currently
// recording track.
if (recordingTrackId < 0) {
// No track is being recorded. We should not be here.
return;
}
if (selectedTrackId != recordingTrackId) {
// No track, or one other than the recording track is selected, don't
// bother.
return;
}
// Update can potentially be lengthy, put it in its own thread:
updateTrackHandler.post(updateTrackRunnable);
super.onChange(selfChange);
}
};
waypointObserver = new ContentObserver(contentHandler) {
@Override
public void onChange(boolean selfChange) {
Log.d(TAG,
"MyTracksMap: ContentObserver.onChange waypoints");
if (selectedTrackId < 0) {
return;
}
Thread t = new Thread() {
@Override
public void run() {
readWaypoints();
ChartActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
chartView.invalidate();
}
});
}
};
t.start();
super.onChange(selfChange);
}
};
readProfileAsync();
} }
@Override @Override
protected void onPause() { protected void onStart() {
super.onPause(); super.onStart();
unregisterContentObservers();
dataHub.registerTrackDataListener(this);
} }
@Override @Override
protected void onResume() { protected void onStop() {
super.onResume(); dataHub.unregisterTrackDataListener(this);
// Make sure any updates that might have happened are propagated to this
// activity:
observer.onChange(false);
waypointObserver.onChange(false);
registerContentObservers();
}
/** super.onStop();
* Register the content observer for the map overlay.
*/
private void registerContentObservers() {
getContentResolver().registerContentObserver(TrackPointsColumns.CONTENT_URI,
false/* notifyForDescendents */, observer);
getContentResolver().registerContentObserver(WaypointsColumns.CONTENT_URI,
false/* notifyForDescendents */, waypointObserver);
}
/**
* Unregister the content observer for the map overlay.
*/
private void unregisterContentObservers() {
getContentResolver().unregisterContentObserver(observer);
getContentResolver().unregisterContentObserver(waypointObserver);
}
private boolean selectedTrackIsRecording() {
return selectedTrackId == recordingTrackId;
} }
private void zoomIn() { private void zoomIn() {
@@ -360,7 +164,7 @@ public class ChartActivity extends Activity implements
if (this.mode != newMode) { if (this.mode != newMode) {
this.mode = newMode; this.mode = newMode;
chartView.setMode(this.mode); chartView.setMode(this.mode);
readProfileAsync(); dataHub.reloadDataFor(this);
} }
} }
@@ -377,34 +181,6 @@ public class ChartActivity extends Activity implements
return chartView.getChartValueSeries(index).isEnabled(); return chartView.getChartValueSeries(index).isEnabled();
} }
private void readWaypoints() {
if (selectedTrackId < 0) {
return;
}
Cursor cursor = null;
chartView.clearWaypoints();
try {
// We will silently drop extra waypoints to make the app responsive.
cursor =
providerUtils.getWaypointsCursor(selectedTrackId, 0,
Constants.MAX_DISPLAYED_TRACK_POINTS);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
Waypoint wpt = providerUtils.createWaypoint(cursor);
chartView.addWaypoint(wpt);
} while (cursor.moveToNext());
}
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
@@ -440,43 +216,41 @@ public class ChartActivity extends Activity implements
* This must be called in order for each point. * This must be called in order for each point.
* *
* @param location the location to get data for (this method takes ownership of that location) * @param location the location to get data for (this method takes ownership of that location)
* @param track the track to get data from
* @param result the resulting point to fill out * @param result the resulting point to fill out
* @return the previous location, now available for reuse * @return the previous location, now available for reuse
*/ */
private Location getDataPoint(Location location, Track track, double[] result) { private void fillDataPoint(Location location, double result[]) {
double timeOrDistance = Double.NaN,
elevation = Double.NaN,
speed = Double.NaN,
power = Double.NaN,
cadence = Double.NaN,
heartRate = Double.NaN;
if (location instanceof MyTracksLocation && if (location instanceof MyTracksLocation &&
((MyTracksLocation) location).getSensorDataSet() != null) { ((MyTracksLocation) location).getSensorDataSet() != null) {
SensorDataSet sensorData = ((MyTracksLocation) location).getSensorDataSet(); SensorDataSet sensorData = ((MyTracksLocation) location).getSensorDataSet();
if (sensorData.hasPower() if (sensorData.hasPower()
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING && sensorData.getPower().getState() == Sensor.SensorState.SENDING
&& sensorData.getPower().hasValue()) { && sensorData.getPower().hasValue()) {
result[3] = sensorData.getPower().getValue(); power = sensorData.getPower().getValue();
} else {
result[3] = Double.NaN;
} }
if (sensorData.hasCadence() if (sensorData.hasCadence()
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING && sensorData.getCadence().getState() == Sensor.SensorState.SENDING
&& sensorData.getCadence().hasValue()) { && sensorData.getCadence().hasValue()) {
result[4] = sensorData.getCadence().getValue(); cadence = sensorData.getCadence().getValue();
} else {
result[4] = Double.NaN;
} }
if (sensorData.hasHeartRate() if (sensorData.hasHeartRate()
&& sensorData.getHeartRate().getState() == Sensor.SensorState.SENDING && sensorData.getHeartRate().getState() == Sensor.SensorState.SENDING
&& sensorData.getHeartRate().hasValue()) { && sensorData.getHeartRate().hasValue()) {
result[5] = sensorData.getHeartRate().getValue(); heartRate = sensorData.getHeartRate().getValue();
} else {
result[5] = Double.NaN;
} }
} else {
result[3] = Double.NaN;
result[4] = Double.NaN;
result[5] = Double.NaN;
} }
// TODO: Account for segment splits?
switch (mode) { switch (mode) {
case BY_DISTANCE: case BY_DISTANCE:
result[0] = profileLength; timeOrDistance = profileLength;
if (lastLocation != null) { if (lastLocation != null) {
double d = lastLocation.distanceTo(location); double d = lastLocation.distanceTo(location);
if (metricUnits) { if (metricUnits) {
@@ -491,7 +265,7 @@ public class ChartActivity extends Activity implements
// Base case // Base case
startTime = location.getTime(); startTime = location.getTime();
} }
result[0] = (location.getTime() - startTime); timeOrDistance = (location.getTime() - startTime);
break; break;
default: default:
Log.w(TAG, "ChartActivity unknown mode: " + mode); Log.w(TAG, "ChartActivity unknown mode: " + mode);
@@ -500,7 +274,7 @@ public class ChartActivity extends Activity implements
elevationBuffer.setNext(metricUnits elevationBuffer.setNext(metricUnits
? location.getAltitude() ? location.getAltitude()
: location.getAltitude() * UnitConversions.M_TO_FT); : location.getAltitude() * UnitConversions.M_TO_FT);
result[1] = elevationBuffer.getAverage(); elevation = elevationBuffer.getAverage();
if (lastLocation == null) { if (lastLocation == null) {
if (Math.abs(location.getSpeed() - 128) > 1) { if (Math.abs(location.getSpeed() - 128) > 1) {
@@ -509,151 +283,134 @@ public class ChartActivity extends Activity implements
} else if (TripStatisticsBuilder.isValidSpeed( } else if (TripStatisticsBuilder.isValidSpeed(
location.getTime(), location.getSpeed(), lastLocation.getTime(), location.getTime(), location.getSpeed(), lastLocation.getTime(),
lastLocation.getSpeed(), speedBuffer) lastLocation.getSpeed(), speedBuffer)
&& (location.getSpeed() <= track.getStatistics().getMaxSpeed())) { && (location.getSpeed() <= trackMaxSpeed)) {
speedBuffer.setNext(location.getSpeed()); speedBuffer.setNext(location.getSpeed());
} }
result[2] = speedBuffer.getAverage() * 3.6; speed = speedBuffer.getAverage() * 3.6;
if (!metricUnits) { if (!metricUnits) {
result[2] *= UnitConversions.KM_TO_MI; speed *= UnitConversions.KM_TO_MI;
} }
if (!reportSpeed && (result[2] != 0)) { if (!reportSpeed) {
// Format as hours per unit if (speed != 0) {
result[2] = (60.0 / result[2]); // Format as hours per unit
speed = (60.0 / speed);
} else {
speed = Double.NaN;
}
} }
Location oldLastLocation = lastLocation; // Keep a copy so the location can be reused.
lastLocation = location; lastLocation = location;
if (oldLastLocation == null) { if (result != null) {
// No previous location, but return a blank one for reuse result[0] = timeOrDistance;
return new MyTracksLocation(""); result[1] = elevation;
result[2] = speed;
result[3] = power;
result[4] = cadence;
result[5] = heartRate;
} }
return oldLastLocation;
} }
/** @Override
* Sets the chart data points reading from the provider. This is non-blocking. public void onProviderStateChange(ProviderState state) {
*/ // We don't care.
private void readProfileAsync() { }
chartView.reset();
updateTrackHandler.post(new Runnable() { @Override
public void onCurrentLocationChanged(Location loc) {
// We don't care.
}
@Override
public void onCurrentHeadingChanged(double heading) {
// We don't care.
}
@Override
public void onSelectedTrackChanged(Track track, boolean isRecording) {
Log.e(TAG, "Visible", new Throwable());
runOnUiThread(new Runnable() {
@Override
public void run() { public void run() {
runOnUiThread(showSpinner); busyPane.setVisibility(View.VISIBLE);
readProfile();
readWaypoints();
runOnUiThread(updateChart);
} }
}); });
} }
/** @Override
* Reads the track profile from the provider. This is a blocking function and public void onTrackUpdated(Track track) {
* should not be run from the UI thread. trackMaxSpeed = track.getStatistics().getMaxSpeed();
*/ }
private void readProfile() {
@Override
public void clearTrackPoints() {
profileLength = 0; profileLength = 0;
lastLocation = null; lastLocation = null;
startTime = -1; startTime = -1;
if (selectedTrackId < 0) { elevationBuffer.reset();
return; speedBuffer.reset();
} chartView.reset();
Track track = providerUtils.getTrack(selectedTrackId); pendingPoints.clear();
if (track == null) {
return;
}
lastSeenLocationId = track.getStartId();
final ArrayList<double[]> theData = readPointsToList(track);
runOnUiThread(new Runnable() {
public void run() {
chartView.setDataPoints(theData);
}
});
} }
/** @Override
* Read all new track points. public void onNewTrackPoint(Location loc) {
*/ if (LocationUtils.isValidLocation(loc)) {
private void readNewTrackPoints() { double[] point = new double[6];
Log.i(TAG, "MyTracks: Updating chart last seen: " + lastSeenLocationId); fillDataPoint(loc, point);
Track track = providerUtils.getTrack(recordingTrackId); pendingPoints.add(point);
if (track == null) {
Log.w(TAG, "MyTracks: track not found");
return;
} }
chartView.addDataPoints(readPointsToList(track));
uiHandler.post(new Runnable() {
public void run() {
chartView.invalidate();
}
});
Log.i(TAG, "MyTracks: Updated chart last seen: " + lastSeenLocationId);
} }
/** @Override
* Get the frequency at which points should be displayed. public void onSampledOutTrackPoint(Location loc) {
* Limit the number of chart readings. Ideally we would want around 1024. // Still account for the point in the smoothing buffers.
* @param track The track which will be displayed. fillDataPoint(loc, null);
* @return The inverse of the frequency of points to be displayed.
*/
private int getSamplingFrequency(Track track) {
long totalLocations = track.getStopId() - track.getStartId();
return Math.max(1, (int) (totalLocations / 1024.0));
} }
private Cursor getLocationsCursor(long lastLocationRead) { @Override
return providerUtils.getLocationsCursor(selectedTrackId, lastLocationRead, BUFFER_SIZE, false); public void onSegmentSplit() {
// Do nothing.
} }
/** @Override
* Read all of the points to a list. public void onNewTrackPointsDone() {
* @param track The track which will be displayed. chartView.addDataPoints(pendingPoints);
* @return pendingPoints.clear();
*/ runOnUiThread(updateChart);
private ArrayList<double[]> readPointsToList(Track track) { }
Cursor cursor = null;
long lastLocationRead = lastSeenLocationId; @Override
int points = 0; public void clearWaypoints() {
int chartSamplingFrequency = getSamplingFrequency(track); chartView.clearWaypoints();
ArrayList<double[]> result = new ArrayList<double[]>(); }
// Need two locations so we can keep track of the last location.
Location location = new MyTracksLocation(""); @Override
try { public void onNewWaypoint(Waypoint wpt) {
while (lastSeenLocationId < track.getStopId()) { chartView.addWaypoint(wpt);
cursor = getLocationsCursor(lastLocationRead); }
if (cursor != null) {
elevationBuffer.reset(); @Override
speedBuffer.reset(); public void onNewWaypointsDone() {
if (cursor.moveToFirst()) { runOnUiThread(updateChart);
final int idColumnIdx = cursor.getColumnIndexOrThrow(TrackPointsColumns._ID); }
while (cursor.moveToNext()) {
points++; @Override
providerUtils.fillLocation(cursor, location); public boolean onUnitsChanged(boolean metric) {
if (MyTracksUtils.isValidLocation(location)) { this.metricUnits = metric;
lastLocationRead = lastSeenLocationId =
cursor.getLong(idColumnIdx); chartView.setMetricUnits(metric);
// TODO Can we be smarter about choosing 3 or 6 entries?
double[] point = new double[6]; return true; // Reload data
location = getDataPoint(location, track, point); }
if (points % chartSamplingFrequency == 0) {
result.add(point); @Override
} public boolean onReportSpeedChanged(boolean reportSpeed) {
} this.reportSpeed = reportSpeed;
}
} else { chartView.setReportSpeed(reportSpeed, this);
lastLocationRead += BUFFER_SIZE;
} return true; // Reload data
} else {
lastLocationRead += BUFFER_SIZE;
}
cursor.close();
cursor = null;
}
return result;
} finally {
if (cursor != null) {
cursor.close();
}
}
} }
} }
File diff suppressed because it is too large Load Diff
@@ -266,9 +266,6 @@ public class SettingsActivity extends PreferenceActivity {
final ListPreference minRequiredAccuracy = final ListPreference minRequiredAccuracy =
(ListPreference) findPreference( (ListPreference) findPreference(
getString(R.string.min_required_accuracy_key)); getString(R.string.min_required_accuracy_key));
final ListPreference splitFrequency =
(ListPreference) findPreference(
getString(R.string.split_frequency_key));
minRecordingDistance.setEntries(isMetric minRecordingDistance.setEntries(isMetric
? R.array.min_recording_distance_options ? R.array.min_recording_distance_options
@@ -279,9 +276,16 @@ public class SettingsActivity extends PreferenceActivity {
minRequiredAccuracy.setEntries(isMetric minRequiredAccuracy.setEntries(isMetric
? R.array.min_required_accuracy_options ? R.array.min_required_accuracy_options
: R.array.min_required_accuracy_options_ft); : R.array.min_required_accuracy_options_ft);
splitFrequency.setEntries(isMetric setTaskOptions(isMetric, R.string.announcement_frequency_key);
? R.array.split_frequency_options setTaskOptions(isMetric, R.string.split_frequency_key);
: R.array.split_frequency_options_ft); }
private void setTaskOptions(boolean isMetric, int listId) {
final ListPreference taskFrequency =
(ListPreference) findPreference(getString(listId));
taskFrequency.setEntries(isMetric
? R.array.task_frequency_options
: R.array.task_frequency_options_ft);
} }
@@ -17,26 +17,15 @@ package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.MyTracksProviderUtilsImpl;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory; import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.ContentObserver;
import android.location.Location; import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@@ -44,66 +33,39 @@ import android.view.MenuItem;
import android.view.Window; import android.view.Window;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
/** /**
* An activity that displays track statistics to the user. * An activity that displays track statistics to the user.
* *
* @author Sandor Dornbush * @author Sandor Dornbush
* @author Rodrigo Damazio
*/ */
public class StatsActivity extends Activity public class StatsActivity extends Activity implements TrackDataListener {
implements OnSharedPreferenceChangeListener {
private StatsUtilities utils; private StatsUtilities utils;
private UIUpdateThread thread; private UIUpdateThread thread;
private ContentObserver observer;
/**
* The id of the currently selected track.
*/
private long selectedTrackId = -1;
/**
* The id of the currently recording track.
*/
private long recordingTrackId = -1;
/** /**
* The start time of the selected track. * The start time of the selected track.
*/ */
private long startTime = -1; private long startTime = -1;
/**
* True if distances should be displayed in metric units (from shared
* preferences).
*/
private boolean metricUnits = true;
/**
* True if pace should be displayed as dist/time (from shared preferences).
*/
private boolean displaySpeed = true;
/**
* true if activity has resumed and is on top
*/
private boolean activityOnTop = false;
/** /**
* If true, the statistics for the current segment are shown, otherwise * If true, the statistics for the current segment are shown, otherwise
* for the full track. * for the full track.
*/ */
private boolean showCurrentSegment = false; private boolean showCurrentSegment = false;
private MyTracksProviderUtils providerUtils; private TrackDataHub dataHub;
/** /**
* A runnable for posting to the UI thread. Will update the total time field. * A runnable for posting to the UI thread. Will update the total time field.
*/ */
private final Runnable updateResults = new Runnable() { private final Runnable updateResults = new Runnable() {
public void run() { public void run() {
updateTotalTime(); if (dataHub.isRecordingSelected()) {
utils.setTime(R.id.total_time_register,
System.currentTimeMillis() - startTime);
}
} }
}; };
@@ -121,13 +83,11 @@ public class StatsActivity extends Activity
public void run() { public void run() {
Log.i(TAG, "Started UI update thread"); Log.i(TAG, "Started UI update thread");
while (MyTracks.getInstance().isRecording()) { while (MyTracks.getInstance().isRecording()) {
long sleeptime = 1000;
runOnUiThread(updateResults); runOnUiThread(updateResults);
try { try {
Thread.sleep(sleeptime); Thread.sleep(1000L);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.w(TAG, Log.w(TAG, "StatsActivity: Caught exception on sleep.", e);
"StatsActivity: Caught exception on sleep.", e);
break; break;
} }
} }
@@ -140,9 +100,9 @@ public class StatsActivity extends Activity
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
dataHub = MyTracks.getInstance().getDataHub();
utils = new StatsUtilities(this); utils = new StatsUtilities(this);
providerUtils = new MyTracksProviderUtilsImpl(getContentResolver());
// The volume we want to control is the Text-To-Speech volume // The volume we want to control is the Text-To-Speech volume
int volumeStream = int volumeStream =
new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream(); new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream();
@@ -153,40 +113,10 @@ public class StatsActivity extends Activity
setContentView(R.layout.stats); setContentView(R.layout.stats);
Handler contentHandler = new Handler();
observer = new ContentObserver(contentHandler) {
@Override
public void onChange(boolean selfChange) {
Log.d(TAG, "StatsActivity: ContentObserver.onChange");
restoreStats();
super.onChange(selfChange);
}
};
ScrollView sv = ((ScrollView) findViewById(R.id.scrolly)); ScrollView sv = ((ScrollView) findViewById(R.id.scrolly));
sv.setScrollBarStyle(ScrollView.SCROLLBARS_OUTSIDE_INSET); sv.setScrollBarStyle(ScrollView.SCROLLBARS_OUTSIDE_INSET);
SharedPreferences preferences = showUnknownLocation();
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
selectedTrackId = preferences.getLong(
getString(R.string.selected_track_key), -1);
recordingTrackId = preferences.getLong(
getString(R.string.recording_track_key), -1);
metricUnits = preferences.getBoolean(
getString(R.string.metric_units_key), true);
displaySpeed =
preferences.getBoolean(getString(R.string.report_speed_key), true);
checkLiveTrack();
restoreStats();
showUnknownLocation();
preferences.registerOnSharedPreferenceChangeListener(this);
}
utils.setMetricUnits(metricUnits);
utils.setReportSpeed(displaySpeed);
utils.updateUnits();
utils.setSpeedLabel(R.id.speed_label, R.string.speed, R.string.pace_label);
utils.setSpeedLabels();
DisplayMetrics metrics = new DisplayMetrics(); DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics); getWindowManager().getDefaultDisplay().getMetrics(metrics);
@@ -196,74 +126,49 @@ public class StatsActivity extends Activity
} }
@Override @Override
protected void onPause() { protected void onStart() {
unregisterLocationListener(); dataHub.registerTrackDataListener(this);
super.onStart();
}
@Override
protected void onStop() {
dataHub.unregisterTrackDataListener(this);
if (thread != null) { if (thread != null) {
thread.interrupt(); thread.interrupt();
thread = null; thread = null;
} }
getContentResolver().unregisterContentObserver(observer);
activityOnTop = false; super.onStop();
super.onPause();
} }
@Override @Override
protected void onResume() { public boolean onUnitsChanged(boolean metric) {
activityOnTop = true; utils.setMetricUnits(metric);
checkLiveTrack(); updateLabels();
restoreStats();
showUnknownLocation(); return true; // Reload data
super.onResume();
} }
@Override @Override
public void onSharedPreferenceChanged( public boolean onReportSpeedChanged(boolean displaySpeed) {
final SharedPreferences sharedPreferences, final String key) { utils.setReportSpeed(displaySpeed);
Log.d(TAG, updateLabels();
"StatsActivity: onSharedPreferences changed " + key);
if (key != null) { return true; // Reload data
runOnUiThread(new Runnable() { }
@Override
public void run() { private void updateLabels() {
if (key.equals(getString(R.string.selected_track_key))) { runOnUiThread(new Runnable() {
selectedTrackId = @Override
sharedPreferences.getLong( public void run() {
getString(R.string.selected_track_key), utils.updateUnits();
-1); utils.setSpeedLabel(R.id.speed_label, R.string.speed, R.string.pace_label);
checkLiveTrack(); utils.setSpeedLabels();
restoreStats(); }
showUnknownLocation(); });
} else if (key.equals(getString(R.string.recording_track_key))) {
recordingTrackId =
sharedPreferences.getLong(
getString(R.string.recording_track_key),
-1);
checkLiveTrack();
restoreStats();
showUnknownLocation();
} else if (key.equals(getString(R.string.metric_units_key))) {
metricUnits =
sharedPreferences.getBoolean(
getString(R.string.metric_units_key), true);
utils.setMetricUnits(metricUnits);
utils.updateUnits();
restoreStats();
} else if (key.equals(getString(R.string.report_speed_key))) {
displaySpeed =
sharedPreferences.getBoolean(
getString(R.string.report_speed_key),
true);
utils.setReportSpeed(displaySpeed);
utils.updateUnits();
utils.setSpeedLabel(
R.id.speed_label, R.string.speed, R.string.pace_label);
Log.w(TAG, "Setting speed labels");
utils.setSpeedLabels();
restoreStats();
}
}
});
}
} }
@Override @Override
@@ -291,137 +196,12 @@ public class StatsActivity extends Activity
switch (item.getItemId()) { switch (item.getItemId()) {
case Constants.MENU_CURRENT_SEGMENT: case Constants.MENU_CURRENT_SEGMENT:
showCurrentSegment = !showCurrentSegment; showCurrentSegment = !showCurrentSegment;
restoreStats(); // TODO: Re-read only the data that interests us
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
private final LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location l) {
if (selectedTrackIsRecording()) {
showLocation(l);
}
}
@Override
public void onProviderDisabled(String provider) {
// Do nothing
}
@Override
public void onProviderEnabled(String provider) {
// Do nothing
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// Do nothing
}
};
/**
* Registers to receive location updates from the GPS location provider.
*/
private void registerLocationListener() {
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) {
LocationProvider gpsProvider =
locationManager.getProvider(Constants.GPS_PROVIDER);
if (gpsProvider == null) {
Toast.makeText(this, getString(R.string.error_no_gps_location_provider),
Toast.LENGTH_LONG).show();
return;
} else {
Log.d(TAG, "StatsActivity: Using location provider "
+ gpsProvider.getName());
}
locationManager.requestLocationUpdates(gpsProvider.getName(),
0/*minTime*/, 0/*minDist*/, locationListener);
}
}
/**
* Unregisters all location listener.
*/
private void unregisterLocationListener() {
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) {
locationManager.removeUpdates(locationListener);
}
}
/**
* @return true if the selected track is the currently recording track
*/
private boolean selectedTrackIsRecording() {
return MyTracks.getInstance().isRecording()
&& selectedTrackId == recordingTrackId;
}
/**
* Reads values for selected tracks from provider and update the UI.
*/
private void restoreStats() {
if (selectedTrackId < 0) {
utils.setAllToUnknown();
return;
}
Track track = providerUtils.getTrack(selectedTrackId);
if (track == null || track.getStatistics() == null) {
utils.setAllToUnknown();
return;
}
startTime = track.getStatistics().getStartTime();
if (!selectedTrackIsRecording()) {
utils.setTime(R.id.total_time_register,
track.getStatistics().getTotalTime());
}
utils.setAllStats(track.getStatistics());
}
/**
* Checks if this activity needs to update live track data or not.
* If so, make sure that:
* a) a thread keeps updating the total time
* b) a location listener is registered
* c) a content observer is registered
* Otherwise unregister listeners, observers, and kill update thread.
*/
private void checkLiveTrack() {
final boolean isRecording = selectedTrackIsRecording();
final boolean startThread =
(thread == null) && isRecording && activityOnTop;
final boolean killThread =
(thread != null) && (!isRecording || !activityOnTop);
if (startThread) {
thread = new UIUpdateThread();
thread.start();
getContentResolver().registerContentObserver(
TracksColumns.CONTENT_URI, false, observer);
getContentResolver().registerContentObserver(
WaypointsColumns.CONTENT_URI, false, observer);
registerLocationListener();
} else if (killThread) {
thread.interrupt();
thread = null;
getContentResolver().unregisterContentObserver(observer);
unregisterLocationListener();
}
}
public void updateTotalTime() {
if (selectedTrackIsRecording()) {
utils.setTime(R.id.total_time_register,
System.currentTimeMillis() - startTime);
}
}
/** /**
* Updates the given location fields (latitude, longitude, altitude) and all * Updates the given location fields (latitude, longitude, altitude) and all
* other fields. * other fields.
@@ -441,4 +221,112 @@ public class StatsActivity extends Activity
utils.setUnknown(R.id.longitude_register); utils.setUnknown(R.id.longitude_register);
utils.setUnknown(R.id.speed_register); utils.setUnknown(R.id.speed_register);
} }
@Override
public void onSelectedTrackChanged(Track track, boolean isRecording) {
/*
* Checks if this activity needs to update live track data or not.
* If so, make sure that:
* a) a thread keeps updating the total time
* b) a location listener is registered
* c) a content observer is registered
* Otherwise unregister listeners, observers, and kill update thread.
*/
final boolean startThread = (thread == null) && isRecording;
final boolean killThread = (thread != null) && (!isRecording);
if (startThread) {
thread = new UIUpdateThread();
thread.start();
} else if (killThread) {
thread.interrupt();
thread = null;
}
if (track == null || track.getStatistics() == null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
utils.setAllToUnknown();
}
});
}
}
@Override
public void onCurrentLocationChanged(Location loc) {
if (dataHub.isRecordingSelected()) {
showLocation(loc);
}
}
@Override
public void onCurrentHeadingChanged(double heading) {
// We don't care.
}
@Override
public void onProviderStateChange(ProviderState state) {
switch (state) {
case DISABLED:
case NO_FIX:
showUnknownLocation();
break;
}
}
@Override
public void onTrackUpdated(final Track track) {
runOnUiThread(new Runnable() {
@Override
public void run() {
startTime = track.getStatistics().getStartTime();
if (!dataHub.isRecordingSelected()) {
utils.setTime(R.id.total_time_register,
track.getStatistics().getTotalTime());
showUnknownLocation();
}
utils.setAllStats(track.getStatistics());
}
});
}
@Override
public void clearWaypoints() {
// We don't care.
}
@Override
public void onNewWaypoint(Waypoint wpt) {
// We don't care.
}
@Override
public void onNewWaypointsDone() {
// We don't care.
}
@Override
public void clearTrackPoints() {
// We don't care.
}
@Override
public void onNewTrackPoint(Location loc) {
// We don't care.
}
@Override
public void onSegmentSplit() {
// We don't care.
}
@Override
public void onSampledOutTrackPoint(Location loc) {
// We don't care.
}
@Override
public void onNewTrackPointsDone() {
// We don't care.
}
} }
@@ -607,7 +607,7 @@ public class TrackDataHub {
if (key.equals(RECORDING_TRACK_KEY)) { if (key.equals(RECORDING_TRACK_KEY)) {
recordingTrackId = sharedPreferences.getLong(RECORDING_TRACK_KEY, -1); recordingTrackId = sharedPreferences.getLong(RECORDING_TRACK_KEY, -1);
} else if (key.equals(MIN_REQUIRED_ACCURACY_KEY)) { } else if (key.equals(MIN_REQUIRED_ACCURACY_KEY)) {
minRequiredAccuracy = sharedPreferences.getInt(RECORDING_TRACK_KEY, minRequiredAccuracy = sharedPreferences.getInt(MIN_REQUIRED_ACCURACY_KEY,
Constants.DEFAULT_MIN_REQUIRED_ACCURACY); Constants.DEFAULT_MIN_REQUIRED_ACCURACY);
} else if (key.equals(METRIC_UNITS_KEY)) { } else if (key.equals(METRIC_UNITS_KEY)) {
useMetricUnits = sharedPreferences.getBoolean(METRIC_UNITS_KEY, true); useMetricUnits = sharedPreferences.getBoolean(METRIC_UNITS_KEY, true);
@@ -33,6 +33,7 @@ import com.google.android.apps.mytracks.services.sensors.SensorManager;
import com.google.android.apps.mytracks.services.sensors.SensorManagerFactory; import com.google.android.apps.mytracks.services.sensors.SensorManagerFactory;
import com.google.android.apps.mytracks.services.tasks.PeriodicTask; import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
import com.google.android.apps.mytracks.services.tasks.PeriodicTaskExecuter; import com.google.android.apps.mytracks.services.tasks.PeriodicTaskExecuter;
import com.google.android.apps.mytracks.services.tasks.SplitTask;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory; import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder; import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
@@ -190,8 +191,6 @@ public class TrackRecordingService extends Service implements LocationListener {
/** /**
* The frequency of status announcements. * The frequency of status announcements.
*/ */
private int announcementFrequency = -1;
private ExecutorService executerServce; private ExecutorService executerServce;
/* /*
@@ -667,6 +666,8 @@ public class TrackRecordingService extends Service implements LocationListener {
(NotificationManager) getSystemService(NOTIFICATION_SERVICE); (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
setUpAnnouncer();
this.splitExecuter = new PeriodicTaskExecuter(this, new SplitTask());
prefManager = new PreferenceManager(this); prefManager = new PreferenceManager(this);
registerLocationListener(); registerLocationListener();
@@ -702,32 +703,20 @@ public class TrackRecordingService extends Service implements LocationListener {
private void setUpAnnouncer() { private void setUpAnnouncer() {
Log.d(TAG, "TrackRecordingService.setUpAnnouncer: " Log.d(TAG, "TrackRecordingService.setUpAnnouncer: "
+ announcementExecuter); + announcementExecuter);
if (announcementFrequency != -1 && recordingTrackId != -1) { StatusAnnouncerFactory statusAnnouncerFactory =
handler.post(new Runnable() { new StatusAnnouncerFactory(ApiFeatures.getInstance());
@Override PeriodicTask announcer = statusAnnouncerFactory.create(
public void run() { TrackRecordingService.this);
if (announcementExecuter == null) { if (announcer == null) {
StatusAnnouncerFactory statusAnnouncerFactory = return;
new StatusAnnouncerFactory(ApiFeatures.getInstance());
PeriodicTask announcer = statusAnnouncerFactory.create(
TrackRecordingService.this);
if (announcer == null) {
return;
}
// TODO: Either use TaskExecuterManager everywhere, or get rid of it
announcementExecuter = new PeriodicTaskExecuter(
TrackRecordingService.this, announcer);
}
announcementExecuter.setTaskFrequency(announcementFrequency * 60000);
}
});
} }
announcementExecuter = new PeriodicTaskExecuter(
TrackRecordingService.this, announcer);
} }
private void shutdownAnnouncer() { private void shutdownExecuters() {
Log.d(TAG, "TrackRecordingService.shutdownAnnouncer: " Log.d(TAG, "TrackRecordingService.shutdownExecuters");
+ announcementExecuter);
if (announcementExecuter != null) { if (announcementExecuter != null) {
try { try {
announcementExecuter.shutdown(); announcementExecuter.shutdown();
@@ -735,6 +724,13 @@ public class TrackRecordingService extends Service implements LocationListener {
announcementExecuter = null; announcementExecuter = null;
} }
} }
if (splitExecuter != null) {
try {
splitExecuter.shutdown();
} finally {
splitExecuter = null;
}
}
} }
@Override @Override
@@ -750,11 +746,7 @@ public class TrackRecordingService extends Service implements LocationListener {
timer.cancel(); timer.cancel();
timer.purge(); timer.purge();
unregisterLocationListener(); unregisterLocationListener();
shutdownAnnouncer(); shutdownExecuters();
announcementExecuter.shutdown();
announcementExecuter = null;
splitExecuter.shutdown();
splitExecuter = null;
if (sensorManager != null) { if (sensorManager != null) {
sensorManager.shutdown(); sensorManager.shutdown();
sensorManager = null; sensorManager = null;
@@ -1109,12 +1101,9 @@ public class TrackRecordingService extends Service implements LocationListener {
waypointStatsBuilder = new TripStatisticsBuilder(startTime); waypointStatsBuilder = new TripStatisticsBuilder(startTime);
waypointStatsBuilder.setMinRecordingDistance(minRecordingDistance); waypointStatsBuilder.setMinRecordingDistance(minRecordingDistance);
currentWaypointId = insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS); currentWaypointId = insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
setUpAnnouncer();
length = 0; length = 0;
showNotification(); showNotification();
registerLocationListener(); registerLocationListener();
announcementExecuter.restore();
splitExecuter.restore();
sensorManager = SensorManagerFactory.getSensorManager(this); sensorManager = SensorManagerFactory.getSensorManager(this);
if (sensorManager != null) { if (sensorManager != null) {
sensorManager.onStartTrack(); sensorManager.onStartTrack();
@@ -1129,6 +1118,8 @@ public class TrackRecordingService extends Service implements LocationListener {
// Notify the world that we're now recording. // Notify the world that we're now recording.
sendTrackBroadcast( sendTrackBroadcast(
R.string.track_started_broadcast_action, recordingTrackId); R.string.track_started_broadcast_action, recordingTrackId);
announcementExecuter.restore();
splitExecuter.restore();
return recordingTrackId; return recordingTrackId;
} }
@@ -1139,7 +1130,7 @@ public class TrackRecordingService extends Service implements LocationListener {
throw new IllegalStateException("No recording track in progress!"); throw new IllegalStateException("No recording track in progress!");
} }
shutdownAnnouncer(); shutdownExecuters();
isRecording = false; isRecording = false;
Track recordingTrack = providerUtils.getTrack(recordingTrackId); Track recordingTrack = providerUtils.getTrack(recordingTrackId);
if (recordingTrack != null) { if (recordingTrack != null) {
@@ -1199,19 +1190,6 @@ public class TrackRecordingService extends Service implements LocationListener {
this.recordingTrackId = recordingTrackId; this.recordingTrackId = recordingTrackId;
} }
public int getAnnouncementFrequency() {
return announcementFrequency;
}
public void setAnnouncementFrequency(int announcementFrequency) {
this.announcementFrequency = announcementFrequency;
if (announcementFrequency == -1) {
shutdownAnnouncer();
} else {
setUpAnnouncer();
}
}
public int getMaxRecordingDistance() { public int getMaxRecordingDistance() {
return maxRecordingDistance; return maxRecordingDistance;
} }
@@ -1257,12 +1235,24 @@ public class TrackRecordingService extends Service implements LocationListener {
this.autoResumeTrackTimeout = autoResumeTrackTimeout; this.autoResumeTrackTimeout = autoResumeTrackTimeout;
} }
public void setAnnouncementFrequency(int announcementFrequency) {
if (announcementExecuter != null) {
announcementExecuter.setTaskFrequency(announcementFrequency);
}
}
public void setSplitFrequency(int frequency) { public void setSplitFrequency(int frequency) {
splitExecuter.setTaskFrequency(frequency); if (splitExecuter != null) {
splitExecuter.setTaskFrequency(frequency);
}
} }
public void setMetricUnits(boolean metric) { public void setMetricUnits(boolean metric) {
announcementExecuter.setMetricUnits(metric); if (announcementExecuter != null) {
splitExecuter.setMetricUnits(metric); announcementExecuter.setMetricUnits(metric);
}
if (splitExecuter != null) {
splitExecuter.setMetricUnits(metric);
}
} }
} }
@@ -61,10 +61,29 @@ public class PeriodicTaskExecuter {
* Restores the manager. * Restores the manager.
*/ */
public void restore() { public void restore() {
if ((isTimeFrequency()) && (timerExecuter != null)) { // TODO: Decouple service from this class once and forever.
timerExecuter.scheduleTask(taskFrequency * 60000); if (!service.isRecording()) {
return;
}
if (!isTimeFrequency()) {
if (timerExecuter != null) {
timerExecuter.shutdown();
timerExecuter = null;
}
}
if (taskFrequency == 0) {
return;
}
if (isTimeFrequency()) {
if (timerExecuter == null) {
timerExecuter = new TimerTaskExecuter(task, service);
}
timerExecuter.scheduleTask(taskFrequency * 60000);
} else {
// For distance based splits.
calculateNextTaskDistance();
} }
calculateNextTaskDistance();
} }
/** /**
@@ -140,30 +159,7 @@ public class PeriodicTaskExecuter {
public void setTaskFrequency(int taskFrequency) { public void setTaskFrequency(int taskFrequency) {
Log.d(TAG, "setTaskFrequency: taskFrequency = " + taskFrequency); Log.d(TAG, "setTaskFrequency: taskFrequency = " + taskFrequency);
this.taskFrequency = taskFrequency; this.taskFrequency = taskFrequency;
restore();
// TODO: Decouple service from this class once and forever.
if (!service.isRecording()) {
return;
}
if (!isTimeFrequency()) {
if (timerExecuter != null) {
timerExecuter.shutdown();
timerExecuter = null;
}
}
if (taskFrequency == 0) {
return;
}
if (isTimeFrequency()) {
if (timerExecuter == null) {
timerExecuter = new TimerTaskExecuter(task, service);
}
timerExecuter.scheduleTask(taskFrequency * 60000);
} else {
// For distance based splits.
calculateNextTaskDistance();
}
} }
public void setMetricUnits(boolean metricUnits) { public void setMetricUnits(boolean metricUnits) {
@@ -24,7 +24,7 @@ import com.google.android.apps.mytracks.services.TrackRecordingService;
* A simple task to insert statistics markers every n minutes. * A simple task to insert statistics markers every n minutes.
* @author Sandor Dornbush * @author Sandor Dornbush
*/ */
public class TimeSplitTask implements PeriodicTask { public class SplitTask implements PeriodicTask {
@Override @Override
public void run(TrackRecordingService service) { public void run(TrackRecordingService service) {
@@ -19,7 +19,6 @@ package com.google.android.apps.mytracks.services.tasks;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.MyTracksSettings;
import com.google.android.apps.mytracks.services.TrackRecordingService; import com.google.android.apps.mytracks.services.TrackRecordingService;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.StringUtils;
@@ -177,7 +176,7 @@ public class StatusAnnouncerTask implements PeriodicTask {
// @VisibleForTesting // @VisibleForTesting
protected String getAnnouncement(TripStatistics stats) { protected String getAnnouncement(TripStatistics stats) {
SharedPreferences preferences = SharedPreferences preferences =
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
boolean metricUnits = true; boolean metricUnits = true;
boolean reportSpeed = true; boolean reportSpeed = true;
if (preferences != null) { if (preferences != null) {
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.services; package com.google.android.apps.mytracks.services.tasks;
import com.google.android.apps.mytracks.services.tasks.PeriodicTask; import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory; import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.services; package com.google.android.apps.mytracks.services.tasks;
import static com.google.android.testing.mocking.AndroidMock.capture; import static com.google.android.testing.mocking.AndroidMock.capture;
import static com.google.android.testing.mocking.AndroidMock.eq; import static com.google.android.testing.mocking.AndroidMock.eq;