287 lines
8.2 KiB
Java
287 lines
8.2 KiB
Java
/*
|
|
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 2009 (C) Sindre Mehus
|
|
*/
|
|
package net.nullsum.audinaut.view;
|
|
|
|
import android.content.Context;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.AbsListView;
|
|
import android.widget.ImageView;
|
|
import android.widget.LinearLayout;
|
|
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
import net.nullsum.audinaut.R;
|
|
import net.nullsum.audinaut.domain.MusicDirectory;
|
|
import net.nullsum.audinaut.util.DrawableTint;
|
|
import net.nullsum.audinaut.util.SilentBackgroundTask;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.WeakHashMap;
|
|
|
|
public abstract class UpdateView<T> extends LinearLayout {
|
|
private static final String TAG = UpdateView.class.getSimpleName();
|
|
private static final WeakHashMap<UpdateView, ?> INSTANCES = new WeakHashMap<>();
|
|
|
|
static Handler backgroundHandler;
|
|
static Handler uiHandler;
|
|
private static Runnable updateRunnable;
|
|
private static int activeActivities = 0;
|
|
Context context;
|
|
T item;
|
|
ImageView moreButton;
|
|
View coverArtView;
|
|
boolean exists = false;
|
|
boolean pinned = false;
|
|
SilentBackgroundTask<Void> imageTask = null;
|
|
boolean checkable;
|
|
private boolean shaded = false;
|
|
private Drawable startBackgroundDrawable;
|
|
|
|
UpdateView(Context context, boolean autoUpdate) {
|
|
super(context);
|
|
this.context = context;
|
|
|
|
setLayoutParams(new AbsListView.LayoutParams(
|
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
|
|
|
if (autoUpdate) {
|
|
INSTANCES.put(this, null);
|
|
}
|
|
startUpdater();
|
|
}
|
|
|
|
private static synchronized void startUpdater() {
|
|
if (uiHandler != null) {
|
|
return;
|
|
}
|
|
|
|
uiHandler = new Handler();
|
|
// Needed so handler is never null until thread creates it
|
|
backgroundHandler = uiHandler;
|
|
updateRunnable = UpdateView::updateAll;
|
|
|
|
new Thread(() -> {
|
|
Looper.prepare();
|
|
backgroundHandler = new Handler(Looper.myLooper());
|
|
uiHandler.post(updateRunnable);
|
|
Looper.loop();
|
|
}, "UpdateView").start();
|
|
}
|
|
|
|
public static synchronized void triggerUpdate() {
|
|
if (backgroundHandler != null) {
|
|
uiHandler.removeCallbacksAndMessages(null);
|
|
backgroundHandler.removeCallbacksAndMessages(null);
|
|
uiHandler.post(updateRunnable);
|
|
}
|
|
}
|
|
|
|
private static void updateAll() {
|
|
try {
|
|
// If nothing can see this, stop updating
|
|
if (activeActivities == 0) {
|
|
activeActivities--;
|
|
return;
|
|
}
|
|
|
|
List<UpdateView> views = new ArrayList<>();
|
|
for (UpdateView view : INSTANCES.keySet()) {
|
|
if (view.isShown()) {
|
|
views.add(view);
|
|
}
|
|
}
|
|
if (views.size() > 0) {
|
|
updateAllLive(views);
|
|
} else {
|
|
uiHandler.postDelayed(updateRunnable, 2000L);
|
|
}
|
|
} catch (Throwable x) {
|
|
Log.w(TAG, "Error when updating song views.", x);
|
|
}
|
|
}
|
|
|
|
private static void updateAllLive(final List<UpdateView> views) {
|
|
final Runnable runnable = () -> {
|
|
try {
|
|
for (UpdateView view : views) {
|
|
view.update();
|
|
}
|
|
} catch (Throwable x) {
|
|
Log.w(TAG, "Error when updating song views.", x);
|
|
}
|
|
uiHandler.postDelayed(updateRunnable, 1000L);
|
|
};
|
|
|
|
backgroundHandler.post(() -> {
|
|
try {
|
|
for (UpdateView view : views) {
|
|
view.updateBackground();
|
|
}
|
|
uiHandler.post(runnable);
|
|
} catch (Throwable x) {
|
|
Log.w(TAG, "Error when updating song views.", x);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void addActiveActivity() {
|
|
activeActivities++;
|
|
|
|
if (activeActivities == 0 && uiHandler != null && updateRunnable != null) {
|
|
activeActivities++;
|
|
uiHandler.post(updateRunnable);
|
|
}
|
|
}
|
|
|
|
public static void removeActiveActivity() {
|
|
activeActivities--;
|
|
}
|
|
|
|
public static MusicDirectory.Entry findEntry(MusicDirectory.Entry entry) {
|
|
for (UpdateView view : INSTANCES.keySet()) {
|
|
MusicDirectory.Entry check = null;
|
|
if (view instanceof SongView) {
|
|
check = ((SongView) view).getEntry();
|
|
} else if (view instanceof AlbumView) {
|
|
check = ((AlbumView) view).getEntry();
|
|
}
|
|
|
|
if (check != null && entry != check && check.getId().equals(entry.getId())) {
|
|
return check;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void setPressed(boolean pressed) {
|
|
|
|
}
|
|
|
|
public void setObject(T obj) {
|
|
if (item == obj) {
|
|
return;
|
|
}
|
|
|
|
item = obj;
|
|
if (imageTask != null) {
|
|
imageTask.cancel();
|
|
imageTask = null;
|
|
}
|
|
if (coverArtView != null && coverArtView instanceof ImageView) {
|
|
((ImageView) coverArtView).setImageDrawable(null);
|
|
}
|
|
setObjectImpl(obj);
|
|
updateBackground();
|
|
update();
|
|
}
|
|
|
|
protected abstract void setObjectImpl(T obj);
|
|
|
|
void updateBackground() {
|
|
|
|
}
|
|
|
|
void update() {
|
|
if (moreButton != null) {
|
|
if (exists || pinned) {
|
|
if (!shaded) {
|
|
moreButton.setImageResource(exists ? R.drawable.download_cached : R.drawable.download_pinned);
|
|
shaded = true;
|
|
}
|
|
} else {
|
|
if (shaded) {
|
|
moreButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.download_none));
|
|
shaded = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (coverArtView != null && coverArtView instanceof RecyclingImageView) {
|
|
RecyclingImageView recyclingImageView = (RecyclingImageView) coverArtView;
|
|
if (recyclingImageView.isInvalidated()) {
|
|
onUpdateImageView();
|
|
}
|
|
}
|
|
}
|
|
|
|
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.setBackground(startBackgroundDrawable);
|
|
startBackgroundDrawable = null;
|
|
}
|
|
}
|
|
|
|
void onUpdateImageView() {
|
|
|
|
}
|
|
|
|
public static class UpdateViewHolder<T> extends RecyclerView.ViewHolder {
|
|
private final View view;
|
|
private UpdateView updateView;
|
|
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) {
|
|
super(view);
|
|
this.view = view;
|
|
}
|
|
|
|
public UpdateView<T> getUpdateView() {
|
|
return updateView;
|
|
}
|
|
|
|
public View getView() {
|
|
return view;
|
|
}
|
|
|
|
public T getItem() {
|
|
return item;
|
|
}
|
|
|
|
public void setItem(T item) {
|
|
this.item = item;
|
|
}
|
|
}
|
|
}
|
|
|