diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index 18b364409..902073628 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -107,6 +107,9 @@ limitations under the License. + diff --git a/MyTracks/res/drawable-hdpi/ic_menu_insert_photo.png b/MyTracks/res/drawable-hdpi/ic_menu_insert_photo.png new file mode 100644 index 000000000..d9d87393e Binary files /dev/null and b/MyTracks/res/drawable-hdpi/ic_menu_insert_photo.png differ diff --git a/MyTracks/res/drawable-mdpi/ic_menu_insert_photo.png b/MyTracks/res/drawable-mdpi/ic_menu_insert_photo.png new file mode 100644 index 000000000..61e231797 Binary files /dev/null and b/MyTracks/res/drawable-mdpi/ic_menu_insert_photo.png differ diff --git a/MyTracks/res/drawable-xhdpi/ic_menu_insert_photo.png b/MyTracks/res/drawable-xhdpi/ic_menu_insert_photo.png new file mode 100644 index 000000000..0323155c4 Binary files /dev/null and b/MyTracks/res/drawable-xhdpi/ic_menu_insert_photo.png differ diff --git a/MyTracks/res/layout/list_item.xml b/MyTracks/res/layout/list_item.xml index f3522dda8..b30288fd3 100644 --- a/MyTracks/res/layout/list_item.xml +++ b/MyTracks/res/layout/list_item.xml @@ -24,9 +24,9 @@ limitations under the License. android:shrinkColumns="1" android:stretchColumns="1" style="@style/ListItem" > - + + + - + - - - - - - - - - - + android:layout_width="match_parent" > + + + + + + + + + + + + + + + + + + + + + - + + + + + + \ No newline at end of file diff --git a/MyTracks/res/layout/marker_detail.xml b/MyTracks/res/layout/marker_detail_fragment.xml similarity index 80% rename from MyTracks/res/layout/marker_detail.xml rename to MyTracks/res/layout/marker_detail_fragment.xml index 166b426d3..573233b74 100644 --- a/MyTracks/res/layout/marker_detail.xml +++ b/MyTracks/res/layout/marker_detail_fragment.xml @@ -21,14 +21,19 @@ limitations under the License. android:layout_width="match_parent" android:orientation="vertical" android:padding="8dp" > - + + @@ -41,6 +46,9 @@ limitations under the License. android:layout_height="wrap_content" android:layout_width="match_parent" android:orientation="vertical" > + diff --git a/MyTracks/res/menu/map.xml b/MyTracks/res/menu/map.xml index a607f71ba..a9e39fadf 100644 --- a/MyTracks/res/menu/map.xml +++ b/MyTracks/res/menu/map.xml @@ -17,7 +17,7 @@ limitations under the License. diff --git a/MyTracks/res/menu/track_detail.xml b/MyTracks/res/menu/track_detail.xml index 8e0d2a51f..3ab15e8bc 100644 --- a/MyTracks/res/menu/track_detail.xml +++ b/MyTracks/res/menu/track_detail.xml @@ -21,68 +21,74 @@ limitations under the License. android:orderInCategory="1" android:showAsAction="ifRoom" android:title="@string/menu_insert_marker"/> + - + \ No newline at end of file diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index 83980baf3..dd781c925 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -1005,6 +1005,10 @@ limitations under the License. translation_description="When deleting multiple markers, the confirmation message."> All markers for this track will be permanently deleted from the device. + + Unable to insert a photo marker. Try again. + Insert marker + + Insert photo + markerIds; @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); - myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this); - markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L); + long markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L); if (markerId == -1L) { Log.d(TAG, "invalid marker id"); finish(); return; } - name = (TextView) findViewById(R.id.marker_detail_name); - waypointSection = findViewById(R.id.marker_detail_waypoint_section); - statisticsSection = findViewById(R.id.marker_detail_statistics_section); + + MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this); + waypoint = myTracksProviderUtils.getWaypoint(markerId); + + markerIds = new ArrayList(); + int markerIndex = -1; + Cursor cursor = null; + + try { + cursor = myTracksProviderUtils.getWaypointCursor(waypoint.getTrackId(), -1L, -1); + if (cursor != null && cursor.moveToFirst()) { + /* + * Yes, this will skip the first waypoint and that is intentional as the + * first waypoint holds the stats for the track. + */ + while (cursor.moveToNext()) { + Waypoint current = myTracksProviderUtils.createWaypoint(cursor); + + markerIds.add(current.getId()); + if (current.getId() == markerId) { + markerIndex = markerIds.size() - 1; + } + } + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + + ViewPager viewPager = (ViewPager) findViewById(R.id.maker_detail_activity_view_pager); + viewPager.setAdapter(new MarkerDetailPagerAdapter(getSupportFragmentManager())); + if (markerIndex != -1) { + viewPager.setCurrentItem(markerIndex); + } } @Override protected int getLayoutResId() { - return R.layout.marker_detail; - } - - @Override - protected void onResume() { - super.onResume(); - waypoint = MyTracksProviderUtils.Factory.get(this).getWaypoint(markerId); - if (waypoint == null) { - Log.d(TAG, "waypoint is null"); - finish(); - return; - } - name.setText(getString(R.string.generic_name_line, waypoint.getName())); - if (waypoint.getType() == WaypointType.WAYPOINT) { - waypointSection.setVisibility(View.VISIBLE); - statisticsSection.setVisibility(View.GONE); - - TextView markerType = (TextView) findViewById(R.id.marker_detail_waypoint_marker_type); - markerType.setText( - getString(R.string.marker_detail_waypoint_marker_type, waypoint.getCategory())); - TextView description = (TextView) findViewById(R.id.marker_detail_waypoint_description); - description.setText(getString(R.string.generic_description_line, waypoint.getDescription())); - } else { - waypointSection.setVisibility(View.GONE); - statisticsSection.setVisibility(View.VISIBLE); - StatsUtils.setTripStatisticsValues(this, waypoint.getTripStatistics()); - StatsUtils.setLocationValues(this, waypoint.getLocation(), false); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.marker_detail, menu); - - Track track = myTracksProviderUtils.getTrack(waypoint.getTrackId()); - boolean isSharedWithMe = track != null ? track.isSharedWithMe() : true; - - menu.findItem(R.id.marker_detail_edit).setVisible(!isSharedWithMe); - menu.findItem(R.id.marker_detail_delete).setVisible(!isSharedWithMe); - return true; + return R.layout.marker_detail_activity; } @Override @@ -120,29 +107,6 @@ public class MarkerDetailActivity extends AbstractMyTracksActivity implements De finish(); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - Intent intent; - switch (item.getItemId()) { - case R.id.marker_detail_show_on_map: - intent = IntentUtils.newIntent(this, TrackDetailActivity.class) - .putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerId); - startActivity(intent); - return true; - case R.id.marker_detail_edit: - intent = IntentUtils.newIntent(this, MarkerEditActivity.class) - .putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId); - startActivity(intent); - return true; - case R.id.marker_detail_delete: - DeleteMarkerDialogFragment.newInstance(new long[] { markerId }) - .show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - @Override public void onDeleteMarkerDone() { runOnUiThread(new Runnable() { @@ -152,4 +116,26 @@ public class MarkerDetailActivity extends AbstractMyTracksActivity implements De } }); } -} + + /** + * Marker detail pager adapter. + * + * @author Jimmy Shih + */ + private class MarkerDetailPagerAdapter extends FragmentStatePagerAdapter { + + public MarkerDetailPagerAdapter(FragmentManager fragmentManager) { + super(fragmentManager); + } + + @Override + public Fragment getItem(int position) { + return MarkerDetailFragment.newInstance(markerIds.get(position)); + } + + @Override + public int getCount() { + return markerIds.size(); + } + } +} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java b/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java index 8c91add55..2846df0f8 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java @@ -169,6 +169,7 @@ public class MarkerEditActivity extends AbstractMyTracksActivity { waypointName.getText().toString(), waypointMarkerType.getText().toString(), waypointDescription.getText().toString(), + null, null); TrackRecordingServiceConnectionUtils.addMarker( this, trackRecordingServiceConnection, waypointCreationRequest); diff --git a/MyTracks/src/com/google/android/apps/mytracks/MarkerListActivity.java b/MyTracks/src/com/google/android/apps/mytracks/MarkerListActivity.java index 55223f799..ce8d68aeb 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MarkerListActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MarkerListActivity.java @@ -63,7 +63,7 @@ public class MarkerListActivity extends AbstractMyTracksActivity implements Dele private static final String[] PROJECTION = new String[] { WaypointsColumns._ID, WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY, - WaypointsColumns.TYPE, WaypointsColumns.TIME }; + WaypointsColumns.TYPE, WaypointsColumns.TIME, WaypointsColumns.PHOTOURL }; // Callback when an item is selected in the contextual action mode private ContextualActionModeCallback @@ -173,19 +173,25 @@ public class MarkerListActivity extends AbstractMyTracksActivity implements Dele public void bindView(View view, Context context, Cursor cursor) { int typeIndex = cursor.getColumnIndex(WaypointsColumns.TYPE); int nameIndex = cursor.getColumnIndex(WaypointsColumns.NAME); - int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY); + int photoUrlIndex = cursor.getColumnIndex(WaypointsColumns.PHOTOURL); int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME); + int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY); int descriptionIndex = cursor.getColumnIndex(WaypointsColumns.DESCRIPTION); boolean statistics = WaypointType.values()[cursor.getInt(typeIndex)] == WaypointType.STATISTICS; int iconId = statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin; + String name = cursor.getString(nameIndex); + String sharedOwner = track.getSharedOwner(); + String photoUrl = cursor.getString(photoUrlIndex); + long time = cursor.getLong(timeIndex); String category = statistics ? null : cursor.getString(categoryIndex); String description = statistics ? null : cursor.getString(descriptionIndex); + ListItemUtils.setListItem(MarkerListActivity.this, view, false, true, iconId, - R.string.icon_marker, cursor.getString(nameIndex), category, null, null, - cursor.getLong(timeIndex), description, track.getSharedOwner()); + R.string.icon_marker, name, sharedOwner, null, null, photoUrl, time, category, + description); } }; listView.setAdapter(resourceCursorAdapter); diff --git a/MyTracks/src/com/google/android/apps/mytracks/SearchListActivity.java b/MyTracks/src/com/google/android/apps/mytracks/SearchListActivity.java index bf42d0a26..0e76ca04e 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/SearchListActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/SearchListActivity.java @@ -83,12 +83,14 @@ public class SearchListActivity extends AbstractSendToGoogleActivity implements private static final String ICON_ID_FIELD = "icon"; private static final String ICON_CONTENT_DESCRIPTION_ID_FIELD = "iconContentDescription"; private static final String NAME_FIELD = "name"; - private static final String CATEGORY_FIELD = "category"; + private static final String SHARED_OWNER_FIELD = "sharedOwner"; private static final String TOTAL_TIME_FIELD = "totalTime"; private static final String TOTAL_DISTANCE_FIELD = "totalDistance"; + private static final String PHOTO_URL_FIELD = "photoUrl"; private static final String START_TIME_FIELD = "startTime"; + private static final String CATEGORY_FIELD = "category"; private static final String DESCRIPTION_FIELD = "description"; - private static final String SHARED_OWNER_FIELD = "sharedOwner"; + private static final String TRACK_ID_FIELD = "trackId"; private static final String MARKER_ID_FIELD = "markerId"; @@ -228,15 +230,17 @@ public class SearchListActivity extends AbstractSendToGoogleActivity implements int iconId = (Integer) resultMap.get(ICON_ID_FIELD); int iconContentDescriptionId = (Integer) resultMap.get(ICON_CONTENT_DESCRIPTION_ID_FIELD); String name = (String) resultMap.get(NAME_FIELD); - String category = (String) resultMap.get(CATEGORY_FIELD); + String sharedOwner = (String) resultMap.get(SHARED_OWNER_FIELD); String totalTime = (String) resultMap.get(TOTAL_TIME_FIELD); String totalDistance = (String) resultMap.get(TOTAL_DISTANCE_FIELD); + String photoUrl = (String) resultMap.get(PHOTO_URL_FIELD); Long startTime = (Long) resultMap.get(START_TIME_FIELD); + String category = (String) resultMap.get(CATEGORY_FIELD); String description = (String) resultMap.get(DESCRIPTION_FIELD); - String sharedOwner = (String) resultMap.get(SHARED_OWNER_FIELD); + ListItemUtils.setListItem(SearchListActivity.this, view, isRecording, isPaused, iconId, - iconContentDescriptionId, name, category, totalTime, totalDistance, startTime, - description, sharedOwner); + iconContentDescriptionId, name, sharedOwner, totalTime, totalDistance, photoUrl, + startTime, category, description); return view; } }; @@ -487,14 +491,15 @@ public class SearchListActivity extends AbstractSendToGoogleActivity implements statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin); resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.icon_marker); resultMap.put(NAME_FIELD, waypoint.getName()); - resultMap.put(CATEGORY_FIELD, statistics ? null : waypoint.getCategory()); + resultMap.put(SHARED_OWNER_FIELD, sharedOwner); // Display the marker's track name in the total time field resultMap.put(TOTAL_TIME_FIELD, trackName == null ? null : getString(R.string.search_list_marker_track_location, trackName)); resultMap.put(TOTAL_DISTANCE_FIELD, null); + resultMap.put(PHOTO_URL_FIELD, waypoint.getPhotoUrl()); resultMap.put(START_TIME_FIELD, waypoint.getLocation().getTime()); + resultMap.put(CATEGORY_FIELD, statistics ? null : waypoint.getCategory()); resultMap.put(DESCRIPTION_FIELD, statistics ? null : waypoint.getDescription()); - resultMap.put(SHARED_OWNER_FIELD, sharedOwner); resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId()); resultMap.put(MARKER_ID_FIELD, waypoint.getId()); } @@ -515,13 +520,14 @@ public class SearchListActivity extends AbstractSendToGoogleActivity implements resultMap.put(ICON_ID_FIELD, TrackIconUtils.getIconDrawable(icon)); resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.icon_track); resultMap.put(NAME_FIELD, track.getName()); - resultMap.put(CATEGORY_FIELD, category); + resultMap.put(SHARED_OWNER_FIELD, track.getSharedOwner()); resultMap.put(TOTAL_TIME_FIELD, StringUtils.formatElapsedTime(tripStatitics.getTotalTime())); resultMap.put(TOTAL_DISTANCE_FIELD, StringUtils.formatDistance(this, tripStatitics.getTotalDistance(), metricUnits)); + resultMap.put(PHOTO_URL_FIELD, null); resultMap.put(START_TIME_FIELD, tripStatitics.getStartTime()); + resultMap.put(CATEGORY_FIELD, category); resultMap.put(DESCRIPTION_FIELD, track.getDescription()); - resultMap.put(SHARED_OWNER_FIELD, track.getSharedOwner()); resultMap.put(TRACK_ID_FIELD, track.getId()); resultMap.put(MARKER_ID_FIELD, null); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java index 46f716255..0218afa42 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java @@ -20,6 +20,7 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils; import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.TrackDataHub; import com.google.android.apps.mytracks.content.Waypoint; +import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.WaypointCreationRequest; import com.google.android.apps.mytracks.fragments.ChartFragment; import com.google.android.apps.mytracks.fragments.ConfirmDeleteDialogFragment; @@ -36,6 +37,7 @@ import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection import com.google.android.apps.mytracks.settings.SettingsActivity; import com.google.android.apps.mytracks.util.AnalyticsUtils; import com.google.android.apps.mytracks.util.ApiAdapterFactory; +import com.google.android.apps.mytracks.util.FileUtils; import com.google.android.apps.mytracks.util.GoogleFeedbackUtils; import com.google.android.apps.mytracks.util.IntentUtils; import com.google.android.apps.mytracks.util.PreferencesUtils; @@ -46,10 +48,15 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; +import android.os.Environment; import android.os.Parcelable; +import android.provider.MediaStore; import android.support.v4.app.TaskStackBuilder; import android.support.v4.view.ViewPager; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -57,6 +64,12 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.TabHost; import android.widget.TabHost.TabSpec; +import android.widget.Toast; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; /** * An activity to show the track detail. @@ -69,9 +82,15 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements public static final String EXTRA_TRACK_ID = "track_id"; public static final String EXTRA_MARKER_ID = "marker_id"; + private static final String TAG = TrackDetailActivity.class.getSimpleName(); private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key"; - + private static final String PHOTO_URI_KEY = "photo_uri_key"; + private static final String HAS_PHOTO_KEY = "has_photo_key"; + // The following are set in onCreate + private boolean hasCamera; + private Uri photoUri; + private boolean hasPhoto; private MyTracksProviderUtils myTracksProviderUtils; private SharedPreferences sharedPreferences; private TrackRecordingServiceConnection trackRecordingServiceConnection; @@ -91,6 +110,7 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements private String sensorType = PreferencesUtils.SENSOR_TYPE_DEFAULT; private MenuItem insertMarkerMenuItem; + private MenuItem insertPhotoMenuItem; private MenuItem playMenuItem; private MenuItem shareMenuItem; private MenuItem exportMenuItem; @@ -107,6 +127,19 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements @Override public void run() { trackController.update(trackId == recordingTrackId, recordingTrackPaused); + if (hasPhoto && photoUri != null) { + WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest( + WaypointType.WAYPOINT, false, null, null, null, null, photoUri.toString()); + long id = TrackRecordingServiceConnectionUtils.addMarker( + TrackDetailActivity.this, trackRecordingServiceConnection, waypointCreationRequest); + hasPhoto = false; + + if (id != -1L) { + Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + mediaScanIntent.setData(photoUri); + sendBroadcast(mediaScanIntent); + } + } } }); } @@ -182,6 +215,10 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); + photoUri = savedInstanceState != null ? (Uri) savedInstanceState.getParcelable(PHOTO_URI_KEY) : null; + hasPhoto = savedInstanceState != null ? savedInstanceState.getBoolean(HAS_PHOTO_KEY, false) : false; + myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this); handleIntent(getIntent()); @@ -266,6 +303,19 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(CURRENT_TAB_TAG_KEY, tabHost.getCurrentTabTag()); + if (photoUri != null) { + outState.putParcelable(PHOTO_URI_KEY, photoUri); + } + outState.putBoolean(HAS_PHOTO_KEY, hasPhoto); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == CAMERA_REQUEST_CODE) { + hasPhoto = resultCode == RESULT_OK; + } else { + super.onActivityResult(requestCode, resultCode, data); + } } @Override @@ -309,6 +359,7 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements .setVisible(ApiAdapterFactory.getApiAdapter().isGoogleFeedbackAvailable()); insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker); + insertPhotoMenuItem = menu.findItem(R.id.track_detail_insert_photo); playMenuItem = menu.findItem(R.id.track_detail_play); shareMenuItem = menu.findItem(R.id.track_detail_share); @@ -338,6 +389,36 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements .putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId); startActivity(intent); return true; + case R.id.track_detail_insert_photo: + try { + if (!FileUtils.isExternalStorageWriteable()) { + Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show(); + return false; + } + + File dir = new File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), + FileUtils.SDCARD_TOP_DIR); + + FileUtils.ensureDirectoryExists(dir); + + String fileName = SimpleDateFormat.getDateTimeInstance().format(new Date()); + File file = new File(dir, FileUtils.buildUniqueFileName(dir, fileName, "jpeg")); + + if (file.exists()) { + Toast.makeText(this, R.string.marker_insert_photo_error, Toast.LENGTH_LONG).show(); + return false; + } + file.createNewFile(); + photoUri = Uri.fromFile(file); + intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra( + MediaStore.EXTRA_OUTPUT, photoUri); + startActivityForResult(intent, CAMERA_REQUEST_CODE); + } catch (IOException e) { + Log.e(TAG, "Unable to insert photo marker", e); + return false; + } + return true; case R.id.track_detail_play: confirmPlay(new long[] {trackId}); return true; @@ -513,6 +594,9 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements if (insertMarkerMenuItem != null) { insertMarkerMenuItem.setVisible(isRecording && !isPaused); } + if (insertPhotoMenuItem != null) { + insertPhotoMenuItem.setVisible(hasCamera && isRecording && !isPaused); + } if (playMenuItem != null) { playMenuItem.setVisible(!isRecording); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java index dd20ae758..b0aba0b1a 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java @@ -361,28 +361,28 @@ public class TrackListActivity extends AbstractSendToGoogleActivity int idIndex = cursor.getColumnIndex(TracksColumns._ID); int iconIndex = cursor.getColumnIndex(TracksColumns.ICON); int nameIndex = cursor.getColumnIndex(TracksColumns.NAME); - int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY); + int sharedOwnerIndex = cursor.getColumnIndex(TracksColumns.SHAREDOWNER); int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME); int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE); - int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME); + int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME); + int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY); int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION); - int sharedOwnerIndex = cursor.getColumnIndex(TracksColumns.SHAREDOWNER); boolean isRecording = cursor.getLong(idIndex) == recordingTrackId; String icon = cursor.getString(iconIndex); int iconId = TrackIconUtils.getIconDrawable(icon); String name = cursor.getString(nameIndex); - String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex); + String sharedOwner = cursor.getString(sharedOwnerIndex); String totalTime = StringUtils.formatElapsedTime(cursor.getLong(totalTimeIndex)); String totalDistance = StringUtils.formatDistance( TrackListActivity.this, cursor.getDouble(totalDistanceIndex), metricUnits); - long startTime = cursor.getLong(startTimeIndex); + long startTime = cursor.getLong(startTimeIndex); + String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex); String description = cursor.getString(descriptionIndex); - String sharedOwner = cursor.getString(sharedOwnerIndex); ListItemUtils.setListItem(TrackListActivity.this, view, isRecording, recordingTrackPaused, - iconId, R.string.icon_track, name, category, totalTime, totalDistance, startTime, - description, sharedOwner); + iconId, R.string.icon_track, name, sharedOwner, totalTime, totalDistance, null, + startTime, category, description); } }; listView.setAdapter(sectionResourceCursorAdapter); diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java index d4d95bf6b..d69176f4c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java @@ -49,7 +49,7 @@ import java.util.ArrayList; public class MyTracksProvider extends ContentProvider { private static final String TAG = MyTracksProvider.class.getSimpleName(); - private static final int DATABASE_VERSION = 21; + private static final int DATABASE_VERSION = 22; @VisibleForTesting static final String DATABASE_NAME = "mytracks.db"; @@ -124,6 +124,12 @@ public class MyTracksProvider extends ContentProvider { db.execSQL("ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD " + TracksColumns.SHAREDOWNER + " STRING"); } + + if (oldVersion <= 21) { + Log.w(TAG, "Upgrade DB: Adding photo url column."); + db.execSQL("ALTER TABLE " + WaypointsColumns.TABLE_NAME + " ADD " + WaypointsColumns.PHOTOURL + + " STRING"); + } } } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/fragments/MarkerDetailFragment.java b/MyTracks/src/com/google/android/apps/mytracks/fragments/MarkerDetailFragment.java new file mode 100644 index 000000000..d7e85a81f --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/fragments/MarkerDetailFragment.java @@ -0,0 +1,209 @@ +/* + * Copyright 2013 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.fragments; + +import com.google.android.apps.mytracks.MarkerEditActivity; +import com.google.android.apps.mytracks.TrackDetailActivity; +import com.google.android.apps.mytracks.content.MyTracksProviderUtils; +import com.google.android.apps.mytracks.content.Track; +import com.google.android.apps.mytracks.content.Waypoint; +import com.google.android.apps.mytracks.content.Waypoint.WaypointType; +import com.google.android.apps.mytracks.util.IntentUtils; +import com.google.android.apps.mytracks.util.PhotoUtils; +import com.google.android.apps.mytracks.util.StatsUtils; +import com.google.android.maps.mytracks.R; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.util.Log; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +/** + * A fragment to show marker details. + * + * @author Jimmy Shih + */ +public class MarkerDetailFragment extends Fragment { + + private static final String TAG = MarkerDetailFragment.class.getSimpleName(); + private static final String KEY_MARKER_ID = "markerId"; + + public static MarkerDetailFragment newInstance(long markerId) { + MarkerDetailFragment fragment = new MarkerDetailFragment(); + + Bundle bundle = new Bundle(); + bundle.putLong(KEY_MARKER_ID, markerId); + fragment.setArguments(bundle); + + return fragment; + } + + private MyTracksProviderUtils myTracksProviderUtils; + private Waypoint waypoint; + private Bitmap bitmap; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + long markerId = getArguments().getLong(KEY_MARKER_ID); + if (markerId == -1L) { + Log.d(TAG, "invalid marker id"); + getFragmentManager().popBackStack(); + return; + } + myTracksProviderUtils = MyTracksProviderUtils.Factory.get(getActivity()); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + setHasOptionsMenu(true); + return inflater.inflate(R.layout.marker_detail_fragment, container, false); + }; + + @Override + public void onResume() { + super.onResume(); + + // Need to update the waypoint in case returning after an edit + updateWaypoint(true); + update(); + } + + public void onPause() { + super.onPause(); + if (bitmap != null) { + bitmap.recycle(); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.marker_detail, menu); + + updateWaypoint(false); + + Track track = myTracksProviderUtils.getTrack(waypoint.getTrackId()); + boolean isSharedWithMe = track != null ? track.isSharedWithMe() : true; + + menu.findItem(R.id.marker_detail_edit).setVisible(!isSharedWithMe); + menu.findItem(R.id.marker_detail_delete).setVisible(!isSharedWithMe); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + long markerId = getArguments().getLong(KEY_MARKER_ID); + FragmentActivity fragmentActivity = getActivity(); + Intent intent; + + switch (item.getItemId()) { + case R.id.marker_detail_show_on_map: + intent = IntentUtils.newIntent(fragmentActivity, TrackDetailActivity.class) + .putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerId); + startActivity(intent); + return true; + case R.id.marker_detail_edit: + intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class) + .putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId); + startActivity(intent); + return true; + case R.id.marker_detail_delete: + DeleteMarkerDialogFragment.newInstance(new long[] { markerId }) + .show(getChildFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * Updates the waypoint. + * + * @param refresh true to always update + */ + private void updateWaypoint(boolean refresh) { + if (refresh || waypoint == null) { + waypoint = myTracksProviderUtils.getWaypoint(getArguments().getLong(KEY_MARKER_ID)); + if (waypoint == null) { + Log.d(TAG, "waypoint is null"); + getFragmentManager().popBackStack(); + } + } + } + + /** + * Updates the UI. + */ + private void update() { + View waypointSection = getView().findViewById(R.id.marker_detail_waypoint_section); + View statisticsSection = getView().findViewById(R.id.marker_detail_statistics_section); + + if (waypoint.getType() == WaypointType.WAYPOINT) { + waypointSection.setVisibility(View.VISIBLE); + statisticsSection.setVisibility(View.GONE); + + ImageView imageView = (ImageView) getView().findViewById(R.id.marker_detail_waypoint_photo); + String photoUrl = waypoint.getPhotoUrl(); + if (photoUrl == null || photoUrl.equals("")) { + imageView.setVisibility(View.GONE); + } else { + imageView.setVisibility(View.VISIBLE); + Display defaultDisplay = getActivity().getWindowManager().getDefaultDisplay(); + @SuppressWarnings("deprecation") + int displayWidth = defaultDisplay.getWidth(); + @SuppressWarnings("deprecation") + int displayHeight = defaultDisplay.getHeight(); + bitmap = PhotoUtils.setImageVew( + imageView, Uri.parse(photoUrl), displayWidth, displayHeight, true); + } + + TextView name = (TextView) getView().findViewById(R.id.marker_detail_waypoint_name); + name.setText(getString(R.string.generic_name_line, waypoint.getName())); + + TextView markerType = (TextView) getView() + .findViewById(R.id.marker_detail_waypoint_marker_type); + markerType.setText( + getString(R.string.marker_detail_waypoint_marker_type, waypoint.getCategory())); + + TextView description = (TextView) getView() + .findViewById(R.id.marker_detail_waypoint_description); + description.setText(getString(R.string.generic_description_line, waypoint.getDescription())); + } else { + waypointSection.setVisibility(View.GONE); + statisticsSection.setVisibility(View.VISIBLE); + + TextView name = (TextView) getView().findViewById(R.id.marker_detail_statistics_name); + name.setText(getString(R.string.generic_name_line, waypoint.getName())); + + StatsUtils.setTripStatisticsValues(getActivity(), waypoint.getTripStatistics()); + StatsUtils.setLocationValues(getActivity(), waypoint.getLocation(), false); + } + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/file/AbstractImporter.java b/MyTracks/src/com/google/android/apps/mytracks/io/file/AbstractImporter.java index 86901a1d3..5879a18ae 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/file/AbstractImporter.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/file/AbstractImporter.java @@ -282,7 +282,7 @@ abstract class AbstractImporter extends DefaultHandler { // Insert waypoint Waypoint newWaypoint = new Waypoint(waypoint.getName(), waypointDescription, waypoint.getCategory(), icon, track.getId(), waypoint.getType(), length, duration, - -1L, -1L, trackPoint, tripStatistics); + -1L, -1L, trackPoint, tripStatistics, ""); myTracksProviderUtils.insertWaypoint(newWaypoint); } waypoint = null; @@ -584,7 +584,7 @@ abstract class AbstractImporter extends DefaultHandler { waypointLocation.setLongitude(180); Waypoint waypoint = new Waypoint(waypointName, waypointDescription, waypointCategory, icon, track.getId(), WaypointType.STATISTICS, length, duration, -1L, -1L, waypointLocation, - tripStatistics); + tripStatistics, ""); myTracksProviderUtils.insertWaypoint(waypoint); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index b6fae3b33..ccafc08fb 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -499,9 +499,13 @@ public class TrackRecordingService extends Service { duration = 0L; } + String photoUrl = waypointCreationRequest.getPhotoUrl() != null ? waypointCreationRequest + .getPhotoUrl() + : ""; + // Insert waypoint Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, - waypointType, length, duration, -1L, -1L, location, tripStatistics); + waypointType, length, duration, -1L, -1L, location, tripStatistics, photoUrl); Uri uri = myTracksProviderUtils.insertWaypoint(waypoint); return Long.parseLong(uri.getLastPathSegment()); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/FileUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/FileUtils.java index 9e52ddcf8..3a0973da5 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/FileUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/FileUtils.java @@ -37,8 +37,7 @@ public class FileUtils { * Name of the top-level directory inside the SD card where our files will be * read from/written to. */ - @VisibleForTesting - static final String SDCARD_TOP_DIR = "MyTracks"; + public static final String SDCARD_TOP_DIR = "MyTracks"; /** * The maximum FAT32 path length. See the FAT32 spec at diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ListItemUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/ListItemUtils.java index 5f0f1c324..75463ff7a 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/ListItemUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/ListItemUtils.java @@ -18,8 +18,11 @@ package com.google.android.apps.mytracks.util; import com.google.android.maps.mytracks.R; +import android.app.Activity; import android.content.Context; +import android.net.Uri; import android.text.format.DateUtils; +import android.view.Display; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -38,23 +41,25 @@ public class ListItemUtils { /** * Sets a list item. * - * @param context the context + * @param activity the activity * @param view the list item view * @param isRecording true if recording * @param isPaused true if paused - * @param name the name value * @param iconId the icon id * @param iconContentDescriptionId the icon content description id - * @param category the category value + * @param name the name value + * @param sharedOwner if shared with me track, the owner, else null * @param totalTime the total time value * @param totalDistance the total distance value + * @param photoUrl the photo url * @param startTime the start time value + * @param category the category value * @param description the description value - * @param sharedOwner if shared with me track, the owner, else null */ - public static void setListItem(Context context, View view, boolean isRecording, boolean isPaused, - int iconId, int iconContentDescriptionId, String name, String category, String totalTime, - String totalDistance, long startTime, String description, String sharedOwner) { + public static void setListItem(Activity activity, View view, boolean isRecording, + boolean isPaused, int iconId, int iconContentDescriptionId, String name, String sharedOwner, + String totalTime, String totalDistance, String photoUrl, long startTime, String category, + String description) { if (isRecording) { iconId = isPaused ? R.drawable.ic_track_paused : R.drawable.ic_track_recording; @@ -64,29 +69,45 @@ public class ListItemUtils { ImageView iconImageView = (ImageView) view.findViewById(R.id.list_item_icon); iconImageView.setImageResource(iconId); - iconImageView.setContentDescription(context.getString(iconContentDescriptionId)); + iconImageView.setContentDescription(activity.getString(iconContentDescriptionId)); + // Set name TextView nameTextView = (TextView) view.findViewById(R.id.list_item_name); nameTextView.setText(name); + // Set sharedOwner/totalTime/totalDistance TextView timeDistanceTextView = (TextView) view.findViewById(R.id.list_item_time_distance); if (isRecording) { - timeDistanceTextView.setTextColor(context.getResources() + timeDistanceTextView.setTextColor(activity.getResources() .getColor(isPaused ? android.R.color.white : R.color.recording_text)); } else { // Need to match the style set in list_item.xml - timeDistanceTextView.setTextAppearance(context, R.style.TextSmall); + timeDistanceTextView.setTextAppearance(activity, R.style.TextSmall); } setTextView(timeDistanceTextView, - getTimeDistance(context, isRecording, isPaused, sharedOwner, totalTime, totalDistance)); + getTimeDistance(activity, isRecording, isPaused, sharedOwner, totalTime, totalDistance)); - String[] startTimeDisplay = getStartTime(isRecording, context, startTime); + // Set photoUrl + ImageView photo = (ImageView) view.findViewById(R.id.list_item_photo); + if (photoUrl == null || photoUrl.equals("")) { + photo.setVisibility(View.GONE); + } else { + photo.setVisibility(View.VISIBLE); + Display defaultDisplay = activity.getWindowManager().getDefaultDisplay(); + // Set the initial width to 35% of the display width + int width = (int) (defaultDisplay.getWidth() * .35); + PhotoUtils.setImageVew(photo, Uri.parse(photoUrl), width, 0, false); + } + + // Set date/time + String[] startTimeDisplay = getStartTime(isRecording, activity, startTime); TextView dateTextView = (TextView) view.findViewById(R.id.list_item_date); setTextView(dateTextView, startTimeDisplay[0]); TextView timeTextView = (TextView) view.findViewById(R.id.list_item_time); setTextView(timeTextView, startTimeDisplay[1]); + // Set category/description TextView descriptionTextView = (TextView) view.findViewById(R.id.list_item_description); setTextView(descriptionTextView, getDescription(isRecording, category, description)); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/PhotoUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/PhotoUtils.java new file mode 100644 index 000000000..b333fb932 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/util/PhotoUtils.java @@ -0,0 +1,160 @@ +/* + * Copyright 2013 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.util; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.media.ExifInterface; +import android.net.Uri; +import android.util.Log; +import android.widget.ImageView; + +import java.io.IOException; + +/** + * Utilities for photos. + * + * @author Jimmy Shih + */ +public class PhotoUtils { + + private static final String TAG = PhotoUtils.class.getSimpleName(); + + private PhotoUtils() {} + + /** + * Sets an image view. + * + * @param imageView the image view + * @param uri the image uri + * @param displayWidth the display width + * @param displayHeight the display height + * @param fitWithIn true to fit within the display area. False to crop and fill + * the display area. + */ + public static Bitmap setImageVew( + ImageView imageView, Uri uri, int displayWidth, int displayHeight, boolean fitWithIn) { + + // Get the image dimensions + BitmapFactory.Options options = new BitmapFactory.Options(); + + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(uri.getPath(), options); + + // Set targetWidth and targetHeight + int targetWidth = displayWidth; + int targetHeight = displayHeight; + if (targetHeight == 0) { + targetHeight = (int) (targetWidth * ((float) options.outHeight / (float) options.outWidth)); + imageView.getLayoutParams().height = targetHeight; + } + + // Set imageWidth and imageHeight based on image rotation + int rotation = getRotation(uri); + int imageWidth; + int imageHeight; + + if (rotation == 0 || rotation == 180) { + imageWidth = options.outWidth; + imageHeight = options.outHeight; + } else { + imageWidth = options.outHeight; + imageHeight = options.outWidth; + } + + // Get a scaled down version of the image + options.inJustDecodeBounds = false; + options.inSampleSize = getInSampleSize( + imageWidth, imageHeight, targetWidth, targetHeight, fitWithIn); + options.inPurgeable = true; + + Bitmap scaledBitmap = BitmapFactory.decodeFile(uri.getPath(), options); + + // Get the final bitmap after rotating the scaled down image + Bitmap bitmap; + if (rotation == 0) { + bitmap = scaledBitmap; + } else { + Matrix matrix = new Matrix(); + matrix.postRotate(rotation); + bitmap = Bitmap.createBitmap( + scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true); + scaledBitmap.recycle(); + } + + imageView.setImageBitmap(bitmap); + return bitmap; + } + + /** + * Gets the image rotation + * + * @param uri the image uri + */ + private static int getRotation(Uri uri) { + try { + ExifInterface exifInterface = new ExifInterface(uri.getPath()); + switch (exifInterface.getAttributeInt( + ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) { + case ExifInterface.ORIENTATION_ROTATE_90: + return 90; + case ExifInterface.ORIENTATION_ROTATE_180: + return 180; + case ExifInterface.ORIENTATION_ROTATE_270: + return 270; + default: + return 0; + } + } catch (IOException e) { + Log.e(TAG, "Unable to get photo orientation", e); + return 0; + } + } + + /** + * Gets the in sample size. + * + * @param imageWidth the image width + * @param imageHeight the image height + * @param targetWidth the target width + * @param targetHeight the target height + * @param fitWithin true to fit within the target area. False to crop and fill + * all target area. + */ + private static int getInSampleSize( + int imageWidth, int imageHeight, int targetWidth, int targetHeight, boolean fitWithin) { + float widthRatio = 1; + if (imageWidth > targetWidth) { + widthRatio = (float) imageWidth / (float) targetWidth; + } + + float heightRatio = 1; + if (imageHeight > targetHeight) { + heightRatio = (float) imageHeight / (float) targetHeight; + } + + /* + * To fit within the target area, return the larger sample ratio so the + * image will not be larger than the target dimensions. To crop and fill the + * target area, return the smaller sample ratio so the image will be greater + * than the target dimensions. Use Math.floor to not under sample. + */ + return (int) Math.floor( + fitWithin ? Math.max(widthRatio, heightRatio) : Math.min(widthRatio, heightRatio)); + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/TrackRecordingServiceConnectionUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/TrackRecordingServiceConnectionUtils.java index c4ec688c9..f84351386 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/TrackRecordingServiceConnectionUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/TrackRecordingServiceConnectionUtils.java @@ -170,7 +170,7 @@ public class TrackRecordingServiceConnectionUtils { /** * Adds a marker. */ - public static void addMarker(Context context, + public static long addMarker(Context context, TrackRecordingServiceConnection trackRecordingServiceConnection, WaypointCreationRequest waypointCreationRequest) { ITrackRecordingService trackRecordingService = trackRecordingServiceConnection @@ -179,9 +179,10 @@ public class TrackRecordingServiceConnectionUtils { Log.d(TAG, "Unable to add marker, no track recording service"); } else { try { - if (trackRecordingService.insertWaypoint(waypointCreationRequest) != -1L) { + long markerId = trackRecordingService.insertWaypoint(waypointCreationRequest); + if (markerId != -1L) { Toast.makeText(context, R.string.marker_add_success, Toast.LENGTH_SHORT).show(); - return; + return markerId; } } catch (RemoteException e) { Log.e(TAG, "Unable to add marker", e); @@ -190,5 +191,6 @@ public class TrackRecordingServiceConnectionUtils { } } Toast.makeText(context, R.string.marker_add_error, Toast.LENGTH_LONG).show(); + return -1L; } } diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java index 7c8a6dcad..5d684476a 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java @@ -363,7 +363,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { int elevationGainIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ELEVATIONGAIN); int minGradeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MINGRADE); int maxGradeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXGRADE); - + int photoUrlIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.PHOTOURL); + Waypoint waypoint = new Waypoint(); if (!cursor.isNull(idIndex)) { @@ -468,6 +469,10 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { if (hasTripStatistics) { waypoint.setTripStatistics(tripStatistics); } + + if (!cursor.isNull(photoUrlIndex)) { + waypoint.setPhotoUrl(cursor.getString(photoUrlIndex)); + } return waypoint; } @@ -672,6 +677,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { values.put(WaypointsColumns.MINGRADE, tripStatistics.getMinGrade()); values.put(WaypointsColumns.MAXGRADE, tripStatistics.getMaxGrade()); } + + values.put(WaypointsColumns.PHOTOURL, waypoint.getPhotoUrl()); return values; } diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/Waypoint.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/Waypoint.java index f9a97888f..dad2be246 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/Waypoint.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/Waypoint.java @@ -47,12 +47,13 @@ public final class Waypoint implements Parcelable { private long stopId = -1L; private Location location = null; private TripStatistics tripStatistics = null; - + private String photoUrl = ""; + public Waypoint() {} public Waypoint(String name, String description, String category, String icon, long trackId, WaypointType type, double length, long duration, long startId, long stopId, Location location, - TripStatistics tripStatistics) { + TripStatistics tripStatistics, String photoUrl) { this.name = name; this.description = description; this.category = category; @@ -65,6 +66,7 @@ public final class Waypoint implements Parcelable { this.stopId = stopId; this.location = location; this.tripStatistics = tripStatistics; + this.photoUrl = photoUrl; } private Waypoint(Parcel source) { @@ -89,6 +91,7 @@ public final class Waypoint implements Parcelable { if (hasStats > 0) { tripStatistics = source.readParcelable(classLoader); } + photoUrl = source.readString(); } @Override @@ -117,6 +120,7 @@ public final class Waypoint implements Parcelable { if (tripStatistics != null) { dest.writeParcelable(tripStatistics, 0); } + dest.writeString(photoUrl); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -233,5 +237,13 @@ public final class Waypoint implements Parcelable { public void setTripStatistics(TripStatistics tripStatistics) { this.tripStatistics = tripStatistics; + } + + public String getPhotoUrl() { + return photoUrl; + } + + public void setPhotoUrl(String photoUrl) { + this.photoUrl = photoUrl; } } diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointCreationRequest.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointCreationRequest.java index 82b238020..caf88ff80 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointCreationRequest.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointCreationRequest.java @@ -34,6 +34,7 @@ public class WaypointCreationRequest implements Parcelable { private String category; private String description; private String iconUrl; + private String photoUrl; public final static WaypointCreationRequest DEFAULT_WAYPOINT = new WaypointCreationRequest( WaypointType.WAYPOINT, false); @@ -43,17 +44,18 @@ public class WaypointCreationRequest implements Parcelable { WaypointType.STATISTICS, true); private WaypointCreationRequest(WaypointType type, boolean isTrackStatistics) { - this(type, isTrackStatistics, null, null, null, null); + this(type, isTrackStatistics, null, null, null, null, null); } public WaypointCreationRequest(WaypointType type, boolean isTrackStatistics, String name, - String category, String description, String iconUrl) { + String category, String description, String iconUrl, String photoUrl) { this.type = type; this.isTrackStatistics = isTrackStatistics; this.name = name; this.category = category; this.description = description; this.iconUrl = iconUrl; + this.photoUrl = photoUrl; } public static class Creator implements Parcelable.Creator { @@ -66,6 +68,7 @@ public class WaypointCreationRequest implements Parcelable { source.readString(), source.readString(), source.readString(), + source.readString(), source.readString()); return request; } @@ -90,6 +93,7 @@ public class WaypointCreationRequest implements Parcelable { parcel.writeString(category); parcel.writeString(description); parcel.writeString(iconUrl); + parcel.writeString(photoUrl); } public WaypointType getType() { @@ -115,4 +119,8 @@ public class WaypointCreationRequest implements Parcelable { public String getIconUrl() { return iconUrl; } + + public String getPhotoUrl() { + return photoUrl; + } } \ No newline at end of file diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointsColumns.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointsColumns.java index a79f096f1..a6e910255 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointsColumns.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/WaypointsColumns.java @@ -70,6 +70,8 @@ public interface WaypointsColumns extends BaseColumns { public static final String ELEVATIONGAIN = "elevationgain"; // elevation gain public static final String MINGRADE = "mingrade"; // min grade public static final String MAXGRADE = "maxgrade"; // max grade + + public static final String PHOTOURL = "photoUrl"; // url for the photo public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -101,7 +103,8 @@ public interface WaypointsColumns extends BaseColumns { + MAXELEVATION + " FLOAT, " + ELEVATIONGAIN + " FLOAT, " + MINGRADE + " FLOAT, " - + MAXGRADE + " FLOAT" + + MAXGRADE + " FLOAT, " + + PHOTOURL + " STRING" + ");"; public static final String[] COLUMNS = { @@ -134,7 +137,8 @@ public interface WaypointsColumns extends BaseColumns { MAXELEVATION, ELEVATIONGAIN, MINGRADE, - MAXGRADE + MAXGRADE, + PHOTOURL }; public static final byte[] COLUMN_TYPES = { @@ -167,6 +171,7 @@ public interface WaypointsColumns extends BaseColumns { FLOAT_TYPE_ID, // max elevation FLOAT_TYPE_ID, // elevation gain FLOAT_TYPE_ID, // min grade - FLOAT_TYPE_ID // max grade + FLOAT_TYPE_ID, // max grade + STRING_TYPE_ID // photo url }; } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/content/WaypointCreationRequestTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/content/WaypointCreationRequestTest.java index 7d15be8ba..9bf8e8a67 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/content/WaypointCreationRequestTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/content/WaypointCreationRequestTest.java @@ -43,7 +43,7 @@ public class WaypointCreationRequestTest extends AndroidTestCase { public void testAllAttributesParceling() { WaypointCreationRequest original = new WaypointCreationRequest( - WaypointType.WAYPOINT, false, "name", "category", "description", "img.png"); + WaypointType.WAYPOINT, false, "name", "category", "description", "img.png", null); Parcel p = Parcel.obtain(); original.writeToParcel(p, 0); p.setDataPosition(0);