From 9c807697dee09996b3276f0d22a822453876e432 Mon Sep 17 00:00:00 2001 From: youtaol Date: Thu, 12 Jul 2012 11:15:17 +0800 Subject: [PATCH] Merge heads --- .../apps/mytracks/io/docs/SendDocsUtils.java | 23 +- .../SendFusionTablesAsyncTask.java | 13 +- .../endtoendtest/CreateAndSendTrackTest.java | 21 +- .../mytracks/endtoendtest/DeleteTest.java | 1 + .../endtoendtest/EndToEndTestUtils.java | 39 +- .../mytracks/endtoendtest/GoogleUtils.java | 341 ++++++++++++++++++ .../mytracks/endtoendtest/MenuItemsTest.java | 9 +- .../mytracks/endtoendtest/SettingTest.java | 14 +- 8 files changed, 418 insertions(+), 43 deletions(-) create mode 100644 MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/GoogleUtils.java diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/docs/SendDocsUtils.java b/MyTracks/src/com/google/android/apps/mytracks/io/docs/SendDocsUtils.java index 906798471..af9af24b7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/docs/SendDocsUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/docs/SendDocsUtils.java @@ -56,26 +56,31 @@ import java.util.Locale; * @author Matthew Simmons */ public class SendDocsUtils { - private static final String GET_SPREADSHEET_BY_TITLE_URI = + @VisibleForTesting + public static final String GET_SPREADSHEET_BY_TITLE_URI = "https://docs.google.com/feeds/documents/private/full?" + "category=mine,spreadsheet&title=%s&title-exact=true"; private static final String CREATE_SPREADSHEET_URI = "https://docs.google.com/feeds/documents/private/full"; private static final String GET_WORKSHEETS_URI = "https://spreadsheets.google.com/feeds/worksheets/%s/private/full"; - private static final String GET_WORKSHEET_URI = + @VisibleForTesting + public static final String GET_WORKSHEET_URI = "https://spreadsheets.google.com/feeds/list/%s/%s/private/full"; - - private static final String SPREADSHEET_ID_PREFIX = + @VisibleForTesting + public static final String SPREADSHEET_ID_PREFIX = "https://docs.google.com/feeds/documents/private/full/spreadsheet%3A"; - - private static final String CONTENT_TYPE = "Content-Type"; - private static final String ATOM_FEED_MIME_TYPE = "application/atom+xml"; + @VisibleForTesting + public static final String CONTENT_TYPE = "Content-Type"; + @VisibleForTesting + public static final String ATOM_FEED_MIME_TYPE = "application/atom+xml"; private static final String OPENDOCUMENT_SPREADSHEET_MIME_TYPE = "application/x-vnd.oasis.opendocument.spreadsheet"; - private static final String AUTHORIZATION = "Authorization"; - private static final String AUTHORIZATION_PREFIX = "GoogleLogin auth="; + @VisibleForTesting + public static final String AUTHORIZATION = "Authorization"; + @VisibleForTesting + public static final String AUTHORIZATION_PREFIX = "GoogleLogin auth="; private static final String SLUG = "Slug"; diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java index ef128be09..72810f2f0 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java @@ -27,6 +27,7 @@ import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.InputStreamContent; import com.google.api.client.util.Strings; +import com.google.common.annotations.VisibleForTesting; import android.accounts.Account; import android.accounts.AccountManager; @@ -52,13 +53,17 @@ import java.util.Vector; */ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask { - private static final String APP_NAME_PREFIX = "Google-MyTracks-"; + @VisibleForTesting + public static final String APP_NAME_PREFIX = "Google-MyTracks-"; private static final String SQL_KEY = "sql="; - private static final String CONTENT_TYPE = "application/x-www-form-urlencoded"; - private static final String FUSION_TABLES_BASE_URL = + @VisibleForTesting + public static final String CONTENT_TYPE = "application/x-www-form-urlencoded"; + @VisibleForTesting + public static final String FUSION_TABLES_BASE_URL = "https://www.google.com/fusiontables/api/query"; private static final int MAX_POINTS_PER_UPLOAD = 2048; - private static final String GDATA_VERSION = "2"; + @VisibleForTesting + public static final String GDATA_VERSION = "2"; private static final int PROGRESS_CREATE_TABLE = 0; private static final int PROGRESS_UNLIST_TABLE = 5; diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/CreateAndSendTrackTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/CreateAndSendTrackTest.java index fdbd35e36..e8bd4ee6d 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/CreateAndSendTrackTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/CreateAndSendTrackTest.java @@ -104,6 +104,8 @@ public class CreateAndSendTrackTest extends ActivityInstrumentationTestCase2 VIEW_MODE_ENGLISH_MULTILINGUAL = new HashMap(); public static String RELATIVE_STARTTIME_POSTFIX = ""; public static String VIEW_MODE = ""; + static { + RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("es", "mins ago"); + RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("de", "Minuten"); + RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("fr", "minute"); + RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("ar", "دقيقة"); + RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("zh", "分钟前"); + + VIEW_MODE_ENGLISH_MULTILINGUAL.put("es", "mode"); + VIEW_MODE_ENGLISH_MULTILINGUAL.put("de", "modus"); + VIEW_MODE_ENGLISH_MULTILINGUAL.put("fr", "Mode"); + VIEW_MODE_ENGLISH_MULTILINGUAL.put("ar", "وضع"); + VIEW_MODE_ENGLISH_MULTILINGUAL.put("zh", "模式"); + } static Solo SOLO; static Instrumentation instrumentation; @@ -86,21 +101,10 @@ public class EndToEndTestUtils { private EndToEndTestUtils() {} + /** + * Checks the language, then sets the fields with right string. + */ private static void checkLanguage() { - RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("es", "mins ago"); - RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("de", "Minuten"); - RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("fr", "minute"); - RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("ar", "دقيقة"); - RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("zh", "分钟前"); - - VIEW_MODE_ENGLISH_MULTILINGUAL.put("es", "mode"); - VIEW_MODE_ENGLISH_MULTILINGUAL.put("de", "modus"); - VIEW_MODE_ENGLISH_MULTILINGUAL.put("fr", "Mode"); - VIEW_MODE_ENGLISH_MULTILINGUAL.put("ar", "وضع"); - VIEW_MODE_ENGLISH_MULTILINGUAL.put("zh", "模式"); - - - // Check the language, test in Chinese or English. String deviceLanguage = instrumentation.getContext().getResources().getConfiguration().locale.getLanguage(); if (RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get(deviceLanguage) != null) { RELATIVE_STARTTIME_POSTFIX = RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get(deviceLanguage); @@ -184,6 +188,7 @@ public class EndToEndTestUtils { checkLanguage(); EndToEndTestUtils.isCheckedFirstLaunch = true; EndToEndTestUtils.setHasActionBar(); + EndToEndTestUtils.resetAllSettings(activityMyTracks, false); } else if (EndToEndTestUtils.SOLO.waitForText( // After reset setting, welcome page will show again. activityMytracks.getString(R.string.welcome_title), 0, 500)) { @@ -353,12 +358,15 @@ public class EndToEndTestUtils { // trusted. EndToEndTestUtils.trackName = EndToEndTestUtils.TRACK_NAME_PREFIX + System.currentTimeMillis(); + SOLO.sendKey(KeyEvent.KEYCODE_DEL); SOLO.enterText(0, trackName); + SOLO.enterText(1, DEFAULTACTIVITY); if(!EndToEndTestUtils.isEmulator) { // Close soft keyboard. EndToEndTestUtils.SOLO.goBack(); } SOLO.clickLongOnText(activityMytracks.getString(R.string.generic_save)); + instrumentation.waitForIdleSync(); } } @@ -520,7 +528,7 @@ public class EndToEndTestUtils { } /** - * Get more menu items operation is different for different Android OS. + * Gets more menu items operation is different for different Android OS. */ public static void showMoreMenuItem() { if (hasActionBar) { @@ -600,4 +608,5 @@ public class EndToEndTestUtils { EndToEndTestUtils.SOLO.goBack(); } } + } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/GoogleUtils.java b/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/GoogleUtils.java new file mode 100644 index 000000000..61fbe80bc --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/GoogleUtils.java @@ -0,0 +1,341 @@ +/* + * Copyright 2012 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.endtoendtest; + +import com.google.android.apps.mytracks.Constants; +import com.google.android.apps.mytracks.io.docs.SendDocsUtils; +import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesAsyncTask; +import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesUtils; +import com.google.android.apps.mytracks.io.gdata.GDataClientFactory; +import com.google.android.apps.mytracks.io.gdata.docs.DocumentsClient; +import com.google.android.apps.mytracks.io.gdata.docs.SpreadsheetsClient; +import com.google.android.apps.mytracks.io.gdata.docs.XmlDocsGDataParserFactory; +import com.google.android.apps.mytracks.io.gdata.maps.MapFeatureEntry; +import com.google.android.apps.mytracks.io.gdata.maps.MapsClient; +import com.google.android.apps.mytracks.io.gdata.maps.MapsConstants; +import com.google.android.apps.mytracks.io.gdata.maps.MapsGDataConverter; +import com.google.android.apps.mytracks.io.gdata.maps.MapsMapMetadata; +import com.google.android.apps.mytracks.io.gdata.maps.XmlMapsGDataParserFactory; +import com.google.android.apps.mytracks.util.ApiAdapterFactory; +import com.google.android.apps.mytracks.util.SystemUtils; +import com.google.android.common.gdata.AndroidXmlParserFactory; +import com.google.api.client.googleapis.GoogleHeaders; +import com.google.api.client.googleapis.MethodOverride; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.InputStreamContent; +import com.google.api.client.util.Strings; +import com.google.wireless.gdata.data.Entry; +import com.google.wireless.gdata.data.Feed; +import com.google.wireless.gdata.parser.GDataParser; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Activity; +import android.content.Context; +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Locale; + +/** + * Provides utilities to access Google Maps, Google Documents, Google Fusion + * Tables. + * + * @author Youtao Liu + */ +public class GoogleUtils { + private static Account account; + public static final String LOG_TAG = "MyTracksTest"; + public static final String DOCUMENT_NAME_PREFIX = "My Tracks"; + public static final String SPREADSHEET_NAME = DOCUMENT_NAME_PREFIX + "-" + EndToEndTestUtils.DEFAULTACTIVITY; + + private static Account getAccount(Context context) { + return account == null ? account = AccountManager.get(context).getAccountsByType( + Constants.ACCOUNT_TYPE)[0] : account; + } + + + /** + * Gets Google maps of a user. + * + * @param context used to get maps + * @param mapsClient the client to access Google Maps + * @return true means set successfully + */ + private static ArrayList getMaps(Context context, MapsClient mapsClient) { + // Reset the per request states + String authToken = null; + ArrayList mapIds = new ArrayList(); + ArrayList mapData = new ArrayList(); + + try { + authToken = AccountManager.get(context).blockingGetAuthToken(getAccount(context), + MapsConstants.SERVICE_NAME, false); + } catch (Exception e) { + Log.d(LOG_TAG, "Unable to get auth token", e); + return mapData; + } + + GDataParser gDataParser = null; + try { + gDataParser = mapsClient.getParserForFeed(MapFeatureEntry.class, MapsClient.getMapsFeed(), + authToken); + gDataParser.init(); + while (gDataParser.hasMoreData()) { + MapFeatureEntry entry = (MapFeatureEntry) gDataParser.readNextEntry(null); + mapIds.add(MapsGDataConverter.getMapidForEntry(entry)); + mapData.add(MapsGDataConverter.getMapMetadataForEntry(entry)); + } + } catch (Exception e) { + Log.d(LOG_TAG, "Unable to get maps", e); + } finally { + if (gDataParser != null) { + gDataParser.close(); + } + } + + return mapData; + } + + /** + * Searches a map in user's Google Maps. + * + * @param title the title of map + * @param activity activity to get context + * @param isDelete whether delete the map of this track in the Google Maps + * @return true means find the map + */ + private static boolean searchMapByTitle(String title, Activity activity, boolean isDelete) { + Context context = activity.getApplicationContext(); + MapsClient mapsClient = new MapsClient(GDataClientFactory.getGDataClient(context), + new XmlMapsGDataParserFactory(new AndroidXmlParserFactory())); + ArrayList mapData = getMaps(context, mapsClient); + for (MapsMapMetadata oneData : mapData) { + if (oneData.getDescription().indexOf(DOCUMENT_NAME_PREFIX) > -1 + && oneData.getTitle().equals(title)) { + if (isDelete) { + try { + mapsClient.deleteEntry(oneData.getGDataEditUri(), AccountManager.get(context).blockingGetAuthToken(getAccount(context), + MapsConstants.SERVICE_NAME, false)); + return true; + } catch (Exception e) { + Log.d(LOG_TAG, "Unable to drop map", e); + return false; + } + } + return true; + } + } + return false; + } + + /** + * Searches a map in user's Google Maps. + * + * @param title the title of map + * @param activity activity to get context + * @return true means find the map + */ + public static boolean searchMap(String title, Activity activity) { + return searchMapByTitle(title, activity, false); + } + + /** + * Searches a map in user's Google Maps and then delete it. + * + * @param title the title of map + * @param activity activity to get context + * @return true means find the map and delete it successfully + */ + public static boolean deleteMap(String title, Activity activity) { + return searchMapByTitle(title, activity, true); + } + + /** + * Searches a doc in user's Google Documents. + * + * @param title the title of doc + * @param activity to get context + * @return the entry of the document, null means can not find the spreadsheet. + */ + private static Entry searchSepeadsheetByTitle(String title, Activity activity) { + Context context = activity.getApplicationContext(); + DocumentsClient documentsClient = new DocumentsClient( + GDataClientFactory.getGDataClient(context), + new XmlDocsGDataParserFactory(new AndroidXmlParserFactory())); + + try { + String documentsAuthToken = AccountManager.get(context) + .blockingGetAuthToken(getAccount(context), documentsClient.getServiceName(), false); + String uri = String.format(Locale.US, SendDocsUtils.GET_SPREADSHEET_BY_TITLE_URI, + URLEncoder.encode(title, "utf-8")); + GDataParser gDataParser = documentsClient.getParserForFeed(Entry.class, uri, + documentsAuthToken); + gDataParser.init(); + + while (gDataParser.hasMoreData()) { + Entry entry = gDataParser.readNextEntry(null); + String entryTitle = entry.getTitle(); + if (entryTitle.equals(title)) { + return entry; + } + } + } catch (Exception e) { + Log.d(LOG_TAG, "Unable to fetch spreadsheet.", e); + } + return null; + } + + + /** + * Searches a track title in a spreadsheet. + * + * @param title the track name to search + * @param activity to get context + * @param spreadsheetTitle the title of spreadsheet + * @param isDelete whether delete the information of this track in the document + * @return true means find the track name in the spreadsheet + */ + public static boolean searchTrackTitleInSpreadsheet(String title, Activity activity, String spreadsheetTitle, boolean isDelete) { + String spreadsheetId = searchSepeadsheetByTitle(spreadsheetTitle, activity).getId().replace(SendDocsUtils.SPREADSHEET_ID_PREFIX, ""); + if(spreadsheetId == null) { + Log.d(LOG_TAG, "Unable to find the spreadsheet -- " + spreadsheetTitle); + return false; + } + Context context = activity.getApplicationContext(); + try { + SpreadsheetsClient spreadsheetsClient = new SpreadsheetsClient( + GDataClientFactory.getGDataClient(context), new XmlDocsGDataParserFactory(new AndroidXmlParserFactory())); + String spreadsheetsAuthToken = AccountManager.get(activity.getApplicationContext()).blockingGetAuthToken( + getAccount(context), spreadsheetsClient.getServiceName(), false); + + String weekSheetId = SendDocsUtils.getWorksheetId(spreadsheetId, spreadsheetsClient, spreadsheetsAuthToken); + String worksheetUri = String.format(Locale.US, SendDocsUtils.GET_WORKSHEET_URI, URLEncoder.encode(spreadsheetId, "utf-8"), + weekSheetId); + + GDataParser gDataParser = spreadsheetsClient.getParserForFeed(Feed.class, worksheetUri, + spreadsheetsAuthToken); + gDataParser.init(); + while (gDataParser.hasMoreData()) { + Entry entry = gDataParser.readNextEntry(null); + String entryTitle = entry.getTitle(); + if (entryTitle.indexOf(title) > -1) { + if (isDelete) { + spreadsheetsClient.deleteEntry(entry.getEditUri(), spreadsheetsAuthToken); + } + return true; + } + } + } catch (Exception e) { + Log.d(LOG_TAG, "Unable to fetch content of spreadsheet.", e); + } + return false; + } + + /** + * Searches a fusion table in user's Google tables. + * + * @param title the title of fusion table + * @param activity to get context + * @return true means find the fusion table + */ + public static boolean searchFusionTableByTitle(String title, Activity activity) { + Context context = activity.getApplicationContext(); + try { + HttpResponse response = sendFusionTableQuery("SHOW TABLES", context); + // We can use index of method to check new table for every track name is unique. + if (response != null && response.parseAsString().indexOf(title) > 0) { + return true; + } + } catch (Exception e) { + Log.d(LOG_TAG, "Unable to query fusion table.", e); + } + return false; + } + + /** + * Drops one fusion table which contain the string in title of current user. + * + * @param title the title of a track to drop + * @param activity to get context + */ + public static boolean dropFusionTables(String title, Activity activity) { + Context context = activity.getApplicationContext(); + + HttpResponse response = sendFusionTableQuery("SHOW TABLES", context); + String[] rowsTable; + try { + rowsTable = response.parseAsString().split("\n"); + for (String row : rowsTable) { + String firstColumn = row.split(",")[0]; + String secondColumn = row.split(",")[1]; + // If the first column is all figure, it is the table id. + String regularExpression = "^[0-9]*$"; + if (firstColumn.matches(regularExpression) && secondColumn.equals(title)) { + sendFusionTableQuery("DROP TABLE " + row.split(",")[0], context); + return true; + } + } + } catch (IOException e) { + Log.d(LOG_TAG, "Failed when delete all fusion tables.", e); + } + return false; + } + + /** + * Sends query to operate fusion tables. + * + * @param query to executed + * @param context application context + * @return the response of execution + */ + private static HttpResponse sendFusionTableQuery(String query, Context context) { + try { + String fusionTableAuthToken = AccountManager.get(context).blockingGetAuthToken(getAccount(context), + SendFusionTablesUtils.SERVICE, false); + + GenericUrl url = new GenericUrl(SendFusionTablesAsyncTask.FUSION_TABLES_BASE_URL); + String sql = "sql=" + query; + ByteArrayInputStream inputStream = new ByteArrayInputStream(Strings.toBytesUtf8(sql)); + InputStreamContent inputStreamContent = new InputStreamContent(null, inputStream); + HttpRequest request; + + request = (ApiAdapterFactory.getApiAdapter().getHttpTransport() + .createRequestFactory(new MethodOverride())).buildPostRequest(url, inputStreamContent); + + GoogleHeaders headers = new GoogleHeaders(); + headers.setApplicationName(SendFusionTablesAsyncTask.APP_NAME_PREFIX + + SystemUtils.getMyTracksVersion(context)); + headers.gdataVersion = SendFusionTablesAsyncTask.GDATA_VERSION; + headers.setGoogleLogin(fusionTableAuthToken); + headers.setContentType(SendFusionTablesAsyncTask.CONTENT_TYPE); + request.setHeaders(headers); + + HttpResponse response; + response = request.execute(); + return response; + } catch (Exception e) { + Log.d(LOG_TAG, "Failed when send fusion table query.", e); + return null; + } + } + +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/MenuItemsTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/MenuItemsTest.java index 2da640f69..58f2ea294 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/MenuItemsTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/endtoendtest/MenuItemsTest.java @@ -67,9 +67,12 @@ public class MenuItemsTest extends ActivityInstrumentationTestCase2 { - - private final String DEFAULTACTIVITY = "TestActivity"; - + private Instrumentation instrumentation; private TrackListActivity activityMyTracks; @@ -93,9 +91,11 @@ public class SettingTest extends ActivityInstrumentationTestCase2