Import minamp 1.0 source

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xockoandClaude Opus 5.5 committed 2026-10-14 19:38:37 +02:00
commit 0df8124629
194 files changed
+7319

No files matched your search

+428
View File
@@ -0,0 +1,428 @@
package com.minamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.minamp.plex.Item;
import com.minamp.plex.Library;
import com.minamp.plex.Server;
import com.minamp.util.Json;
public class HomeActivity extends BaseActivity {
private static final int TAB_HOME = 0, TAB_ARTISTS = 1, TAB_ALBUMS = 2, TAB_PLAYLISTS = 3;
private static final String[] ALBUM_SORTS = {"titleSort", "artist.titleSort", "addedAt:desc", "year:desc", "viewCount:desc"};
private static final String[] ALBUM_SORT_LABELS = {"Title", "Artist", "Recently added", "Year (newest)", "Most played"};
private TextView[] tabs;
private View tabLine;
private ListView artistsList, albumsList, playlistsList;
private ItemAdapter artists, albums, playlists;
private LinearLayout homeContent;
private int tab = -1;
private boolean[] loaded = new boolean[4];
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
if (!app.isConfigured()) {
startActivity(new Intent(this, SetupActivity.class));
finish();
return;
}
setContentView(R.layout.activity_home);
setupTopBar(app.sectionTitle(), connectionLine(), false);
barAction(R.id.btn_action, Glyph.SEARCH, new View.OnClickListener() {
public void onClick(View v) { startActivity(new Intent(HomeActivity.this, SearchActivity.class)); }
});
barAction(R.id.btn_action2, Glyph.MORE, new View.OnClickListener() {
public void onClick(View v) { openOptionsMenu(); }
});
tabs = new TextView[] {
(TextView) findViewById(R.id.tab_home), (TextView) findViewById(R.id.tab_artists),
(TextView) findViewById(R.id.tab_albums), (TextView) findViewById(R.id.tab_playlists)
};
tabLine = findViewById(R.id.tab_line);
for (int i = 0; i < tabs.length; i++) {
final int t = i;
tabs[i].setOnClickListener(new View.OnClickListener() { public void onClick(View v) { showTab(t); } });
}
homeContent = (LinearLayout) findViewById(R.id.home_content);
artistsList = (ListView) findViewById(R.id.list_artists);
albumsList = (ListView) findViewById(R.id.list_albums);
playlistsList = (ListView) findViewById(R.id.list_playlists);
artists = new ItemAdapter(this, ItemAdapter.MODE_DEFAULT);
albums = new ItemAdapter(this, ItemAdapter.MODE_DEFAULT);
playlists = new ItemAdapter(this, ItemAdapter.MODE_DEFAULT);
wire(artistsList, artists);
wire(albumsList, albums);
wire(playlistsList, playlists);
artists.setPager(new ItemAdapter.Pager() { public void loadMore(int from) { loadArtists(from); } });
albums.setPager(new ItemAdapter.Pager() { public void loadMore(int from) { loadAlbums(from); } });
Server.listener = new Server.Listener() {
public void onConnectionChanged(Server s) {
app.handler.post(new Runnable() { public void run() { if (!destroyed) setBarSub(connectionLine()); } });
}
};
showTab(app.prefs.getInt("tab", TAB_HOME));
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// library or server may have changed in setup
if (!app.isConfigured()) { startActivity(new Intent(this, SetupActivity.class)); finish(); return; }
setupTopBar(app.sectionTitle(), connectionLine(), false);
loaded = new boolean[4];
int t = tab;
tab = -1;
showTab(t);
}
@Override
protected void onConnectionChanged() { setBarSub(connectionLine()); }
private String connectionLine() {
Server s = app.server;
if (s == null) return "";
Server.Conn c = s.active();
return s.name + (c != null ? " · " + Server.kindLabel(c.kind) : "");
}
private void wire(final ListView lv, final ItemAdapter ad) {
lv.setAdapter(ad);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> p, View v, int pos, long id) { open(ad.getItem(pos), null, 0, null); }
});
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> p, View v, int pos, long id) { itemMenu(ad.getItem(pos)); return true; }
});
}
private void showTab(int t) {
if (t == tab) return;
tab = t;
app.prefs.edit().putInt("tab", t).commit();
int accent = getResources().getColor(R.color.accent), dim = getResources().getColor(R.color.text2);
for (int i = 0; i < tabs.length; i++) tabs[i].setTextColor(i == t ? accent : dim);
int w = getResources().getDisplayMetrics().widthPixels / 4;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabLine.getLayoutParams();
lp.width = w;
lp.leftMargin = w * t;
tabLine.setLayoutParams(lp);
findViewById(R.id.home_scroll).setVisibility(t == TAB_HOME ? View.VISIBLE : View.GONE);
artistsList.setVisibility(t == TAB_ARTISTS ? View.VISIBLE : View.GONE);
albumsList.setVisibility(t == TAB_ALBUMS ? View.VISIBLE : View.GONE);
playlistsList.setVisibility(t == TAB_PLAYLISTS ? View.VISIBLE : View.GONE);
hideStatus();
if (!loaded[t]) {
loaded[t] = true;
switch (t) {
case TAB_HOME: buildHome(); break;
case TAB_ARTISTS: loadArtists(0); break;
case TAB_ALBUMS: loadAlbums(0); break;
case TAB_PLAYLISTS: loadPlaylists(); break;
}
}
}
// ------------------------------------------------------------------ home tab
private void buildHome() {
homeContent.removeAllViews();
LayoutInflater inf = getLayoutInflater();
int pad = Ui.dp(this, 8);
LinearLayout row1 = tileRow();
row1.addView(tile(inf, Glyph.RADIO, "Library radio", "Shuffle everything", new View.OnClickListener() {
public void onClick(View v) {
final Library lib = lib();
runRadio("Library radio", new Task<List<Item>>() { public List<Item> work() throws Exception { return lib.libraryRadio(); } });
}
}));
row1.addView(spacer());
row1.addView(tile(inf, Glyph.HEART, "Loved radio", "Your top rated", new View.OnClickListener() {
public void onClick(View v) {
final Library lib = lib();
runRadio("Loved radio", new Task<List<Item>>() { public List<Item> work() throws Exception { return lib.topRatedRadio(); } });
}
}));
homeContent.addView(row1);
LinearLayout row2 = tileRow();
row2.addView(tile(inf, Glyph.CLOCK, "Time travel", "Pick a decade", new View.OnClickListener() {
public void onClick(View v) { timeTravel(); }
}));
row2.addView(spacer());
row2.addView(tile(inf, Glyph.SHUFFLE, "Random album", "Feeling lucky", new View.OnClickListener() {
public void onClick(View v) { randomAlbum(); }
}));
homeContent.addView(row2);
addShelf(inf, "Recently added", lib().recentlyAddedPath(20), ALBUM_SORTS[2]);
addShelf(inf, "Recently played", lib().recentlyPlayedPath(20), null);
homeContent.setPadding(0, pad / 2, 0, pad);
}
private LinearLayout tileRow() {
LinearLayout r = new LinearLayout(this);
r.setOrientation(LinearLayout.HORIZONTAL);
int p = Ui.dp(this, 8);
r.setPadding(p, p / 2, p, p / 2);
return r;
}
private View spacer() {
View v = new View(this);
v.setLayoutParams(new LinearLayout.LayoutParams(Ui.dp(this, 8), 1));
return v;
}
private View tile(LayoutInflater inf, int glyph, String title, String sub, View.OnClickListener l) {
View t = inf.inflate(R.layout.view_home_tile, null);
t.setLayoutParams(new LinearLayout.LayoutParams(0, Ui.dp(this, 56), 1f));
Ui.icon((ImageView) t.findViewById(R.id.tile_icon), glyph, getResources().getColor(R.color.accent), 22);
((TextView) t.findViewById(R.id.tile_title)).setText(title);
((TextView) t.findViewById(R.id.tile_sub)).setText(sub);
t.setOnClickListener(l);
return t;
}
private void addShelf(LayoutInflater inf, final String title, final String path, final String seeAllSort) {
LinearLayout head = new LinearLayout(this);
head.setOrientation(LinearLayout.HORIZONTAL);
TextView h = (TextView) inf.inflate(R.layout.row_header, null);
h.setText(title.toUpperCase());
head.addView(h, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
if (seeAllSort != null) {
TextView all = (TextView) inf.inflate(R.layout.row_header, null);
all.setText("SEE ALL ›");
all.setTextColor(getResources().getColor(R.color.text2));
all.setPadding(Ui.dp(this, 10), Ui.dp(this, 12), Ui.dp(this, 10), Ui.dp(this, 4));
all.setBackgroundResource(R.drawable.bg_pressable);
all.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
app.prefs.edit().putInt("albumSort", 2).commit();
loaded[TAB_ALBUMS] = false;
showTab(TAB_ALBUMS);
}
});
head.addView(all, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
homeContent.addView(head);
HorizontalScrollView hs = new HorizontalScrollView(this);
hs.setHorizontalScrollBarEnabled(false);
hs.setHorizontalFadingEdgeEnabled(false);
final LinearLayout strip = new LinearLayout(this);
strip.setOrientation(LinearLayout.HORIZONTAL);
strip.setPadding(Ui.dp(this, 6), 0, Ui.dp(this, 6), 0);
hs.addView(strip);
final TextView loading = new TextView(this);
loading.setText("Loading…");
loading.setTextColor(getResources().getColor(R.color.text2));
loading.setTextSize(12);
loading.setPadding(Ui.dp(this, 6), Ui.dp(this, 30), 0, Ui.dp(this, 30));
strip.addView(loading);
homeContent.addView(hs);
final Library lib = lib();
run(new Task<List<Item>>() {
public List<Item> work() throws Exception { return lib.server.items(path); }
public void done(List<Item> items) {
strip.removeAllViews();
if (items.isEmpty()) { loading.setText("Nothing here yet"); strip.addView(loading); return; }
LayoutInflater li = getLayoutInflater();
int art = Ui.dp(HomeActivity.this, 88);
for (int i = 0; i < items.size(); i++) {
final Item it = items.get(i);
View card = li.inflate(R.layout.view_album_card, strip, false);
((TextView) card.findViewById(R.id.card_title)).setText(it.title);
((TextView) card.findViewById(R.id.card_sub)).setText(it.artist);
app.images.load((ImageView) card.findViewById(R.id.card_art), it.thumb, art, R.drawable.art_placeholder);
card.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { open(it, null, 0, null); } });
card.setOnLongClickListener(new View.OnLongClickListener() { public boolean onLongClick(View v) { itemMenu(it); return true; } });
strip.addView(card);
}
}
public void fail(Exception e) {
loading.setText("Couldn't load: " + msg(e) + " (tap to retry)");
loading.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) { loaded[TAB_HOME] = false; buildHome(); loaded[TAB_HOME] = true; }
});
}
});
}
private void timeTravel() {
final Library lib = lib();
toast("Finding decades…");
run(new Task<List<Integer>>() {
public List<Integer> work() throws Exception { return lib.decades(); }
public void done(final List<Integer> decades) {
if (decades.isEmpty()) { toast("No release years in this library"); return; }
String[] labels = new String[decades.size() + 1];
labels[0] = "Surprise me";
for (int i = 0; i < decades.size(); i++) labels[i + 1] = decades.get(i) + "s";
new AlertDialog.Builder(HomeActivity.this).setTitle("Time travel radio")
.setItems(labels, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int which) {
final int dec = which == 0 ? decades.get(new Random().nextInt(decades.size())).intValue()
: decades.get(which - 1).intValue();
runRadio(dec + "s radio", new Task<List<Item>>() {
public List<Item> work() throws Exception { return lib.timeTravelRadio(dec); }
});
}
}).show();
}
public void fail(Exception e) { toast(msg(e)); }
});
}
private void randomAlbum() {
final Library lib = lib();
run(new Task<List<Item>>() {
public List<Item> work() throws Exception { return lib.server.items(lib.albumsPath("random", 0, 1)); }
public void done(List<Item> l) {
if (l.isEmpty()) { toast("No albums"); return; }
Item a = l.get(0);
playContainer(a, false, 0);
open(a, null, 0, null);
}
public void fail(Exception e) { toast(msg(e)); }
});
}
// ------------------------------------------------------------------ list tabs
private int totalOf(Json.Obj mc, int fallback) {
if (mc == null) return fallback;
return mc.optInt("totalSize", fallback);
}
private void loadArtists(final int from) {
final Library lib = lib();
if (from == 0) showStatus(true, "Loading artists…", null);
run(new Task<Object[]>() {
public Object[] work() throws Exception {
Json.Obj mc = lib.server.get(lib.artistsPath(from, 100));
return new Object[] { Item.listFrom(mc), Integer.valueOf(totalOf(mc, -1)) };
}
@SuppressWarnings("unchecked")
public void done(Object[] r) {
if (tab == TAB_ARTISTS) hideStatus();
List<Item> l = (List<Item>) r[0];
int total = ((Integer) r[1]).intValue();
if (from == 0) artists.set(l, total); else artists.append(l, total);
if (from == 0 && l.isEmpty() && tab == TAB_ARTISTS) showStatus(false, "No artists in this library", null);
}
public void fail(Exception e) {
artists.loadFailed();
if (from == 0 && tab == TAB_ARTISTS) showStatus(false, msg(e), new Runnable() { public void run() { loadArtists(0); } });
else toast(msg(e));
}
});
}
private void loadAlbums(final int from) {
final Library lib = lib();
final String sort = ALBUM_SORTS[Math.min(app.prefs.getInt("albumSort", 0), ALBUM_SORTS.length - 1)];
if (from == 0) showStatus(true, "Loading albums…", null);
run(new Task<Object[]>() {
public Object[] work() throws Exception {
Json.Obj mc = lib.server.get(lib.albumsPath(sort, from, 100));
return new Object[] { Item.listFrom(mc), Integer.valueOf(totalOf(mc, -1)) };
}
@SuppressWarnings("unchecked")
public void done(Object[] r) {
if (tab == TAB_ALBUMS) hideStatus();
List<Item> l = (List<Item>) r[0];
int total = ((Integer) r[1]).intValue();
if (from == 0) { albums.set(l, total); albumsList.setSelection(0); } else albums.append(l, total);
if (from == 0 && l.isEmpty() && tab == TAB_ALBUMS) showStatus(false, "No albums in this library", null);
}
public void fail(Exception e) {
albums.loadFailed();
if (from == 0 && tab == TAB_ALBUMS) showStatus(false, msg(e), new Runnable() { public void run() { loadAlbums(0); } });
else toast(msg(e));
}
});
}
private void loadPlaylists() {
final Library lib = lib();
showStatus(true, "Loading playlists…", null);
run(new Task<List<Item>>() {
public List<Item> work() throws Exception { return lib.server.items(lib.playlistsPath()); }
public void done(List<Item> l) {
if (tab == TAB_PLAYLISTS) hideStatus();
List<Item> audio = new ArrayList<Item>();
for (int i = 0; i < l.size(); i++) if (l.get(i).type == Item.PLAYLIST) audio.add(l.get(i));
playlists.set(audio, -1);
if (audio.isEmpty() && tab == TAB_PLAYLISTS) showStatus(false, "No music playlists yet", null);
}
public void fail(Exception e) {
if (tab == TAB_PLAYLISTS) showStatus(false, msg(e), new Runnable() { public void run() { loadPlaylists(); } });
}
});
}
// ------------------------------------------------------------------ menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, 10, 0, "Sort albums");
menu.add(0, 11, 0, "Refresh");
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem s = menu.findItem(10);
if (s != null) s.setVisible(tab == TAB_ALBUMS);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == 10) {
new AlertDialog.Builder(this).setTitle("Sort albums by")
.setSingleChoiceItems(ALBUM_SORT_LABELS, app.prefs.getInt("albumSort", 0), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int which) {
d.dismiss();
app.prefs.edit().putInt("albumSort", which).commit();
loadAlbums(0);
}
}).show();
return true;
}
if (item.getItemId() == 11) {
loaded = new boolean[4];
int t = tab;
tab = -1;
showTab(t);
return true;
}
return super.onOptionsItemSelected(item);
}
}