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); showCrashReportIfAny(); 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>() { public List 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>() { public List 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>() { public List work() throws Exception { return lib.server.items(path); } public void done(List 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>() { public List work() throws Exception { return lib.decades(); } public void done(final List 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>() { public List 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>() { public List work() throws Exception { return lib.server.items(lib.albumsPath("random", 0, 1)); } public void done(List 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() { 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 l = (List) 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() { 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 l = (List) 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>() { public List work() throws Exception { return lib.server.items(lib.playlistsPath()); } public void done(List l) { if (tab == TAB_PLAYLISTS) hideStatus(); List audio = new ArrayList(); 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); } }