diff --git a/MyTracks/res/layout/track_list_header.xml b/MyTracks/res/layout/track_list_header.xml
new file mode 100644
index 000000000..814644b0a
--- /dev/null
+++ b/MyTracks/res/layout/track_list_header.xml
@@ -0,0 +1,25 @@
+
+
+
diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml
index 21934680e..e3f8b85be 100644
--- a/MyTracks/res/values/strings.xml
+++ b/MyTracks/res/values/strings.xml
@@ -2424,7 +2424,12 @@ limitations under the License.
success.">
Now recording a new track.
-
+
+ Shared with me
+
+
diff --git a/MyTracks/src/com/google/android/apps/mytracks/SectionResourceCursorAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/SectionResourceCursorAdapter.java
new file mode 100644
index 000000000..d350287e0
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/SectionResourceCursorAdapter.java
@@ -0,0 +1,168 @@
+/*
+ * 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;
+
+import com.google.android.apps.mytracks.content.TracksColumns;
+import com.google.android.maps.mytracks.R;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.support.v4.widget.ResourceCursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * A section based resource cursor adapter. Currently only supports one
+ * additional section header, "Shared with me".
+ *
+ * @author Jimmy Shih
+ */
+public abstract class SectionResourceCursorAdapter extends ResourceCursorAdapter {
+
+ private enum ItemType {
+ TRACK, HEADER
+ }
+
+ private final LayoutInflater layoutInflater;
+ private int sharedWithMeIndex = -1;
+
+ public SectionResourceCursorAdapter(Context context, int layout, Cursor cursor, int flags) {
+ super(context, layout, cursor, flags);
+ this.layoutInflater = LayoutInflater.from(context);
+
+ if (cursor != null) {
+ sharedWithMeIndex = getSharedWithMeIndex();
+ cursor.registerDataSetObserver(dataSetObserver);
+ }
+ }
+
+ private DataSetObserver dataSetObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ sharedWithMeIndex = getSharedWithMeIndex();
+ };
+
+ @Override
+ public void onInvalidated() {
+ sharedWithMeIndex = -1;
+ };
+ };
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (getItemViewType(position) == ItemType.TRACK.ordinal()) {
+ return super.getView(getCursorPosition(position), convertView, parent);
+ } else {
+ TextView textView;
+ if (convertView == null) {
+ textView = (TextView) layoutInflater.inflate(R.layout.track_list_header, parent, false);
+ } else {
+ textView = (TextView) convertView;
+ }
+ return textView;
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return ItemType.values().length;
+ }
+
+ @Override
+ public int getCount() {
+ int count = super.getCount();
+ return count != 0 && sharedWithMeIndex != -1 ? count + 1 : count;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItemViewType(position) == ItemType.TRACK.ordinal();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (getItemViewType(position) == ItemType.TRACK.ordinal()) {
+ return super.getItem(getCursorPosition(position));
+ }
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (getItemViewType(position) == ItemType.TRACK.ordinal()) {
+ return super.getItemId(getCursorPosition(position));
+ }
+ return -1L;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (sharedWithMeIndex != -1 && sharedWithMeIndex == position) {
+ return ItemType.HEADER.ordinal();
+ }
+ return ItemType.TRACK.ordinal();
+ }
+
+ @Override
+ public Cursor swapCursor(Cursor newCursor) {
+ if (getCursor() != null) {
+ getCursor().unregisterDataSetObserver(dataSetObserver);
+ }
+
+ Cursor oldCursor = super.swapCursor(newCursor);
+ if (newCursor != null) {
+ sharedWithMeIndex = getSharedWithMeIndex();
+ newCursor.registerDataSetObserver(dataSetObserver);
+ }
+ return oldCursor;
+ }
+
+ /**
+ * Gets the Shared with me header index.
+ */
+ private int getSharedWithMeIndex() {
+ int i = 0;
+ Cursor cursor = getCursor();
+ if (cursor != null && cursor.moveToFirst()) {
+ int index = cursor.getColumnIndex(TracksColumns.SHAREDWITHME);
+ do {
+ int sharedWithMe = cursor.getInt(index);
+ if (sharedWithMe == 1) {
+ // Returns the index where sharedWithMe == 1
+ return i;
+ }
+ i++;
+ } while (cursor.moveToNext());
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the underlying cursor position.
+ *
+ * @param position the requested position
+ */
+ private int getCursorPosition(int position) {
+ if (sharedWithMeIndex != -1 && position > sharedWithMeIndex) {
+ return position - 1;
+ }
+ return position;
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
index 31eba1360..866a16e43 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
@@ -66,7 +66,6 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.support.v4.widget.ResourceCursorAdapter;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
@@ -97,7 +96,8 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
private static final int GOOGLE_PLAY_SERVICES_REQUEST_CODE = 0;
private static final String[] PROJECTION = new String[] { TracksColumns._ID, TracksColumns.NAME,
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME,
- TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON };
+ TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON,
+ TracksColumns.SHAREDWITHME};
// Callback when the trackRecordingServiceConnection binding changes.
private final Runnable bindChangedCallback = new Runnable() {
@@ -180,7 +180,7 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
boolean isRecording = recordingTrackId
!= PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
updateMenuItems(isRecording);
- resourceCursorAdapter.notifyDataSetChanged();
+ sectionResourceCursorAdapter.notifyDataSetChanged();
trackController.update(isRecording, recordingTrackPaused);
}
});
@@ -333,7 +333,7 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private TrackController trackController;
private ListView listView;
- private ResourceCursorAdapter resourceCursorAdapter;
+ private SectionResourceCursorAdapter sectionResourceCursorAdapter;
private TrackDataHub trackDataHub;
// Preferences
@@ -379,7 +379,7 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
startActivity(intent);
}
});
- resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
+ sectionResourceCursorAdapter = new SectionResourceCursorAdapter(this, R.layout.list_item, null, 0) {
@Override
public void bindView(View view, Context context, Cursor cursor) {
int idIndex = cursor.getColumnIndex(TracksColumns._ID);
@@ -406,7 +406,7 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
totalDistance, startTimeDisplay, cursor.getString(descriptionIndex));
}
};
- listView.setAdapter(resourceCursorAdapter);
+ listView.setAdapter(sectionResourceCursorAdapter);
ApiAdapterFactory.getApiAdapter()
.configureListViewContextualMenu(this, listView, contextualActionModeCallback);
@@ -414,17 +414,17 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
@Override
public Loader onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null,
- null, TracksColumns._ID + " DESC");
+ null, TracksColumns.SHAREDWITHME + " ASC, " + TracksColumns._ID + " DESC");
}
@Override
public void onLoadFinished(Loader loader, Cursor cursor) {
- resourceCursorAdapter.swapCursor(cursor);
+ sectionResourceCursorAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader loader) {
- resourceCursorAdapter.swapCursor(null);
+ sectionResourceCursorAdapter.swapCursor(null);
}
});
trackDataHub = TrackDataHub.newInstance(this);
@@ -462,7 +462,7 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
// Update UI
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
updateMenuItems(isRecording);
- resourceCursorAdapter.notifyDataSetChanged();
+ sectionResourceCursorAdapter.notifyDataSetChanged();
trackController.update(isRecording, recordingTrackPaused);
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java
index a01f4a3ef..f3d9ffac2 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java
@@ -161,14 +161,18 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
context, R.string.drive_deleted_list_key, PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT);
String deletedIds[] = TextUtils.split(driveDeletedList, ";");
for (String id : deletedIds) {
- File driveFile = drive.files().get(id).execute();
- if (SyncUtils.isInFolder(driveFile, folderId)) {
- if (!driveFile.getLabels().getTrashed()) {
- drive.files().trash(id).execute();
+ try {
+ File driveFile = drive.files().get(id).execute();
+ if (SyncUtils.isInFolder(driveFile, folderId)) {
+ if (!driveFile.getLabels().getTrashed()) {
+ drive.files().trash(id).execute();
+ }
+ // if trashed, ignore
+ } else if (SyncUtils.isSharedWithMe(driveFile)) {
+ drive.files().delete(id).execute();
}
- // if trashed, ignore
- } else if (SyncUtils.isSharedWithMe(driveFile)) {
- drive.files().delete(id).execute();
+ } catch (IOException e) {
+ // safe to ignore
}
}
PreferencesUtils.setString(