Private
Public Access
Import minamp 1.0 source
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
0df8124629
194 files changed
+7319
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user