mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 09:33:06 +02:00
Code cleanup: GoogleUtils
This commit is contained in:
+1
-1
@@ -43,7 +43,7 @@ public class EndToEndTestRunner extends InstrumentationTestRunner {
|
||||
EndToEndTestUtils.emulatorPort = Integer.parseInt(port);
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG,
|
||||
"Unable to get emulator port parameter, use the default value", e);
|
||||
"Unable to get emulator port parameter, use the default value.", e);
|
||||
}
|
||||
}
|
||||
Log.i(EndToEndTestUtils.LOG_TAG, "Emulator port: " + EndToEndTestUtils.emulatorPort);
|
||||
|
||||
+251
-284
@@ -13,6 +13,7 @@
|
||||
* 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;
|
||||
@@ -26,6 +27,7 @@ import com.google.android.apps.mytracks.io.gdata.maps.XmlMapsGDataParserFactory;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||
import com.google.android.apps.mytracks.io.spreadsheets.SendSpreadsheetsAsyncTask;
|
||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.api.client.auth.oauth2.BearerToken;
|
||||
@@ -35,7 +37,6 @@ import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccoun
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.services.drive.Drive;
|
||||
import com.google.api.services.drive.model.File;
|
||||
import com.google.api.services.drive.model.FileList;
|
||||
import com.google.api.services.fusiontables.Fusiontables;
|
||||
import com.google.api.services.fusiontables.model.Table;
|
||||
import com.google.gdata.client.spreadsheet.SpreadsheetService;
|
||||
@@ -47,7 +48,6 @@ 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;
|
||||
|
||||
@@ -58,374 +58,341 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Provides utilities to access Google Maps, Google Documents, Google Fusion
|
||||
* Tables.
|
||||
* Utilities for accessing Google Drive, Google Maps, Google Fusion Tables, and
|
||||
* Google Spreadsheets.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class GoogleUtils {
|
||||
|
||||
public static final String DOCUMENT_NAME_PREFIX = "My Tracks";
|
||||
// This is the preferred account.
|
||||
public static final String ACCOUNT_NAME_1 = "mytrackstest@gmail.com";
|
||||
public static final String ACCOUNT_NAME_2 = "mytrackstest2@gmail.com";
|
||||
public static final String SPREADSHEET_NAME = DOCUMENT_NAME_PREFIX + "-"
|
||||
+ EndToEndTestUtils.activityType;
|
||||
private static final String WORK_SHEET_NAME = "Log";
|
||||
private static final String TRANCK_NAME_COLUMN = "Name";
|
||||
public static final String ACCOUNT_1 = "mytrackstest@gmail.com";
|
||||
public static final String ACCOUNT_2 = "mytrackstest2@gmail.com";
|
||||
|
||||
private static final String TEST_TRACKS_QUERY = "'root' in parents and title contains '"
|
||||
private static final String MY_TRACKS_PREFIX = "My Tracks";
|
||||
private static final String SPREADSHEETS_NAME = MY_TRACKS_PREFIX + "-"
|
||||
+ EndToEndTestUtils.activityType;
|
||||
|
||||
private static final String SPREADSHEETS_WORKSHEET_NAME = "Log";
|
||||
private static final String SPREADSHEETS_TRANCK_NAME_COLUMN = "Name";
|
||||
private static final String DRIVE_TEST_FILES_QUERY = "'root' in parents and title contains '"
|
||||
+ EndToEndTestUtils.TRACK_NAME_PREFIX + "' and trashed = false";
|
||||
|
||||
/**
|
||||
* Gets the account to access Google Services.
|
||||
* Deletes Google Drive test files.
|
||||
*
|
||||
* @param context context used to get account
|
||||
* @return the first account which is bound with current device
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
*/
|
||||
private static Account getAccount(Context context) {
|
||||
return AccountManager.get(context).getAccountsByType(Constants.ACCOUNT_TYPE)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<MapsMapMetadata> getMaps(Context context, MapsClient mapsClient) {
|
||||
String authToken = null;
|
||||
ArrayList<String> mapIds = new ArrayList<String>();
|
||||
ArrayList<MapsMapMetadata> mapData = new ArrayList<MapsMapMetadata>();
|
||||
|
||||
public static void deleteDriveTestFiles(Context context, String accountName) {
|
||||
try {
|
||||
authToken = AccountManager.get(context).blockingGetAuthToken(getAccount(context),
|
||||
MapsConstants.SERVICE_NAME, false);
|
||||
} catch (Exception e) {
|
||||
Log.d(EndToEndTestUtils.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(EndToEndTestUtils.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<MapsMapMetadata> 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(EndToEndTestUtils.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes old tracks created by MyTracks test.
|
||||
*
|
||||
* @param activity
|
||||
* @param accountName
|
||||
*/
|
||||
public static void deleteTestTracksOnGoogleDrive(Activity activity, String accountName) {
|
||||
try {
|
||||
GoogleAccountCredential driveCredential = SendToGoogleUtils.getGoogleAccountCredential(
|
||||
activity.getApplicationContext(), accountName, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (driveCredential == null) {
|
||||
GoogleAccountCredential googleAccountCredential = SendToGoogleUtils
|
||||
.getGoogleAccountCredential(context, accountName, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (googleAccountCredential == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Drive drive = SyncUtils.getDriveService(driveCredential);
|
||||
com.google.api.services.drive.Drive.Files.List list = drive.files().list()
|
||||
.setQ(TEST_TRACKS_QUERY);
|
||||
Drive drive = SyncUtils.getDriveService(googleAccountCredential);
|
||||
com.google.api.services.drive.Drive.Files.List list = drive.files()
|
||||
.list().setQ(DRIVE_TEST_FILES_QUERY);
|
||||
List<File> files = list.execute().getItems();
|
||||
for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) {
|
||||
Iterator<File> iterator = files.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
File file = (File) iterator.next();
|
||||
drive.files().delete(file.getId()).execute();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Delete test tracks failed.");
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to delete Google Drive test files.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes old tracks on Google Maps created by MyTracks test.
|
||||
* Deletes Google Maps of the first Google account.
|
||||
*
|
||||
* @param activity
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param mapTitle the map title
|
||||
* @return true if deletion is success.
|
||||
*/
|
||||
public static void deleteTracksOnGoogleMaps(Activity activity) {
|
||||
Context context = activity.getApplicationContext();
|
||||
public static boolean deleteMaps(Context context, String accountName, String mapTitle) {
|
||||
Account account = getGoogleAccount(context, accountName);
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
MapsClient mapsClient = new MapsClient(GDataClientFactory.getGDataClient(context),
|
||||
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||
ArrayList<MapsMapMetadata> mapData = getMaps(context, mapsClient);
|
||||
for (MapsMapMetadata oneData : mapData) {
|
||||
|
||||
try {
|
||||
mapsClient.deleteEntry(oneData.getGDataEditUri(), AccountManager.get(context)
|
||||
.blockingGetAuthToken(getAccount(context), MapsConstants.SERVICE_NAME, false));
|
||||
} catch (Exception e) {
|
||||
Log.d(EndToEndTestUtils.LOG_TAG, "Unable to drop map", e);
|
||||
ArrayList<MapsMapMetadata> mapMetadata = getMaps(context, account, mapsClient);
|
||||
for (MapsMapMetadata data : mapMetadata) {
|
||||
if (data.getDescription().indexOf(MY_TRACKS_PREFIX) > -1
|
||||
&& data.getTitle().equals(mapTitle)) {
|
||||
try {
|
||||
mapsClient.deleteEntry(data.getGDataEditUri(), AccountManager.get(context)
|
||||
.blockingGetAuthToken(account, MapsConstants.SERVICE_NAME, false));
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to delete maps.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete spreadsheet which name is title.
|
||||
*
|
||||
* @param title the name of spreadsheet
|
||||
* @param activity to get context
|
||||
* @param accountName the name of Google account
|
||||
* @return true means delete successfully
|
||||
*/
|
||||
public static boolean deleteSpreadsheetByTitle(String title, Activity activity, String accountName) {
|
||||
try {
|
||||
GoogleAccountCredential driveCredential = SendToGoogleUtils.getGoogleAccountCredential(
|
||||
activity.getApplicationContext(), accountName, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (driveCredential == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Drive drive = SyncUtils.getDriveService(driveCredential);
|
||||
com.google.api.services.drive.Drive.Files.List list = drive.files().list()
|
||||
.setQ(String.format(Locale.US, SendSpreadsheetsAsyncTask.GET_SPREADSHEET_QUERY, title));
|
||||
List<File> files = list.execute().getItems();
|
||||
for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) {
|
||||
File file = (File) iterator.next();
|
||||
drive.files().delete(file.getId()).execute();
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Search spreadsheet failed.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches docs in user's Google Documents.
|
||||
* Deletes all Google Maps of the first Google account
|
||||
*
|
||||
* @param title the title of doc
|
||||
* @param activity to get context
|
||||
* @param accountName the name of Google account
|
||||
* @return the file list of the document, null means can not find the
|
||||
* spreadsheets
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
*/
|
||||
public static List<File> searchAllSpreadsheetByTitle(String title, Activity activity,
|
||||
String accountName) {
|
||||
public static void deleteAllMaps(Context context, String accountName) {
|
||||
Account account = getGoogleAccount(context, accountName);
|
||||
if (account == null) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to get account.");
|
||||
return;
|
||||
}
|
||||
MapsClient mapsClient = new MapsClient(GDataClientFactory.getGDataClient(context),
|
||||
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||
ArrayList<MapsMapMetadata> mapMetadata = getMaps(context, account, mapsClient);
|
||||
for (MapsMapMetadata oneData : mapMetadata) {
|
||||
try {
|
||||
mapsClient.deleteEntry(oneData.getGDataEditUri(), AccountManager.get(context)
|
||||
.blockingGetAuthToken(account, MapsConstants.SERVICE_NAME, false));
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to delete maps.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes Google Fusion Tables.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param tableName the table name
|
||||
* @return true if deletion is success.
|
||||
*/
|
||||
public static boolean deleteFusionTables(Context context, String accountName, String tableName) {
|
||||
try {
|
||||
GoogleAccountCredential driveCredential = SendToGoogleUtils.getGoogleAccountCredential(
|
||||
activity.getApplicationContext(), accountName, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (driveCredential == null) {
|
||||
GoogleAccountCredential googleAccountCredential = SendToGoogleUtils
|
||||
.getGoogleAccountCredential(context, accountName, SendToGoogleUtils.FUSION_TABLES_SCOPE);
|
||||
if (googleAccountCredential == null) {
|
||||
return false;
|
||||
}
|
||||
Fusiontables fusiontables = new Fusiontables.Builder(
|
||||
AndroidHttp.newCompatibleTransport(), new GsonFactory(), googleAccountCredential).build();
|
||||
List<Table> tables = fusiontables.table().list().execute().getItems();
|
||||
Iterator<Table> iterator = tables.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Table table = (Table) iterator.next();
|
||||
String name = table.getName();
|
||||
if (name != null && name.equals(tableName)) {
|
||||
fusiontables.table().delete(table.getTableId()).execute();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to search fusion tables.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches Google Spreadsheets.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @return the list of spreadsheets matching the title. Null if unable to
|
||||
* search.
|
||||
*/
|
||||
public static List<File> searchSpreadsheets(Context context, String accountName) {
|
||||
try {
|
||||
GoogleAccountCredential googleAccountCredential = SendToGoogleUtils
|
||||
.getGoogleAccountCredential(context, accountName, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (googleAccountCredential == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Drive drive = SyncUtils.getDriveService(driveCredential);
|
||||
com.google.api.services.drive.Drive.Files.List list = drive.files().list()
|
||||
.setQ(String.format(Locale.US, SendSpreadsheetsAsyncTask.GET_SPREADSHEET_QUERY, title));
|
||||
FileList result = list.execute();
|
||||
return result.getItems();
|
||||
Drive drive = SyncUtils.getDriveService(googleAccountCredential);
|
||||
com.google.api.services.drive.Drive.Files.List list = drive.files().list().setQ(String.format(
|
||||
Locale.US, SendSpreadsheetsAsyncTask.GET_SPREADSHEET_QUERY, SPREADSHEETS_NAME));
|
||||
return list.execute().getItems();
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Search spreadsheet failed.");
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to search spreadsheets.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a track title in a spreadsheet.
|
||||
* Deletes Google Spreadsheets.
|
||||
*
|
||||
* @param trackName 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
|
||||
* @param accountName the name of Google account
|
||||
* @return true means find the track name in the spreadsheet
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @return true if deletion is success.
|
||||
*/
|
||||
private static boolean searchTrackTitleInSpreadsheet(String trackName, Activity activity,
|
||||
String spreadsheetTitle, boolean isDelete, String accountName) {
|
||||
public static boolean deleteSpreadsheets(Context context, String accountName) {
|
||||
try {
|
||||
// Get spreadsheet Id.
|
||||
String spreadsheetId = searchAllSpreadsheetByTitle(spreadsheetTitle, activity, accountName)
|
||||
.get(0).getId();
|
||||
GoogleAccountCredential googleAccountCredential = SendToGoogleUtils
|
||||
.getGoogleAccountCredential(context, accountName, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (googleAccountCredential == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get spreadsheet service.
|
||||
SpreadsheetService spreadsheetService = new SpreadsheetService(spreadsheetTitle);
|
||||
Drive drive = SyncUtils.getDriveService(googleAccountCredential);
|
||||
com.google.api.services.drive.Drive.Files.List list = drive.files().list().setQ(String.format(
|
||||
Locale.US, SendSpreadsheetsAsyncTask.GET_SPREADSHEET_QUERY, SPREADSHEETS_NAME));
|
||||
List<File> files = list.execute().getItems();
|
||||
Iterator<File> iterator = files.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
File file = (File) iterator.next();
|
||||
drive.files().delete(file.getId()).execute();
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to delete spreadsheets.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes Google Spreadsheets row.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param trackName the track name
|
||||
* @return true if deletion is success.
|
||||
*/
|
||||
public static boolean deleteSpreadsheetsRow(
|
||||
Context context, String accountName, String trackName) {
|
||||
try {
|
||||
// Get spreadsheet Id
|
||||
List<File> files = searchSpreadsheets(context, accountName);
|
||||
if (files == null || files.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
String spreadsheetId = files.get(0).getId();
|
||||
|
||||
// Get spreadsheet service
|
||||
SpreadsheetService spreadsheetService = new SpreadsheetService(
|
||||
"MyTracks-" + SystemUtils.getMyTracksVersion(context));
|
||||
Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod());
|
||||
credential.setAccessToken(SendToGoogleUtils.getToken(activity.getApplicationContext(),
|
||||
accountName, SendToGoogleUtils.SPREADSHEETS_SCOPE));
|
||||
credential.setAccessToken(
|
||||
SendToGoogleUtils.getToken(context, accountName, SendToGoogleUtils.SPREADSHEETS_SCOPE));
|
||||
spreadsheetService.setOAuth2Credentials(credential);
|
||||
|
||||
// Get work sheet.
|
||||
WorksheetFeed feed = spreadsheetService.getFeed(
|
||||
new URL(String.format(Locale.US, SendSpreadsheetsAsyncTask.GET_WORKSHEETS_URI,
|
||||
spreadsheetId)), WorksheetFeed.class);
|
||||
List<WorksheetEntry> data = feed.getEntries();
|
||||
for (Iterator<WorksheetEntry> iterator = data.iterator(); iterator.hasNext();) {
|
||||
WorksheetEntry worksheetEntry = (WorksheetEntry) iterator.next();
|
||||
String title = worksheetEntry.getTitle().getPlainText();
|
||||
if (title.equals(WORK_SHEET_NAME)) {
|
||||
URL listFeedUrl = worksheetEntry.getListFeedUrl();
|
||||
List<ListEntry> listFeed = spreadsheetService.getFeed(listFeedUrl, ListFeed.class)
|
||||
.getEntries();
|
||||
for (Iterator<ListEntry> iterator2 = listFeed.iterator(); iterator2.hasNext();) {
|
||||
ListEntry listEntry = (ListEntry) iterator2.next();
|
||||
String name = listEntry.getCustomElements().getValue(TRANCK_NAME_COLUMN);
|
||||
// Get work sheet
|
||||
WorksheetFeed worksheetFeed = spreadsheetService.getFeed(new URL(
|
||||
String.format(Locale.US, SendSpreadsheetsAsyncTask.GET_WORKSHEETS_URI, spreadsheetId)),
|
||||
WorksheetFeed.class);
|
||||
Iterator<WorksheetEntry> worksheetEntryIterator = worksheetFeed.getEntries().iterator();
|
||||
while (worksheetEntryIterator.hasNext()) {
|
||||
WorksheetEntry worksheetEntry = (WorksheetEntry) worksheetEntryIterator.next();
|
||||
String worksheetTitle = worksheetEntry.getTitle().getPlainText();
|
||||
if (worksheetTitle.equals(SPREADSHEETS_WORKSHEET_NAME)) {
|
||||
URL url = worksheetEntry.getListFeedUrl();
|
||||
Iterator<ListEntry> listEntryIterator = spreadsheetService.getFeed(url, ListFeed.class)
|
||||
.getEntries().iterator();
|
||||
while (listEntryIterator.hasNext()) {
|
||||
ListEntry listEntry = (ListEntry) listEntryIterator.next();
|
||||
String name = listEntry.getCustomElements().getValue(SPREADSHEETS_TRANCK_NAME_COLUMN);
|
||||
if (name.equals(trackName)) {
|
||||
if (isDelete) {
|
||||
listEntry.delete();
|
||||
}
|
||||
listEntry.delete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Search spreadsheet failed.");
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to delete spreadsheets row.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches and deletes a track in spreadsheet.
|
||||
*
|
||||
* @param title the track name to search
|
||||
* @param activity to get context
|
||||
* @return true means find and delete successfully
|
||||
*/
|
||||
public static boolean deleteTrackInSpreadSheet(String title, Activity activity, String account) {
|
||||
return searchTrackTitleInSpreadsheet(title, activity, GoogleUtils.SPREADSHEET_NAME, true,
|
||||
account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a fusion table in user's Google tables.
|
||||
*
|
||||
* @param tableName name of fusion table to search
|
||||
* @param context android context
|
||||
* @param accountName name of account
|
||||
* @param isDelete whether delete this track
|
||||
* @return true means find and delete fusion table
|
||||
*/
|
||||
public static boolean searchFusionTableByTitle(String tableName, Context context,
|
||||
String accountName, boolean isDelete) {
|
||||
try {
|
||||
GoogleAccountCredential credential = SendToGoogleUtils.getGoogleAccountCredential(context,
|
||||
accountName, SendToGoogleUtils.FUSION_TABLES_SCOPE);
|
||||
if (credential == null) {
|
||||
return false;
|
||||
}
|
||||
Fusiontables fusiontables = new Fusiontables.Builder(AndroidHttp.newCompatibleTransport(),
|
||||
new GsonFactory(), credential).build();
|
||||
List<Table> tables = fusiontables.table().list().execute().getItems();
|
||||
for (Iterator<Table> iterator = tables.iterator(); iterator.hasNext();) {
|
||||
Table table = (Table) iterator.next();
|
||||
String title = table.getName();
|
||||
if (title.equals(tableName)) {
|
||||
if (isDelete) {
|
||||
fusiontables.table().delete(table.getTableId()).execute();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.d(EndToEndTestUtils.LOG_TAG, "Failed when operate fusion table.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the status of account is right to use.
|
||||
*
|
||||
* @return true means the status of account is good for sending
|
||||
* Returns true if an account is available.
|
||||
*/
|
||||
public static boolean isAccountAvailable() {
|
||||
// Check whether no account is binded with this device.
|
||||
// Check the no account dialog
|
||||
if (EndToEndTestUtils.SOLO.waitForText(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.send_google_no_account_title), 1,
|
||||
EndToEndTestUtils.SHORT_WAIT_TIME)) {
|
||||
EndToEndTestUtils.getButtonOnScreen(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.generic_ok), true, true);
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.generic_ok), false, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether need to choose account.
|
||||
// Check the choose account dialog
|
||||
if (EndToEndTestUtils.SOLO.waitForText(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.send_google_choose_account_title), 1,
|
||||
EndToEndTestUtils.SHORT_WAIT_TIME)) {
|
||||
EndToEndTestUtils.SOLO.clickOnText(GoogleUtils.ACCOUNT_NAME_1);
|
||||
EndToEndTestUtils.SOLO.clickOnText(ACCOUNT_1);
|
||||
EndToEndTestUtils.getButtonOnScreen(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.generic_ok), false, true);
|
||||
}
|
||||
|
||||
// Check whether no account permission.
|
||||
// Check the no account permission dialog
|
||||
if (EndToEndTestUtils.SOLO.waitForText(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.send_google_no_account_permission),
|
||||
1, EndToEndTestUtils.SHORT_WAIT_TIME)) {
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.send_google_no_account_permission), 1,
|
||||
EndToEndTestUtils.SHORT_WAIT_TIME)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the google account.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
*/
|
||||
private static Account getGoogleAccount(Context context, String accountName) {
|
||||
Account[] accounts = AccountManager.get(context).getAccountsByType(Constants.ACCOUNT_TYPE);
|
||||
for (Account account : accounts) {
|
||||
if (account.name.equals(accountName)) {
|
||||
return account;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Google Maps.
|
||||
*
|
||||
* @param context the context
|
||||
* @param account the account
|
||||
* @param mapsClient the map client
|
||||
* @return a list of maps.
|
||||
*/
|
||||
private static ArrayList<MapsMapMetadata> getMaps(
|
||||
Context context, Account account, MapsClient mapsClient) {
|
||||
String authToken = null;
|
||||
ArrayList<String> mapIds = new ArrayList<String>();
|
||||
ArrayList<MapsMapMetadata> mapMetadata = new ArrayList<MapsMapMetadata>();
|
||||
|
||||
try {
|
||||
authToken = AccountManager.get(context)
|
||||
.blockingGetAuthToken(account, MapsConstants.SERVICE_NAME, false);
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to get auth token.", e);
|
||||
return mapMetadata;
|
||||
}
|
||||
|
||||
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));
|
||||
mapMetadata.add(MapsGDataConverter.getMapMetadataForEntry(entry));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(EndToEndTestUtils.LOG_TAG, "Unable to get maps.", e);
|
||||
} finally {
|
||||
if (gDataParser != null) {
|
||||
gDataParser.close();
|
||||
}
|
||||
}
|
||||
return mapMetadata;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -123,9 +123,9 @@ public class RunConfiguration {
|
||||
EndToEndTestUtils.findMenuItem(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.menu_sync_drive), true);
|
||||
boolean canRun = EndToEndTestUtils.SOLO.waitForText(
|
||||
GoogleUtils.ACCOUNT_NAME_1, 1, EndToEndTestUtils.SHORT_WAIT_TIME)
|
||||
GoogleUtils.ACCOUNT_1, 1, EndToEndTestUtils.SHORT_WAIT_TIME)
|
||||
&& EndToEndTestUtils.SOLO.waitForText(
|
||||
GoogleUtils.ACCOUNT_NAME_2, 1, EndToEndTestUtils.TINY_WAIT_TIME);
|
||||
GoogleUtils.ACCOUNT_2, 1, EndToEndTestUtils.TINY_WAIT_TIME);
|
||||
EndToEndTestUtils.SOLO.clickOnText(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.generic_cancel));
|
||||
return canRun;
|
||||
|
||||
+16
-12
@@ -47,8 +47,9 @@ public class ExportSingleTrackTest extends ActivityInstrumentationTestCase2<Trac
|
||||
instrumentation = getInstrumentation();
|
||||
activityMyTracks = getActivity();
|
||||
|
||||
GoogleUtils.deleteTracksOnGoogleMaps(activityMyTracks);
|
||||
GoogleUtils.deleteTestTracksOnGoogleDrive(activityMyTracks, GoogleUtils.ACCOUNT_NAME_1);
|
||||
GoogleUtils.deleteAllMaps(activityMyTracks.getApplicationContext(), GoogleUtils.ACCOUNT_1);
|
||||
GoogleUtils.deleteDriveTestFiles(
|
||||
activityMyTracks.getApplicationContext(), GoogleUtils.ACCOUNT_1);
|
||||
EndToEndTestUtils.setupForAllTest(instrumentation, activityMyTracks);
|
||||
}
|
||||
|
||||
@@ -79,8 +80,8 @@ public class ExportSingleTrackTest extends ActivityInstrumentationTestCase2<Trac
|
||||
public void testSendTwoTracksWithSameActivity() {
|
||||
String testActivity = "(TestActivity)";
|
||||
EndToEndTestUtils.activityType = testActivity;
|
||||
GoogleUtils.deleteSpreadsheetByTitle("My Tracks-" + EndToEndTestUtils.activityType,
|
||||
EndToEndTestUtils.activityMytracks, GoogleUtils.ACCOUNT_NAME_1);
|
||||
GoogleUtils.deleteSpreadsheets(
|
||||
EndToEndTestUtils.activityMytracks.getApplicationContext(), GoogleUtils.ACCOUNT_1);
|
||||
EndToEndTestUtils.createSimpleTrack(1, false);
|
||||
boolean result = sendToGoogle(activityMyTracks.getString(R.string.export_google_maps))
|
||||
&& sendToGoogle(activityMyTracks.getString(R.string.export_google_fusion_tables))
|
||||
@@ -92,9 +93,8 @@ public class ExportSingleTrackTest extends ActivityInstrumentationTestCase2<Trac
|
||||
// Result is true mean has account bound with this device and send
|
||||
// successful.
|
||||
if (result) {
|
||||
List<File> fileList = GoogleUtils.searchAllSpreadsheetByTitle(
|
||||
GoogleUtils.DOCUMENT_NAME_PREFIX + "-" + EndToEndTestUtils.activityType,
|
||||
activityMyTracks, GoogleUtils.ACCOUNT_NAME_1);
|
||||
List<File> fileList = GoogleUtils.searchSpreadsheets(
|
||||
activityMyTracks.getApplicationContext(), GoogleUtils.ACCOUNT_1);
|
||||
assertEquals(1, fileList.size());
|
||||
}
|
||||
}
|
||||
@@ -112,11 +112,15 @@ public class ExportSingleTrackTest extends ActivityInstrumentationTestCase2<Trac
|
||||
// Spreadsheet.
|
||||
// assertTrue(SyncTestUtils.checkFile(EndToEndTestUtils.trackName, true,
|
||||
// SyncTestUtils.getGoogleDrive(activityMyTracks.getApplicationContext())));
|
||||
assertTrue(GoogleUtils.deleteMap(EndToEndTestUtils.trackName, activityMyTracks));
|
||||
assertTrue(GoogleUtils.searchFusionTableByTitle(EndToEndTestUtils.trackName, activityMyTracks,
|
||||
GoogleUtils.ACCOUNT_NAME_1, true));
|
||||
assertTrue(GoogleUtils.deleteTrackInSpreadSheet(EndToEndTestUtils.trackName, activityMyTracks,
|
||||
GoogleUtils.ACCOUNT_NAME_1));
|
||||
assertTrue(GoogleUtils.deleteMaps(
|
||||
activityMyTracks.getApplicationContext(), GoogleUtils.ACCOUNT_1,
|
||||
EndToEndTestUtils.trackName));
|
||||
assertTrue(GoogleUtils.deleteFusionTables(
|
||||
activityMyTracks.getApplicationContext(), GoogleUtils.ACCOUNT_1,
|
||||
EndToEndTestUtils.trackName));
|
||||
assertTrue(GoogleUtils.deleteSpreadsheetsRow(
|
||||
activityMyTracks.getApplicationContext(), GoogleUtils.ACCOUNT_1,
|
||||
EndToEndTestUtils.trackName));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-7
@@ -72,13 +72,13 @@ public class ShareTest extends ActivityInstrumentationTestCase2<TrackListActivit
|
||||
activityMyTracks.getString(R.string.send_google_choose_account_title), 1,
|
||||
EndToEndTestUtils.SHORT_WAIT_TIME)) {
|
||||
// Whether can found account2.
|
||||
if (EndToEndTestUtils.SOLO.waitForText(GoogleUtils.ACCOUNT_NAME_2, 1,
|
||||
EndToEndTestUtils.VERY_SHORT_WAIT_TIME)) {
|
||||
if (EndToEndTestUtils.SOLO.waitForText(
|
||||
GoogleUtils.ACCOUNT_2, 1, EndToEndTestUtils.VERY_SHORT_WAIT_TIME)) {
|
||||
isAccount2Bound = true;
|
||||
}
|
||||
EndToEndTestUtils.SOLO.clickOnText(GoogleUtils.ACCOUNT_NAME_1);
|
||||
EndToEndTestUtils.getButtonOnScreen(activityMyTracks.getString(R.string.generic_ok), true,
|
||||
true);
|
||||
EndToEndTestUtils.SOLO.clickOnText(GoogleUtils.ACCOUNT_1);
|
||||
EndToEndTestUtils.getButtonOnScreen(
|
||||
activityMyTracks.getString(R.string.generic_ok), true, true);
|
||||
}
|
||||
|
||||
// Check all check boxes.
|
||||
@@ -95,7 +95,7 @@ public class ShareTest extends ActivityInstrumentationTestCase2<TrackListActivit
|
||||
// Input account to share and click OK button.
|
||||
assertTrue(EndToEndTestUtils.SOLO.waitForText(activityMyTracks
|
||||
.getString(R.string.share_track_emails_hint)));
|
||||
EndToEndTestUtils.enterTextAvoidSoftKeyBoard(0, GoogleUtils.ACCOUNT_NAME_2);
|
||||
EndToEndTestUtils.enterTextAvoidSoftKeyBoard(0, GoogleUtils.ACCOUNT_2);
|
||||
EndToEndTestUtils
|
||||
.getButtonOnScreen(activityMyTracks.getString(R.string.generic_ok), true, true);
|
||||
|
||||
@@ -111,7 +111,7 @@ public class ShareTest extends ActivityInstrumentationTestCase2<TrackListActivit
|
||||
// Make more checks if the second account is also bound with this device.
|
||||
if (isAccount2Bound) {
|
||||
EndToEndTestUtils.SOLO.goBack();
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_2);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_2);
|
||||
EndToEndTestUtils.findMenuItem(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.menu_sync_now), true);
|
||||
|
||||
|
||||
+8
-8
@@ -47,7 +47,7 @@ public class MultiAccountsSyncTest extends ActivityInstrumentationTestCase2<Trac
|
||||
instrumentation = getInstrumentation();
|
||||
trackListActivity = getActivity();
|
||||
SyncTestUtils.setUpForSyncTest(instrumentation, trackListActivity);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_1);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_1);
|
||||
drive = SyncTestUtils
|
||||
.getGoogleDrive(EndToEndTestUtils.activityMytracks.getApplicationContext());
|
||||
}
|
||||
@@ -71,12 +71,12 @@ public class MultiAccountsSyncTest extends ActivityInstrumentationTestCase2<Trac
|
||||
if (!RunConfiguration.getInstance().getRunSyncTest()) {
|
||||
return;
|
||||
}
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_1);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_1);
|
||||
// Create tracks with first track.
|
||||
EndToEndTestUtils.createSimpleTrack(0, true);
|
||||
|
||||
// Create tracks with second track.
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_2);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_2);
|
||||
EndToEndTestUtils.createSimpleTrack(0, true);
|
||||
|
||||
// Sync with Google Drive and then check it of the second account.
|
||||
@@ -86,14 +86,14 @@ public class MultiAccountsSyncTest extends ActivityInstrumentationTestCase2<Trac
|
||||
SyncTestUtils.checkFilesNumber(drive);
|
||||
|
||||
// Check Google Drive of the first account.
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_1);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_1);
|
||||
EndToEndTestUtils.findMenuItem(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.menu_sync_now), true);
|
||||
drive = SyncTestUtils.getGoogleDrive(trackListActivity.getApplicationContext());
|
||||
SyncTestUtils.checkFilesNumber(drive);
|
||||
|
||||
// Back to account1 change check that files are still existed.
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_2);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_2);
|
||||
drive = SyncTestUtils.getGoogleDrive(trackListActivity.getApplicationContext());
|
||||
SyncTestUtils.checkFilesNumber(drive);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ public class MultiAccountsSyncTest extends ActivityInstrumentationTestCase2<Trac
|
||||
.getString(R.string.generic_yes));
|
||||
|
||||
// Switch account and delete another track.
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_2);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_2);
|
||||
EndToEndTestUtils.SOLO.clickOnView(EndToEndTestUtils.SOLO.getCurrentViews(ListView.class)
|
||||
.get(0).getChildAt(0));
|
||||
EndToEndTestUtils.SOLO.clickOnMenuItem(EndToEndTestUtils.activityMytracks
|
||||
@@ -137,14 +137,14 @@ public class MultiAccountsSyncTest extends ActivityInstrumentationTestCase2<Trac
|
||||
.getString(R.string.generic_yes));
|
||||
|
||||
// Check Google Drive of the first account.
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_1);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_1);
|
||||
EndToEndTestUtils.findMenuItem(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.menu_sync_now), true);
|
||||
drive = SyncTestUtils.getGoogleDrive(trackListActivity.getApplicationContext());
|
||||
SyncTestUtils.checkFilesNumber(drive);
|
||||
|
||||
// Check Google Drive of the second account.
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_2);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_2);
|
||||
EndToEndTestUtils.findMenuItem(
|
||||
EndToEndTestUtils.activityMytracks.getString(R.string.menu_sync_now), true);
|
||||
drive = SyncTestUtils.getGoogleDrive(trackListActivity.getApplicationContext());
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ public class SyncDriveWithMyTracksTest extends ActivityInstrumentationTestCase2<
|
||||
instrumentation = getInstrumentation();
|
||||
trackListActivity = getActivity();
|
||||
SyncTestUtils.setUpForSyncTest(instrumentation, trackListActivity);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_1);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_1);
|
||||
drive = SyncTestUtils
|
||||
.getGoogleDrive(EndToEndTestUtils.activityMytracks.getApplicationContext());
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ public class SyncMyTracksWithDriveTest extends ActivityInstrumentationTestCase2<
|
||||
instrumentation = getInstrumentation();
|
||||
trackListActivity = getActivity();
|
||||
SyncTestUtils.setUpForSyncTest(instrumentation, trackListActivity);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_1);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_1);
|
||||
drive = SyncTestUtils
|
||||
.getGoogleDrive(EndToEndTestUtils.activityMytracks.getApplicationContext());
|
||||
}
|
||||
|
||||
+2
-2
@@ -72,12 +72,12 @@ public class SyncTestUtils {
|
||||
isCheckedRunSyncTest = true;
|
||||
}
|
||||
if (RunConfiguration.getInstance().getRunSyncTest()) {
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_1);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_1);
|
||||
Drive drive1 = SyncTestUtils.getGoogleDrive(EndToEndTestUtils.activityMytracks
|
||||
.getApplicationContext());
|
||||
removeKMLFiles(drive1);
|
||||
EndToEndTestUtils.deleteAllTracks();
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_NAME_2);
|
||||
SyncTestUtils.enableSync(GoogleUtils.ACCOUNT_2);
|
||||
Drive drive2 = SyncTestUtils.getGoogleDrive(EndToEndTestUtils.activityMytracks
|
||||
.getApplicationContext());
|
||||
removeKMLFiles(drive2);
|
||||
|
||||
Reference in New Issue
Block a user