diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index e618828c3..d6f558ac4 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -18,7 +18,7 @@ limitations under the License. package="com.google.android.maps.mytracks" android:installLocation="auto" android:versionCode="25" - android:versionName="1.1.2"> + android:versionName="1.1.3rc1"> @@ -110,7 +110,7 @@ limitations under the License. - + diff --git a/MyTracks/res/drawable-hdpi/failure.png b/MyTracks/res/drawable-hdpi/failure.png new file mode 100644 index 000000000..127f55275 Binary files /dev/null and b/MyTracks/res/drawable-hdpi/failure.png differ diff --git a/MyTracks/res/drawable-hdpi/success.png b/MyTracks/res/drawable-hdpi/success.png new file mode 100644 index 000000000..977d08fbe Binary files /dev/null and b/MyTracks/res/drawable-hdpi/success.png differ diff --git a/MyTracks/res/drawable-ldpi/failure.png b/MyTracks/res/drawable-ldpi/failure.png new file mode 100644 index 000000000..3722b0794 Binary files /dev/null and b/MyTracks/res/drawable-ldpi/failure.png differ diff --git a/MyTracks/res/drawable-ldpi/success.png b/MyTracks/res/drawable-ldpi/success.png new file mode 100644 index 000000000..1d44ba697 Binary files /dev/null and b/MyTracks/res/drawable-ldpi/success.png differ diff --git a/MyTracks/res/drawable-mdpi/failure.png b/MyTracks/res/drawable-mdpi/failure.png new file mode 100644 index 000000000..5ff6b1ebb Binary files /dev/null and b/MyTracks/res/drawable-mdpi/failure.png differ diff --git a/MyTracks/res/drawable-mdpi/success.png b/MyTracks/res/drawable-mdpi/success.png new file mode 100644 index 000000000..31f2ee563 Binary files /dev/null and b/MyTracks/res/drawable-mdpi/success.png differ diff --git a/MyTracks/res/layout/send_to_google_result.xml b/MyTracks/res/layout/send_to_google_result.xml new file mode 100644 index 000000000..f2d93780f --- /dev/null +++ b/MyTracks/res/layout/send_to_google_result.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + diff --git a/MyTracks/res/layout/send_to_google_result_list_item.xml b/MyTracks/res/layout/send_to_google_result_list_item.xml new file mode 100644 index 000000000..045a60584 --- /dev/null +++ b/MyTracks/res/layout/send_to_google_result_list_item.xml @@ -0,0 +1,49 @@ + + + + + + + + + + diff --git a/MyTracks/res/values-ja/arrays.xml b/MyTracks/res/values-ja/arrays.xml index a3b6e1fde..582056996 100644 --- a/MyTracks/res/values-ja/arrays.xml +++ b/MyTracks/res/values-ja/arrays.xml @@ -287,4 +287,11 @@ limitations under the License. 噴水 ウェイポイント + + + なし + ANT+ センサー + SRM ANT+ ブリッジ + Zephyr Bluetooth + diff --git a/MyTracks/res/values-ja/strings.xml b/MyTracks/res/values-ja/strings.xml index 52719208a..7ae58dc2c 100644 --- a/MyTracks/res/values-ja/strings.xml +++ b/MyTracks/res/values-ja/strings.xml @@ -35,6 +35,7 @@ limitations under the License. この端末でロケーションマネージャを発見できませんでした. この端末でGPSロケーションプロバイダを発見できました. + Googleマイマップへのトラック送信でエラーが発生しました.しばらく待って試して下さい. Google Fusion Tablesへのトラック送信でエラーが発生しました.しばらく待って試して下さい. Google Docsへのトラック送信でエラーが発生しました.しばらく待って試して下さい. 記録作成開始できません.もう一度試して下さい. @@ -50,11 +51,13 @@ limitations under the License. GPS位置精度がよくなるまで待機しています.空が見えるところにいて下さい. 新しいトラックを記録中です. トラック記録を停止しました. + 新しいマイマップをmap.google.comに作成しました. 新しいGoogle Fusion Tableをgoogle.com/fusiontablesに作成しました. トラックをmaps.google.comに送信しました. トラックをdocs.google.comの新しいスプレッドシートに送信しました. トラックをdocs.google.comに送信しました. - ここをタップして「位置情報とセキュリティの設定」を開いて「GPS機能を使用」にチェックして下さい.バックボタンで戻ってください. + 新しいトラックが表示されるまでに数分かかることがあります. + ここをタップして「位置情報とセキュリティの設定」を開いて「GPS機能を使用」にチェックを入れて下さい.バックボタンで戻ってください. ご注意: 携帯電話中継局とWi-Fiネットワークから大まかな位置を特定します. 現在位置決定ができません.位置取得源を増やし(位置情報とセキュリティの設定 をご覧ください)、空が見えるようにして下さい.もう一度試して下さい. ウェイポイントマーカーを挿入しました. @@ -62,14 +65,17 @@ limitations under the License. お待ちください... - トラックをGoogleに送信しました. + トラックをGoogleに送信しています. トラックを読んでいます... トラックを準備しています... Google Fusion Tablesへの認証を行っています... + Googleマップへの認証を行っています... Google Docsへの認証を行っています... トラックをGoogle Docsに送信しています... + トラックをGoogleマップに送信しています... + 新しいマイマップを作成しています... トラックをFusion Tablesに送信しています... - 新しいFusion Tablesを生成しています... + 新しいFusionテーブルを生成しています... GPSトラックをインポートしています.若干時間がかかります. SDカードにデータを書き込んでいます.若干時間がかかります. トラック統計情報を更新しています... @@ -84,6 +90,10 @@ limitations under the License. Android用My Tracksで作成 Android用<a href=\'http://mytracks.appspot.com\'>My Tracks</a>で作成しました. + + Android用<a href=\'http://mytracks.appspot.com\'>My Tracks</a>で作成しました. + 新規マップ + ポイント 記録日 @@ -113,10 +123,12 @@ limitations under the License. 表示 - Google Fusion Tablesへ送信 - 新規Google Fusion Tableへ送信 - Google Docsへ送信 - Googleへ送信... + Googleマイマップに送信 + 新規Googleマイマップに送信 + Google Fusion Tablesに送信 + 新規Google Fusion Tableに送信 + Google Docsに送信 + Googleに送信... 端末から削除... 詳細編集 SDへの書き込み... @@ -124,20 +136,33 @@ limitations under the License. KMLファイルの保存 CVSファイルの保存 TCXファイルの保存 - Google Fusion Tablesへの送信 - 全トラックの削除 + Google Fusion Tablesに送信 + Googleマイマップに送信 + 全て削除 - 全トラックの出力 + 全て出力 出力終了 トラックを選択します.オプションを表示するには長押しして下さい. - 全てのトラック: + 全トラック: このトラック 共有... + マップへのリンク GPXファイル KMLファイル CSVファイル TCXファイル - マップの消去 + マップのクリア + + 全てインポート + インポート終了 + + %3$s からGPXファイル %2$d件のうち %1$d件をインポートしました + %s ではGPXファイルがありません.SDカードを読み込めないかディレクトリにGPXファイルが無いかも知れません. + ファイル %s のインポートエラー 全てのマーカーの削除 @@ -190,7 +215,7 @@ limitations under the License. マップの公開 アップロードされたマップはmaps.google.comで誰でも閲覧できます - アップロードされたマップはmaps.google.comのリストに公開されません. + アップロードされたマップはmaps.google.comのリストに公開されません 速度表示 速度が表示されます @@ -242,6 +267,23 @@ limitations under the License. GPS衛星 + + アカウントからのマップ選択: + 新規マップ作成... + Googleマイマップに送信 + マップをロード中... + 今回はマップをロードできませんでした! + このマップを選択 + マップ詳細編集 + トラック共有 + アカウントからマップを削除 + マップタイトル: + マップ説明: + このマップまたはコンテンツを検索結果に表示 + このマップまたはコンテンツを検索結果に表示しない(直接URLで見られる可能性があります) + このマップまたはコンテンツは検索結果に表示されます + このマップもコンテンツも検索結果に表示されませんが、直接リンクで見られる可能性があります + マップ共有 マップを共有したいと思います @@ -332,7 +374,6 @@ limitations under the License. ケイデンス 心拍数 最大心拍数 - - 待機中... @@ -350,8 +391,10 @@ limitations under the License. 送信... - Googleマイマップ + Googleマイマップ 今すぐ送信 + 新規マップ作成 + 既存マップ選択 トラック統計情報送信 統計情報とポイント送信 @@ -364,7 +407,7 @@ limitations under the License. 外部ストレージを発見できませんでした.SDカードが刺さっているか、端末がコンピュータにマウントされていないか確認してください. ファイルへの書き込みができません. ファイルからの読み込みができません. - SDカードにディレクトリを作成できません. + SDカード上でディレクトリを作成できません. SDカードへの書き込み終了. SDカードからの読み込み終了. @@ -397,11 +440,11 @@ limitations under the License. デフォルトの単位はメートルです.設定を変更しますか? - Bluetoothオプション ペアリング ペアリングを行うには「Bluetooth設定」を開いて下さい bluetoothSensorsEnabled + ANTオプション センサーオプション @@ -425,7 +468,7 @@ limitations under the License. トラック記録通知受信 トラック読み込み - トラック書き込み トラック記録の開始・終了 + トラック書き込みとトラック記録の開始・終了 トラック記録の開始・終了時に通知されます. ポイントやウェイポイントといったトラックの読み込みを可能にします. 記録開始やウェイポイント追加といったトラック記録を可能にします. diff --git a/MyTracks/res/values/do_not_translate.xml b/MyTracks/res/values/do_not_translate.xml index 861738bf7..116175d63 100644 --- a/MyTracks/res/values/do_not_translate.xml +++ b/MyTracks/res/values/do_not_translate.xml @@ -34,4 +34,9 @@ limitations under the License. ANT SRM_ANT_BRIDGE ZEPHYR + + + google.com/fusiontables + maps.google.com + docs.google.com diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index 40eca8a08..524dad3ed 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -35,9 +35,10 @@ limitations under the License. No location manager was found on this device! No GPS location provider was found on this device. - There was an error while sending your track to Google My Map. Please try again later. - There was an error while sending your track to Google Fusion Tables. Please try again later. - There was an error while sending your track to Google Docs. Please try again later. + There was an error while sending your track to Google My Map. + There was an error while sending your track to Google Fusion Tables. + There was an error while sending your track to Google Docs. + Please try again later. Unable to start recording. Try again. Unable to stop recording. Try again. Unable to read from file. @@ -56,12 +57,13 @@ limitations under the License. Your track has been sent to maps.google.com. Your track has been sent to a new spreadsheet at docs.google.com. Your track has been sent to docs.google.com. - It may sometimes take a few minutes before new tracks appear. + It may take a few minutes before new tracks appear. Please tap here to go to Security and Location Settings and check \'enable GPS satellites\'. Press back button to return. Note: An approximate location as determined from cell towers and WIFI networks is shown. A location can currently not be determined. Enable more location sources (see Security and Location Settings) and make sure the sky is visible. Try again later. A way point marker was inserted. A statistics marker was inserted. + Upload status: Please wait... @@ -276,7 +278,7 @@ limitations under the License. Your maps could not be loaded at this time! Select this Map Edit Map Details - Share Track with Friends + Share Track Delete Map from your Account Map Title: Map Description: @@ -295,7 +297,6 @@ limitations under the License. I think you might be interested in this track. To view this track use Google Earth or other mapping software. - Share track with friends Public diff --git a/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java b/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java index 69d673296..5dec3e5dd 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java @@ -1,12 +1,12 @@ /* * 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 @@ -15,20 +15,20 @@ */ package com.google.android.apps.mytracks; +import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog; import com.google.android.maps.mytracks.R; +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; -import android.view.View; import android.view.WindowManager.BadTokenException; /** * A class to handle all dialog related events for My Tracks. - * + * * @author Sandor Dornbush */ public class DialogManager { @@ -37,14 +37,12 @@ public class DialogManager { public static final int DIALOG_IMPORT_PROGRESS = 2; public static final int DIALOG_PROGRESS = 3; public static final int DIALOG_SEND_TO_GOOGLE = 4; - public static final int DIALOG_SEND_TO_GOOGLE_RESULT = 5; - public static final int DIALOG_WRITE_PROGRESS = 6; + public static final int DIALOG_WRITE_PROGRESS = 5; private ProgressDialog progressDialog; private ProgressDialog importProgressDialog; private ProgressDialog writeProgressDialog; - private SendToGoogleDialog sendToGoogleDialog; - private AlertDialog sendToGoogleResultDialog; + private SendDialog sendToGoogleDialog; private ChartSettingsDialog chartSettingsDialog; private MyTracks activity; @@ -52,7 +50,7 @@ public class DialogManager { public DialogManager(MyTracks activity) { this.activity = activity; } - + protected Dialog onCreateDialog(int id, Bundle args) { switch (id) { case DIALOG_CHART_SETTINGS: @@ -77,26 +75,8 @@ public class DialogManager { progressDialog.setProgress(10); return progressDialog; case DIALOG_SEND_TO_GOOGLE: - sendToGoogleDialog = new SendToGoogleDialog(activity); + sendToGoogleDialog = new SendDialog(activity); return sendToGoogleDialog; - case DIALOG_SEND_TO_GOOGLE_RESULT: - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setIcon(android.R.drawable.ic_dialog_info); - builder.setTitle("Title"); - builder.setMessage("Message"); - builder.setPositiveButton(activity.getString(R.string.ok), null); - builder.setNeutralButton(activity.getString(R.string.share_track), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - SendToGoogleDialog sendToGoogleDialog = getSendToGoogleDialog(); - final boolean sentToMyMaps = sendToGoogleDialog.getSendToMyMaps(); - final boolean sentToFusionTables = sendToGoogleDialog.getSendToFusionTables(); - activity.shareLinkToMap(sentToMyMaps, sentToFusionTables); - dialog.dismiss(); - } - }); - sendToGoogleResultDialog = builder.create(); - return sendToGoogleResultDialog; case DIALOG_WRITE_PROGRESS: writeProgressDialog = new ProgressDialog(activity); writeProgressDialog.setIcon(android.R.drawable.ic_dialog_info); @@ -115,24 +95,6 @@ public class DialogManager { case DIALOG_SEND_TO_GOOGLE: activity.resetSendToGoogleStatus(); break; - case DIALOG_SEND_TO_GOOGLE_RESULT: - boolean success = activity.getSendToGoogleSuccess(); - sendToGoogleResultDialog.setTitle( - success ? R.string.success : R.string.error); - sendToGoogleResultDialog.setIcon(success - ? android.R.drawable.ic_dialog_info - : android.R.drawable.ic_dialog_alert); - sendToGoogleResultDialog.setMessage(activity.getSendToGoogleResultMessage()); - - boolean canShare = - activity.getSendToFusionTablesTableId() != null && - activity.getSendToMyMapsMapId() != null; - View share = - sendToGoogleResultDialog.findViewById(android.R.id.button3); - if (share != null) { - share.setVisibility(canShare ? View.VISIBLE : View.GONE); - } - break; case DIALOG_CHART_SETTINGS: Log.d(MyTracksConstants.TAG, "MyTracks.onPrepare chart dialog"); chartSettingsDialog.setup(activity.getChartActivity()); @@ -151,7 +113,7 @@ public class DialogManager { } }); } - + public void setProgressValue(final int percent) { activity.runOnUiThread(new Runnable() { public void run() { @@ -167,7 +129,7 @@ public class DialogManager { /** * @return the sendToGoogleDialog */ - public SendToGoogleDialog getSendToGoogleDialog() { + public SendDialog getSendToGoogleDialog() { return sendToGoogleDialog; } @@ -196,9 +158,9 @@ public class DialogManager { } /** - * Just like showDialog, but will catch a BadTokenException that sometimes - * (very rarely) gets thrown. This might happen if the user hits the "back" - * button immediately after sending tracks to google. + * Just like showDialog, but will catch a {@link BadTokenException} that + * sometimes (very rarely) gets thrown. This might happen if the user hits + * the "back" button immediately after sending tracks to google. * * @param id the dialog id */ @@ -218,6 +180,24 @@ public class DialogManager { }); } + /** + * The equivalent of {@link #showDialogSafely(int)}, but for a specific + * dialog instance. + */ + public static void showDialogSafely(Activity activity, final Dialog dialog) { + activity.runOnUiThread(new Runnable() { + public void run() { + try { + dialog.show(); + } catch (BadTokenException e) { + Log.w(MyTracksConstants.TAG, "Could not display dialog", e); + } catch (IllegalStateException e) { + Log.w(MyTracksConstants.TAG, "Could not display dialog", e); + } + } + }); + } + /** * Dismisses the progress dialog if it is showing. Executed on the UI thread. */ diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java index b8bbc03f8..afcd37996 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java @@ -36,6 +36,10 @@ import com.google.android.apps.mytracks.io.TrackWriter; import com.google.android.apps.mytracks.io.TrackWriterFactory; import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat; import com.google.android.apps.mytracks.io.mymaps.MapsService; +import com.google.android.apps.mytracks.io.sendtogoogle.ResultDialogFactory; +import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog; +import com.google.android.apps.mytracks.io.sendtogoogle.SendResult; +import com.google.android.apps.mytracks.io.sendtogoogle.SendType; import com.google.android.apps.mytracks.services.ITrackRecordingService; import com.google.android.apps.mytracks.services.StatusAnnouncerFactory; import com.google.android.apps.mytracks.services.TrackRecordingService; @@ -77,7 +81,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import javax.xml.parsers.ParserConfigurationException; @@ -139,9 +145,9 @@ public class MyTracks extends TabActivity implements OnTouchListener, private ITrackRecordingService trackRecordingService; - /** - * Does the user want to share the current track. - */ + /** + * Does the user want to share the current track. + */ private boolean shareRequested = false; /** @@ -909,11 +915,11 @@ public class MyTracks extends TabActivity implements OnTouchListener, * Initiates the process to send tracks to google. * This is called once the user has selected sending options via the * SendToGoogleDialog. - * + * * TODO: Change this whole flow to an actual state machine. */ public void sendToGoogle() { - SendToGoogleDialog sendToGoogleDialog = + SendDialog sendToGoogleDialog = dialogManager.getSendToGoogleDialog(); if (sendToGoogleDialog == null) { return; @@ -934,7 +940,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, } } - private void sendToGoogleMapsOrPickMap(SendToGoogleDialog sendToGoogleDialog) { + private void sendToGoogleMapsOrPickMap(SendDialog sendToGoogleDialog) { if (!sendToGoogleDialog.getCreateNewMap()) { // Ask the user to choose a map to upload into Intent listIntent = new Intent(this, MyMapsList.class); @@ -989,7 +995,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, } private void onSendToGoogleMapsDone() { - SendToGoogleDialog sendToGoogleDialog = dialogManager.getSendToGoogleDialog(); + SendDialog sendToGoogleDialog = dialogManager.getSendToGoogleDialog(); if (sendToGoogleDialog.getSendToFusionTables()) { authenticateToFusionTables(null); } else if (sendToGoogleDialog.getSendToDocs()) { @@ -1029,7 +1035,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, Log.w(MyTracksConstants.TAG, "Updating table id failed.", e); } } - + onSendToFusionTablesDone(); } }; @@ -1044,7 +1050,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, } private void onSendToFusionTablesDone() { - SendToGoogleDialog sendToGoogleDialog = dialogManager.getSendToGoogleDialog(); + SendDialog sendToGoogleDialog = dialogManager.getSendToGoogleDialog(); if (sendToGoogleDialog.getSendToDocs()) { authenticateToGoogleDocs(); } else { @@ -1076,7 +1082,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, Log.d(MyTracksConstants.TAG, "Sending to Docs...."); setProgressValue(50); setProgressMessage(R.string.progress_message_sending_docs); - final SendToDocs sender = new SendToDocs(this, + final SendToDocs sender = new SendToDocs(this, authMap.get(SendToDocs.GDATA_SERVICE_NAME_TRIX), authMap.get(SendToDocs.GDATA_SERVICE_NAME_DOCLIST), trackId); sendToTrackId = trackId; @@ -1099,30 +1105,53 @@ public class MyTracks extends TabActivity implements OnTouchListener, } private void onSendToGoogleDone() { - SendToGoogleDialog sendToGoogleDialog = dialogManager.getSendToGoogleDialog(); + SendDialog sendToGoogleDialog = dialogManager.getSendToGoogleDialog(); final boolean sentToMyMaps = sendToGoogleDialog.getSendToMyMaps(); final boolean sentToFusionTables = sendToGoogleDialog.getSendToFusionTables(); - dialogManager.dismissDialogSafely(DIALOG_PROGRESS); - runOnUiThread(new Runnable() { - public void run() { - if (shareRequested) { - Toast.makeText(MyTracks.this, getSendToGoogleResultMessage(), Toast.LENGTH_LONG) - .show(); - if (shareLinkToMap(sentToMyMaps, sentToFusionTables)) { - return; - } + // We've finished sending the track to the user-selected services. Now + // we tell them the results of the upload, and optionally share the track. + // There are a few different paths through this code: + // + // 1. The user pre-requested a share (shareRequested == true). We're going + // to display the result dialog *without* the share button (the share + // listener will be null). The OK button listener will initiate the + // share. + // + // 2. The user did not pre-request a share, and the set of services to + // which we succeeded in uploading the track are compatible with + // sharing. We'll display a share button (the share listener will be + // non-null), and will share the link if the user clicks it. + // + // 3. The user did not pre-request a share, and the set of services to + // which we succeeded in uploading the track are incompatible with + // sharing. We won't display a share button. + + final boolean canShare = getSendToFusionTablesTableId() != null + || getSendToMyMapsMapId() != null; + + DialogInterface.OnClickListener doShareListener = null; + if (canShare) { + doShareListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + shareLinkToMap(sentToMyMaps, sentToFusionTables); } + }; + } - // If anything failed or sharing was not requested, show the dialog - dialogManager.showDialogSafely( - DialogManager.DIALOG_SEND_TO_GOOGLE_RESULT); - } - }); + DialogInterface.OnClickListener onOkListener = (canShare && shareRequested) + ? doShareListener : null; + DialogInterface.OnClickListener onShareListener = (canShare && !shareRequested) + ? doShareListener : null; + + AlertDialog sendToGoogleResultDialog = ResultDialogFactory.makeDialog(this, + makeSendToGoogleResults(sendToGoogleDialog), onOkListener, onShareListener); + DialogManager.showDialogSafely(this, sendToGoogleResultDialog); } - boolean shareLinkToMap(boolean sentToMyMaps, boolean sentToFusionTables) { + private boolean shareLinkToMap(boolean sentToMyMaps, boolean sentToFusionTables) { String url = null; if (sentToMyMaps && sendToMyMapsSuccess) { // Prefer a link to My Maps @@ -1145,27 +1174,24 @@ public class MyTracks extends TabActivity implements OnTouchListener, return SendToFusionTables.getMapVisualizationUrl(track); } - String getSendToGoogleResultMessage() { - StringBuilder message = new StringBuilder(); - SendToGoogleDialog sendToGoogleDialog = - dialogManager.getSendToGoogleDialog(); - if (sendToGoogleDialog.getSendToMyMaps()) { - message.append(sendToMyMapsMessage); + /** + * Creates a list of {@link SendResult} instances based on the set of + * services selected in {@link SendDialog} and the results as known to + * this class. + */ + private List makeSendToGoogleResults(SendDialog dialog) { + List results = new ArrayList(); + if (dialog.getSendToMyMaps()) { + results.add(new SendResult(SendType.MYMAPS, sendToMyMapsSuccess)); } - if (sendToGoogleDialog.getSendToFusionTables()) { - message.append(sendToFusionTablesMessage); + if (dialog.getSendToFusionTables()) { + results.add(new SendResult(SendType.FUSION_TABLES, sendToFusionTablesSuccess)); } - if (sendToGoogleDialog.getSendToDocs()) { - if (message.length() > 0) { - message.append(' '); - } - message.append(sendToDocsMessage); + if (dialog.getSendToDocs()) { + results.add(new SendResult(SendType.DOCS, sendToDocsSuccess)); } - if (sendToMyMapsSuccess && sendToFusionTablesSuccess && sendToDocsSuccess) { - message.append(' '); - message.append(getString(R.string.status_mymap_info)); - } - return message.toString(); + + return results; } void clearSelectedTrack() { @@ -1293,7 +1319,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, } public boolean getSendToGoogleSuccess() { - return sendToFusionTablesSuccess && sendToDocsSuccess; + return sendToMyMapsSuccess && sendToFusionTablesSuccess && sendToDocsSuccess; } // @VisibleForTesting @@ -1305,12 +1331,12 @@ public class MyTracks extends TabActivity implements OnTouchListener, SharedPreferences getSharedPreferences() { return sharedPreferences; } - + // @VisibleForTesting static void clearInstance() { instance = null; } - + // @VisibleForTesting ITrackRecordingService getTrackRecordingService() { return trackRecordingService; diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java index 085b93e46..85e6a04b5 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java @@ -232,10 +232,14 @@ public class MyTracksSettings extends PreferenceActivity { // Update the ANT+ sensors. // TODO: Only enable on phones that have ANT+. - findPreference(getString(R.string.ant_heart_rate_sensor_id_key)) - .setEnabled(getString(R.string.ant_sensor_type).equals(sensorType)); - findPreference(getString(R.string.ant_srm_bridge_sensor_id_key)) - .setEnabled(getString(R.string.srm_ant_bridge_sensor_type).equals(sensorType)); + Preference antHrm = findPreference(getString(R.string.ant_heart_rate_sensor_id_key)); + Preference antSrm = findPreference(getString(R.string.ant_srm_bridge_sensor_id_key)); + if (antHrm != null && antSrm != null) { + antHrm + .setEnabled(getString(R.string.ant_sensor_type).equals(sensorType)); + antSrm + .setEnabled(getString(R.string.srm_ant_bridge_sensor_type).equals(sensorType)); + } } /** diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToFusionTables.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToFusionTables.java index 9b9224e3c..c15013a50 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToFusionTables.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToFusionTables.java @@ -172,7 +172,7 @@ public class SendToFusionTables implements Runnable { private void doUpload() { ((GoogleHeaders) transport.defaultHeaders).setGoogleLogin(auth.getAuthToken()); int statusMessageId = R.string.error_sending_to_fusiontables; - boolean success = true; + boolean success = false; try { progressIndicator.setProgressValue(PROGRESS_INITIALIZATION); progressIndicator.setProgressMessage(R.string.progress_message_reading_track); @@ -204,10 +204,11 @@ public class SendToFusionTables implements Runnable { } statusMessageId = R.string.status_new_fusiontable_has_been_created; + success = true; Log.d(MyTracksConstants.TAG, "SendToFusionTables: Done: " + success); progressIndicator.setProgressValue(PROGRESS_COMPLETE); - } finally { + } finally { final boolean finalSuccess = success; final int finalStatusMessageId = statusMessageId; context.runOnUiThread(new Runnable() { diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultDialogFactory.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultDialogFactory.java new file mode 100644 index 000000000..f4d77892e --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultDialogFactory.java @@ -0,0 +1,106 @@ +/* + * Copyright 2011 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. + */ +package com.google.android.apps.mytracks.io.sendtogoogle; + +import com.google.android.maps.mytracks.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.view.View; +import android.widget.ListView; + +import java.util.List; + +/** + * Creates the dialog used to display the results of sending track data to + * Google. The dialog lists the services to which data was uploaded, along + * with an indicator of success/failure. If possible, a button is displayed + * offering to share the uploaded track. + * + * Implementation note: This class is a factory, rather than a {@link Dialog} + * subclass, because alert dialogs have to be created with + * {@link AlertDialog.Builder}. Attempts to subclass {@link AlertDialog} + * directly have been unsuccessful, as {@link AlertDialog.Builder} uses a + * private subclass to implement most of the interesting behavior. + * + * @author Matthew Simmons + */ +public class ResultDialogFactory { + private ResultDialogFactory() {} + + /** + * Create a send-to-Google result dialog. The caller is responsible for + * showing it. + * + * @param activity the activity associated with the dialog + * @param results the results to be displayed in the dialog + * @param onOkClickListener the listener to invoke if the OK button is + * clicked + * @param onShareClickListener the listener to invoke if the Share button is + * clicked. If no share listener is provided, the Share button will not + * be displayed. + * @return the created dialog + */ + public static AlertDialog makeDialog(Activity activity, List results, + DialogInterface.OnClickListener onOkClickListener, + DialogInterface.OnClickListener onShareClickListener) { + boolean success = true; + for (SendResult result : results) { + if (!result.isSuccess()) { + success = false; + break; + } + } + + AlertDialog.Builder builder = new AlertDialog.Builder(activity) + .setView(makeDialogContent(activity, results, success)); + + if (success) { + builder.setTitle(R.string.success); + builder.setIcon(android.R.drawable.ic_dialog_info); + } else { + builder.setTitle(R.string.error); + builder.setIcon(android.R.drawable.ic_dialog_alert); + } + + builder.setPositiveButton(activity.getString(R.string.ok), onOkClickListener); + if (onShareClickListener != null) { + builder.setNeutralButton(activity.getString(R.string.share_track), + onShareClickListener); + } + + return builder.create(); + } + + private static View makeDialogContent(Activity activity, List results, + boolean success) { + ResultListAdapter resultListAdapter = new ResultListAdapter(activity, + R.layout.send_to_google_result_list_item, results); + + View content = activity.getLayoutInflater().inflate(R.layout.send_to_google_result, null); + ListView resultList = (ListView) content.findViewById(R.id.send_to_google_result_list); + resultList.setAdapter(resultListAdapter); + + content.findViewById(R.id.send_to_google_result_comment) + .setVisibility(success ? View.VISIBLE : View.GONE); + content.findViewById(R.id.send_to_google_result_error) + .setVisibility(success ? View.GONE : View.VISIBLE); + + return content; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultListAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultListAdapter.java new file mode 100644 index 000000000..68653067b --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultListAdapter.java @@ -0,0 +1,85 @@ +/* + * Copyright 2011 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. + */ +package com.google.android.apps.mytracks.io.sendtogoogle; + +import com.google.android.maps.mytracks.R; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListAdapter; +import android.widget.TextView; + +import java.util.List; + +/** + * This class implements the {@link ListAdapter} used by the send-to-Google + * result dialog created by {@link ResultDialogFactory}. It generates views + * for each entry in an array of {@link SendResult} instances. + * @author Matthew Simmons + */ +class ResultListAdapter extends ArrayAdapter { + public ResultListAdapter(Context context, int textViewResourceId, List results) { + super(context, textViewResourceId, results); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = convertView; + if (view == null) { + LayoutInflater inflater + = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = inflater.inflate(R.layout.send_to_google_result_list_item, null); + } + + SendResult result = getItem(position); + setImage(view, result.isSuccess() ? R.drawable.success : R.drawable.failure); + setName(view, result.getType().getServiceName()); + setUrl(view, result.getType().getServiceUrl()); + return view; + } + + @Override + public boolean areAllItemsEnabled() { + // We don't want the displayed items to be clickable + return false; + } + + // The following protected methods exist to be overridden for testing + // purposes. Doing so insulates the test class from the details of the + // layout. + + protected void setImage(View content, int drawableId) { + ImageView imageView = (ImageView) content.findViewById(R.id.send_to_google_result_icon); + imageView.setImageDrawable(getContext().getResources().getDrawable(drawableId)); + } + + protected void setName(View content, int nameId) { + setTextViewText(content, R.id.send_to_google_result_name, nameId); + } + + protected void setUrl(View content, int urlId) { + setTextViewText(content, R.id.send_to_google_result_url, urlId); + } + + private void setTextViewText(View content, int viewId, int textId) { + TextView textView = (TextView) content.findViewById(viewId); + textView.setText(getContext().getString(textId)); + } +}; diff --git a/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendDialog.java similarity index 95% rename from MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java rename to MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendDialog.java index be5b83e81..9296de275 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendDialog.java @@ -1,20 +1,23 @@ /* * Copyright 2008 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. */ -package com.google.android.apps.mytracks; +package com.google.android.apps.mytracks.io.sendtogoogle; +import com.google.android.apps.mytracks.DialogManager; +import com.google.android.apps.mytracks.MyTracks; +import com.google.android.apps.mytracks.MyTracksSettings; import com.google.android.maps.mytracks.R; import android.app.Dialog; @@ -37,7 +40,7 @@ import android.widget.RadioGroup; * * @author Leif Hendrik Wilden */ -public class SendToGoogleDialog extends Dialog { +public class SendDialog extends Dialog { private RadioGroup groupMyMaps; private RadioButton createNewMapRadioButton; @@ -49,7 +52,7 @@ public class SendToGoogleDialog extends Dialog { private RadioButton sendStatsAndPointsRadioButton; private Button sendButton; - public SendToGoogleDialog(Context context) { + public SendDialog(Context context) { super(context); } @@ -81,8 +84,6 @@ public class SendToGoogleDialog extends Dialog { sendToFusionTablesCheckBox.isChecked() || sendToDocsCheckBox.isChecked()); groupMyMaps.setVisibility(sendToMyMapsCheckBox.isChecked() ? View.VISIBLE : View.INVISIBLE); - //groupDocs.setVisibility( - // sendToDocsCheckBox.isChecked() ? View.VISIBLE : View.INVISIBLE); } }; diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendResult.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendResult.java new file mode 100644 index 000000000..34ad4b13d --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendResult.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011 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. + */ +package com.google.android.apps.mytracks.io.sendtogoogle; + +/** + * This class represents the the result of the uploading of track data to a + * single service. + * + * @author Matthew Simmons + */ +public class SendResult { + private final SendType type; + private final boolean success; + + /** + * @param type the service to which track data was uploaded + * @param success true if the uploading succeeded + */ + public SendResult(SendType type, boolean success) { + this.type = type; + this.success = success; + } + + /** Returns the service to which the track data was uploaded */ + public SendType getType() { + return type; + } + + /** Returns true if the uploading succeeded */ + public boolean isSuccess() { + return success; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendType.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendType.java new file mode 100644 index 000000000..66feccf41 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendType.java @@ -0,0 +1,50 @@ +/* + * Copyright 2011 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. + */ +package com.google.android.apps.mytracks.io.sendtogoogle; + +import com.google.android.maps.mytracks.R; + +/** + * Enumerates the services to which we can upload track data. + * + * @author Matthew Simmons + */ +public enum SendType { + MYMAPS(R.string.sendtogoogle_google_mymaps, + R.string.sendtogoogle_google_mymaps_url), + FUSION_TABLES(R.string.sendtogoogle_google_fusiontables, + R.string.sendtogoogle_google_fusiontables_url), + DOCS(R.string.sendtogoogle_google_docs, + R.string.sendtogoogle_google_docs_url); + + private int serviceName; + private int serviceUrl; + + private SendType(int serviceName, int serviceUrl) { + this.serviceName = serviceName; + this.serviceUrl = serviceUrl; + } + + /** Returns the resource ID for the printable (short) name of the service */ + public int getServiceName() { + return serviceName; + } + + /** Returns the resource ID for the service's URL */ + public int getServiceUrl() { + return serviceUrl; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java b/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java index 756f1393e..bed0e1185 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java @@ -1,12 +1,12 @@ /* * Copyright 2009 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 @@ -128,6 +128,25 @@ public class StatusAnnouncerTask implements PeriodicTask { */ @Override public void run(TrackRecordingService service) { + if (service == null) { + Log.e(MyTracksConstants.TAG, "StatusAnnouncer TrackRecordingService not initialized"); + return; + } + + runWithStatistics(service.getTripStatistics()); + } + + /** + * This method exists as a convenience for testing code, allowing said code + * to avoid needing to instantiate an entire {@link TrackRecordingService} + * just to test the announcer. + */ + protected void runWithStatistics(TripStatistics statistics) { + if (statistics == null) { + Log.e(MyTracksConstants.TAG, "StatusAnnouncer stats not initialized."); + return; + } + if (!ready || tts == null) { Log.e(MyTracksConstants.TAG, "StatusAnnouncer Tts not ready."); return; @@ -139,12 +158,7 @@ public class StatusAnnouncerTask implements PeriodicTask { return; } - if (service == null || service.getTripStatistics() == null) { - Log.e(MyTracksConstants.TAG, "StatusAnnouncer stats not initialized."); - return; - } - - String announcement = getAnnouncement(service.getTripStatistics()); + String announcement = getAnnouncement(statistics); Log.d(MyTracksConstants.TAG, "Announcement: " + announcement); speakAnnouncment(announcement); } @@ -165,7 +179,7 @@ public class StatusAnnouncerTask implements PeriodicTask { boolean metricUnits = true; boolean reportSpeed = true; if (preferences != null) { - metricUnits = + metricUnits = preferences.getBoolean(context.getString(R.string.metric_units_key), true); reportSpeed = diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index f4c455167..5079e4dce 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -902,7 +902,7 @@ public class TrackRecordingService extends Service implements LocationListener { // Make it an impossible location. Location l = new Location(""); l.setLatitude(100); - l.setLongitude(200); + l.setLongitude(180); wpt.setLocation(l); } else { // A null location is ok, and expected on track start. diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/DialogManagerTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/DialogManagerTest.java index bacd00edf..92cf13598 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/DialogManagerTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/DialogManagerTest.java @@ -1,12 +1,12 @@ /* * 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 @@ -15,7 +15,8 @@ */ package com.google.android.apps.mytracks; -import android.app.AlertDialog; +import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog; + import android.app.Dialog; import android.app.ProgressDialog; import android.test.ActivityInstrumentationTestCase2; @@ -66,15 +67,7 @@ public class DialogManagerTest Dialog d = getActivity().onCreateDialog(DialogManager.DIALOG_SEND_TO_GOOGLE, null); assertNotNull(d); - assertTrue(d instanceof SendToGoogleDialog); - } - - public void test_onCreateSendToGoogleResult() { - Dialog d = - getActivity().onCreateDialog( - DialogManager.DIALOG_SEND_TO_GOOGLE_RESULT, null); - assertNotNull(d); - assertTrue(d instanceof AlertDialog); + assertTrue(d instanceof SendDialog); } public void test_onCreateWriteProgress() { diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/docs/DocsHelper_AddTrackRowTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/docs/DocsHelper_AddTrackRowTest.java index e357605b4..89c0cfa3a 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/io/docs/DocsHelper_AddTrackRowTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/docs/DocsHelper_AddTrackRowTest.java @@ -18,31 +18,31 @@ package com.google.android.apps.mytracks.io.docs; import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.io.AuthManager; import com.google.android.apps.mytracks.stats.TripStatistics; +import com.google.android.maps.mytracks.R; import com.google.android.testing.mocking.AndroidMock; import com.google.android.testing.mocking.UsesMocks; -import com.google.android.maps.mytracks.R; import android.content.Context; import android.content.res.Resources; import android.test.mock.MockContext; import android.test.mock.MockResources; -import junit.framework.TestCase; - import java.io.IOException; +import junit.framework.TestCase; + /** * Tests for {@link DocsHelper#addTrackRow} - * + * * @author Matthew Simmons */ public class DocsHelper_AddTrackRowTest extends TestCase { private static final long TIME = 1288721514000L; - + private static class StringWritingDocsHelper extends DocsHelper { String writtenSheetUri = null; String writtenData = null; - + @Override protected void writeRowData(AuthManager trixAuth, String worksheetUri, String postText) { @@ -54,7 +54,7 @@ public class DocsHelper_AddTrackRowTest extends TestCase { public void testAddTrackRow_imperial() throws Exception { StringWritingDocsHelper docsHelper = new StringWritingDocsHelper(); addTrackRow(docsHelper, false); - + String expectedData = "" @@ -81,11 +81,11 @@ public class DocsHelper_AddTrackRowTest extends TestCase { + ""; assertEquals( - "http://spreadsheets.google.com/feeds/list/ssid/wsid/private/full", + "http://spreadsheets.google.com/feeds/list/ssid/wsid/private/full", docsHelper.writtenSheetUri); assertEquals(expectedData, docsHelper.writtenData); } - + public void testAddTrackRow_metric() throws Exception { StringWritingDocsHelper docsHelper = new StringWritingDocsHelper(); addTrackRow(docsHelper, true); @@ -100,18 +100,17 @@ public class DocsHelper_AddTrackRowTest extends TestCase { "")); assertTrue(docsHelper.writtenData.contains( "")); - + assertTrue(docsHelper.writtenData.contains( "")); } - @UsesMocks({AuthManager.class, MockContext.class, MockResources.class, - Track.class}) /** Adds a row to the spreadsheet, using the provided helper. */ - private void addTrackRow(DocsHelper docsHelper, boolean useMetric) + @UsesMocks({AuthManager.class, MockResources.class, Track.class}) + private void addTrackRow(DocsHelper docsHelper, boolean useMetric) throws IOException { - Resources mockResources = AndroidMock.createMock(MockResources.class); - + final Resources mockResources = AndroidMock.createMock(MockResources.class); + if (useMetric) { AndroidMock.expect(mockResources.getString(R.string.kilometer)) .andReturn("km"); @@ -128,15 +127,17 @@ public class DocsHelper_AddTrackRowTest extends TestCase { .andReturn("feet"); } AndroidMock.replay(mockResources); - - Context mockContext = AndroidMock.createMock(MockContext.class); - AndroidMock.expect(mockContext.getResources()) - .andReturn(mockResources).anyTimes(); - AndroidMock.replay(mockContext); + + Context mockContext = new MockContext() { + @Override + public Resources getResources() { + return mockResources; + } + }; AuthManager mockAuthManager = AndroidMock.createMock(AuthManager.class); AndroidMock.replay(mockAuthManager); - + TripStatistics stats = new TripStatistics(); stats.setStartTime(TIME); stats.setTotalTime(5000); @@ -146,14 +147,14 @@ public class DocsHelper_AddTrackRowTest extends TestCase { stats.setTotalElevationGain(6000); stats.setMinElevation(-500); stats.setMaxElevation(550); - + Track track = new Track(); track.setName("trackName"); track.setDescription("trackDescription"); track.setMapId("trackMapId"); track.setStatistics(stats); - - docsHelper.addTrackRow(mockContext, mockAuthManager, "ssid", "wsid", + + docsHelper.addTrackRow(mockContext, mockAuthManager, "ssid", "wsid", track, useMetric); } } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultDialogFactoryTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultDialogFactoryTest.java new file mode 100644 index 000000000..c88035e71 --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultDialogFactoryTest.java @@ -0,0 +1,121 @@ +/* + * Copyright 2011 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. + */ +package com.google.android.apps.mytracks.io.sendtogoogle; + +import com.google.android.apps.mytracks.MyTracks; +import com.google.android.maps.mytracks.R; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; +import android.widget.ListAdapter; +import android.widget.ListView; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests {@link ResultDialogFactory}. + * + * @author Matthew Simmons + */ +public class ResultDialogFactoryTest extends ActivityInstrumentationTestCase2 { + public ResultDialogFactoryTest() { + super(MyTracks.class); + } + + private List makeResults(SendResult... results) { + List resultsList = new ArrayList(); + for (SendResult result : results) { + resultsList.add(result); + } + return resultsList; + } + + private DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) {} + }; + + public void testSuccess_noShare() { + List results = makeResults(new SendResult(SendType.MYMAPS, true), + new SendResult(SendType.DOCS, true)); + + AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results, clickListener, null); + dialog.show(); + + ListView listView = (ListView) dialog.findViewById(R.id.send_to_google_result_list); + ListAdapter listAdapter = listView.getAdapter(); + assertEquals(2, listAdapter.getCount()); + + assertEquals(SendType.MYMAPS, ((SendResult) listAdapter.getItem(0)).getType()); + assertEquals(SendType.DOCS, ((SendResult) listAdapter.getItem(1)).getType()); + + // Checking the return code of Dialog#getButton doesn't appear to be + // sufficient, as a Button will be returned even if it doesn't appear in + // the rendered dialog. + assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility()); + assertEquals(View.GONE, dialog.getButton(AlertDialog.BUTTON_NEUTRAL).getVisibility()); + + assertEquals(View.VISIBLE, + dialog.findViewById(R.id.send_to_google_result_comment).getVisibility()); + assertEquals(View.GONE, + dialog.findViewById(R.id.send_to_google_result_error).getVisibility()); + + // TODO: Test correct listener invocation. Neither TouchUtils#clickView + // nor Button#performClick appear to cause listener invocation. This + // might be Android bug + // http://code.google.com/p/android/issues/detail?id=6564 + } + + public void testSuccess_share() { + List results = makeResults(new SendResult(SendType.MYMAPS, true), + new SendResult(SendType.DOCS, true)); + + AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results, + clickListener, clickListener); + dialog.show(); + + assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility()); + assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_NEUTRAL).getVisibility()); + } + + public void testFailure_noShare() { + List results = makeResults(new SendResult(SendType.MYMAPS, true), + new SendResult(SendType.DOCS, false)); + + AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results, clickListener, null); + dialog.show(); + + assertEquals(View.GONE, + dialog.findViewById(R.id.send_to_google_result_comment).getVisibility()); + assertEquals(View.VISIBLE, + dialog.findViewById(R.id.send_to_google_result_error).getVisibility()); + } + + public void testFailure_share() { + List results = makeResults(new SendResult(SendType.MYMAPS, true), + new SendResult(SendType.DOCS, false)); + + AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results, + clickListener, clickListener); + dialog.show(); + + assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility()); + assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_NEUTRAL).getVisibility()); + } +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultListAdapterTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultListAdapterTest.java new file mode 100644 index 000000000..eb0dcb5f2 --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/ResultListAdapterTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2011 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. + */ +package com.google.android.apps.mytracks.io.sendtogoogle; + +import com.google.android.maps.mytracks.R; + +import android.content.Context; +import android.test.AndroidTestCase; +import android.view.LayoutInflater; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests {@link ResultListAdapter}. + * + * @author Matthew Simmons + */ +public class ResultListAdapterTest extends AndroidTestCase { + private static class TestResultListAdapter extends ResultListAdapter { + public View contentView; + public int drawableId; + public int nameId; + public int urlId; + + public TestResultListAdapter(Context context, List results) { + super(context, 0, results); + } + + @Override + protected void setImage(View content, int drawableId) { + saveContentView(content); + this.drawableId = drawableId; + } + + @Override + protected void setName(View content, int nameId) { + saveContentView(content); + this.nameId = nameId; + } + + @Override + protected void setUrl(View content, int urlId) { + saveContentView(content); + this.urlId = urlId; + } + + private void saveContentView(View contentView) { + if (this.contentView == null) { + this.contentView = contentView; + } else { + AndroidTestCase.assertEquals(this.contentView, contentView); + } + } + } + + private final SendType sendType = SendType.MYMAPS; + + private View inflateContentView(int viewId) { + LayoutInflater layoutInflater + = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + return layoutInflater.inflate(viewId, null); + } + + private List makeResult(SendType type, boolean success) { + List results = new ArrayList(); + results.add(new SendResult(type, success)); + return results; + } + + public void testSuccess_createsNewView() { + TestResultListAdapter adapter = new TestResultListAdapter(getContext(), + makeResult(sendType, true)); + adapter.getView(0, null, null); + + assertEquals(R.drawable.success, adapter.drawableId); + assertEquals(sendType.getServiceName(), adapter.nameId); + assertEquals(sendType.getServiceUrl(), adapter.urlId); + } + + public void testSuccess_reusesView() { + View contentView = inflateContentView(R.layout.send_to_google_result_list_item); + TestResultListAdapter adapter = new TestResultListAdapter(getContext(), + makeResult(sendType, true)); + adapter.getView(0, contentView, null); + + assertEquals(contentView, adapter.contentView); + assertEquals(R.drawable.success, adapter.drawableId); + assertEquals(sendType.getServiceName(), adapter.nameId); + assertEquals(sendType.getServiceUrl(), adapter.urlId); + } + + public void testFailure() { + TestResultListAdapter adapter = new TestResultListAdapter(getContext(), + makeResult(sendType, false)); + adapter.getView(0, null, null); + + assertEquals(R.drawable.failure, adapter.drawableId); + assertEquals(sendType.getServiceName(), adapter.nameId); + assertEquals(sendType.getServiceUrl(), adapter.urlId); + } +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/StatusAnnouncerTaskTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/StatusAnnouncerTaskTest.java index 59ef6853c..4bc7a3664 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/services/StatusAnnouncerTaskTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/StatusAnnouncerTaskTest.java @@ -1,12 +1,12 @@ /* * 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 @@ -255,7 +255,6 @@ public class StatusAnnouncerTaskTest extends AndroidTestCase { @UsesMocks({ StatusAnnouncerTask.class, StringUtils.class, - TrackRecordingService.class, }) @Override protected void setUp() throws Exception { @@ -390,9 +389,6 @@ public class StatusAnnouncerTaskTest extends AndroidTestCase { public void testRun() throws Exception { // Expect service data calls TripStatistics stats = new TripStatistics(); - TrackRecordingService service = - AndroidMock.createMock(TrackRecordingService.class); - expect(service.getTripStatistics()).andStubReturn(stats); // Expect announcement building call expect(mockTask.getAnnouncement(same(stats))).andStubReturn(ANNOUNCEMENT); @@ -407,75 +403,63 @@ public class StatusAnnouncerTaskTest extends AndroidTestCase { .andReturn(TextToSpeech.SUCCESS); // Run the announcement - AndroidMock.replay(tts, stringUtils, service); - task.run(service); - AndroidMock.verify(mockTask, tts, stringUtils, service); + AndroidMock.replay(tts, stringUtils); + task.runWithStatistics(stats); + AndroidMock.verify(mockTask, tts, stringUtils); } public void testRun_notReady() throws Exception { - TrackRecordingService service = - AndroidMock.createMock(TrackRecordingService.class); - // Put task in "not ready" state startTask(TextToSpeech.ERROR); // Run the announcement - AndroidMock.replay(tts, stringUtils, service); - task.run(service); - AndroidMock.verify(mockTask, tts, stringUtils, service); + AndroidMock.replay(tts, stringUtils); + task.runWithStatistics(null); + AndroidMock.verify(mockTask, tts, stringUtils); } public void testRun_duringCall() throws Exception { - TrackRecordingService service = - AndroidMock.createMock(TrackRecordingService.class); - startTask(TextToSpeech.SUCCESS); expect(tts.isSpeaking()).andStubReturn(false); // Run the announcement - AndroidMock.replay(tts, stringUtils, service); + AndroidMock.replay(tts, stringUtils); PhoneStateListener phoneListener = phoneListenerCapture.getValue(); phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null); - task.run(service); - AndroidMock.verify(mockTask, tts, stringUtils, service); + task.runWithStatistics(null); + AndroidMock.verify(mockTask, tts, stringUtils); } public void testRun_ringWhileSpeaking() throws Exception { - TrackRecordingService service = - AndroidMock.createMock(TrackRecordingService.class); - startTask(TextToSpeech.SUCCESS); expect(tts.isSpeaking()).andStubReturn(true); expect(tts.stop()).andReturn(TextToSpeech.SUCCESS); - AndroidMock.replay(tts, stringUtils, service); + AndroidMock.replay(tts, stringUtils); // Update the state to ringing - this should stop the current announcement. PhoneStateListener phoneListener = phoneListenerCapture.getValue(); phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null); // Run the announcement - this should do nothing. - task.run(service); + task.runWithStatistics(null); - AndroidMock.verify(mockTask, tts, stringUtils, service); + AndroidMock.verify(mockTask, tts, stringUtils); } public void testRun_whileRinging() throws Exception { - TrackRecordingService service = - AndroidMock.createMock(TrackRecordingService.class); - startTask(TextToSpeech.SUCCESS); expect(tts.isSpeaking()).andStubReturn(false); // Run the announcement - AndroidMock.replay(tts, stringUtils, service); + AndroidMock.replay(tts, stringUtils); PhoneStateListener phoneListener = phoneListenerCapture.getValue(); phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null); - task.run(service); - AndroidMock.verify(mockTask, tts, stringUtils, service); + task.runWithStatistics(null); + AndroidMock.verify(mockTask, tts, stringUtils); } public void testRun_noService() throws Exception { @@ -489,16 +473,13 @@ public class StatusAnnouncerTaskTest extends AndroidTestCase { public void testRun_noStats() throws Exception { // Expect service data calls - TrackRecordingService service = - AndroidMock.createMock(TrackRecordingService.class); - expect(service.getTripStatistics()).andStubReturn(null); startTask(TextToSpeech.SUCCESS); // Run the announcement - AndroidMock.replay(tts, stringUtils, service); - task.run(service); - AndroidMock.verify(mockTask, tts, stringUtils, service); + AndroidMock.replay(tts, stringUtils); + task.runWithStatistics(null); + AndroidMock.verify(mockTask, tts, stringUtils); } private void startTask(int state) {