Swipey tabs

This commit is contained in:
Christopher Eby 2012-01-08 07:29:33 -06:00
parent 38d5623ed2
commit ea19c4f8d6
57 changed files with 577 additions and 293 deletions

BIN
libs/android-support-v4.jar Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/ic_tab_albums_selected" />
<item
android:drawable="@drawable/ic_tab_albums_unselected" />
</selector>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/ic_tab_artists_selected" />
<item
android:drawable="@drawable/ic_tab_artists_unselected" />
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_tab_files_selected" android:state_selected="true" android:state_pressed="false" />
<item android:drawable="@drawable/ic_tab_files_unselected" />
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_tab_genres_selected" android:state_selected="true" android:state_pressed="false" />
<item android:drawable="@drawable/ic_tab_genres_unselected" />
</selector>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/ic_tab_playlists_selected" />
<item
android:drawable="@drawable/ic_tab_playlists_unselected" />
</selector>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/ic_tab_songs_selected" />
<item
android:drawable="@drawable/ic_tab_songs_unselected" />
</selector>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Non focused states -->
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/vpi__tab_unselected_holo" />
<item android:state_focused="false" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/vpi__tab_selected_holo" />
<!-- Focused states -->
<item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/vpi__tab_unselected_focused_holo" />
<item android:state_focused="true" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/vpi__tab_selected_focused_holo" />
<!-- Pressed -->
<!-- Non focused states -->
<item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/vpi__tab_unselected_pressed_holo" />
<item android:state_focused="false" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/vpi__tab_selected_pressed_holo" />
<!-- Focused states -->
<item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/vpi__tab_unselected_pressed_holo" />
<item android:state_focused="true" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/vpi__tab_selected_pressed_holo" />
</selector>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2010 Christopher Eby <kreed@kreed.org>
Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -20,91 +20,56 @@ 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.
-->
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tab_host"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<LinearLayout
android:id="@+id/content"
android:orientation="vertical"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.viewpagerindicator.TabPageIndicator
android:id="@+id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1">
<ListView
android:id="@+id/artist_list"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<ListView
android:id="@+id/album_list"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<ListView
android:id="@+id/song_list"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<ListView
android:id="@+id/playlist_list"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<ListView
android:id="@+id/genre_list"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<ListView
android:id="@+id/file_list"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
<HorizontalScrollView
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1" />
<HorizontalScrollView
android:id="@+id/limiter_scroller"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left">
<LinearLayout
android:id="@+id/limiter_layout"
android:layout_width="wrap_content"
android:layout_height="fill_parent" />
</HorizontalScrollView>
<LinearLayout
android:id="@+id/search_box"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingTop="4dip"
android:paddingLeft="4dip"
android:visibility="gone"
android:background="@drawable/search_background">
<EditText
android:id="@+id/filter_text"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left">
<LinearLayout
android:id="@+id/limiter_layout"
android:layout_width="wrap_content"
android:layout_height="fill_parent" />
</HorizontalScrollView>
<LinearLayout
android:id="@+id/search_box"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingTop="4dip"
android:layout_weight="1"
android:inputType="textFilter" />
<ImageButton
android:id="@+id/clear_button"
android:scaleType="fitCenter"
android:paddingLeft="4dip"
android:visibility="gone"
android:background="@drawable/search_background">
<EditText
android:id="@+id/filter_text"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textFilter" />
<ImageButton
android:id="@+id/clear_button"
android:scaleType="fitCenter"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:paddingBottom="4dip"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@null"
android:src="@drawable/close" />
</LinearLayout>
android:paddingRight="4dip"
android:paddingBottom="4dip"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@null"
android:src="@drawable/close" />
</LinearLayout>
</TabHost>
</LinearLayout>

View File

@ -0,0 +1,205 @@
/*
* Copyright (C) 2011 The Android Open Source Project
* Copyright (C) 2011 Jake Wharton
*
* 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.viewpagerindicator;
import android.content.Context;
import android.graphics.Typeface;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.kreed.vanilla.R;
/**
* This widget implements the dynamic action bar tab behavior that can change
* across different configurations or circumstances.
*/
public class TabPageIndicator extends HorizontalScrollView
implements ViewPager.OnPageChangeListener
, View.OnClickListener
{
Runnable mTabSelector;
private final LinearLayout mTabLayout;
private ViewPager mViewPager;
private ViewPager.OnPageChangeListener mListener;
private int mSelectedTabIndex;
public TabPageIndicator(Context context) {
this(context, null);
}
public TabPageIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
setHorizontalScrollBarEnabled(false);
mTabLayout = new LinearLayout(getContext());
addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.FILL_PARENT));
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
setFillViewport(lockedExpanded);
final int oldWidth = getMeasuredWidth();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int newWidth = getMeasuredWidth();
if (lockedExpanded && oldWidth != newWidth) {
// Recenter the tab display if we're at a new (scrollable) size.
setCurrentItem(mSelectedTabIndex);
}
}
private void animateToTab(final int position) {
final View tabView = mTabLayout.getChildAt(position);
if (mTabSelector != null) {
removeCallbacks(mTabSelector);
}
mTabSelector = new Runnable() {
@Override
public void run() {
final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
smoothScrollTo(scrollPos, 0);
mTabSelector = null;
}
};
post(mTabSelector);
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
if (mTabSelector != null) {
// Re-post the selector we saved
post(mTabSelector);
}
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mTabSelector != null) {
removeCallbacks(mTabSelector);
}
}
private void addTab(CharSequence text, int index) {
Context context = getContext();
DisplayMetrics dm = context.getResources().getDisplayMetrics();
int padX = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 22, dm);
int padY = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, dm);
TextView tabView = new TextView(context);
tabView.setBackgroundResource(R.drawable.vpi__tab_indicator);
tabView.setPadding(padX, padY, padX, padY);
tabView.setGravity(Gravity.CENTER);
tabView.setTextColor(0xfff3f3f3);
tabView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
tabView.setTypeface(tabView.getTypeface(), Typeface.BOLD);
tabView.setSingleLine();
tabView.setTag(index);
tabView.setFocusable(true);
tabView.setOnClickListener(this);
tabView.setText(text);
mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, LayoutParams.FILL_PARENT, 1));
}
@Override
public void onPageScrollStateChanged(int arg0) {
if (mListener != null) {
mListener.onPageScrollStateChanged(arg0);
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
if (mListener != null) {
mListener.onPageScrolled(arg0, arg1, arg2);
}
}
@Override
public void onPageSelected(int arg0) {
setCurrentItem(arg0);
if (mListener != null) {
mListener.onPageSelected(arg0);
}
}
public void setViewPager(ViewPager view) {
final PagerAdapter adapter = view.getAdapter();
if (adapter == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
view.setOnPageChangeListener(this);
notifyDataSetChanged();
}
public void notifyDataSetChanged() {
mTabLayout.removeAllViews();
PagerAdapter adapter = mViewPager.getAdapter();
final int count = adapter.getCount();
for (int i = 0; i < count; i++) {
addTab(adapter.getPageTitle(i), i);
}
if (mSelectedTabIndex > count) {
mSelectedTabIndex = count - 1;
}
setCurrentItem(mSelectedTabIndex);
requestLayout();
}
public void setCurrentItem(int item) {
if (mViewPager == null) {
throw new IllegalStateException("ViewPager has not been bound.");
}
mSelectedTabIndex = item;
final int tabCount = mTabLayout.getChildCount();
for (int i = 0; i < tabCount; i++) {
final View child = mTabLayout.getChildAt(i);
final boolean isSelected = (i == item);
child.setSelected(isSelected);
if (isSelected) {
animateToTab(item);
}
}
}
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
mListener = listener;
}
@Override
public void onClick(View view) {
mViewPager.setCurrentItem((Integer)view.getTag());
}
}

View File

@ -90,15 +90,9 @@ public final class CoverBitmap {
private static void loadIcons(Context context)
{
Resources res = context.getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.ic_tab_songs_selected);
SONG_ICON = Bitmap.createScaledBitmap(bitmap, TEXT_SIZE, TEXT_SIZE, false);
bitmap.recycle();
bitmap = BitmapFactory.decodeResource(res, R.drawable.ic_tab_albums_selected);
ALBUM_ICON = Bitmap.createScaledBitmap(bitmap, TEXT_SIZE, TEXT_SIZE, false);
bitmap.recycle();
bitmap = BitmapFactory.decodeResource(res, R.drawable.ic_tab_artists_selected);
ARTIST_ICON = Bitmap.createScaledBitmap(bitmap, TEXT_SIZE, TEXT_SIZE, false);
bitmap.recycle();
SONG_ICON = BitmapFactory.decodeResource(res, R.drawable.ic_tab_songs_selected);
ALBUM_ICON = BitmapFactory.decodeResource(res, R.drawable.ic_tab_albums_selected);
ARTIST_ICON = BitmapFactory.decodeResource(res, R.drawable.ic_tab_artists_selected);
}
/**

View File

@ -32,14 +32,14 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@ -55,12 +55,13 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;
import com.viewpagerindicator.TabPageIndicator;
import java.io.File;
import java.io.IOException;
import junit.framework.Assert;
@ -72,9 +73,9 @@ public class LibraryActivity
extends PlaybackActivity
implements AdapterView.OnItemClickListener
, TextWatcher
, TabHost.OnTabChangeListener
, DialogInterface.OnClickListener
, DialogInterface.OnDismissListener
, ViewPager.OnPageChangeListener
{
public static final int ACTION_PLAY = 0;
public static final int ACTION_ENQUEUE = 1;
@ -87,8 +88,7 @@ public class LibraryActivity
{ SongTimeline.MODE_PLAY, SongTimeline.MODE_ENQUEUE, -1,
SongTimeline.MODE_PLAY_ID_FIRST, SongTimeline.MODE_ENQUEUE_ID_FIRST };
private TabHost mTabHost;
private ViewPager mViewPager;
private View mSearchBox;
private boolean mSearchBoxVisible;
private TextView mTextFilter;
@ -100,6 +100,7 @@ public class LibraryActivity
private ImageView mCover;
private View mEmptyQueue;
private HorizontalScrollView mLimiterScroller;
private ViewGroup mLimiterViews;
private int mDefaultAction;
@ -123,6 +124,15 @@ public class LibraryActivity
* Each adapter, in the same order as MediaUtils.TYPE_*.
*/
final LibraryAdapter[] mAdapters = new LibraryAdapter[ADAPTER_COUNT];
/**
* The human-readable title for each page.
*/
public static final int[] TITLES = { R.string.artists, R.string.albums, R.string.songs,
R.string.playlists, R.string.genres, R.string.files };
/**
* Limiters that should be passed to an adapter constructor.
*/
public final Limiter[] mPendingLimiters = new Limiter[ADAPTER_COUNT];
MediaAdapter mArtistAdapter;
MediaAdapter mAlbumAdapter;
MediaAdapter mSongAdapter;
@ -135,7 +145,103 @@ public class LibraryActivity
@Override
public void onChange(boolean selfChange)
{
postRequestRequery(mPlaylistAdapter);
if (mPlaylistAdapter != null) {
postRequestRequery(mPlaylistAdapter);
}
}
};
private final PagerAdapter mPagerAdapter = new PagerAdapter() {
@Override
public Object instantiateItem(ViewGroup container, int position)
{
ListView view = mLists[position];
if (view == null) {
LibraryAdapter adapter;
switch (position) {
case 0:
adapter = mArtistAdapter = new MediaAdapter(LibraryActivity.this, MediaUtils.TYPE_ARTIST, true, true, null);
mArtistAdapter.setHeaderText(getHeaderText());
break;
case 1:
adapter = mAlbumAdapter = new MediaAdapter(LibraryActivity.this, MediaUtils.TYPE_ALBUM, true, true, mPendingLimiters[position]);
mAlbumAdapter.setHeaderText(getHeaderText());
break;
case 2:
adapter = mSongAdapter = new MediaAdapter(LibraryActivity.this, MediaUtils.TYPE_SONG, false, true, mPendingLimiters[position]);
mSongAdapter.setHeaderText(getHeaderText());
break;
case 3:
adapter = mPlaylistAdapter = new MediaAdapter(LibraryActivity.this, MediaUtils.TYPE_PLAYLIST, true, false, null);
break;
case 4:
adapter = mGenreAdapter = new MediaAdapter(LibraryActivity.this, MediaUtils.TYPE_GENRE, true, false, null);
break;
case 5:
adapter = mFilesAdapter = new FileSystemAdapter(LibraryActivity.this, mPendingLimiters[position]);
break;
default:
throw new IllegalArgumentException("Invalid position: " + position);
}
view = new ListView(LibraryActivity.this);
view.setOnItemClickListener(LibraryActivity.this);
view.setOnCreateContextMenuListener(LibraryActivity.this);
view.setDivider(null);
view.setFastScrollEnabled(true);
view.setAdapter(adapter);
if (position != 5)
loadSortOrder((MediaAdapter)adapter);
String filter = mTextFilter.getText().toString();
if (filter.length() != 0) {
adapter.setFilter(filter);
}
mAdapters[position] = adapter;
mLists[position] = view;
mRequeryNeeded[position] = true;
if (mCurrentAdapter == null && mViewPager.getCurrentItem() == position) {
// Special case: for first time initialization onPageSelected
// is not called (for initial position = 0) or called before
// the adapter is initialized (for all other initial positions).
// So do required work here.
mCurrentAdapter = adapter;
updateLimiterViews();
}
}
requeryIfNeeded(position);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
container.removeView(mLists[position]);
}
@Override
public CharSequence getPageTitle(int position)
{
return getResources().getText(TITLES[position]);
}
@Override
public int getCount()
{
return ADAPTER_COUNT;
}
@Override
public boolean isViewFromObject(View view, Object object)
{
return view == object;
}
};
@ -185,44 +291,18 @@ public class LibraryActivity
mClearButton = findViewById(R.id.clear_button);
mClearButton.setOnClickListener(this);
mLimiterScroller = (HorizontalScrollView)findViewById(R.id.limiter_scroller);
mLimiterViews = (ViewGroup)findViewById(R.id.limiter_layout);
mTabHost = (TabHost)findViewById(R.id.tab_host);
mTabHost.setup();
mViewPager = (ViewPager)findViewById(R.id.pager);
mViewPager.setAdapter(mPagerAdapter);
mArtistAdapter = new MediaAdapter(this, MediaUtils.TYPE_ARTIST, true, true, null);
mAlbumAdapter = new MediaAdapter(this, MediaUtils.TYPE_ALBUM, true, true, state == null ? null : (Limiter)state.getSerializable("limiter_albums"));
mSongAdapter = new MediaAdapter(this, MediaUtils.TYPE_SONG, false, true, state == null ? null : (Limiter)state.getSerializable("limiter_songs"));
mPlaylistAdapter = new MediaAdapter(this, MediaUtils.TYPE_PLAYLIST, true, false, null);
mGenreAdapter = new MediaAdapter(this, MediaUtils.TYPE_GENRE, true, false, null);
mFilesAdapter = new FileSystemAdapter(this, state == null ? null : (Limiter)state.getSerializable("limiter_files"));
setupView(0, R.id.artist_list, R.string.artists, R.drawable.ic_tab_artists, mArtistAdapter);
setupView(1, R.id.album_list, R.string.albums, R.drawable.ic_tab_albums, mAlbumAdapter);
setupView(2, R.id.song_list, R.string.songs, R.drawable.ic_tab_songs, mSongAdapter);
setupView(3, R.id.playlist_list, R.string.playlists, R.drawable.ic_tab_playlists, mPlaylistAdapter);
setupView(4, R.id.genre_list, R.string.genres, R.drawable.ic_tab_genres, mGenreAdapter);
setupView(5, R.id.file_list, R.string.files, R.drawable.ic_tab_files, mFilesAdapter);
TabPageIndicator tabs = (TabPageIndicator)findViewById(R.id.tabs);
tabs.setViewPager(mViewPager);
tabs.setOnPageChangeListener(this);
getContentResolver().registerContentObserver(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, true, mPlaylistObserver);
int currentTab = 0;
String filter = null;
if (state != null) {
if (state.getBoolean("search_box_visible"))
setSearchBoxVisible(true);
currentTab = state.getInt("current_tab", 0);
filter = state.getString("filter");
}
mTabHost.setCurrentTab(currentTab);
mTabHost.setOnTabChangedListener(this);
onTabChanged(null);
if (filter != null)
mTextFilter.setText(filter);
onNewIntent(getIntent());
}
@ -256,15 +336,29 @@ public class LibraryActivity
}
}
@Override
public void onRestoreInstanceState(Bundle in)
{
super.onRestoreInstanceState(in);
if (in.getBoolean("search_box_visible"))
setSearchBoxVisible(true);
mPendingLimiters[1] = (Limiter)in.getSerializable("limiter_albums");
mPendingLimiters[2] = (Limiter)in.getSerializable("limiter_songs");
mPendingLimiters[5] = (Limiter)in.getSerializable("limiter_files");
}
@Override
protected void onSaveInstanceState(Bundle out)
{
super.onSaveInstanceState(out);
out.putBoolean("search_box_visible", mSearchBoxVisible);
out.putInt("current_tab", mTabHost.getCurrentTab());
out.putString("filter", mTextFilter.getText().toString());
out.putSerializable("limiter_albums", mAlbumAdapter.getLimiter());
out.putSerializable("limiter_songs", mSongAdapter.getLimiter());
out.putSerializable("limiter_files", mFilesAdapter.getLimiter());
if (mAlbumAdapter != null)
out.putSerializable("limiter_albums", mAlbumAdapter.getLimiter());
if (mSongAdapter != null)
out.putSerializable("limiter_songs", mSongAdapter.getLimiter());
if (mFilesAdapter != null)
out.putSerializable("limiter_files", mFilesAdapter.getLimiter());
}
@Override
@ -306,21 +400,31 @@ public class LibraryActivity
return false;
}
/**
* Returns either "Play All" or "Enqueue All", depending on the current
* default action.
*/
private String getHeaderText()
{
int action = mDefaultAction;
if (action == ACTION_LAST_USED)
action = mLastAction;
return getString(action == ACTION_ENQUEUE || action == ACTION_ENQUEUE_ALL ? R.string.enqueue_all : R.string.play_all);
}
/**
* Update the first row of the lists with the appropriate action (play all
* or enqueue all).
*/
private void updateHeaders()
{
int action = mDefaultAction;
if (action == ACTION_LAST_USED)
action = mLastAction;
int res = action == ACTION_ENQUEUE || action == ACTION_ENQUEUE_ALL ? R.string.enqueue_all : R.string.play_all;
String text = getString(res);
mArtistAdapter.setHeaderText(text);
mAlbumAdapter.setHeaderText(text);
mSongAdapter.setHeaderText(text);
String text = getHeaderText();
if (mArtistAdapter != null)
mArtistAdapter.setHeaderText(text);
if (mAlbumAdapter != null)
mAlbumAdapter.setHeaderText(text);
if (mSongAdapter != null)
mSongAdapter.setHeaderText(text);
}
/**
@ -386,15 +490,27 @@ public class LibraryActivity
private void clearLimiter(int type)
{
if (type == MediaUtils.TYPE_FILE) {
mFilesAdapter.setLimiter(null);
requestRequery(mFilesAdapter);
if (mFilesAdapter == null) {
mPendingLimiters[5] = null;
} else {
mFilesAdapter.setLimiter(null);
requestRequery(mFilesAdapter);
}
} else {
mAlbumAdapter.setLimiter(null);
mSongAdapter.setLimiter(null);
loadSortOrder(mSongAdapter);
loadSortOrder(mAlbumAdapter);
requestRequery(mSongAdapter);
requestRequery(mAlbumAdapter);
if (mAlbumAdapter == null) {
mPendingLimiters[1] = null;
} else {
mAlbumAdapter.setLimiter(null);
loadSortOrder(mAlbumAdapter);
requestRequery(mAlbumAdapter);
}
if (mSongAdapter == null) {
mPendingLimiters[2] = null;
} else {
mSongAdapter.setLimiter(null);
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
}
}
updateLimiterViews();
}
@ -411,40 +527,64 @@ public class LibraryActivity
switch (limiter.type) {
case MediaUtils.TYPE_ALBUM:
mSongAdapter.setLimiter(limiter);
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
if (mSongAdapter == null) {
mPendingLimiters[2] = limiter;
} else {
mSongAdapter.setLimiter(limiter);
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
}
tab = 2;
break;
case MediaUtils.TYPE_ARTIST:
mAlbumAdapter.setLimiter(limiter);
mSongAdapter.setLimiter(limiter);
loadSortOrder(mSongAdapter);
loadSortOrder(mAlbumAdapter);
requestRequery(mSongAdapter);
requestRequery(mAlbumAdapter);
if (mAlbumAdapter == null) {
mPendingLimiters[1] = limiter;
} else {
mAlbumAdapter.setLimiter(limiter);
loadSortOrder(mAlbumAdapter);
requestRequery(mAlbumAdapter);
}
if (mSongAdapter == null) {
mPendingLimiters[2] = limiter;
} else {
mSongAdapter.setLimiter(limiter);
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
}
tab = 1;
break;
case MediaUtils.TYPE_GENRE:
mSongAdapter.setLimiter(limiter);
mAlbumAdapter.setLimiter(null);
loadSortOrder(mSongAdapter);
loadSortOrder(mAlbumAdapter);
requestRequery(mSongAdapter);
requestRequery(mAlbumAdapter);
if (mAlbumAdapter == null) {
mPendingLimiters[1] = limiter;
} else {
mAlbumAdapter.setLimiter(limiter);
loadSortOrder(mAlbumAdapter);
requestRequery(mAlbumAdapter);
}
if (mSongAdapter == null) {
mPendingLimiters[2] = null;
} else {
mSongAdapter.setLimiter(limiter);
loadSortOrder(mSongAdapter);
requestRequery(mSongAdapter);
}
tab = 2;
break;
case MediaUtils.TYPE_FILE:
mFilesAdapter.setLimiter(limiter);
requestRequery(mFilesAdapter);
if (mFilesAdapter == null) {
mPendingLimiters[5] = limiter;
} else {
mFilesAdapter.setLimiter(limiter);
requestRequery(mFilesAdapter);
}
tab = 5;
break;
default:
throw new IllegalArgumentException("Unsupported limiter type: " + limiter.type);
}
if (switchTab && mTabHost.getCurrentTab() != tab) {
mTabHost.setCurrentTab(tab);
if (switchTab && mViewPager.getCurrentItem() != tab) {
mViewPager.setCurrentItem(tab);
} else {
updateLimiterViews();
}
@ -499,44 +639,44 @@ public class LibraryActivity
{
String filter = text.toString();
for (LibraryAdapter adapter : mAdapters) {
adapter.setFilter(filter);
requestRequery(adapter);
if (adapter != null) {
adapter.setFilter(filter);
requestRequery(adapter);
}
}
}
private void updateLimiterViews()
{
if (mLimiterViews == null)
return;
mLimiterViews.removeAllViews();
LibraryAdapter adapter = mCurrentAdapter;
if (adapter == null)
return;
Limiter limiterData;
if (adapter != null && (limiterData = adapter.getLimiter()) != null) {
String[] limiter = limiterData.names;
Limiter limiterData = adapter.getLimiter();
if (limiterData == null)
return;
String[] limiter = limiterData.names;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.leftMargin = 5;
for (int i = 0; i != limiter.length; ++i) {
PaintDrawable background = new PaintDrawable(Color.GRAY);
background.setCornerRadius(5);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.leftMargin = 5;
for (int i = 0; i != limiter.length; ++i) {
PaintDrawable background = new PaintDrawable(Color.GRAY);
background.setCornerRadius(5);
TextView view = new TextView(this);
view.setSingleLine();
view.setEllipsize(TextUtils.TruncateAt.MARQUEE);
view.setText(limiter[i] + " | X");
view.setTextColor(Color.WHITE);
view.setBackgroundDrawable(background);
view.setLayoutParams(params);
view.setPadding(5, 2, 5, 2);
view.setTag(i);
view.setOnClickListener(this);
mLimiterViews.addView(view);
}
TextView view = new TextView(this);
view.setSingleLine();
view.setEllipsize(TextUtils.TruncateAt.MARQUEE);
view.setText(limiter[i] + " | X");
view.setTextColor(Color.WHITE);
view.setBackgroundDrawable(background);
view.setLayoutParams(params);
view.setPadding(5, 2, 5, 2);
view.setTag(i);
view.setOnClickListener(this);
mLimiterViews.addView(view);
mLimiterScroller.setVisibility(View.VISIBLE);
} else {
mLimiterScroller.setVisibility(View.GONE);
}
}
@ -891,40 +1031,6 @@ public class LibraryActivity
}
}
/**
* Hook up a ListView to this Activity and the supplied adapter
*
* @param index Where to put the view and adapter in mLists/mAdapters.
* @param id The id of the ListView
* @param adapter The adapter to hook up.
* @param label The text to show on the tab.
* @param icon The icon to show on the tab.
*/
private void setupView(int index, int id, int label, int icon, LibraryAdapter adapter)
{
ListView view = (ListView)findViewById(id);
view.setOnItemClickListener(this);
view.setOnCreateContextMenuListener(this);
view.setDivider(null);
view.setFastScrollEnabled(true);
view.setAdapter(adapter);
if (adapter instanceof MediaAdapter)
loadSortOrder((MediaAdapter)adapter);
Resources res = getResources();
String labelRes = res.getString(label);
Drawable iconRes = res.getDrawable(icon);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
mTabHost.addTab(mTabHost.newTabSpec(labelRes).setIndicator(null, iconRes).setContent(id));
else
mTabHost.addTab(mTabHost.newTabSpec(labelRes).setIndicator(labelRes, iconRes).setContent(id));
mAdapters[index] = adapter;
mLists[index] = view;
mRequeryNeeded[index] = true;
}
/**
* Call addToPlaylist with the results from a NewPlaylistDialog stored in
* obj.
@ -1055,7 +1161,9 @@ public class LibraryActivity
public void onMediaChange()
{
for (LibraryAdapter adapter : mAdapters) {
postRequestRequery(adapter);
if (adapter != null) {
postRequestRequery(adapter);
}
}
}
@ -1124,17 +1232,6 @@ public class LibraryActivity
}
}
@Override
public void onTabChanged(String tag)
{
LibraryAdapter adapter = mAdapters[mTabHost.getCurrentTab()];
mCurrentAdapter = adapter;
if (mRequeryNeeded[adapter.getMediaType() - 1]) {
runQuery(adapter);
}
updateLimiterViews();
}
/**
* Set the saved sort mode for the given adapter. The adapter should
* be re-queried after calling this.
@ -1173,10 +1270,45 @@ public class LibraryActivity
// Force a new FastScroller to be created so the scroll sections
// are updated.
ListView view = (ListView)mTabHost.getCurrentView();
ListView view = mLists[mViewPager.getCurrentItem()];
view.setFastScrollEnabled(false);
view.setFastScrollEnabled(true);
mHandler.sendMessage(mHandler.obtainMessage(MSG_SAVE_SORT, adapter));
}
/**
* Requery the adapter at the given position if it exists and needs a requery.
*
* @param position An index in mAdapters.
*/
public void requeryIfNeeded(int position)
{
LibraryAdapter adapter = mAdapters[position];
if (adapter != null && mRequeryNeeded[position]) {
runQuery(adapter);
}
}
@Override
public void onPageScrollStateChanged(int arg0)
{
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2)
{
}
@Override
public void onPageSelected(int position)
{
mCurrentAdapter = mAdapters[position];
requeryIfNeeded(position);
if (position < ADAPTER_COUNT - 1)
requeryIfNeeded(position + 1);
if (position > 0)
requeryIfNeeded(position - 1);
updateLimiterViews();
}
}