diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4e9e093c..75ef2461 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -125,6 +125,9 @@ THE SOFTWARE.
+
diff --git a/res/layout/playlist_activity.xml b/res/layout/playlist_activity.xml
index 361de5da..0217910a 100644
--- a/res/layout/playlist_activity.xml
+++ b/res/layout/playlist_activity.xml
@@ -22,9 +22,8 @@ THE SOFTWARE.
-->
diff --git a/res/layout/playlist_row.xml b/res/layout/playlist_row.xml
index 8314e361..c38189fd 100644
--- a/res/layout/playlist_row.xml
+++ b/res/layout/playlist_row.xml
@@ -23,7 +23,6 @@ THE SOFTWARE.
diff --git a/res/layout/tab_order.xml b/res/layout/tab_order.xml
new file mode 100644
index 00000000..42103af9
--- /dev/null
+++ b/res/layout/tab_order.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/tab_order_row.xml b/res/layout/tab_order_row.xml
new file mode 100644
index 00000000..3869f5fc
--- /dev/null
+++ b/res/layout/tab_order_row.xml
@@ -0,0 +1,30 @@
+
+
+
diff --git a/res/values/translatable.xml b/res/values/translatable.xml
index f43d3f35..1ddd3cbf 100644
--- a/res/values/translatable.xml
+++ b/res/values/translatable.xml
@@ -185,6 +185,10 @@ THE SOFTWARE.
Scan in progress…Finished scanning. Tap to scan again.
+ Tab Order
+ Adjust the order and visibility of library tabs
+ Restore default
+
Last used action
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index c7afcd83..357c8892 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -113,6 +113,7 @@ THE SOFTWARE.
android:title="@string/controls_in_selector_title"
android:summary="@string/controls_in_selector_summary"
android:defaultValue="false" />
+ = Build.VERSION_CODES.HONEYCOMB) {
- CompatHoneycomb.addActionBarTabs(this);
pager.setOnPageChangeListener(pagerAdapter);
View controls = getLayoutInflater().inflate(R.layout.actionbar_controls, null);
@@ -196,6 +196,7 @@ public class LibraryActivity
TabPageIndicator tabs = new TabPageIndicator(this);
tabs.setViewPager(pager);
tabs.setOnPageChangeListener(pagerAdapter);
+ mTabs = tabs;
LinearLayout content = (LinearLayout)findViewById(R.id.content);
content.addView(tabs, 0, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
@@ -239,11 +240,17 @@ public class LibraryActivity
super.onStart();
SharedPreferences settings = PlaybackService.getSettings(this);
-
if (settings.getBoolean("controls_in_selector", false) != (mControls != null)) {
finish();
startActivity(new Intent(this, LibraryActivity.class));
}
+ if (mPagerAdapter.loadTabOrder()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ CompatHoneycomb.addActionBarTabs(this);
+ } else {
+ mTabs.notifyDataSetChanged();
+ }
+ }
mDefaultAction = Integer.parseInt(settings.getString("default_action_int", "0"));
mLastActedId = LibraryAdapter.INVALID_ID;
updateHeaders();
@@ -273,7 +280,7 @@ public class LibraryActivity
String data = String.format("album_id=%d", albumId);
Limiter limiter = new Limiter(MediaUtils.TYPE_ALBUM, fields, data);
int tab = mPagerAdapter.setLimiter(limiter);
- if (tab == mViewPager.getCurrentItem())
+ if (tab == -1 || tab == mViewPager.getCurrentItem())
updateLimiterViews();
else
mViewPager.setCurrentItem(tab);
@@ -386,7 +393,7 @@ public class LibraryActivity
mode = modeForAction[mode];
QueryTask query = buildQueryFromIntent(intent, false, all);
- PlaybackService.get(this).addSongs(mode, query, intent.getIntExtra("type", -1));
+ PlaybackService.get(this).addSongs(mode, query, intent.getIntExtra("type", MediaUtils.TYPE_INVALID));
mLastActedId = id;
@@ -405,10 +412,10 @@ public class LibraryActivity
*/
private void expand(Intent intent)
{
- int type = intent.getIntExtra("type", 1);
+ int type = intent.getIntExtra("type", MediaUtils.TYPE_INVALID);
long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID);
- int tab = mPagerAdapter.setLimiter(mPagerAdapter.mAdapters[type - 1].buildLimiter(id));
- if (tab == mViewPager.getCurrentItem())
+ int tab = mPagerAdapter.setLimiter(mPagerAdapter.mAdapters[type].buildLimiter(id));
+ if (tab == -1 || tab == mViewPager.getCurrentItem())
updateLimiterViews();
else
mViewPager.setCurrentItem(tab);
@@ -455,7 +462,7 @@ public class LibraryActivity
*/
public void onItemExpanded(Intent rowData)
{
- int type = rowData.getIntExtra(LibraryAdapter.DATA_TYPE, -1);
+ int type = rowData.getIntExtra(LibraryAdapter.DATA_TYPE, MediaUtils.TYPE_INVALID);
if (type == MediaUtils.TYPE_PLAYLIST)
editPlaylist(rowData);
else
@@ -575,7 +582,7 @@ public class LibraryActivity
*/
private QueryTask buildQueryFromIntent(Intent intent, boolean empty, boolean all)
{
- int type = intent.getIntExtra("type", 1);
+ int type = intent.getIntExtra("type", MediaUtils.TYPE_INVALID);
String[] projection;
if (type == MediaUtils.TYPE_PLAYLIST)
@@ -588,7 +595,7 @@ public class LibraryActivity
if (type == MediaUtils.TYPE_FILE) {
query = MediaUtils.buildFileQuery(intent.getStringExtra("file"), projection);
} else if (all || id == LibraryAdapter.HEADER_ID) {
- query = ((MediaAdapter)mPagerAdapter.mAdapters[type - 1]).buildSongQuery(projection);
+ query = ((MediaAdapter)mPagerAdapter.mAdapters[type]).buildSongQuery(projection);
query.setExtra(id);
} else {
query = MediaUtils.buildQuery(type, id, projection, null);
@@ -622,7 +629,7 @@ public class LibraryActivity
menu.add(0, MENU_ENQUEUE_ALL, 0, R.string.enqueue_all).setIntent(rowData);
menu.addSubMenu(0, MENU_ADD_TO_PLAYLIST, 0, R.string.add_to_playlist).getItem().setIntent(rowData);
} else {
- int type = rowData.getIntExtra(LibraryAdapter.DATA_TYPE, -1);
+ int type = rowData.getIntExtra(LibraryAdapter.DATA_TYPE, MediaUtils.TYPE_INVALID);
boolean isAllAdapter = type <= MediaUtils.TYPE_SONG;
menu.setHeaderTitle(rowData.getStringExtra(LibraryAdapter.DATA_TITLE));
@@ -680,8 +687,8 @@ public class LibraryActivity
*/
private void delete(Intent intent)
{
- int type = intent.getIntExtra("type", 1);
- long id = intent.getLongExtra("id", -1);
+ int type = intent.getIntExtra("type", MediaUtils.TYPE_INVALID);
+ long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID);
String message = null;
Resources res = getResources();
@@ -1005,17 +1012,18 @@ public class LibraryActivity
}
/**
- * Called when a new adapter has been made visible.
+ * Called when a new page becomes visible.
*
+ * @param position The position of the new page.
* @param adapter The new visible adapter.
*/
- public void onAdapterSelected(LibraryAdapter adapter)
+ public void onPageChanged(int position, LibraryAdapter adapter)
{
mCurrentAdapter = adapter;
mLastActedId = LibraryAdapter.INVALID_ID;
updateLimiterViews();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- CompatHoneycomb.selectTab(this, mViewPager.getCurrentItem());
+ CompatHoneycomb.selectTab(this, position);
}
}
diff --git a/src/org/kreed/vanilla/LibraryPagerAdapter.java b/src/org/kreed/vanilla/LibraryPagerAdapter.java
index 241c4a32..4a682018 100644
--- a/src/org/kreed/vanilla/LibraryPagerAdapter.java
+++ b/src/org/kreed/vanilla/LibraryPagerAdapter.java
@@ -42,6 +42,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
+import java.util.Arrays;
/**
* PagerAdapter that manages the library media ListViews.
@@ -54,41 +55,64 @@ public class LibraryPagerAdapter
, AdapterView.OnItemClickListener
{
/**
- * The number of adapters/lists (determines array sizes).
+ * The number of unique list types. The number of visible lists may be
+ * smaller.
*/
- private static final int ADAPTER_COUNT = 6;
+ public static final int MAX_ADAPTER_COUNT = 6;
/**
- * The human-readable title for each page.
+ * The human-readable title for each list. The positions correspond to the
+ * MediaUtils ids, so e.g. TITLES[MediaUtils.TYPE_SONG] = R.string.songs
*/
- private static final int[] TITLES = { R.string.artists, R.string.albums, R.string.songs,
- R.string.playlists, R.string.genres, R.string.files };
-
+ public static final int[] TITLES = { R.string.artists, R.string.albums, R.string.songs,
+ R.string.playlists, R.string.genres, R.string.files };
/**
- * The ListView for each adapter, in the same order as MediaUtils.TYPE_*.
+ * Default tab order.
*/
- private final ListView[] mLists = new ListView[ADAPTER_COUNT];
+ public static final int[] DEFAULT_ORDER = { MediaUtils.TYPE_ARTIST, MediaUtils.TYPE_ALBUM, MediaUtils.TYPE_SONG,
+ MediaUtils.TYPE_PLAYLIST, MediaUtils.TYPE_GENRE, MediaUtils.TYPE_FILE };
+ /**
+ * The user-chosen tab order.
+ */
+ int[] mTabOrder;
+ /**
+ * The number of visible tabs.
+ */
+ private int mTabCount;
+ /**
+ * The ListView for each adapter. Each index corresponds to that list's
+ * MediaUtils id.
+ */
+ private final ListView[] mLists = new ListView[MAX_ADAPTER_COUNT];
+ /**
+ * The adapters. Each index corresponds to that adapter's MediaUtils id.
+ */
+ public LibraryAdapter[] mAdapters = new LibraryAdapter[MAX_ADAPTER_COUNT];
/**
* Whether the adapter corresponding to each index has stale data.
*/
- private final boolean[] mRequeryNeeded = new boolean[ADAPTER_COUNT];
+ private final boolean[] mRequeryNeeded = new boolean[MAX_ADAPTER_COUNT];
/**
- * Each adapter, in the same order as MediaUtils.TYPE_*.
+ * The artist adapter instance, also stored at mAdapters[MediaUtils.TYPE_ARTIST].
*/
- public final LibraryAdapter[] mAdapters = new LibraryAdapter[ADAPTER_COUNT];
+ private MediaAdapter mArtistAdapter;
/**
- * The album adapter instance, also stored at mAdapters[1].
+ * The album adapter instance, also stored at mAdapters[MediaUtils.TYPE_ALBUM].
*/
private MediaAdapter mAlbumAdapter;
/**
- * The song adapter instance, also stored at mAdapters[2].
+ * The song adapter instance, also stored at mAdapters[MediaUtils.TYPE_SONG].
*/
private MediaAdapter mSongAdapter;
/**
- * The playlist adapter instance, also stored at mAdapters[3].
+ * The playlist adapter instance, also stored at mAdapters[MediaUtils.TYPE_PLAYLIST].
*/
MediaAdapter mPlaylistAdapter;
/**
- * The file adapter instance, also stored at mAdapters[5].
+ * The genre adapter instance, also stored at mAdapters[MediaUtils.TYPE_GENRE].
+ */
+ private MediaAdapter mGenreAdapter;
+ /**
+ * The file adapter instance, also stored at mAdapters[MediaUtils.TYPE_FILE].
*/
private FileSystemAdapter mFilesAdapter;
/**
@@ -125,7 +149,7 @@ public class LibraryPagerAdapter
*/
private final Handler mUiHandler;
/**
- * A Handler runing on a worker thread.
+ * A Handler running on a worker thread.
*/
private final Handler mWorkerHandler;
/**
@@ -140,6 +164,14 @@ public class LibraryPagerAdapter
* The current filter text, or null if none.
*/
private String mFilter;
+ /**
+ * The position of the songs page, or -1 if it is hidden.
+ */
+ private int mSongsPosition = -1;
+ /**
+ * The position of the albums page, or -1 if it is hidden.
+ */
+ private int mAlbumsPosition = -1;
private final ContentObserver mPlaylistObserver = new ContentObserver(null) {
@Override
@@ -167,10 +199,85 @@ public class LibraryPagerAdapter
activity.getContentResolver().registerContentObserver(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, true, mPlaylistObserver);
}
+ /**
+ * Load the tab order from SharedPreferences.
+ *
+ * @return True if order has changed.
+ */
+ public boolean loadTabOrder()
+ {
+ String in = PlaybackService.getSettings(mActivity).getString("tab_order", null);
+ int[] order;
+ int count;
+ if (in == null || in.length() != MAX_ADAPTER_COUNT) {
+ order = DEFAULT_ORDER;
+ count = MAX_ADAPTER_COUNT;
+ } else {
+ char[] chars = in.toCharArray();
+ order = new int[MAX_ADAPTER_COUNT];
+ count = 0;
+ for (int i = 0; i != MAX_ADAPTER_COUNT; ++i) {
+ char v = chars[i];
+ if (v >= 128) {
+ v -= 128;
+ if (v >= MediaUtils.TYPE_COUNT) {
+ // invalid media type; use default order
+ order = DEFAULT_ORDER;
+ count = MAX_ADAPTER_COUNT;
+ break;
+ }
+ order[count++] = v;
+ }
+ }
+ }
+
+ if (count != mTabCount || !Arrays.equals(order, mTabOrder)) {
+ mTabOrder = order;
+ mTabCount = count;
+ notifyDataSetChanged();
+ computeExpansions();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines whether adapters should be expandable from the visibility of
+ * the adapters each expands to. Also updates mSongsPosition/mAlbumsPositions.
+ */
+ public void computeExpansions()
+ {
+ int[] order = mTabOrder;
+ int songsPosition = -1;
+ int albumsPosition = -1;
+ for (int i = mTabCount; --i != -1; ) {
+ switch (order[i]) {
+ case MediaUtils.TYPE_ALBUM:
+ albumsPosition = i;
+ break;
+ case MediaUtils.TYPE_SONG:
+ songsPosition = i;
+ break;
+ }
+ }
+
+ if (mArtistAdapter != null)
+ mArtistAdapter.setExpandable(songsPosition != -1 || albumsPosition != -1);
+ if (mAlbumAdapter != null)
+ mAlbumAdapter.setExpandable(songsPosition != -1);
+ if (mGenreAdapter != null)
+ mGenreAdapter.setExpandable(songsPosition != -1);
+
+ mSongsPosition = songsPosition;
+ mAlbumsPosition = albumsPosition;
+ }
+
@Override
public Object instantiateItem(ViewGroup container, int position)
{
- ListView view = mLists[position];
+ int type = mTabOrder[position];
+ ListView view = mLists[type];
if (view == null) {
LibraryActivity activity = mActivity;
@@ -178,75 +285,91 @@ public class LibraryPagerAdapter
LibraryAdapter adapter;
TextView header = null;
- switch (position) {
- case 0:
- adapter = new MediaAdapter(activity, MediaUtils.TYPE_ARTIST, null);
+ switch (type) {
+ case MediaUtils.TYPE_ARTIST:
+ adapter = mArtistAdapter = new MediaAdapter(activity, MediaUtils.TYPE_ARTIST, null);
+ mArtistAdapter.setExpandable(mSongsPosition != -1 || mAlbumsPosition != -1);
mArtistHeader = header = (TextView)inflater.inflate(R.layout.library_row, null);
break;
- case 1:
+ case MediaUtils.TYPE_ALBUM:
adapter = mAlbumAdapter = new MediaAdapter(activity, MediaUtils.TYPE_ALBUM, mPendingAlbumLimiter);
+ mAlbumAdapter.setExpandable(mSongsPosition != -1);
mPendingAlbumLimiter = null;
mAlbumHeader = header = (TextView)inflater.inflate(R.layout.library_row, null);
break;
- case 2:
+ case MediaUtils.TYPE_SONG:
adapter = mSongAdapter = new MediaAdapter(activity, MediaUtils.TYPE_SONG, mPendingSongLimiter);
mPendingSongLimiter = null;
mSongHeader = header = (TextView)inflater.inflate(R.layout.library_row, null);
break;
- case 3:
+ case MediaUtils.TYPE_PLAYLIST:
adapter = mPlaylistAdapter = new MediaAdapter(activity, MediaUtils.TYPE_PLAYLIST, null);
break;
- case 4:
- adapter = new MediaAdapter(activity, MediaUtils.TYPE_GENRE, null);
+ case MediaUtils.TYPE_GENRE:
+ adapter = mGenreAdapter = new MediaAdapter(activity, MediaUtils.TYPE_GENRE, null);
+ mGenreAdapter.setExpandable(mSongsPosition != -1);
break;
- case 5:
+ case MediaUtils.TYPE_FILE:
adapter = mFilesAdapter = new FileSystemAdapter(activity, mPendingFileLimiter);
mPendingFileLimiter = null;
break;
default:
- throw new IllegalArgumentException("Invalid position: " + position);
+ throw new IllegalArgumentException("Invalid media type: " + type);
}
view = (ListView)inflater.inflate(R.layout.listview, null);
view.setOnCreateContextMenuListener(this);
view.setOnItemClickListener(this);
+ view.setTag(type);
if (header != null) {
header.setText(mHeaderText);
- header.setTag(position + 1);
+ header.setTag(type);
view.addHeaderView(header);
}
view.setAdapter(adapter);
- if (position != 5)
+ if (type != MediaUtils.TYPE_FILE)
loadSortOrder((MediaAdapter)adapter);
enableFastScroll(view);
adapter.setFilter(mFilter);
- mAdapters[position] = adapter;
- mLists[position] = view;
- mRequeryNeeded[position] = true;
+ mAdapters[type] = adapter;
+ mLists[type] = view;
+ mRequeryNeeded[type] = true;
}
- requeryIfNeeded(position);
+ requeryIfNeeded(type);
container.addView(view);
return view;
}
+ @Override
+ public int getItemPosition(Object item)
+ {
+ int type = (Integer)((ListView)item).getTag();
+ int[] order = mTabOrder;
+ for (int i = mTabCount; --i != -1; ) {
+ if (order[i] == type)
+ return i;
+ }
+ return POSITION_NONE;
+ }
+
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
- container.removeView(mLists[position]);
+ container.removeView((View)object);
}
@Override
public CharSequence getPageTitle(int position)
{
- return mActivity.getResources().getText(TITLES[position]);
+ return mActivity.getResources().getText(TITLES[mTabOrder[position]]);
}
@Override
public int getCount()
{
- return ADAPTER_COUNT;
+ return mTabCount;
}
@Override
@@ -258,12 +381,13 @@ public class LibraryPagerAdapter
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object)
{
- LibraryAdapter adapter = mAdapters[position];
- if (adapter != mCurrentAdapter) {
- requeryIfNeeded(position);
+ int type = mTabOrder[position];
+ LibraryAdapter adapter = mAdapters[type];
+ if (position != mCurrentPage || adapter != mCurrentAdapter) {
+ requeryIfNeeded(type);
mCurrentAdapter = adapter;
mCurrentPage = position;
- mActivity.onAdapterSelected(adapter);
+ mActivity.onPageChanged(position, adapter);
}
}
@@ -288,9 +412,9 @@ public class LibraryPagerAdapter
if (mFilesAdapter != null)
out.putSerializable("limiter_files", mFilesAdapter.getLimiter());
- int[] savedPositions = new int[ADAPTER_COUNT];
+ int[] savedPositions = new int[MAX_ADAPTER_COUNT];
ListView[] lists = mLists;
- for (int i = ADAPTER_COUNT; --i != -1; ) {
+ for (int i = MAX_ADAPTER_COUNT; --i != -1; ) {
if (lists[i] != null) {
savedPositions[i] = lists[i].getFirstVisiblePosition();
}
@@ -350,7 +474,7 @@ public class LibraryPagerAdapter
* Update the adapters with the given limiter.
*
* @param limiter The limiter to set.
- * @return The tab appropriate for expanding a row.
+ * @return The tab type that should be switched to to expand the row.
*/
public int setLimiter(Limiter limiter)
{
@@ -365,7 +489,7 @@ public class LibraryPagerAdapter
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
}
- tab = 2;
+ tab = mSongsPosition;
break;
case MediaUtils.TYPE_ARTIST:
if (mAlbumAdapter == null) {
@@ -382,7 +506,9 @@ public class LibraryPagerAdapter
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
}
- tab = 1;
+ tab = mAlbumsPosition;
+ if (tab == -1)
+ tab = mSongsPosition;
break;
case MediaUtils.TYPE_GENRE:
if (mAlbumAdapter == null) {
@@ -399,7 +525,7 @@ public class LibraryPagerAdapter
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
}
- tab = 2;
+ tab = mSongsPosition;
break;
case MediaUtils.TYPE_FILE:
if (mFilesAdapter == null) {
@@ -408,7 +534,7 @@ public class LibraryPagerAdapter
mFilesAdapter.setLimiter(limiter);
requestRequery(mFilesAdapter);
}
- tab = 5;
+ tab = -1;
break;
default:
throw new IllegalArgumentException("Unsupported limiter type: " + limiter.type);
@@ -461,7 +587,7 @@ public class LibraryPagerAdapter
switch (message.what) {
case MSG_RUN_QUERY: {
LibraryAdapter adapter = (LibraryAdapter)message.obj;
- int index = adapter.getMediaType() - 1;
+ int index = adapter.getMediaType();
Handler handler = mUiHandler;
handler.sendMessage(handler.obtainMessage(MSG_COMMIT_QUERY, index, 0, adapter.query()));
break;
@@ -508,7 +634,7 @@ public class LibraryPagerAdapter
if (adapter == mCurrentAdapter) {
postRunQuery(adapter);
} else {
- mRequeryNeeded[adapter.getMediaType() - 1] = true;
+ mRequeryNeeded[adapter.getMediaType()] = true;
// Clear the data for non-visible adapters (so we don't show the old
// data briefly when we later switch to that adapter)
adapter.clear();
@@ -534,21 +660,21 @@ public class LibraryPagerAdapter
*/
private void postRunQuery(LibraryAdapter adapter)
{
- mRequeryNeeded[adapter.getMediaType() - 1] = false;
+ mRequeryNeeded[adapter.getMediaType()] = false;
Handler handler = mWorkerHandler;
handler.removeMessages(MSG_RUN_QUERY, adapter);
handler.sendMessage(handler.obtainMessage(MSG_RUN_QUERY, adapter));
}
/**
- * Requery the adapter at the given position if it exists and needs a requery.
+ * Requery the adapter of the given type if it exists and needs a requery.
*
- * @param position An index in mAdapters.
+ * @param type One of MediaUtils.TYPE_*
*/
- private void requeryIfNeeded(int position)
+ private void requeryIfNeeded(int type)
{
- LibraryAdapter adapter = mAdapters[position];
- if (adapter != null && mRequeryNeeded[position]) {
+ LibraryAdapter adapter = mAdapters[type];
+ if (adapter != null && mRequeryNeeded[type]) {
postRunQuery(adapter);
}
}
@@ -598,7 +724,7 @@ public class LibraryPagerAdapter
// Force a new FastScroller to be created so the scroll sections
// are updated.
- ListView view = mLists[mCurrentPage];
+ ListView view = mLists[mTabOrder[mCurrentPage]];
view.setFastScrollEnabled(false);
enableFastScroll(view);
diff --git a/src/org/kreed/vanilla/MediaAdapter.java b/src/org/kreed/vanilla/MediaAdapter.java
index f2df67e2..26b0c969 100644
--- a/src/org/kreed/vanilla/MediaAdapter.java
+++ b/src/org/kreed/vanilla/MediaAdapter.java
@@ -36,8 +36,9 @@ import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.CursorAdapter;
+import android.widget.BaseAdapter;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.SectionIndexer;
import android.widget.TextView;
import java.util.regex.Pattern;
@@ -54,7 +55,7 @@ import java.util.regex.Pattern;
* See getLimiter and setLimiter for details.
*/
public class MediaAdapter
- extends CursorAdapter
+ extends BaseAdapter
implements SectionIndexer
, LibraryAdapter
, View.OnClickListener
@@ -69,6 +70,10 @@ public class MediaAdapter
* A LayoutInflater to use.
*/
private final LayoutInflater mInflater;
+ /**
+ * The current data.
+ */
+ private Cursor mCursor;
/**
* The type of media represented by this adapter. Must be one of the
* MediaUtils.FIELD_* constants. Determines which content provider to query for
@@ -132,9 +137,9 @@ public class MediaAdapter
*/
private int mSortMode;
/**
- * The layout used for each row.
+ * If true, show the expander button on each row.
*/
- private int mLayout;
+ private boolean mExpandable;
/**
* Construct a MediaAdapter representing the given type of
@@ -148,8 +153,6 @@ public class MediaAdapter
*/
public MediaAdapter(LibraryActivity activity, int type, Limiter limiter)
{
- super(activity, null, false);
-
mActivity = activity;
mType = type;
mLimiter = limiter;
@@ -164,7 +167,6 @@ public class MediaAdapter
mSongSort = MediaUtils.DEFAULT_SORT;
mSortEntries = new int[] { R.string.name, R.string.number_of_tracks };
mSortValues = new String[] { "artist_key %1$s", "number_of_tracks %1$s,artist_key %1$s" };
- mLayout = R.layout.library_row_expandable;
break;
case MediaUtils.TYPE_ALBUM:
mStore = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
@@ -174,7 +176,6 @@ public class MediaAdapter
mSongSort = "album_key,track";
mSortEntries = new int[] { R.string.name, R.string.artist_album, R.string.year, R.string.number_of_tracks };
mSortValues = new String[] { "album_key %1$s", "artist_key %1$s,album_key %1$s", "minyear %1$s,album_key %1$s", "numsongs %1$s,album_key %1$s" };
- mLayout = R.layout.library_row_expandable;
break;
case MediaUtils.TYPE_SONG:
mStore = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
@@ -182,7 +183,6 @@ public class MediaAdapter
mFieldKeys = new String[] { MediaStore.Audio.Media.ARTIST_KEY, MediaStore.Audio.Media.ALBUM_KEY, MediaStore.Audio.Media.TITLE_KEY };
mSortEntries = new int[] { R.string.name, R.string.artist_album_track, R.string.artist_album_title, R.string.artist_year, R.string.year };
mSortValues = new String[] { "title_key %1$s", "artist_key %1$s,album_key %1$s,track %1$s", "artist_key %1$s,album_key %1$s,title_key %1$s", "artist_key %1$s,year %1$s,track %1$s", "year %1$s,title_key %1$s" };
- mLayout = R.layout.library_row;
break;
case MediaUtils.TYPE_PLAYLIST:
mStore = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
@@ -190,7 +190,7 @@ public class MediaAdapter
mFieldKeys = null;
mSortEntries = new int[] { R.string.name, R.string.date_added };
mSortValues = new String[] { "name %1$s", "date_added %1$s" };
- mLayout = R.layout.library_row_expandable;
+ mExpandable = true;
break;
case MediaUtils.TYPE_GENRE:
mStore = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
@@ -198,7 +198,6 @@ public class MediaAdapter
mFieldKeys = null;
mSortEntries = new int[] { R.string.name };
mSortValues = new String[] { "name %1$s" };
- mLayout = R.layout.library_row_expandable;
break;
default:
throw new IllegalArgumentException("Invalid value for type: " + type);
@@ -210,6 +209,20 @@ public class MediaAdapter
mProjection = new String[] { BaseColumns._ID, mFields[mFields.length - 1], mFields[0] };
}
+ /**
+ * Set whether or not the expander button should be shown in each row.
+ * Defaults to true for playlist adapter and false for all others.
+ *
+ * @param expandable True to show expander, false to hide.
+ */
+ public void setExpandable(boolean expandable)
+ {
+ if (expandable != mExpandable) {
+ mExpandable = expandable;
+ notifyDataSetChanged();
+ }
+ }
+
@Override
public void setFilter(String filter)
{
@@ -359,7 +372,7 @@ public class MediaAdapter
String[] fields;
Object data;
- Cursor cursor = getCursor();
+ Cursor cursor = mCursor;
if (cursor == null)
return null;
for (int i = 0, count = cursor.getCount(); i != count; ++i) {
@@ -388,11 +401,24 @@ public class MediaAdapter
return new Limiter(mType, fields, data);
}
- @Override
+ /**
+ * Set a new cursor for this adapter. The old cursor will be closed.
+ *
+ * @param cursor The new cursor.
+ */
public void changeCursor(Cursor cursor)
{
- super.changeCursor(cursor);
+ Cursor old = mCursor;
+ mCursor = cursor;
+ if (cursor == null) {
+ notifyDataSetInvalidated();
+ } else {
+ notifyDataSetChanged();
+ }
mIndexer.setCursor(cursor);
+ if (old != null) {
+ old.close();
+ }
}
@Override
@@ -432,13 +458,36 @@ public class MediaAdapter
public ImageView arrow;
}
- /**
- * Update the values in the given view.
- */
@Override
- public void bindView(View view, Context context, Cursor cursor)
+ public View getView(int position, View view, ViewGroup parent)
{
- ViewHolder holder = (ViewHolder)view.getTag();
+ ViewHolder holder;
+
+ if (view == null || mExpandable != view instanceof LinearLayout) {
+ // We must create a new view if we're not given a recycle view or
+ // if the recycle view has the wrong layout.
+
+ int layout = mExpandable ? R.layout.library_row_expandable : R.layout.library_row;
+ view = mInflater.inflate(layout, null);
+ holder = new ViewHolder();
+ view.setTag(holder);
+
+ if (mExpandable) {
+ holder.text = (TextView)view.findViewById(R.id.text);
+ holder.arrow = (ImageView)view.findViewById(R.id.arrow);
+ holder.arrow.setOnClickListener(this);
+ } else {
+ holder.text = (TextView)view;
+ view.setLongClickable(true);
+ }
+
+ holder.text.setOnClickListener(this);
+ } else {
+ holder = (ViewHolder)view.getTag();
+ }
+
+ Cursor cursor = mCursor;
+ cursor.moveToPosition(position);
holder.id = cursor.getLong(0);
if (mFields.length > 1) {
String line1 = cursor.getString(1);
@@ -454,29 +503,7 @@ public class MediaAdapter
holder.text.setText(title);
holder.title = title;
}
- }
- /**
- * Generate a new view.
- */
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent)
- {
- int layout = mLayout;
- View view = mInflater.inflate(layout, null);
- ViewHolder holder = new ViewHolder();
- view.setTag(holder);
-
- if (layout == R.layout.library_row_expandable) {
- holder.text = (TextView)view.findViewById(R.id.text);
- holder.arrow = (ImageView)view.findViewById(R.id.arrow);
- holder.arrow.setOnClickListener(this);
- } else {
- holder.text = (TextView)view;
- view.setLongClickable(true);
- }
-
- holder.text.setOnClickListener(this);
return view;
}
@@ -546,7 +573,7 @@ public class MediaAdapter
intent.putExtra(LibraryAdapter.DATA_TYPE, mType);
intent.putExtra(LibraryAdapter.DATA_ID, holder.id);
intent.putExtra(LibraryAdapter.DATA_TITLE, holder.title);
- intent.putExtra(LibraryAdapter.DATA_EXPANDABLE, mType != MediaUtils.TYPE_SONG);
+ intent.putExtra(LibraryAdapter.DATA_EXPANDABLE, mExpandable);
return intent;
}
@@ -554,7 +581,7 @@ public class MediaAdapter
public void onClick(View view)
{
int id = view.getId();
- if (mLayout == R.layout.library_row_expandable)
+ if (mExpandable)
view = (View)view.getParent();
Intent intent = createData(view);
if (id == R.id.arrow) {
@@ -563,4 +590,35 @@ public class MediaAdapter
mActivity.onItemClicked(intent);
}
}
+
+ @Override
+ public int getCount()
+ {
+ Cursor cursor = mCursor;
+ if (cursor == null)
+ return 0;
+ return cursor.getCount();
+ }
+
+ @Override
+ public Object getItem(int position)
+ {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position)
+ {
+ Cursor cursor = mCursor;
+ if (cursor == null)
+ return 0;
+ cursor.moveToPosition(position);
+ return cursor.getLong(0);
+ }
+
+ @Override
+ public boolean hasStableIds()
+ {
+ return true;
+ }
}
diff --git a/src/org/kreed/vanilla/MediaUtils.java b/src/org/kreed/vanilla/MediaUtils.java
index 9992ddd0..3252fc59 100644
--- a/src/org/kreed/vanilla/MediaUtils.java
+++ b/src/org/kreed/vanilla/MediaUtils.java
@@ -42,32 +42,36 @@ public class MediaUtils {
/**
* A special invalid media type.
*/
- public static final int TYPE_INVALID = 0;
+ public static final int TYPE_INVALID = -1;
/**
* Type indicating an id represents an artist.
*/
- public static final int TYPE_ARTIST = 1;
+ public static final int TYPE_ARTIST = 0;
/**
* Type indicating an id represents an album.
*/
- public static final int TYPE_ALBUM = 2;
+ public static final int TYPE_ALBUM = 1;
/**
* Type indicating an id represents a song.
*/
- public static final int TYPE_SONG = 3;
+ public static final int TYPE_SONG = 2;
/**
* Type indicating an id represents a playlist.
*/
- public static final int TYPE_PLAYLIST = 4;
+ public static final int TYPE_PLAYLIST = 3;
/**
* Type indicating ids represent genres.
*/
- public static final int TYPE_GENRE = 5;
+ public static final int TYPE_GENRE = 4;
/**
* Special type for files and folders. Most methods do not accept this type
* since files have no MediaStore id and require special handling.
*/
- public static final int TYPE_FILE = 6;
+ public static final int TYPE_FILE = 5;
+ /**
+ * The number of different valid media types.
+ */
+ public static final int TYPE_COUNT = 6;
/**
* The default sort order for media queries. First artist, then album, then
diff --git a/src/org/kreed/vanilla/PlaylistActivity.java b/src/org/kreed/vanilla/PlaylistActivity.java
index 467a870f..89836f46 100644
--- a/src/org/kreed/vanilla/PlaylistActivity.java
+++ b/src/org/kreed/vanilla/PlaylistActivity.java
@@ -97,7 +97,7 @@ public class PlaylistActivity extends Activity
setContentView(R.layout.playlist_activity);
- DragListView view = (DragListView)findViewById(R.id.playlist);
+ DragListView view = (DragListView)findViewById(R.id.list);
view.setOnItemClickListener(this);
view.setOnCreateContextMenuListener(this);
mListView = view;
@@ -204,11 +204,12 @@ public class PlaylistActivity extends Activity
{
int itemId = item.getItemId();
Intent intent = item.getIntent();
+ int pos = intent.getIntExtra("position", -1);
if (itemId == MENU_REMOVE) {
- mAdapter.remove(intent.getLongExtra("id", -1));
+ mAdapter.remove(pos);
} else {
- performAction(itemId, intent.getIntExtra("position", -1), intent.getLongExtra("audioId", -1));
+ performAction(itemId, pos, intent.getLongExtra("audioId", -1));
}
return true;
diff --git a/src/org/kreed/vanilla/PlaylistAdapter.java b/src/org/kreed/vanilla/PlaylistAdapter.java
index 8e425b22..b72d2946 100644
--- a/src/org/kreed/vanilla/PlaylistAdapter.java
+++ b/src/org/kreed/vanilla/PlaylistAdapter.java
@@ -42,7 +42,7 @@ import android.widget.TextView;
/**
* CursorAdapter backed by MediaStore playlists.
*/
-public class PlaylistAdapter extends CursorAdapter implements Handler.Callback {
+public class PlaylistAdapter extends CursorAdapter implements Handler.Callback, DragListView.DragAdapter {
private static final String[] PROJECTION = new String[] {
MediaStore.Audio.Playlists.Members._ID,
MediaStore.Audio.Playlists.Members.TITLE,
@@ -162,12 +162,7 @@ public class PlaylistAdapter extends CursorAdapter implements Handler.Callback {
return query.runQuery(resolver);
}
- /**
- * Move a song to a new position.
- *
- * @param from The old position.
- * @param to The new position.
- */
+ @Override
public void move(int from, int to)
{
if (from == to)
@@ -221,16 +216,12 @@ public class PlaylistAdapter extends CursorAdapter implements Handler.Callback {
changeCursor(runQuery(resolver));
}
- /**
- * Remove the song with the given id.
- *
- * @param id The MediaStore id of the row to remove.
- */
- public void remove(long id)
+ @Override
+ public void remove(int position)
{
ContentResolver resolver = mContext.getContentResolver();
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", mPlaylistId);
- resolver.delete(ContentUris.withAppendedId(uri, id), null, null);
+ resolver.delete(ContentUris.withAppendedId(uri, getItemId(position)), null, null);
changeCursor(runQuery(resolver));
}
}
diff --git a/src/org/kreed/vanilla/TabOrderActivity.java b/src/org/kreed/vanilla/TabOrderActivity.java
new file mode 100644
index 00000000..17c82146
--- /dev/null
+++ b/src/org/kreed/vanilla/TabOrderActivity.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 Christopher Eby
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.kreed.vanilla;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+
+/**
+ * The preferences activity in which one can change application preferences.
+ */
+public class TabOrderActivity extends Activity implements View.OnClickListener, OnItemClickListener {
+ private TabOrderAdapter mAdapter;
+ private DragListView mList;
+
+ /**
+ * Initialize the activity, loading the preference specifications.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setTitle(R.string.tabs);
+ setContentView(R.layout.tab_order);
+
+ mAdapter = new TabOrderAdapter(this);
+ DragListView list = (DragListView)findViewById(R.id.list);
+ list.setAdapter(mAdapter);
+ list.setEditable(true);
+ list.setOnItemClickListener(this);
+ mList = list;
+ load();
+
+ findViewById(R.id.done).setOnClickListener(this);
+ findViewById(R.id.restore_default).setOnClickListener(this);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onClick(View view)
+ {
+ switch (view.getId()) {
+ case R.id.done:
+ finish();
+ break;
+ case R.id.restore_default:
+ restoreDefault();
+ break;
+ }
+ }
+
+ /**
+ * Restore the default tab order and visibility.
+ */
+ public void restoreDefault()
+ {
+ android.util.Log.i("VanillaMusic", "restore default");
+ mAdapter.setTabIds(LibraryPagerAdapter.DEFAULT_ORDER.clone());
+ DragListView list = mList;
+ for (int i = 0; i != LibraryPagerAdapter.MAX_ADAPTER_COUNT; ++i) {
+ list.setItemChecked(i, true);
+ }
+ save();
+ }
+
+ /**
+ * Save tab order and visibility to SharedPreferences as a string.
+ */
+ public void save()
+ {
+ int[] ids = mAdapter.getTabIds();
+ DragListView list = mList;
+ char[] out = new char[LibraryPagerAdapter.MAX_ADAPTER_COUNT];
+ for (int i = 0; i != LibraryPagerAdapter.MAX_ADAPTER_COUNT; ++i) {
+ out[i] = (char)(list.isItemChecked(i) ? 128 + ids[i] : 127 - ids[i]);
+ }
+
+ SharedPreferences.Editor editor = PlaybackService.getSettings(this).edit();
+ editor.putString("tab_order", new String(out));
+ editor.commit();
+ }
+
+ /**
+ * Load tab order settings from SharedPreferences and apply it to the
+ * activity.
+ */
+ public void load()
+ {
+ String in = PlaybackService.getSettings(this).getString("tab_order", null);
+ if (in != null && in.length() == LibraryPagerAdapter.MAX_ADAPTER_COUNT) {
+ char[] chars = in.toCharArray();
+ int[] ids = new int[LibraryPagerAdapter.MAX_ADAPTER_COUNT];
+ for (int i = 0; i != LibraryPagerAdapter.MAX_ADAPTER_COUNT; ++i) {
+ int v = chars[i];
+ v = v < 128 ? -(v - 127) : v - 128;
+ if (v >= MediaUtils.TYPE_COUNT) {
+ ids = null;
+ break;
+ }
+ ids[i] = v;
+ }
+
+ if (ids != null) {
+ mAdapter.setTabIds(ids);
+ DragListView list = mList;
+ for (int i = 0; i != LibraryPagerAdapter.MAX_ADAPTER_COUNT; ++i) {
+ list.setItemChecked(i, chars[i] >= 128);
+ }
+ }
+
+ return;
+ }
+
+ restoreDefault();
+ }
+
+ @Override
+ public void onItemClick(AdapterView> arg0, View arg1, int arg2, long arg3)
+ {
+ save();
+ }
+}
diff --git a/src/org/kreed/vanilla/TabOrderAdapter.java b/src/org/kreed/vanilla/TabOrderAdapter.java
new file mode 100644
index 00000000..12871b0c
--- /dev/null
+++ b/src/org/kreed/vanilla/TabOrderAdapter.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 Christopher Eby
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.kreed.vanilla;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+/**
+ * CursorAdapter backed by MediaStore playlists.
+ */
+public class TabOrderAdapter extends BaseAdapter implements DragListView.DragAdapter {
+ private final TabOrderActivity mActivity;
+ private final LayoutInflater mInflater;
+ private int[] mTabIds;
+
+ /**
+ * Create a tab order adapter.
+ *
+ * @param activity The activity that will own this adapter. The activity
+ * will be notified when items have been moved.
+ */
+ public TabOrderAdapter(TabOrderActivity activity)
+ {
+ mActivity = activity;
+ mInflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ /**
+ * Set the array containing the order of tab ids.
+ */
+ public void setTabIds(int[] ids)
+ {
+ mTabIds = ids;
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Returns the array containing the order of tab ids. Do not modify the array.
+ */
+ public int[] getTabIds()
+ {
+ return mTabIds;
+ }
+
+ @Override
+ public void move(int from, int to)
+ {
+ if (from == to)
+ return;
+
+ int[] ids = mTabIds;
+ int tempId = ids[from];
+
+ if (from > to) {
+ System.arraycopy(ids, to, ids, to + 1, from - to);
+ } else {
+ System.arraycopy(ids, from + 1, ids, from, to - from);
+ }
+
+ ids[to] = tempId;
+ notifyDataSetChanged();
+ mActivity.save();
+ }
+
+ @Override
+ public void remove(int position)
+ {
+ // not implemented
+ }
+
+ @Override
+ public int getCount()
+ {
+ return LibraryPagerAdapter.MAX_ADAPTER_COUNT;
+ }
+
+ @Override
+ public Object getItem(int position)
+ {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position)
+ {
+ return mTabIds[position];
+ }
+
+ @Override
+ public View getView(int position, View convert, ViewGroup parent)
+ {
+ DragTextView text;
+ if (convert == null) {
+ text = (DragTextView)mInflater.inflate(R.layout.tab_order_row, null);
+ } else {
+ text = (DragTextView)convert;
+ }
+ text.setText(LibraryPagerAdapter.TITLES[mTabIds[position]]);
+ return text;
+ }
+
+ @Override
+ public boolean hasStableIds()
+ {
+ return true;
+ }
+}
diff --git a/src/org/kreed/vanilla/TabOrderPreference.java b/src/org/kreed/vanilla/TabOrderPreference.java
new file mode 100644
index 00000000..8396cae3
--- /dev/null
+++ b/src/org/kreed/vanilla/TabOrderPreference.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 Christopher Eby
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.kreed.vanilla;
+
+import android.content.Context;
+import android.content.Intent;
+import android.preference.Preference;
+import android.util.AttributeSet;
+
+/**
+ * A preference that opens the tab order selector.
+ */
+public class TabOrderPreference extends Preference {
+ public TabOrderPreference(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ setTitle(R.string.tabs);
+ setSummary(R.string.customize_tab_order);
+ }
+
+ @Override
+ public void onClick()
+ {
+ Context context = getContext();
+ context.startActivity(new Intent(context, TabOrderActivity.class));
+ }
+}