diff options
Diffstat (limited to 'app/src/main/java/github/daneren2005/dsub/view')
22 files changed, 607 insertions, 1329 deletions
diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java deleted file mode 100644 index 8707ece7..00000000 --- a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - This file is part of Subsonic. - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see <http://www.gnu.org/licenses/>. - Copyright 2014 (C) Scott Jackson -*/ - -package github.daneren2005.dsub.view; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.RatingBar; -import android.widget.TextView; - -import java.io.File; - -import github.daneren2005.dsub.R; -import github.daneren2005.dsub.domain.MusicDirectory; -import github.daneren2005.dsub.util.FileUtil; -import github.daneren2005.dsub.util.ImageLoader; - -public class AlbumCell extends UpdateView { - private static final String TAG = AlbumCell.class.getSimpleName(); - - private Context context; - private MusicDirectory.Entry album; - private File file; - - private View coverArtView; - private TextView titleView; - private TextView artistView; - private boolean showArtist = true; - - public AlbumCell(Context context) { - super(context); - this.context = context; - LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true); - - coverArtView = findViewById(R.id.album_coverart); - titleView = (TextView) findViewById(R.id.album_title); - artistView = (TextView) findViewById(R.id.album_artist); - - ratingBar = (RatingBar) findViewById(R.id.album_rating); - ratingBar.setFocusable(false); - starButton = (ImageButton) findViewById(R.id.album_star); - starButton.setFocusable(false); - moreButton = (ImageView) findViewById(R.id.album_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); - } - - public void setShowArtist(boolean showArtist) { - this.showArtist = showArtist; - } - - protected void setObjectImpl(Object obj1, Object obj2) { - this.album = (MusicDirectory.Entry) obj1; - titleView.setText(album.getAlbumDisplay()); - String artist = ""; - if(showArtist) { - artist = album.getArtist(); - if (artist == null) { - artist = ""; - } - if (album.getYear() != null) { - artist += " - " + album.getYear(); - } - } else if(album.getYear() != null) { - artist += album.getYear(); - } - artistView.setText(album.getArtist() == null ? "" : artist); - imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true); - file = null; - } - - @Override - protected void updateBackground() { - if(file == null) { - file = FileUtil.getAlbumDirectory(context, album); - } - - exists = file.exists(); - isStarred = album.isStarred(); - isRated = album.getRating(); - } - - public MusicDirectory.Entry getEntry() { - return album; - } - - public File getFile() { - return file; - } -} diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java new file mode 100644 index 00000000..1ae9ef8e --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java @@ -0,0 +1,130 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import java.util.ArrayList; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.service.MusicService; +import github.daneren2005.dsub.service.MusicServiceFactory; +import github.daneren2005.dsub.util.Constants; +import github.daneren2005.dsub.util.FileUtil; +import github.daneren2005.dsub.util.Util; + +public class AlbumListCountView extends UpdateView2<Integer, Void> { + private final String TAG = AlbumListCountView.class.getSimpleName(); + + private TextView titleView; + private TextView countView; + private int startCount; + private int count = 0; + + public AlbumListCountView(Context context) { + super(context, false); + this.context = context; + LayoutInflater.from(context).inflate(R.layout.basic_count_item, this, true); + + titleView = (TextView) findViewById(R.id.basic_count_name); + countView = (TextView) findViewById(R.id.basic_count_count); + } + + protected void setObjectImpl(Integer albumListString, Void dummy) { + titleView.setText(albumListString); + + SharedPreferences prefs = Util.getPreferences(context); + startCount = prefs.getInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0); + count = startCount; + update(); + } + + @Override + protected void updateBackground() { + try { + String recentAddedFile = Util.getCacheName(context, "recent_count"); + ArrayList<String> recents = FileUtil.deserialize(context, recentAddedFile, ArrayList.class); + if (recents == null) { + recents = new ArrayList<String>(); + } + + MusicService musicService = MusicServiceFactory.getMusicService(context); + MusicDirectory recentlyAdded = musicService.getAlbumList("newest", 20, 0, false, context, null); + + // If first run, just put everything in it and return 0 + boolean firstRun = recents.isEmpty(); + + // Count how many new albums are in the list + count = 0; + for (MusicDirectory.Entry album : recentlyAdded.getChildren()) { + if (!recents.contains(album.getId())) { + recents.add(album.getId()); + count++; + } + } + + // Keep recents list from growing infinitely + while (recents.size() > 40) { + recents.remove(0); + } + FileUtil.serialize(context, recents, recentAddedFile); + + if (!firstRun) { + // Add the old count which will get cleared out after viewing recents + count += startCount; + SharedPreferences.Editor editor = Util.getPreferences(context).edit(); + editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), count); + editor.commit(); + } + } catch(Exception e) { + Log.w(TAG, "Failed to refresh most recent count", e); + } + } + + @Override + protected void update() { + // Update count display with appropriate information + if(count <= 0) { + countView.setVisibility(View.GONE); + } else { + String displayName; + if(count < 10) { + displayName = "0" + count; + } else { + displayName = "" + count; + } + + countView.setText(displayName); + countView.setVisibility(View.VISIBLE); + } + } + + @Override + public void onClick() { + SharedPreferences.Editor editor = Util.getPreferences(context).edit(); + editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0); + editor.commit(); + + count = 0; + update(); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java index bd54ea1e..e521babf 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java @@ -16,10 +16,10 @@ Copyright 2009 (C) Sindre Mehus */ + package github.daneren2005.dsub.view; import android.content.Context; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageButton; @@ -30,75 +30,76 @@ import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.util.FileUtil; import github.daneren2005.dsub.util.ImageLoader; -import github.daneren2005.dsub.util.Util; + import java.io.File; -import java.util.List; -/** - * Used to display albums in a {@code ListView}. - * - * @author Sindre Mehus - */ -public class AlbumView extends UpdateView { +public class AlbumView extends UpdateView2<MusicDirectory.Entry, ImageLoader> { private static final String TAG = AlbumView.class.getSimpleName(); - private Context context; - private MusicDirectory.Entry album; private File file; - + private View coverArtView; private TextView titleView; private TextView artistView; - private View coverArtView; + private boolean showArtist = true; - public AlbumView(Context context) { + public AlbumView(Context context, boolean cell) { super(context); - this.context = context; - LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); + if(cell) { + LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true); + } else { + LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); + } + + coverArtView = findViewById(R.id.album_coverart); titleView = (TextView) findViewById(R.id.album_title); artistView = (TextView) findViewById(R.id.album_artist); - coverArtView = findViewById(R.id.album_coverart); + ratingBar = (RatingBar) findViewById(R.id.album_rating); + ratingBar.setFocusable(false); starButton = (ImageButton) findViewById(R.id.album_star); starButton.setFocusable(false); + moreButton = (ImageView) findViewById(R.id.more_button); - moreButton = (ImageView) findViewById(R.id.album_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); + checkable = true; + } + + public void setShowArtist(boolean showArtist) { + this.showArtist = showArtist; } - protected void setObjectImpl(Object obj1, Object obj2) { - this.album = (MusicDirectory.Entry) obj1; + protected void setObjectImpl(MusicDirectory.Entry album, ImageLoader imageLoader) { titleView.setText(album.getAlbumDisplay()); - String artist = album.getArtist(); - if(artist == null) { - artist = ""; - } - if(album.getYear() != null) { - artist += " - " + album.getYear(); + String artist = ""; + if(showArtist) { + artist = album.getArtist(); + if (artist == null) { + artist = ""; + } + if (album.getYear() != null) { + artist += " - " + album.getYear(); + } + } else if(album.getYear() != null) { + artist += album.getYear(); } - artistView.setText(artist); - artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE); - imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true); + artistView.setText(album.getArtist() == null ? "" : artist); + imageTask = imageLoader.loadImage(coverArtView, album, false, true); file = null; } @Override protected void updateBackground() { if(file == null) { - file = FileUtil.getAlbumDirectory(context, album); + file = FileUtil.getAlbumDirectory(context, item); } exists = file.exists(); - isStarred = album.isStarred(); - isRated = album.getRating(); + isStarred = item.isStarred(); + isRated = item.getRating(); } public MusicDirectory.Entry getEntry() { - return album; + return item; } public File getFile() { diff --git a/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java index 71bdeb78..f6a6adb3 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java @@ -19,7 +19,6 @@ package github.daneren2005.dsub.view; import android.content.Context; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageButton; @@ -28,26 +27,21 @@ import android.widget.TextView; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.util.FileUtil; -import github.daneren2005.dsub.util.ImageLoader; -import github.daneren2005.dsub.util.Util; + import java.io.File; /** * Used to display albums in a {@code ListView}. * * @author Sindre Mehus */ -public class ArtistEntryView extends UpdateView { +public class ArtistEntryView extends UpdateView<MusicDirectory.Entry> { private static final String TAG = ArtistEntryView.class.getSimpleName(); - private Context context; - private MusicDirectory.Entry artist; private File file; - private TextView titleView; public ArtistEntryView(Context context) { super(context); - this.context = context; LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true); titleView = (TextView) findViewById(R.id.item_name); @@ -61,8 +55,7 @@ public class ArtistEntryView extends UpdateView { }); } - protected void setObjectImpl(Object obj) { - this.artist = (MusicDirectory.Entry) obj; + protected void setObjectImpl(MusicDirectory.Entry artist) { titleView.setText(artist.getTitle()); file = FileUtil.getArtistDirectory(context, artist); } @@ -70,7 +63,7 @@ public class ArtistEntryView extends UpdateView { @Override protected void updateBackground() { exists = file.exists(); - isStarred = artist.isStarred(); + isStarred = item.isStarred(); } public File getFile() { diff --git a/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java index c255be69..d86c5d26 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java @@ -35,18 +35,14 @@ import java.io.File; * * @author Sindre Mehus */ -public class ArtistView extends UpdateView { +public class ArtistView extends UpdateView<Artist> { private static final String TAG = ArtistView.class.getSimpleName(); - - private Context context; - private Artist artist; - private File file; + private File file; private TextView titleView; public ArtistView(Context context) { super(context); - this.context = context; LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true); titleView = (TextView) findViewById(R.id.item_name); @@ -60,8 +56,7 @@ public class ArtistView extends UpdateView { }); } - protected void setObjectImpl(Object obj) { - this.artist = (Artist) obj; + protected void setObjectImpl(Artist artist) { titleView.setText(artist.getName()); file = FileUtil.getArtistDirectory(context, artist); } @@ -69,7 +64,7 @@ public class ArtistView extends UpdateView { @Override protected void updateBackground() { exists = file.exists(); - isStarred = artist.isStarred(); + isStarred = item.isStarred(); } public File getFile() { diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java new file mode 100644 index 00000000..01aa42e1 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java @@ -0,0 +1,40 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.TextView; + +import github.daneren2005.dsub.R; + +public class BasicHeaderView extends UpdateView<String> { + TextView nameView; + + public BasicHeaderView(Context context) { + this(context, R.layout.basic_header); + } + public BasicHeaderView(Context context, int layout) { + super(context, false); + + LayoutInflater.from(context).inflate(layout, this, true); + nameView = (TextView) findViewById(R.id.item_name); + } + + protected void setObjectImpl(String string) { + nameView.setText(string); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java new file mode 100644 index 00000000..ca7e8993 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java @@ -0,0 +1,44 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import github.daneren2005.dsub.R; + +public class BasicListView extends UpdateView<String> { + private TextView titleView; + + public BasicListView(Context context) { + super(context, false); + LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true); + + titleView = (TextView) findViewById(R.id.item_name); + starButton = (ImageButton) findViewById(R.id.item_star); + starButton.setFocusable(false); + moreButton = (ImageView) findViewById(R.id.item_more); + moreButton.setVisibility(View.GONE); + } + + protected void setObjectImpl(String string) { + titleView.setText(string); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java index 096583c7..e3d24485 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java +++ b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java @@ -42,7 +42,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; -import android.app.AlertDialog; +import android.support.v7.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; diff --git a/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java index 0b9d05a0..3d6eaa52 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java +++ b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java @@ -19,7 +19,7 @@ package github.daneren2005.dsub.view; import android.app.Activity; -import android.app.AlertDialog; +import android.support.v7.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; diff --git a/app/src/main/java/github/daneren2005/dsub/view/GenreView.java b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java index 8dbcf89d..b29aefba 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/GenreView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java @@ -25,7 +25,7 @@ import android.widget.TextView; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.Genre; -public class GenreView extends UpdateView { +public class GenreView extends UpdateView<Genre> { private static final String TAG = GenreView.class.getSimpleName(); private TextView titleView; @@ -41,8 +41,7 @@ public class GenreView extends UpdateView { albumsView = (TextView) findViewById(R.id.genre_albums); } - public void setObjectImpl(Object obj) { - Genre genre = (Genre) obj; + public void setObjectImpl(Genre genre) { titleView.setText(genre.getName()); if(genre.getAlbumCount() != null) { diff --git a/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java new file mode 100644 index 00000000..3bb3e8a1 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java @@ -0,0 +1,99 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.graphics.Rect; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.TypedValue; +import android.view.View; + +public class GridSpacingDecoration extends RecyclerView.ItemDecoration { + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + + int spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, view.getResources().getDisplayMetrics()); + int halfSpacing = spacing / 2; + + int childCount = parent.getChildCount(); + int childIndex = parent.getChildPosition(view); + int spanCount = getTotalSpan(view, parent); + int spanIndex = childIndex % spanCount; + int spanSize = getSpanSize(parent, childIndex); + + /* INVALID SPAN */ + if (spanCount < 1 || spanSize > 1) return; + + outRect.top = halfSpacing; + outRect.bottom = halfSpacing; + outRect.left = halfSpacing; + outRect.right = halfSpacing; + + if (isTopEdge(childIndex, spanCount)) { + outRect.top = spacing; + } + + if (isLeftEdge(spanIndex, spanCount)) { + outRect.left = spacing; + } + + if (isRightEdge(spanIndex, spanCount)) { + outRect.right = spacing; + } + + if (isBottomEdge(childIndex, childCount, spanCount)) { + outRect.bottom = spacing; + } + } + + protected int getTotalSpan(View view, RecyclerView parent) { + RecyclerView.LayoutManager mgr = parent.getLayoutManager(); + if (mgr instanceof GridLayoutManager) { + return ((GridLayoutManager) mgr).getSpanCount(); + } + + return -1; + } + protected int getSpanSize(RecyclerView parent, int childIndex) { + RecyclerView.LayoutManager mgr = parent.getLayoutManager(); + if (mgr instanceof GridLayoutManager) { + GridLayoutManager.SpanSizeLookup lookup = ((GridLayoutManager) mgr).getSpanSizeLookup(); + if(lookup != null) { + return lookup.getSpanSize(childIndex); + } + } + + return 1; + } + + protected boolean isLeftEdge(int spanIndex, int spanCount) { + return spanIndex == 0; + } + + protected boolean isRightEdge(int spanIndex, int spanCount) { + return spanIndex == spanCount - 1; + } + + protected boolean isTopEdge(int childIndex, int spanCount) { + return childIndex < spanCount; + } + + protected boolean isBottomEdge(int childIndex, int childCount, int spanCount) { + return childIndex >= childCount - spanCount; + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java b/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java deleted file mode 100644 index 8a82f353..00000000 --- a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java +++ /dev/null @@ -1,836 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ -package github.daneren2005.dsub.view; - -import android.annotation.TargetApi; -import android.content.Context; -import android.database.DataSetObservable; -import android.database.DataSetObserver; -import android.os.Build; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.*; - -import java.lang.reflect.Field; -import java.util.ArrayList; - -/** - * A {@link GridView} that supports adding header rows in a - * very similar way to {@link android.widget.ListView}. - * See {@link HeaderGridView#addHeaderView(View, Object, boolean)} - * See {@link HeaderGridView#addFooterView(View, Object, boolean)} - */ -public class HeaderGridView extends GridView { - private static final String TAG = HeaderGridView.class.getSimpleName(); - public static boolean DEBUG = false; - - /** - * A class that represents a fixed view in a list, for example a header at the top - * or a footer at the bottom. - */ - private static class FixedViewInfo { - /** - * The view to add to the grid - */ - public View view; - public ViewGroup viewContainer; - /** - * The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. - */ - public Object data; - /** - * <code>true</code> if the fixed view should be selectable in the grid - */ - public boolean isSelectable; - } - - private int mNumColumns = AUTO_FIT; - private View mViewForMeasureRowHeight = null; - private int mRowHeight = -1; - private static final String LOG_TAG = HeaderGridView.class.getSimpleName(); - - private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>(); - private ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>(); - - private void initHeaderGridView() { - } - - public HeaderGridView(Context context) { - super(context); - initHeaderGridView(); - } - - public HeaderGridView(Context context, AttributeSet attrs) { - super(context, attrs); - initHeaderGridView(); - } - - public HeaderGridView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initHeaderGridView(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - ListAdapter adapter = getAdapter(); - if (adapter != null && adapter instanceof HeaderViewGridAdapter) { - ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumnsCompatible()); - ((HeaderViewGridAdapter) adapter).setRowHeight(getRowHeight()); - } - } - - @Override - public void setClipChildren(boolean clipChildren) { - // Ignore, since the header rows depend on not being clipped - } - - /** - * Do not call this method unless you know how it works. - * - * @param clipChildren - */ - public void setClipChildrenSupper(boolean clipChildren) { - super.setClipChildren(false); - } - - /** - * Add a fixed view to appear at the top of the grid. If addHeaderView is - * called more than once, the views will appear in the order they were - * added. Views added using this call can take focus if they want. - * <p/> - * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap - * the supplied cursor with one that will also account for header views. - * - * @param v The view to add. - */ - public void addHeaderView(View v) { - addHeaderView(v, null, true); - } - - /** - * Add a fixed view to appear at the top of the grid. If addHeaderView is - * called more than once, the views will appear in the order they were - * added. Views added using this call can take focus if they want. - * <p/> - * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap - * the supplied cursor with one that will also account for header views. - * - * @param v The view to add. - * @param data Data to associate with this view - * @param isSelectable whether the item is selectable - */ - public void addHeaderView(View v, Object data, boolean isSelectable) { - ListAdapter adapter = getAdapter(); - if (adapter != null && !(adapter instanceof HeaderViewGridAdapter)) { - throw new IllegalStateException( - "Cannot add header view to grid -- setAdapter has already been called."); - } - - ViewGroup.LayoutParams lyp = v.getLayoutParams(); - - FixedViewInfo info = new FixedViewInfo(); - FrameLayout fl = new FullWidthFixedViewLayout(getContext()); - - if (lyp != null) { - v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height)); - fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height)); - } - fl.addView(v); - info.view = v; - info.viewContainer = fl; - info.data = data; - info.isSelectable = isSelectable; - mHeaderViewInfos.add(info); - // in the case of re-adding a header view, or adding one later on, - // we need to notify the observer - if (adapter != null) { - ((HeaderViewGridAdapter) adapter).notifyDataSetChanged(); - } - } - - public void addFooterView(View v) { - addFooterView(v, null, true); - } - - public void addFooterView(View v, Object data, boolean isSelectable) { - ListAdapter mAdapter = getAdapter(); - if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) { - throw new IllegalStateException( - "Cannot add header view to grid -- setAdapter has already been called."); - } - - ViewGroup.LayoutParams lyp = v.getLayoutParams(); - - FixedViewInfo info = new FixedViewInfo(); - FrameLayout fl = new FullWidthFixedViewLayout(getContext()); - - if (lyp != null) { - v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height)); - fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height)); - } - fl.addView(v); - info.view = v; - info.viewContainer = fl; - info.data = data; - info.isSelectable = isSelectable; - mFooterViewInfos.add(info); - - if (mAdapter != null) { - ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged(); - } - } - - public int getHeaderViewCount() { - return mHeaderViewInfos.size(); - } - - public int getFooterViewCount() { - return mFooterViewInfos.size(); - } - - /** - * Removes a previously-added header view. - * - * @param v The view to remove - * @return true if the view was removed, false if the view was not a header - * view - */ - public boolean removeHeaderView(View v) { - if (mHeaderViewInfos.size() > 0) { - boolean result = false; - ListAdapter adapter = getAdapter(); - if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) { - result = true; - } - removeFixedViewInfo(v, mHeaderViewInfos); - return result; - } - return false; - } - - /** - * Removes a previously-added footer view. - * - * @param v The view to remove - * @return true if the view was removed, false if the view was not a header - * view - */ - public boolean removeFooterView(View v) { - if (mFooterViewInfos.size() > 0) { - boolean result = false; - ListAdapter adapter = getAdapter(); - if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) { - result = true; - } - removeFixedViewInfo(v, mFooterViewInfos); - return result; - } - return false; - } - - private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) { - int len = where.size(); - for (int i = 0; i < len; ++i) { - FixedViewInfo info = where.get(i); - if (info.view == v) { - where.remove(i); - break; - } - } - } - - @TargetApi(11) - private int getNumColumnsCompatible() { - if (Build.VERSION.SDK_INT >= 11) { - return super.getNumColumns(); - } else { - try { - Field numColumns = GridView.class.getSuperclass().getDeclaredField("mNumColumns"); - numColumns.setAccessible(true); - return numColumns.getInt(this); - } catch (Exception e) { - if (mNumColumns != -1) { - return mNumColumns; - } else { - return 2; - } - } - } - } - - @TargetApi(16) - private int getColumnWidthCompatible() { - if (Build.VERSION.SDK_INT >= 16) { - return super.getColumnWidth(); - } else { - try { - Field numColumns = getClass().getSuperclass().getDeclaredField("mColumnWidth"); - numColumns.setAccessible(true); - return numColumns.getInt(this); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mViewForMeasureRowHeight = null; - } - - public void invalidateRowHeight() { - mRowHeight = -1; - } - - public int getHeaderHeight(int row) { - if (row >= 0) { - return mHeaderViewInfos.get(row).view.getMeasuredHeight(); - } - - return 0; - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - public int getVerticalSpacing(){ - int value = 0; - - try { - int currentapiVersion = android.os.Build.VERSION.SDK_INT; - if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){ - Field field = this.getClass().getSuperclass().getDeclaredField("mVerticalSpacing"); - field.setAccessible(true); - value = field.getInt(this); - } else{ - value = super.getVerticalSpacing(); - } - - }catch (Exception ex){ - - } - - return value; - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - public int getHorizontalSpacing(){ - int value = 0; - - try { - int currentapiVersion = android.os.Build.VERSION.SDK_INT; - if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){ - Field field = this.getClass().getSuperclass().getDeclaredField("mHorizontalSpacing"); - field.setAccessible(true); - value = field.getInt(this); - } else{ - value = super.getHorizontalSpacing(); - } - - }catch (Exception ex){ - - } - - return value; - } - - public int getRowHeight() { - if (mRowHeight > 0) { - // return mRowHeight; - } - ListAdapter adapter = getAdapter(); - int numColumns = getNumColumnsCompatible(); - - // adapter has not been set or has no views in it; - if (adapter == null || adapter.getCount() <= numColumns * (mHeaderViewInfos.size() + mFooterViewInfos.size()) || numColumns == -1) { - return -1; - } - int mColumnWidth = getColumnWidthCompatible(); - View view = getAdapter().getView(numColumns * mHeaderViewInfos.size(), mViewForMeasureRowHeight, this); - AbsListView.LayoutParams p = (AbsListView.LayoutParams) view.getLayoutParams(); - if (p == null) { - p = new AbsListView.LayoutParams(-1, -2, 0); - view.setLayoutParams(p); - } - int childHeightSpec = getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height); - int childWidthSpec = getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width); - view.measure(childWidthSpec, childHeightSpec); - mViewForMeasureRowHeight = view; - mRowHeight = view.getMeasuredHeight(); - return mRowHeight; - } - - @TargetApi(11) - public void tryToScrollToBottomSmoothly() { - int lastPos = getAdapter().getCount() - 1; - if (Build.VERSION.SDK_INT >= 11) { - smoothScrollToPositionFromTop(lastPos, 0); - } else { - setSelection(lastPos); - } - } - - @TargetApi(11) - public void tryToScrollToBottomSmoothly(int duration) { - int lastPos = getAdapter().getCount() - 1; - if (Build.VERSION.SDK_INT >= 11) { - smoothScrollToPositionFromTop(lastPos, 0, duration); - } else { - setSelection(lastPos); - } - } - - @Override - public void setAdapter(ListAdapter adapter) { - if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) { - HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); - int numColumns = getNumColumnsCompatible(); - if (numColumns > 1) { - headerViewGridAdapter.setNumColumns(numColumns); - } - headerViewGridAdapter.setRowHeight(getRowHeight()); - super.setAdapter(headerViewGridAdapter); - } else { - super.setAdapter(adapter); - } - } - - /** - * full width - */ - private class FullWidthFixedViewLayout extends FrameLayout { - - public FullWidthFixedViewLayout(Context context) { - super(context); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int realLeft = HeaderGridView.this.getPaddingLeft() + getPaddingLeft(); - // Try to make where it should be, from left, full width - if (realLeft != left) { - offsetLeftAndRight(realLeft - left); - } - super.onLayout(changed, left, top, right, bottom); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int targetWidth = HeaderGridView.this.getMeasuredWidth() - - HeaderGridView.this.getPaddingLeft() - - HeaderGridView.this.getPaddingRight(); - widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, - MeasureSpec.getMode(widthMeasureSpec)); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - @Override - public void setNumColumns(int numColumns) { - super.setNumColumns(numColumns); - mNumColumns = numColumns; - ListAdapter adapter = getAdapter(); - if (adapter != null && adapter instanceof HeaderViewGridAdapter) { - ((HeaderViewGridAdapter) adapter).setNumColumns(numColumns); - } - } - - /** - * ListAdapter used when a HeaderGridView has header views. This ListAdapter - * wraps another one and also keeps track of the header views and their - * associated data objects. - * <p>This is intended as a base class; you will probably not need to - * use this class directly in your own code. - */ - private static class HeaderViewGridAdapter extends BaseAdapter implements WrapperListAdapter, Filterable { - // This is used to notify the container of updates relating to number of columns - // or headers changing, which changes the number of placeholders needed - private final DataSetObservable mDataSetObservable = new DataSetObservable(); - private final ListAdapter mAdapter; - static final ArrayList<FixedViewInfo> EMPTY_INFO_LIST = - new ArrayList<FixedViewInfo>(); - - // This ArrayList is assumed to NOT be null. - ArrayList<FixedViewInfo> mHeaderViewInfos; - ArrayList<FixedViewInfo> mFooterViewInfos; - private int mNumColumns = 1; - private int mRowHeight = -1; - boolean mAreAllFixedViewsSelectable; - private final boolean mIsFilterable; - private boolean mCachePlaceHoldView = true; - // From Recycle Bin or calling getView, this a question... - private boolean mCacheFirstHeaderView = false; - - public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ArrayList<FixedViewInfo> footViewInfos, ListAdapter adapter) { - mAdapter = adapter; - mIsFilterable = adapter instanceof Filterable; - if (headerViewInfos == null) { - mHeaderViewInfos = EMPTY_INFO_LIST; - } else { - mHeaderViewInfos = headerViewInfos; - } - - if (footViewInfos == null) { - mFooterViewInfos = EMPTY_INFO_LIST; - } else { - mFooterViewInfos = footViewInfos; - } - mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos) - && areAllListInfosSelectable(mFooterViewInfos); - } - - public void setNumColumns(int numColumns) { - if (numColumns < 1) { - return; - } - if (mNumColumns != numColumns) { - mNumColumns = numColumns; - notifyDataSetChanged(); - } - } - - public void setRowHeight(int height) { - mRowHeight = height; - } - - public int getHeadersCount() { - return mHeaderViewInfos.size(); - } - - public int getFootersCount() { - return mFooterViewInfos.size(); - } - - @Override - public boolean isEmpty() { - return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0; - } - - private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) { - if (infos != null) { - for (FixedViewInfo info : infos) { - if (!info.isSelectable) { - return false; - } - } - } - return true; - } - - public boolean removeHeader(View v) { - for (int i = 0; i < mHeaderViewInfos.size(); i++) { - FixedViewInfo info = mHeaderViewInfos.get(i); - if (info.view == v) { - mHeaderViewInfos.remove(i); - mAreAllFixedViewsSelectable = - areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); - mDataSetObservable.notifyChanged(); - return true; - } - } - return false; - } - - public boolean removeFooter(View v) { - for (int i = 0; i < mFooterViewInfos.size(); i++) { - FixedViewInfo info = mFooterViewInfos.get(i); - if (info.view == v) { - mFooterViewInfos.remove(i); - mAreAllFixedViewsSelectable = - areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); - mDataSetObservable.notifyChanged(); - return true; - } - } - return false; - } - - @Override - public int getCount() { - if (mAdapter != null) { - return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount(); - } else { - return (getFootersCount() + getHeadersCount()) * mNumColumns; - } - } - - @Override - public boolean areAllItemsEnabled() { - if (mAdapter != null) { - return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled(); - } else { - return true; - } - } - - private int getAdapterAndPlaceHolderCount() { - final int adapterCount = (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns); - return adapterCount; - } - - @Override - public boolean isEnabled(int position) { - // Header (negative positions will throw an IndexOutOfBoundsException) - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (position < numHeadersAndPlaceholders) { - return position % mNumColumns == 0 - && mHeaderViewInfos.get(position / mNumColumns).isSelectable; - } - - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition < adapterCount) { - return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition); - } - } - - // Footer (off-limits positions will throw an IndexOutOfBoundsException) - final int footerPosition = adjPosition - adapterCount; - return footerPosition % mNumColumns == 0 - && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable; - } - - @Override - public Object getItem(int position) { - // Header (negative positions will throw an ArrayIndexOutOfBoundsException) - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (position < numHeadersAndPlaceholders) { - if (position % mNumColumns == 0) { - return mHeaderViewInfos.get(position / mNumColumns).data; - } - return null; - } - - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition < adapterCount) { - if (adjPosition < mAdapter.getCount()) { - return mAdapter.getItem(adjPosition); - } else { - return null; - } - } - } - - // Footer (off-limits positions will throw an IndexOutOfBoundsException) - final int footerPosition = adjPosition - adapterCount; - if (footerPosition % mNumColumns == 0) { - return mFooterViewInfos.get(footerPosition).data; - } else { - return null; - } - } - - @Override - public long getItemId(int position) { - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (mAdapter != null && position >= numHeadersAndPlaceholders) { - int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = mAdapter.getCount(); - if (adjPosition < adapterCount) { - return mAdapter.getItemId(adjPosition); - } - } - return -1; - } - - @Override - public boolean hasStableIds() { - if (mAdapter != null) { - return mAdapter.hasStableIds(); - } - return false; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (DEBUG) { - Log.d(LOG_TAG, String.format("getView: %s, reused: %s", position, convertView == null)); - } - // Header (negative positions will throw an ArrayIndexOutOfBoundsException) - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (position < numHeadersAndPlaceholders) { - View headerViewContainer = mHeaderViewInfos - .get(position / mNumColumns).viewContainer; - if (position % mNumColumns == 0) { - return headerViewContainer; - } else { - if (convertView == null) { - convertView = new View(parent.getContext()); - } - // We need to do this because GridView uses the height of the last item - // in a row to determine the height for the entire row. - convertView.setVisibility(View.INVISIBLE); - convertView.setMinimumHeight(headerViewContainer.getHeight()); - return convertView; - } - } - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition < adapterCount) { - if (adjPosition < mAdapter.getCount()) { - View view = mAdapter.getView(adjPosition, convertView, parent); - return view; - } else { - if (convertView == null) { - convertView = new View(parent.getContext()); - } - convertView.setVisibility(View.INVISIBLE); - convertView.setMinimumHeight(mRowHeight); - return convertView; - } - } - } - // Footer - final int footerPosition = adjPosition - adapterCount; - if (footerPosition < getCount()) { - View footViewContainer = mFooterViewInfos - .get(footerPosition / mNumColumns).viewContainer; - if (position % mNumColumns == 0) { - return footViewContainer; - } else { - if (convertView == null) { - convertView = new View(parent.getContext()); - } - // We need to do this because GridView uses the height of the last item - // in a row to determine the height for the entire row. - convertView.setVisibility(View.INVISIBLE); - convertView.setMinimumHeight(footViewContainer.getHeight()); - return convertView; - } - } - throw new ArrayIndexOutOfBoundsException(position); - } - - @Override - public int getItemViewType(int position) { - - final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1; - int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; - if (mCachePlaceHoldView) { - // Header - if (position < numHeadersAndPlaceholders) { - if (position == 0) { - if (mCacheFirstHeaderView) { - type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1; - } - } - if (position % mNumColumns != 0) { - type = adapterViewTypeStart + (position / mNumColumns + 1); - } - } - } - - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition >= 0 && adjPosition < adapterCount) { - if (adjPosition < mAdapter.getCount()) { - type = mAdapter.getItemViewType(adjPosition); - } else { - if (mCachePlaceHoldView) { - type = adapterViewTypeStart + mHeaderViewInfos.size() + 1; - } - } - } - } - - if (mCachePlaceHoldView) { - // Footer - final int footerPosition = adjPosition - adapterCount; - if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) { - type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1); - } - } - if (DEBUG) { - Log.d(LOG_TAG, String.format("getItemViewType: pos: %s, result: %s", position, type, mCachePlaceHoldView, mCacheFirstHeaderView)); - } - return type; - } - - /** - * content view, content view holder, header[0], header and footer placeholder(s) - * - * @return - */ - @Override - public int getViewTypeCount() { - int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount(); - if (mCachePlaceHoldView) { - int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size(); - if (mCacheFirstHeaderView) { - offset += 1; - } - count += offset; - } - if (DEBUG) { - Log.d(LOG_TAG, String.format("getViewTypeCount: %s", count)); - } - return count; - } - - @Override - public void registerDataSetObserver(DataSetObserver observer) { - mDataSetObservable.registerObserver(observer); - if (mAdapter != null) { - mAdapter.registerDataSetObserver(observer); - } - } - - @Override - public void unregisterDataSetObserver(DataSetObserver observer) { - mDataSetObservable.unregisterObserver(observer); - if (mAdapter != null) { - mAdapter.unregisterDataSetObserver(observer); - } - } - - @Override - public Filter getFilter() { - if (mIsFilterable) { - return ((Filterable) mAdapter).getFilter(); - } - return null; - } - - @Override - public ListAdapter getWrappedAdapter() { - return mAdapter; - } - - public void notifyDataSetChanged() { - mDataSetObservable.notifyChanged(); - } - } -} diff --git a/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java index 0264a785..581b92e4 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java @@ -34,16 +34,12 @@ import github.daneren2005.dsub.util.FileUtil; import github.daneren2005.dsub.util.SyncUtil; import github.daneren2005.dsub.util.Util; -public class PlaylistSongView extends UpdateView { +public class PlaylistSongView extends UpdateView2<Playlist, List<MusicDirectory.Entry>> { private static final String TAG = PlaylistSongView.class.getSimpleName(); - private Context context; - private Playlist playlist; - private TextView titleView; private TextView countView; private int count = 0; - private List<MusicDirectory.Entry> songs; public PlaylistSongView(Context context) { super(context, false); @@ -54,9 +50,7 @@ public class PlaylistSongView extends UpdateView { countView = (TextView) findViewById(R.id.basic_count_count); } - protected void setObjectImpl(Object obj1, Object obj2) { - this.playlist = (Playlist) obj1; - this.songs = (List<MusicDirectory.Entry>) obj2; + protected void setObjectImpl(Playlist playlist, List<MusicDirectory.Entry> songs) { count = 0; titleView.setText(playlist.getName()); // Make sure to hide initially so it's not present briefly before update @@ -69,11 +63,11 @@ public class PlaylistSongView extends UpdateView { count = 0; // Don't try to lookup playlist for Create New - if(!"-1".equals(playlist.getId())) { - MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", playlist.getId()), MusicDirectory.class); + if(!"-1".equals(item.getId())) { + MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", item.getId()), MusicDirectory.class); if(cache != null) { // Try to find song instances in the given playlists - for(MusicDirectory.Entry song: songs) { + for(MusicDirectory.Entry song: item2) { if(cache.getChildren().contains(song)) { count++; } diff --git a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java index 25613984..2cd27b19 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java @@ -21,11 +21,11 @@ package github.daneren2005.dsub.view; import android.content.Context; import android.view.LayoutInflater; import android.view.View; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.Playlist; +import github.daneren2005.dsub.util.ImageLoader; import github.daneren2005.dsub.util.SyncUtil; /** @@ -33,37 +33,31 @@ import github.daneren2005.dsub.util.SyncUtil; * * @author Sindre Mehus */ -public class PlaylistView extends UpdateView { +public class PlaylistView extends UpdateView<Playlist> { private static final String TAG = PlaylistView.class.getSimpleName(); - private Context context; - private Playlist playlist; - + private View coverArtView; private TextView titleView; + private ImageLoader imageLoader; - public PlaylistView(Context context) { + public PlaylistView(Context context, ImageLoader imageLoader, boolean largeCell) { super(context); - this.context = context; - LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true); + LayoutInflater.from(context).inflate(largeCell ? R.layout.playlist_cell_item : R.layout.playlist_list_item, this, true); - titleView = (TextView) findViewById(R.id.item_name); - starButton = (ImageButton) findViewById(R.id.item_star); - starButton.setFocusable(false); + coverArtView = findViewById(R.id.playlist_coverart); + titleView = (TextView) findViewById(R.id.playlist_title); moreButton = (ImageView) findViewById(R.id.item_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); + + this.imageLoader = imageLoader; } - protected void setObjectImpl(Object obj) { - this.playlist = (Playlist) obj; + protected void setObjectImpl(Playlist playlist) { titleView.setText(playlist.getName()); + imageTask = imageLoader.loadImage(coverArtView, playlist, false, true); } @Override protected void updateBackground() { - pinned = SyncUtil.isSyncedPlaylist(context, playlist.getId()); + pinned = SyncUtil.isSyncedPlaylist(context, item.getId()); } } diff --git a/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java index ada8019e..4878ad67 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java @@ -30,18 +30,14 @@ import github.daneren2005.dsub.util.SyncUtil; import github.daneren2005.dsub.util.FileUtil; import java.io.File; -public class PodcastChannelView extends UpdateView { +public class PodcastChannelView extends UpdateView<PodcastChannel> { private static final String TAG = PodcastChannelView.class.getSimpleName(); - private Context context; - private PodcastChannel channel; private File file; - private TextView titleView; public PodcastChannelView(Context context) { super(context); - this.context = context; LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true); titleView = (TextView) findViewById(R.id.item_name); @@ -55,8 +51,7 @@ public class PodcastChannelView extends UpdateView { }); } - protected void setObjectImpl(Object obj) { - channel = (PodcastChannel) obj; + protected void setObjectImpl(PodcastChannel channel) { if(channel.getName() != null) { titleView.setText(channel.getName()); } else { @@ -67,7 +62,7 @@ public class PodcastChannelView extends UpdateView { @Override protected void updateBackground() { - if(SyncUtil.isSyncedPodcast(context, channel.getId())) { + if(SyncUtil.isSyncedPodcast(context, item.getId())) { if(exists) { shaded = false; exists = false; diff --git a/app/src/main/java/github/daneren2005/dsub/view/SettingView.java b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java index 1c78706e..d46dc5d2 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/SettingView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java @@ -17,34 +17,42 @@ package github.daneren2005.dsub.view; import android.content.Context; import android.view.LayoutInflater; -import android.view.View; -import android.widget.CheckedTextView; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.TextView; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.User; import static github.daneren2005.dsub.domain.User.Setting; -public class SettingView extends UpdateView { - Setting setting; - - CheckedTextView view; +public class SettingView extends UpdateView2<Setting, Boolean> { + private final TextView titleView; + private final CheckBox checkBox; public SettingView(Context context) { super(context, false); this.context = context; - LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_multiple_choice, this, true); + LayoutInflater.from(context).inflate(R.layout.basic_choice_item, this, true); - view = (CheckedTextView) findViewById(android.R.id.text1); + titleView = (TextView) findViewById(R.id.item_name); + checkBox = (CheckBox) findViewById(R.id.item_checkbox); + checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if(item != null) { + item.setValue(isChecked); + } + } + }); + checkBox.setClickable(false); } - protected void setObjectImpl(Object obj, Object editable) { - this.setting = (Setting) obj; - + protected void setObjectImpl(Setting setting, Boolean isEditable) { // Can't edit non-role parts String name = setting.getName(); if(name.indexOf("Role") == -1) { - editable = false; + item2 = false; } int res = -1; @@ -74,29 +82,28 @@ public class SettingView extends UpdateView { res = R.string.admin_role_lastfm; } else { // Last resort to display the raw value - view.setText(name); + titleView.setText(name); } if(res != -1) { - view.setText(res); + titleView.setText(res); } if(setting.getValue()) { - view.setChecked(setting.getValue()); + checkBox.setChecked(setting.getValue()); } else { - view.setChecked(false); + checkBox.setChecked(false); } - if((Boolean) editable) { - view.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - view.toggle(); - setting.setValue(view.isChecked()); - } - }); - } else { - view.setOnClickListener(null); - } + checkBox.setEnabled(item2); + } + + @Override + public boolean isCheckable() { + return item2; + } + + public void setChecked(boolean checked) { + checkBox.setChecked(checked); } } diff --git a/app/src/main/java/github/daneren2005/dsub/view/ShareView.java b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java index bfb5b198..12294369 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/ShareView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java @@ -31,7 +31,7 @@ import java.util.Locale; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.Share; -public class ShareView extends UpdateView { +public class ShareView extends UpdateView<Share> { private static final String TAG = ShareView.class.getSimpleName(); private TextView titleView; @@ -53,8 +53,7 @@ public class ShareView extends UpdateView { }); } - public void setObjectImpl(Object obj) { - Share share = (Share) obj; + public void setObjectImpl(Share share) { titleView.setText(share.getName()); if(share.getExpires() != null) { descriptionView.setText(context.getResources().getString(R.string.share_expires, new SimpleDateFormat("E MMM d, yyyy", Locale.ENGLISH).format(share.getExpires()))); diff --git a/app/src/main/java/github/daneren2005/dsub/view/SongView.java b/app/src/main/java/github/daneren2005/dsub/view/SongView.java index 2fbaedc3..b9c5fa50 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/SongView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/SongView.java @@ -29,6 +29,7 @@ import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.PodcastEpisode; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.DownloadFile; +import github.daneren2005.dsub.util.DrawableTint; import github.daneren2005.dsub.util.Util; import java.io.File; @@ -38,20 +39,17 @@ import java.io.File; * * @author Sindre Mehus */ -public class SongView extends UpdateView implements Checkable { - private static final String TAG = SongView.class.getSimpleName(); +public class SongView extends UpdateView2<MusicDirectory.Entry, Boolean> { + private static final String TAG = SongView.class.getSimpleName(); - private MusicDirectory.Entry song; - - private CheckedTextView checkedTextView; - private TextView titleTextView; - private TextView artistTextView; - private TextView durationTextView; - private TextView statusTextView; + private TextView titleTextView; + private TextView artistTextView; + private TextView durationTextView; + private TextView statusTextView; private ImageView statusImageView; private ImageView bookmarkButton; private View bottomRowView; - + private DownloadService downloadService; private long revision = -1; private DownloadFile downloadFile; @@ -68,35 +66,28 @@ public class SongView extends UpdateView implements Checkable { private boolean isBookmarked = false; private boolean bookmarked = false; - public SongView(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true); + public SongView(Context context) { + super(context); + LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true); - checkedTextView = (CheckedTextView) findViewById(R.id.song_check); - titleTextView = (TextView) findViewById(R.id.song_title); - artistTextView = (TextView) findViewById(R.id.song_artist); - durationTextView = (TextView) findViewById(R.id.song_duration); - statusTextView = (TextView) findViewById(R.id.song_status); + titleTextView = (TextView) findViewById(R.id.song_title); + artistTextView = (TextView) findViewById(R.id.song_artist); + durationTextView = (TextView) findViewById(R.id.song_duration); + statusTextView = (TextView) findViewById(R.id.song_status); statusImageView = (ImageView) findViewById(R.id.song_status_icon); ratingBar = (RatingBar) findViewById(R.id.song_rating); - starButton = (ImageButton) findViewById(R.id.song_star); - starButton.setFocusable(false); + starButton = (ImageButton) findViewById(R.id.song_star); + starButton.setFocusable(false); bookmarkButton = (ImageButton) findViewById(R.id.song_bookmark); bookmarkButton.setFocusable(false); - moreButton = (ImageView) findViewById(R.id.artist_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); + moreButton = (ImageView) findViewById(R.id.more_button); bottomRowView = findViewById(R.id.song_bottom); - } + } + + public void setObjectImpl(MusicDirectory.Entry song, Boolean checkable) { + this.checkable = checkable; - public void setObjectImpl(Object obj1, Object obj2) { - this.song = (MusicDirectory.Entry) obj1; - boolean checkable = (Boolean) obj2; - - StringBuilder artist = new StringBuilder(40); + StringBuilder artist = new StringBuilder(40); boolean isPodcast = song instanceof PodcastEpisode; if(!song.isVideo() || isPodcast) { @@ -110,11 +101,11 @@ public class SongView extends UpdateView implements Checkable { else if(song.getArtist() != null) { artist.append(song.getArtist()); } - + if(isPodcast) { String status = ((PodcastEpisode) song).getStatus(); int statusRes = -1; - + if("error".equals(status)) { statusRes = R.string.song_details_error; } else if("skipped".equals(status)) { @@ -122,7 +113,7 @@ public class SongView extends UpdateView implements Checkable { } else if("downloading".equals(status)) { statusRes = R.string.song_details_downloading; } - + if(statusRes != -1) { artist.append(" ("); artist.append(getContext().getString(statusRes)); @@ -136,16 +127,15 @@ public class SongView extends UpdateView implements Checkable { bottomRowView.setVisibility(View.GONE); statusTextView.setText(Util.formatDuration(song.getDuration())); } - + String title = song.getTitle(); Integer track = song.getTrack(); if(track != null && Util.getDisplayTrack(context)) { title = String.format("%02d", track) + " " + title; } - titleTextView.setText(title); + titleTextView.setText(title); artistTextView.setText(artist); - checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE); this.setBackgroundColor(0x00000000); ratingBar.setVisibility(View.GONE); @@ -155,28 +145,28 @@ public class SongView extends UpdateView implements Checkable { loaded = false; dontChangeDownloadFile = false; } - + public void setDownloadFile(DownloadFile downloadFile) { this.downloadFile = downloadFile; dontChangeDownloadFile = true; } - + public DownloadFile getDownloadFile() { return downloadFile; } - + @Override protected void updateBackground() { - if (downloadService == null) { + if (downloadService == null) { downloadService = DownloadService.getInstance(); if(downloadService == null) { return; } - } + } long newRevision = downloadService.getDownloadListUpdateRevision(); if((revision != newRevision && dontChangeDownloadFile == false) || downloadFile == null) { - downloadFile = downloadService.forSong(song); + downloadFile = downloadService.forSong(item); revision = newRevision; } @@ -184,28 +174,31 @@ public class SongView extends UpdateView implements Checkable { isSaved = downloadFile.isSaved(); partialFile = downloadFile.getPartialFile(); partialFileExists = partialFile.exists(); - isStarred = song.isStarred(); - isBookmarked = song.getBookmark() != null; - isRated = song.getRating(); - + isStarred = item.isStarred(); + isBookmarked = item.getBookmark() != null; + isRated = item.getRating(); + // Check if needs to load metadata: check against all fields that we know are null in offline mode - if(song.getBitRate() == null && song.getDuration() == null && song.getDiscNumber() == null && isWorkDone) { - song.loadMetadata(downloadFile.getCompleteFile()); + if(item.getBitRate() == null && item.getDuration() == null && item.getDiscNumber() == null && isWorkDone) { + item.loadMetadata(downloadFile.getCompleteFile()); loaded = true; } } @Override - protected void update() { + protected void update() { if(loaded) { - setObjectImpl(song, checkedTextView.getVisibility() == View.VISIBLE); + setObjectImpl(item, item2); + } + if (downloadService == null || downloadFile == null) { + return; } - if (downloadService == null || downloadFile == null) { - return; - } - if(song.isStarred()) { + if(item.isStarred()) { if(!starred) { + if(starButton.getDrawable() == null) { + starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star)); + } starButton.setVisibility(View.VISIBLE); starred = true; } @@ -216,13 +209,13 @@ public class SongView extends UpdateView implements Checkable { } } - if (isWorkDone) { + if (isWorkDone) { int moreImage = isSaved ? R.drawable.download_pinned : R.drawable.download_cached; if(moreImage != this.moreImage) { moreButton.setImageResource(moreImage); this.moreImage = moreImage; } - } else if(this.moreImage != R.drawable.download_none_light) { + } else if(this.moreImage != R.drawable.download_none_light) { int[] attrs = new int[] {R.attr.download_none}; TypedArray typedArray = context.obtainStyledAttributes(attrs); moreButton.setImageResource(typedArray.getResourceId(0, 0)); @@ -230,7 +223,7 @@ public class SongView extends UpdateView implements Checkable { this.moreImage = R.drawable.download_none_light; } - if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) { + if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) { double percentage = (partialFile.length() * 100.0) / downloadFile.getEstimatedSize(); percentage = Math.min(percentage, 100); statusTextView.setText((int)percentage + " %"); @@ -238,29 +231,33 @@ public class SongView extends UpdateView implements Checkable { statusImageView.setVisibility(View.VISIBLE); rightImage = true; } - } else if(rightImage) { - statusTextView.setText(null); + } else if(rightImage) { + statusTextView.setText(null); statusImageView.setVisibility(View.GONE); rightImage = false; - } + } - boolean playing = downloadService.getCurrentPlaying() == downloadFile; - if (playing) { + boolean playing = downloadService.getCurrentPlaying() == downloadFile; + if (playing) { if(!this.playing) { this.playing = playing; int[] attrs = new int[] {R.attr.media_button_start}; TypedArray typedArray = context.obtainStyledAttributes(attrs); - titleTextView.setCompoundDrawablesWithIntrinsicBounds(typedArray.getResourceId(0, 0), 0, 0, 0); + titleTextView.setCompoundDrawablesWithIntrinsicBounds(typedArray.getResourceId(0, 0), 0, 0, 0); } - } else { + } else { if(this.playing) { this.playing = playing; - titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } } if(isBookmarked) { if(!bookmarked) { + if(bookmarkButton.getDrawable() == null) { + bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected)); + } + bookmarkButton.setVisibility(View.VISIBLE); bookmarked = true; } @@ -295,24 +292,9 @@ public class SongView extends UpdateView implements Checkable { rating = isRated; } - } - - @Override - public void setChecked(boolean b) { - checkedTextView.setChecked(b); - } - - @Override - public boolean isChecked() { - return checkedTextView.isChecked(); - } - - @Override - public void toggle() { - checkedTextView.toggle(); - } + } public MusicDirectory.Entry getEntry() { - return song; + return item; } } diff --git a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java b/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java deleted file mode 100644 index 3047d5d7..00000000 --- a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java +++ /dev/null @@ -1,128 +0,0 @@ -package github.daneren2005.dsub.view; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.widget.AbsListView; -import android.widget.GridView; -import android.widget.ListAdapter; - -import java.lang.reflect.Field; - -/** - * Created by Scott on 4/26/2014. - */ -public class UnscrollableGridView extends GridView { - private static final String TAG = UnscrollableGridView.class.getSimpleName(); - - public UnscrollableGridView(Context context) { - super(context); - } - - public UnscrollableGridView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public UnscrollableGridView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public int getColumnWidth() { - // This method will be called from onMeasure() too. - // It's better to use getMeasuredWidth(), as it is safe in this case. - - int hSpacing = 20; - try { - Field field = GridView.class.getDeclaredField("mHorizontalSpacing"); - field.setAccessible(true); - hSpacing = field.getInt(this); - } catch(Exception e) { - - } - - final int totalHorizontalSpacing = getNumColumnsCompat() > 0 ? (getNumColumnsCompat() - 1) * hSpacing : 0; - return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - totalHorizontalSpacing) / getNumColumnsCompat(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Sets the padding for this view. - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - final int measuredWidth = getMeasuredWidth(); - final int childWidth = getColumnWidth(); - int childHeight = 0; - - // If there's an adapter, use it to calculate the height of this view. - final ListAdapter adapter = getAdapter(); - final int count; - - // There shouldn't be any inherent size (due to padding) if there are no child views. - if (adapter == null || (count = adapter.getCount()) == 0) { - setMeasuredDimension(0, 0); - return; - } - - // Get the first child from the adapter. - final View child = adapter.getView(0, null, this); - if (child != null) { - // Set a default LayoutParams on the child, if it doesn't have one on its own. - AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams(); - if (params == null) { - params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT, - AbsListView.LayoutParams.WRAP_CONTENT); - child.setLayoutParams(params); - } - - // Measure the exact width of the child, and the height based on the width. - // Note: the child takes care of calculating its height. - int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY); - int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - child.measure(childWidthSpec, childHeightSpec); - childHeight = child.getMeasuredHeight(); - } - - int vSpacing = 10; - try { - Field field = GridView.class.getDeclaredField("mVerticalSpacing"); - field.setAccessible(true); - vSpacing = field.getInt(this); - } catch(Exception e) { - - } - - // Number of rows required to 'mTotal' items. - final int rows = (int) Math.ceil((double) getCount() / getNumColumnsCompat()); - final int childrenHeight = childHeight * rows; - final int totalVerticalSpacing = rows > 0 ? (rows - 1) * vSpacing : 0; - - // Total height of this view. - final int measuredHeight = Math.abs(childrenHeight + getPaddingTop() + getPaddingBottom() + totalVerticalSpacing); - setMeasuredDimension(measuredWidth, measuredHeight); - } - - private int getNumColumnsCompat() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - return getNumColumnsCompat11(); - } else { - int columns = 0; - int children = getChildCount(); - if (children > 0) { - int width = getChildAt(0).getMeasuredWidth(); - if (width > 0) { - columns = getWidth() / width; - } - } - return columns > 0 ? columns : AUTO_FIT; - } - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private int getNumColumnsCompat11() { - return getNumColumns(); - } -} diff --git a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java index f9c62121..fd74c872 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java @@ -20,9 +20,10 @@ package github.daneren2005.dsub.view; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; +import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -37,20 +38,21 @@ import java.util.List; import java.util.WeakHashMap; import github.daneren2005.dsub.domain.MusicDirectory; -import github.daneren2005.dsub.util.ImageLoader; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.util.DrawableTint; import github.daneren2005.dsub.util.SilentBackgroundTask; -public class UpdateView extends LinearLayout { +public abstract class UpdateView<T> extends LinearLayout { private static final String TAG = UpdateView.class.getSimpleName(); private static final WeakHashMap<UpdateView, ?> INSTANCES = new WeakHashMap<UpdateView, Object>(); - private static Handler backgroundHandler; - private static Handler uiHandler; + protected static Handler backgroundHandler; + protected static Handler uiHandler; private static Runnable updateRunnable; private static int activeActivities = 0; protected Context context; + protected T item; protected RatingBar ratingBar; protected ImageButton starButton; protected ImageView moreButton; @@ -63,8 +65,10 @@ public class UpdateView extends LinearLayout { protected int isRated = 0; protected int rating = 0; protected SilentBackgroundTask<Void> imageTask = null; + protected Drawable startBackgroundDrawable; protected final boolean autoUpdate; + protected boolean checkable; public UpdateView(Context context) { this(context, true); @@ -75,8 +79,8 @@ public class UpdateView extends LinearLayout { this.autoUpdate = autoUpdate; setLayoutParams(new AbsListView.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); if(autoUpdate) { INSTANCES.put(this, null); @@ -89,37 +93,16 @@ public class UpdateView extends LinearLayout { } - public void setObject(Object obj) { + public void setObject(T obj) { + item = obj; setObjectImpl(obj); updateBackground(); update(); } - public void setObject(Object obj1, Object obj2) { - if(imageTask != null) { - imageTask.cancel(); - imageTask = null; - } - - setObjectImpl(obj1, obj2); - backgroundHandler.post(new Runnable() { - @Override - public void run() { - updateBackground(); - uiHandler.post(new Runnable() { - @Override - public void run() { - update(); - } - }); - } - }); - } - protected void setObjectImpl(Object obj) { - - } - protected void setObjectImpl(Object obj1, Object obj2) { - + public void setObject(T obj1, Object obj2) { + setObject(obj1, null); } + protected abstract void setObjectImpl(T obj); private static synchronized void startUpdater() { if(uiHandler != null) { @@ -224,8 +207,6 @@ public class UpdateView extends LinearLayout { MusicDirectory.Entry check = null; if(view instanceof SongView) { check = ((SongView) view).getEntry(); - } else if(view instanceof AlbumCell) { - check = ((AlbumCell) view).getEntry(); } else if(view instanceof AlbumView) { check = ((AlbumView) view).getEntry(); } @@ -261,6 +242,9 @@ public class UpdateView extends LinearLayout { if(starButton != null) { if(isStarred) { if(!starred) { + if(starButton.getDrawable() == null) { + starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star)); + } starButton.setVisibility(View.VISIBLE); starred = true; } @@ -283,4 +267,55 @@ public class UpdateView extends LinearLayout { rating = isRated; } } + + public boolean isCheckable() { + return checkable; + } + public void setChecked(boolean checked) { + View child = getChildAt(0); + if (checked && startBackgroundDrawable == null) { + startBackgroundDrawable = child.getBackground(); + child.setBackgroundColor(DrawableTint.getColorRes(context, R.attr.colorPrimary)); + } else if (!checked && startBackgroundDrawable != null) { + child.setBackgroundDrawable(startBackgroundDrawable); + startBackgroundDrawable = null; + } + } + + public void onClick() { + + } + + public static class UpdateViewHolder<T> extends RecyclerView.ViewHolder { + private UpdateView updateView; + private View view; + private T item; + + public UpdateViewHolder(UpdateView itemView) { + super(itemView); + + this.updateView = itemView; + this.view = itemView; + } + + // Different is so that call is not ambiguous + public UpdateViewHolder(View view, boolean different) { + super(view); + this.view = view; + } + + public UpdateView<T> getUpdateView() { + return updateView; + } + public View getView() { + return view; + } + public void setItem(T item) { + this.item = item; + } + public T getItem() { + return item; + } + } } + diff --git a/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java new file mode 100644 index 00000000..0f0b5455 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java @@ -0,0 +1,47 @@ +package github.daneren2005.dsub.view; + +import android.content.Context; + +public abstract class UpdateView2<T1, T2> extends UpdateView<T1> { + protected T2 item2; + + public UpdateView2(Context context) { + super(context); + } + + public UpdateView2(Context context, boolean autoUpdate) { + super(context, autoUpdate); + } + + public final void setObject(T1 obj1) { + setObject(obj1, null); + } + @Override + public void setObject(T1 obj1, Object obj2) { + item = obj1; + item2 = (T2) obj2; + if(imageTask != null) { + imageTask.cancel(); + imageTask = null; + } + + setObjectImpl(item, item2); + backgroundHandler.post(new Runnable() { + @Override + public void run() { + updateBackground(); + uiHandler.post(new Runnable() { + @Override + public void run() { + update(); + } + }); + } + }); + } + + protected final void setObjectImpl(T1 obj1) { + setObjectImpl(obj1, null); + } + protected abstract void setObjectImpl(T1 obj1, T2 obj2); +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/UserView.java b/app/src/main/java/github/daneren2005/dsub/view/UserView.java index dec8dbef..a97d755b 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/UserView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/UserView.java @@ -25,15 +25,12 @@ import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.User; import github.daneren2005.dsub.util.ImageLoader; -public class UserView extends UpdateView { - private User user; - +public class UserView extends UpdateView2<User, ImageLoader> { private TextView usernameView; private ImageView avatarView; public UserView(Context context) { super(context, false); - this.context = context; LayoutInflater.from(context).inflate(R.layout.user_list_item, this, true); usernameView = (TextView) findViewById(R.id.item_name); @@ -46,9 +43,8 @@ public class UserView extends UpdateView { }); } - protected void setObjectImpl(Object obj, Object obj2) { - this.user = (User) obj; + protected void setObjectImpl(User user, ImageLoader imageLoader) { usernameView.setText(user.getUsername()); - imageTask = ((ImageLoader)obj2).loadAvatar(context, avatarView, user.getUsername()); + imageTask = imageLoader.loadAvatar(context, avatarView, user.getUsername()); } } |