Update the code to choose a Google Map by using

an AsyncTask to perform the network request.
This commit is contained in:
Jimmy Shih
2012-01-24 18:24:04 -08:00
parent 9207600130
commit 15ff1998cb
16 changed files with 546 additions and 573 deletions
+3 -6
View File
@@ -139,11 +139,6 @@ limitations under the License.
</intent-filter>
</activity>
<activity android:name="com.google.android.apps.mytracks.MapsList"
android:label="@string/maps_list_title"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|fontScale"
android:exported="false" />
<!-- TODO: VIEW and PICK for track cursor dir -->
<activity android:name="com.google.android.apps.mytracks.TrackList" />
@@ -246,11 +241,13 @@ limitations under the License.
android:label="@string/welcome_title"
android:exported="false" />
<activity android:name="com.google.android.apps.mytracks.io.maps.ChooseMapActivity"
android:label="@string/maps_list_title"/>
<activity android:name="com.google.android.apps.mytracks.io.sendtogoogle.UploadServiceChooserActivity"/>
<activity android:name="com.google.android.apps.mytracks.io.sendtogoogle.AccountChooserActivity"/>
<activity android:name="com.google.android.apps.mytracks.io.sendtogoogle.SendFusionTablesActivity"/>
<activity android:name="com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity"/>
<service android:name="com.google.android.apps.mytracks.services.TrackRecordingService"
android:label="@string/my_tracks_recording_service"
android:icon="@drawable/arrow_320"
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:orientation="vertical">
<TextView
android:background="@drawable/list_bar"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:padding="5dp"
android:text="@string/maps_list_pick_map"
android:textColor="@color/white"
android:textSize="16sp"/>
<ListView
android:id="@+id/choose_map_list_view"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_width="fill_parent"
android:padding="5dp"/>
<TextView
android:id="@+id/choose_map_empty_view"
android:gravity="center"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_width="fill_parent"
android:padding="5dp"
android:textColor="@color/red"
android:textSize="16sp"
android:textStyle="bold"/>
</LinearLayout>
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical"
android:paddingBottom="2dp"
android:paddingTop="2dp">
<TextView
android:id="@+id/choose_map_list_item_title"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginTop="6dp"
android:layout_width="fill_parent"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:singleLine="true"/>
<TextView
android:id="@+id/choose_map_list_item_description"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_width="fill_parent"
android:textSize="14sp"
android:singleLine="true"/>
<TextView
android:id="@+id/choose_map_list_item_search_status"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_width="fill_parent"
android:textSize="14sp"
android:singleLine="true"/>
</LinearLayout>
-103
View File
@@ -1,103 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apl/res/com.google.android.maps"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_bar"
android:layout_weight="0"
android:padding="3dip">
<TextView android:id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="16dip"
android:textColor="#fff"
android:text="@string/maps_list_pick_map"
android:layout_weight="0"/>
</LinearLayout>
<ListView android:id="@+id/maplist"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView android:id="@+id/mapslist_empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:gravity="center"
android:padding="3dip"
android:textSize="16dip"
android:textColor="#c00"
android:textStyle="bold"
android:text="@string/maps_list_no_maps"
android:visibility="gone"/>
<TextView android:id="@+id/failed"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="3dip"
android:textSize="16dip"
android:textColor="#c00"
android:textStyle="bold"
android:text="@string/maps_list_error"
android:visibility="gone"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_bar"
android:layout_weight="0"
android:padding="3dip">
<LinearLayout android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="3dip"
android:layout_gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<ProgressBar
android:layout_height="20dip"
android:layout_width="20dip"
android:layout_gravity="center"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="3dip"
android:textSize="16dip"
android:textColor="#fff"
android:textStyle="bold"
android:text="@string/maps_list_loading"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
-40
View File
@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="2dip"
android:orientation="vertical">
<TextView android:id="@+id/maplistitem"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="9pt"
android:textStyle="bold"
android:textColor="#fff"
android:singleLine="true"/>
<TextView android:id="@+id/maplistdesc"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="8pt"
android:singleLine="true"/>
<TextView android:id="@+id/maplistpublic"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="7pt"
android:singleLine="true"/>
</LinearLayout>
+1 -1
View File
@@ -833,7 +833,7 @@ limitations under the License.
<string name="maps_list_loading"
translation_description="When sending a track to an existing Google Map, the progress dialog
message informing the user that his maps are being loaded.">
Loading maps...
Loading your Google Maps.
</string>
<string name="maps_list_no_maps"
translation_description="When sending a track to an existing Google Map, the text on the
@@ -41,7 +41,7 @@ public abstract class Constants {
* global order across all activities.
*/
public static final int GET_LOGIN = 0;
public static final int GET_MAP = 1;
public static final int CHOOSE_MAP = 1;
public static final int SHOW_TRACK = 2;
public static final int AUTHENTICATE_TO_MAPS = 3;
public static final int AUTHENTICATE_TO_DOCLIST = 4;
@@ -1,175 +0,0 @@
/*
* 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;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.io.AuthManager;
import com.google.android.apps.mytracks.io.AuthManager.AuthCallback;
import com.google.android.apps.mytracks.io.AuthManagerFactory;
import com.google.android.apps.mytracks.io.maps.MapsConstants;
import com.google.android.apps.mytracks.io.maps.MapsFacade;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
/**
* Activity which displays the list of current Google Maps for the user.
* Returns RESULT_OK if the user picked a map, and returns "mapid" as an extra.
*
* @author Rodrigo Damazio
*/
public class MapsList extends Activity implements MapsFacade.MapsListCallback {
private static final int GET_LOGIN = 1;
public static final String EXTRA_ACCOUNT_NAME = "accountName";
public static final String EXTRA_ACCOUNT_TYPE = "accountType";
private MapsFacade mapsClient;
private AuthManager auth;
private MapsListAdapter listAdapter;
private final OnItemClickListener clickListener =
new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
Intent result = new Intent();
result.putExtra("mapid", (String) listAdapter.getItem(position));
setResult(RESULT_OK, result);
finish();
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
auth = AuthManagerFactory.getAuthManager(this, GET_LOGIN, null, true,
MapsConstants.SERVICE_NAME);
setContentView(R.layout.list);
listAdapter = new MapsListAdapter(this);
ListView list = (ListView) findViewById(R.id.maplist);
list.setOnItemClickListener(clickListener);
list.setAdapter(listAdapter);
startLogin();
}
private void startLogin() {
// Starts in the UI thread.
// TODO fix this for non-froyo devices.
if (AuthManagerFactory.useModernAuthManager()) {
Intent intent = getIntent();
String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
if (accountName == null || accountType == null) {
Log.e(TAG, "Didn't receive account name or type");
setResult(RESULT_CANCELED);
finish();
return;
}
doLogin(auth.getAccountObject(accountName, accountType));
} else {
doLogin(null);
}
}
private void doLogin(final Object account) {
// Starts in the UI thread.
auth.doLogin(new AuthCallback() {
@Override
public void onAuthResult(boolean success) {
if (!success) {
setResult(RESULT_CANCELED);
finish();
return;
}
// Runs in UI thread.
mapsClient = new MapsFacade(MapsList.this, auth);
startLookup();
}
}, account);
}
private void startLookup() {
// Starts in the UI thread.
new Thread() {
@Override
public void run() {
// Communication with Maps happens in its own thread.
// This will call onReceivedMapListing below.
final boolean success = mapsClient.getMapsList(MapsList.this);
runOnUiThread(new Runnable() {
@Override
public void run() {
// Updating the UI when done happens in the UI thread.
onLookupDone(success);
}
});
}
}.start();
}
@Override
public void onReceivedMapListing(final String mapId, final String title,
final String description, final boolean isPublic) {
// Starts in the communication thread.
runOnUiThread(new Runnable() {
@Override
public void run() {
// Updating the list with new contents happens in the UI thread.
listAdapter.addMapListing(mapId, title, description, isPublic);
}
});
}
private void onLookupDone(boolean success) {
// Starts in the UI thread.
findViewById(R.id.loading).setVisibility(View.GONE);
if (!success) {
findViewById(R.id.failed).setVisibility(View.VISIBLE);
}
TextView emptyView = (TextView) findViewById(R.id.mapslist_empty);
ListView list = (ListView) findViewById(R.id.maplist);
list.setEmptyView(emptyView);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == GET_LOGIN) {
auth.authResult(resultCode, data);
}
super.onActivityResult(requestCode, resultCode, data);
}
}
@@ -1,135 +0,0 @@
package com.google.android.apps.mytracks;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.TextView;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
public class MapsListAdapter implements ListAdapter {
private Vector<String[]> mapsList;
private Vector<Boolean> publicList;
private Set<DataSetObserver> observerSet;
private final Activity activity;
public MapsListAdapter(Activity activity) {
this.activity = activity;
mapsList = new Vector<String[]>();
publicList = new Vector<Boolean>();
observerSet = new HashSet<DataSetObserver>();
}
public void addMapListing(String mapId, String title,
String description, boolean isPublic) {
synchronized (mapsList) {
// Search through the maps list to see if it has the mapid and
// remove it if so, so that we can replace it with updated info
for (int i = 0; i < mapsList.size(); ++i) {
if (mapId.equals(mapsList.get(i)[0])) {
mapsList.remove(i);
publicList.remove(i);
--i;
}
}
mapsList.add(new String[] { mapId, title, description });
publicList.add(isPublic);
}
Iterator<DataSetObserver> iter = observerSet.iterator();
while (iter.hasNext()) {
iter.next().onChanged();
}
}
public String[] getMapListingArray(int position) {
return mapsList.get(position);
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
@Override
public boolean isEnabled(int position) {
return true;
}
@Override
public int getCount() {
return mapsList.size();
}
@Override
public Object getItem(int position) {
return mapsList.get(position)[0];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView =
activity.getLayoutInflater().inflate(R.layout.listitem, parent, false);
}
String[] map = mapsList.get(position);
((TextView) convertView.findViewById(R.id.maplistitem)).setText(map[1]);
((TextView) convertView.findViewById(R.id.maplistdesc)).setText(map[2]);
TextView publicUnlisted =
(TextView) convertView.findViewById(R.id.maplistpublic);
if (publicList.get(position)) {
publicUnlisted.setTextColor(Color.RED);
publicUnlisted.setText(R.string.maps_list_public_label);
} else {
publicUnlisted.setTextColor(Color.GREEN);
publicUnlisted.setText(R.string.maps_list_unlisted_label);
}
return convertView;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isEmpty() {
return mapsList.isEmpty();
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
observerSet.add(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
observerSet.remove(observer);
}
}
@@ -0,0 +1,239 @@
/*
* 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.io.maps;
import com.google.android.maps.mytracks.R;
import android.accounts.Account;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
/**
* An activity to choose a Google Map.
*
* @author jshih@google.com (Jimmy Shih)
*/
public class ChooseMapActivity extends Activity {
// parameters in the input intent
public static final String ACCOUNT = "account";
// parameters in the output intent
public static final String MAP_ID = "mapId";
private static final int PROGRESS_DIALOG = 1;
private static final int ERROR_DIALOG = 2;
private ChooseMapAsyncTask asyncTask;
private ProgressDialog progressDialog;
private ArrayAdapter<ListItem> arrayAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.choose_map);
arrayAdapter = new ArrayAdapter<ListItem>(this, R.layout.choose_map_item, new ArrayList<
ListItem>()) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.choose_map_item, parent, false);
}
MapsMapMetadata mapData = getItem(position).getMapData();
TextView title = (TextView) convertView.findViewById(R.id.choose_map_list_item_title);
title.setText(mapData.getTitle());
TextView description = (TextView) convertView.findViewById(
R.id.choose_map_list_item_description);
String descriptionText = mapData.getDescription();
if (descriptionText == null || descriptionText.equals("")) {
description.setVisibility(View.GONE);
} else {
description.setVisibility(View.VISIBLE);
description.setText(descriptionText);
}
TextView searchStatus = (TextView) convertView.findViewById(
R.id.choose_map_list_item_search_status);
searchStatus.setTextColor(mapData.getSearchable() ? Color.RED : Color.GREEN);
searchStatus.setText(mapData.getSearchable() ? R.string.maps_list_public_label
: R.string.maps_list_unlisted_label);
return convertView;
}
};
ListView list = (ListView) findViewById(R.id.choose_map_list_view);
list.setEmptyView(findViewById(R.id.choose_map_empty_view));
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent result = new Intent();
result.putExtra(MAP_ID, arrayAdapter.getItem(position).getMapId());
setResult(RESULT_OK, result);
finish();
}
});
list.setAdapter(arrayAdapter);
Intent intent = getIntent();
Account account = intent.getParcelableExtra(ACCOUNT);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof ChooseMapAsyncTask) {
asyncTask = (ChooseMapAsyncTask) retained;
asyncTask.setActivity(this);
} else {
asyncTask = new ChooseMapAsyncTask(this, account);
asyncTask.execute();
}
}
@Override
public Object onRetainNonConfigurationInstance() {
asyncTask.setActivity(null);
return asyncTask;
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setMessage(getString(R.string.maps_list_loading));
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
asyncTask.cancel(true);
setResult(RESULT_CANCELED);
finish();
}
});
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setTitle(R.string.generic_progress_title);
return progressDialog;
case ERROR_DIALOG:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(R.string.generic_error_title);
builder.setMessage(R.string.maps_list_error);
builder.setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
Intent result = new Intent();
result.putExtra(MAP_ID, (String) null);
setResult(RESULT_OK, result);
finish();
}
});
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Intent result = new Intent();
result.putExtra(MAP_ID, (String) null);
setResult(RESULT_OK, result);
finish();
}
});
return builder.create();
default:
return null;
}
}
/**
* Invokes when the associated AsyncTask completes.
*
* @param success true if success
* @param mapIds an array of map ids
* @param mapData an array of map data
*/
public void onAsyncTaskCompleted(
boolean success, ArrayList<String> mapIds, ArrayList<MapsMapMetadata> mapData) {
removeDialog(PROGRESS_DIALOG);
if (success) {
arrayAdapter.clear();
// To prevent displaying the emptyView message momentarily before the
// arrayAdapter is set, don't set the emptyView message in the xml layout.
// Instead, set it only when needed.
if (mapIds.size() == 0) {
TextView emptyView = (TextView) findViewById(R.id.choose_map_empty_view);
emptyView.setText(R.string.maps_list_no_maps);
} else {
for (int i = 0; i < mapIds.size(); i++) {
arrayAdapter.add(new ListItem(mapIds.get(i), mapData.get(i)));
}
}
} else {
showDialog(ERROR_DIALOG);
}
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(PROGRESS_DIALOG);
}
/**
* A class containing {@link ChooseMapActivity} list item.
*
* @author jshih@google.com (Jimmy Shih)
*/
private class ListItem {
private String mapId;
private MapsMapMetadata mapData;
private ListItem(String mapId, MapsMapMetadata mapData) {
this.mapId = mapId;
this.mapData = mapData;
}
/**
* Gets the map id.
*/
public String getMapId() {
return mapId;
}
/**
* Gets the map data.
*/
public MapsMapMetadata getMapData() {
return mapData;
}
}
}
@@ -0,0 +1,186 @@
// Copyright 2012 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.maps;
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
import com.google.android.common.gdata.AndroidXmlParserFactory;
import com.google.wireless.gdata.client.GDataClient;
import com.google.wireless.gdata.client.HttpException;
import com.google.wireless.gdata.parser.GDataParser;
import com.google.wireless.gdata.parser.ParseException;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
import java.util.ArrayList;
/**
* AsyncTask for {@link ChooseMapActivity} to get all the maps from Google Maps.
*
* @author jshih@google.com (Jimmy Shih)
*/
public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = ChooseMapAsyncTask.class.getSimpleName();
private ChooseMapActivity activity;
private final Account account;
private final Context context;
private final GDataClient gDataClient;
private final MapsClient mapsClient;
/**
* True if can retry sending to Google Fusion Tables.
*/
private boolean canRetry;
/**
* True if the AsyncTask has completed.
*/
private boolean completed;
/**
* True if the result is success.
*/
private boolean success;
// The following variables are for per request states
private String authToken;
private ArrayList<String> mapIds;
private ArrayList<MapsMapMetadata> mapData;
public ChooseMapAsyncTask(ChooseMapActivity activity, Account account) {
this.activity = activity;
this.account = account;
context = activity.getApplicationContext();
gDataClient = GDataClientFactory.getGDataClient(context);
mapsClient = new MapsClient(
gDataClient, new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
canRetry = true;
completed = false;
success = false;
}
/**
* Sets the activity associated with this AyncTask.
*
* @param activity the activity.
*/
public void setActivity(ChooseMapActivity activity) {
this.activity = activity;
if (completed && activity != null) {
activity.onAsyncTaskCompleted(success, mapIds, mapData);
}
}
@Override
protected void onPreExecute() {
activity.showProgressDialog();
}
@Override
protected Boolean doInBackground(Void... params) {
return getMaps();
}
@Override
protected void onCancelled() {
closeClient();
}
@Override
protected void onPostExecute(Boolean result) {
closeClient();
success = result;
completed = true;
if (activity != null) {
activity.onAsyncTaskCompleted(success, mapIds, mapData);
}
}
/**
* Closes the gdata client.
*/
private void closeClient() {
if (gDataClient != null) {
gDataClient.close();
}
}
/**
* Gets all the maps from Google Maps.
*
* @return true if success.
*/
private boolean getMaps() {
// Reset the per request states
authToken = null;
mapIds = new ArrayList<String>();
mapData = new ArrayList<MapsMapMetadata>();
try {
authToken = AccountManager.get(context).blockingGetAuthToken(
account, MapsConstants.SERVICE_NAME, false);
} catch (OperationCanceledException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
} catch (AuthenticatorException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
} catch (IOException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
}
if (isCancelled()) {
return false;
}
try {
GDataParser 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));
}
gDataParser.close();
} catch (ParseException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
} catch (IOException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
} catch (HttpException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
}
return true;
}
/**
* Retries upload. Invalidates the authToken. If can retry, invokes
* {@link ChooseMapAsyncTask#getMaps()}. Returns false if cannot retry.
*/
private boolean retryUpload() {
if (isCancelled()) {
return false;
}
AccountManager.get(context).invalidateAuthToken(MapsConstants.SERVICE_NAME, authToken);
if (canRetry) {
canRetry = false;
return getMaps();
}
return false;
}
}
@@ -1,50 +0,0 @@
// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.maps;
import com.google.android.maps.GeoPoint;
/**
* A rectangle in geographical space.
*/
class GeoRect {
public int top;
public int left;
public int bottom;
public int right;
public GeoRect() {
top = 0;
left = 0;
bottom = 0;
right = 0;
}
public GeoRect(GeoPoint center, int latSpan, int longSpan) {
top = center.getLatitudeE6() - latSpan / 2;
left = center.getLongitudeE6() - longSpan / 2;
bottom = center.getLatitudeE6() + latSpan / 2;
right = center.getLongitudeE6() + longSpan / 2;
}
public GeoPoint getCenter() {
return new GeoPoint(top / 2 + bottom / 2, left / 2 + right / 2);
}
public int getLatSpan() {
return bottom - top;
}
public int getLongSpan() {
return right - left;
}
public boolean contains(GeoPoint geoPoint) {
if (geoPoint.getLatitudeE6() >= top
&& geoPoint.getLatitudeE6() <= bottom
&& geoPoint.getLongitudeE6() >= left
&& geoPoint.getLongitudeE6() <= right) {
return true;
}
return false;
}
}
@@ -8,7 +8,6 @@ import com.google.android.maps.GeoPoint;
import com.google.android.maps.mytracks.R;
import com.google.wireless.gdata.client.HttpException;
import com.google.wireless.gdata.data.Entry;
import com.google.wireless.gdata.parser.GDataParser;
import com.google.wireless.gdata.parser.ParseException;
import android.content.Context;
@@ -28,15 +27,6 @@ import org.xmlpull.v1.XmlPullParserException;
*/
public class MapsFacade {
/**
* Interface for receiving data back from getMapsList.
* All calls to the interface will happen before getMapsList returns.
*/
public interface MapsListCallback {
void onReceivedMapListing(String mapId, String title, String description,
boolean isPublic);
}
private static final String END_ICON_URL =
"http://maps.google.com/mapfiles/ms/micons/red-dot.png";
private static final String START_ICON_URL =
@@ -65,35 +55,6 @@ public class MapsFacade {
return MapsClient.buildMapUrl(mapId);
}
/**
* Returns a list of all maps for the current user.
*
* @param callback callback to call for each map returned
* @return true on success, false otherwise
*/
public boolean getMapsList(final MapsListCallback callback) {
return wrapper.runQuery(new MapsGDataWrapper.QueryFunction() {
@Override
public void query(MapsClient client) throws IOException, Exception {
GDataParser listParser = client.getParserForFeed(
MapFeatureEntry.class, MapsClient.getMapsFeed(), authToken);
listParser.init();
while (listParser.hasMoreData()) {
MapFeatureEntry entry =
(MapFeatureEntry) listParser.readNextEntry(null);
MapsMapMetadata metadata =
MapsGDataConverter.getMapMetadataForEntry(entry);
String mapId = MapsGDataConverter.getMapidForEntry(entry);
callback.onReceivedMapListing(
mapId, metadata.getTitle(), metadata.getDescription(), metadata.getSearchable());
}
listParser.close();
listParser = null;
}
});
}
/**
* Creates a new map for the current user.
*
@@ -34,11 +34,7 @@ class MapsGDataConverter {
metadata.setSearchable(false);
}
metadata.setTitle(entry.getTitle());
String desc = entry.getContent();
if (desc.length() > 12) {
metadata.setDescription(desc.substring(9, desc.length() - 3));
}
metadata.setDescription(entry.getSummary());
String editUri = entry.getEditUri();
if (editUri != null) {
@@ -19,7 +19,6 @@ import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.MapsList;
import com.google.android.apps.mytracks.ProgressIndicator;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
@@ -28,6 +27,7 @@ import com.google.android.apps.mytracks.io.AuthManager.AuthCallback;
import com.google.android.apps.mytracks.io.AuthManagerFactory;
import com.google.android.apps.mytracks.io.SendToDocs;
import com.google.android.apps.mytracks.io.SendToMaps;
import com.google.android.apps.mytracks.io.maps.ChooseMapActivity;
import com.google.android.apps.mytracks.io.maps.MapsConstants;
import com.google.android.apps.mytracks.io.maps.MapsFacade;
import com.google.android.apps.mytracks.util.SystemUtils;
@@ -308,12 +308,9 @@ public class SendActivity extends Activity implements ProgressIndicator {
private SendState pickMap() {
if (!sendToMapsNewMap) {
// Ask the user to choose a map to upload into
Intent listIntent = new Intent(this, MapsList.class);
listIntent.putExtra(MapsList.EXTRA_ACCOUNT_NAME, account.name);
listIntent.putExtra(MapsList.EXTRA_ACCOUNT_TYPE, account.type);
startActivityForResult(listIntent, Constants.GET_MAP);
// The callback for GET_MAP calls authenticateToGoogleMaps
Intent intent = new Intent(this, ChooseMapActivity.class);
intent.putExtra(ChooseMapActivity.ACCOUNT, account);
startActivityForResult(intent, Constants.CHOOSE_MAP);
return SendState.NOT_READY;
} else {
return SendState.SEND_TO_MAPS;
@@ -487,17 +484,17 @@ public class SendActivity extends Activity implements ProgressIndicator {
final Intent results) {
SendState nextState = null;
switch (requestCode) {
case Constants.GET_MAP: {
// User picked a map to upload to
Log.d(TAG, "Get map result: " + resultCode);
if (resultCode == RESULT_OK) {
if (results.hasExtra("mapid")) {
sendToMapsMapId = results.getStringExtra("mapid");
}
nextState = SendState.SEND_TO_MAPS;
} else {
case Constants.CHOOSE_MAP: {
if (resultCode == RESULT_CANCELED) {
nextState = SendState.FINISH;
break;
}
sendToMapsMapId = results.getStringExtra(ChooseMapActivity.MAP_ID);
if (sendToMapsMapId == null) {
nextState = SendState.FINISH;
break;
}
nextState = SendState.SEND_TO_MAPS;
break;
}
case Constants.CHOOSE_ACCOUNT: {
@@ -119,7 +119,7 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
*/
public void setActivity(SendFusionTablesActivity activity) {
this.activity = activity;
if (completed) {
if (completed && activity != null) {
activity.onAsyncTaskCompleted(success, tableId);
}
}
@@ -173,10 +173,13 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
try {
authToken = AccountManager.get(context).blockingGetAuthToken(account, SERVICE_ID, false);
} catch (OperationCanceledException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
} catch (AuthenticatorException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
} catch (IOException e) {
Log.d(TAG, e.getMessage());
return retryUpload();
}
@@ -454,6 +457,7 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
* <ul>
* <li>set the stop time</li>
* <li>decimate locations precision</li>
* <li>make sure the segment has more than 1 point</li>
* </ul>
* The prepared track will be added to the splitTracks.
*
@@ -472,7 +476,11 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
// Decimate to 2 meter precision. Fusion Tables doesn't like too many
// points.
LocationUtils.decimate(segment, 2.0);
splitTracks.add(segment);
// Only add the segment if it has more than 1 point.
if (segment.getLocations().size() > 1) {
splitTracks.add(segment);
}
}
/**