Private
Public Access
Minamp 1.6: adjustable UI size, larger text and controls
- New setting "Text and button size" (Normal / Large / Extra large): scales text, icons and buttons on top of the phone's density (App.UI_SCALES) - Larger default text, rows, tiles, touch targets and player icons; all sizes now live in res/values/dimens.xml - Seek bar knob drawn in code, since Android 2.x caches XML shapes at the size they were first loaded - Full-width buttons wrap to two lines instead of cutting words off - Shorter labels so they fit 240x320 screens in both languages - tools/check_fit.py checks that UI texts fit their slots at every size and language using Droid Sans metrics; build.sh runs it and setup_toolchain.sh fetches the font Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fa67e95fde
commit
bbdab09f83
32 files changed
+457
-190
No files matched your search
+25
-4
@@ -111,7 +111,18 @@ public class App extends Application {
|
||||
return code;
|
||||
}
|
||||
|
||||
/** Forces the app's language on a context's resources (call before inflating any layout). */
|
||||
/** UI size: everything (text, icons, buttons) is scaled by this on top of the phone's density. */
|
||||
public static final float[] UI_SCALES = {1.0f, 1.15f, 1.3f};
|
||||
|
||||
public int uiScaleIndex() {
|
||||
int i = prefs.getInt("uiScale", 0);
|
||||
return i < 0 || i >= UI_SCALES.length ? 0 : i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the app's language and UI size on a context's resources (call before inflating any
|
||||
* layout). The phone's own density comes from the system resources, which are never touched.
|
||||
*/
|
||||
public static void applyLanguage(android.content.Context c) {
|
||||
App app = instance;
|
||||
if (app == null) return;
|
||||
@@ -119,9 +130,19 @@ public class App extends Application {
|
||||
java.util.Locale.setDefault(loc);
|
||||
android.content.res.Resources r = c.getResources();
|
||||
android.content.res.Configuration cfg = r.getConfiguration();
|
||||
if (loc.equals(cfg.locale)) return;
|
||||
cfg.locale = loc;
|
||||
r.updateConfiguration(cfg, r.getDisplayMetrics());
|
||||
android.util.DisplayMetrics dm = r.getDisplayMetrics();
|
||||
float base = android.content.res.Resources.getSystem().getDisplayMetrics().density;
|
||||
float want = base * UI_SCALES[app.uiScaleIndex()];
|
||||
boolean localeOk = loc.equals(cfg.locale);
|
||||
boolean sizeOk = Math.abs(dm.density - want) < 0.001f;
|
||||
if (localeOk && sizeOk) return;
|
||||
// a copy, so Resources sees what changed (and drops resources cached for the old locale)
|
||||
android.content.res.Configuration nc = new android.content.res.Configuration(cfg);
|
||||
nc.locale = loc;
|
||||
dm.density = want;
|
||||
dm.densityDpi = (int) (want * 160 + 0.5f);
|
||||
dm.scaledDensity = want * (nc.fontScale > 0 ? nc.fontScale : 1f);
|
||||
r.updateConfiguration(nc, dm);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -87,7 +87,7 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
android.widget.ScrollView sv = new android.widget.ScrollView(this);
|
||||
TextView tv = new TextView(this);
|
||||
tv.setText(report);
|
||||
tv.setTextSize(10);
|
||||
tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.text_log) + Ui.dp(this, 1));
|
||||
tv.setTextColor(0xFFDDDDDD);
|
||||
int p = Ui.dp(this, 8);
|
||||
tv.setPadding(p, p, p, p);
|
||||
@@ -196,11 +196,11 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
miniSub = (TextView) findViewById(R.id.mini_sub);
|
||||
miniPlay = (ImageButton) findViewById(R.id.mini_play);
|
||||
miniProgress = (ProgressBar) findViewById(R.id.mini_progress);
|
||||
miniPlayGlyph = Ui.glyph(this, Glyph.PLAY, getResources().getColor(R.color.text), 18);
|
||||
miniPlayGlyph = Ui.glyph(this, Glyph.PLAY, getResources().getColor(R.color.text), 24);
|
||||
miniPlayGlyph.scale(1f);
|
||||
miniPlay.setImageDrawable(miniPlayGlyph);
|
||||
ImageButton next = (ImageButton) findViewById(R.id.mini_next);
|
||||
Ui.icon(next, Glyph.NEXT, getResources().getColor(R.color.text), 16);
|
||||
Ui.icon(next, Glyph.NEXT, getResources().getColor(R.color.text), 21);
|
||||
miniPlay.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) { PlayerService.send(BaseActivity.this, PlayerService.ACTION_TOGGLE); }
|
||||
});
|
||||
@@ -225,7 +225,7 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
miniPlayGlyph.setType(PlayerService.isPlaying() ? Glyph.PAUSE : Glyph.PLAY);
|
||||
if (!it.thumb.equals(miniThumb)) {
|
||||
miniThumb = it.thumb;
|
||||
app.images.load(miniArt, it.thumb, Ui.dp(this, 40), R.drawable.art_placeholder);
|
||||
app.images.load(miniArt, it.thumb, Ui.dimen(this, R.dimen.mini_art), R.drawable.art_placeholder);
|
||||
}
|
||||
updateMiniProgress();
|
||||
}
|
||||
@@ -404,6 +404,7 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
str(R.string.set_connection, conn),
|
||||
str(R.string.set_log),
|
||||
str(R.string.set_language, App.languageName(app.language())),
|
||||
str(R.string.set_size, sizeLabels()[app.uiScaleIndex()]),
|
||||
str(R.string.sign_out),
|
||||
};
|
||||
new AlertDialog.Builder(this)
|
||||
@@ -439,7 +440,8 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
break;
|
||||
case 7: showDiagnostics(); break;
|
||||
case 8: pickLanguage(); break;
|
||||
case 9: confirmSignOut(); break;
|
||||
case 9: pickSize(); break;
|
||||
case 10: confirmSignOut(); break;
|
||||
}
|
||||
}
|
||||
}).show();
|
||||
@@ -453,7 +455,7 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
protected void showDiagnostics() {
|
||||
final android.widget.ScrollView sv = new android.widget.ScrollView(this);
|
||||
final TextView tv = new TextView(this);
|
||||
tv.setTextSize(9);
|
||||
tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.text_log));
|
||||
tv.setTextColor(0xFFDDDDDD);
|
||||
tv.setTypeface(android.graphics.Typeface.MONOSPACE);
|
||||
int p = Ui.dp(this, 6);
|
||||
@@ -576,6 +578,34 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
return l;
|
||||
}
|
||||
|
||||
private String[] sizeLabels() {
|
||||
return new String[] {str(R.string.size_normal), str(R.string.size_large), str(R.string.size_xlarge)};
|
||||
}
|
||||
|
||||
private void pickSize() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.size_title)
|
||||
.setSingleChoiceItems(sizeLabels(), app.uiScaleIndex(), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int which) {
|
||||
d.dismiss();
|
||||
if (which == app.uiScaleIndex()) return;
|
||||
app.prefs.edit().putInt("uiScale", which).commit();
|
||||
relaunchUi();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
/** Rebuilds all screens so a new language or size takes effect everywhere. */
|
||||
private void relaunchUi() {
|
||||
App.applyLanguage(getApplicationContext());
|
||||
App.applyLanguage(BaseActivity.this);
|
||||
app.images.clearMemory(); // artwork is cached at the old pixel sizes
|
||||
startActivity(new Intent(BaseActivity.this, HomeActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra("relaunch", true));
|
||||
// HomeActivity rebuilds itself when it gets the "relaunch" intent; anything else just closes
|
||||
if (!(this instanceof HomeActivity)) finish();
|
||||
}
|
||||
|
||||
private void pickLanguage() {
|
||||
int sel = 0;
|
||||
for (int i = 0; i < App.LANGUAGES.length; i++) if (App.LANGUAGES[i].equals(app.language())) sel = i;
|
||||
@@ -586,12 +616,7 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
d.dismiss();
|
||||
if (App.LANGUAGES[which].equals(app.language())) return;
|
||||
app.prefs.edit().putString("lang", App.LANGUAGES[which]).commit();
|
||||
App.applyLanguage(getApplicationContext());
|
||||
App.applyLanguage(BaseActivity.this);
|
||||
// rebuild the screens in the new language
|
||||
startActivity(new Intent(BaseActivity.this, HomeActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra("relaunch", true));
|
||||
finish();
|
||||
relaunchUi();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public class BrowseActivity extends BaseActivity {
|
||||
public void onClick(View v) { openKey(item.artistKey, Item.ARTIST, item.artist); }
|
||||
});
|
||||
}
|
||||
app.images.load((ImageView) header.findViewById(R.id.detail_art), item.thumb, Ui.dp(this, 104), R.drawable.art_placeholder);
|
||||
app.images.load((ImageView) header.findViewById(R.id.detail_art), item.thumb, Ui.dimen(this, R.dimen.detail_art), R.drawable.art_placeholder);
|
||||
}
|
||||
|
||||
private void setMeta(List<Item> children) {
|
||||
|
||||
@@ -168,7 +168,8 @@ public class HomeActivity extends BaseActivity {
|
||||
runRadio(str(R.string.library_radio), new Task<List<Item>>() { public List<Item> work() throws Exception { return lib.libraryRadio(); } });
|
||||
}
|
||||
}));
|
||||
row1.addView(spacer());
|
||||
homeContent.addView(row1);
|
||||
row1 = tileRow();
|
||||
row1.addView(tile(inf, Glyph.HEART, str(R.string.loved_radio), str(R.string.loved_radio_sub), new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
final Library lib = lib();
|
||||
@@ -181,7 +182,8 @@ public class HomeActivity extends BaseActivity {
|
||||
row2.addView(tile(inf, Glyph.CLOCK, str(R.string.time_travel), str(R.string.time_travel_sub), new View.OnClickListener() {
|
||||
public void onClick(View v) { timeTravel(); }
|
||||
}));
|
||||
row2.addView(spacer());
|
||||
homeContent.addView(row2);
|
||||
row2 = tileRow();
|
||||
row2.addView(tile(inf, Glyph.SHUFFLE, str(R.string.random_album), str(R.string.random_album_sub), new View.OnClickListener() {
|
||||
public void onClick(View v) { randomAlbum(); }
|
||||
}));
|
||||
@@ -196,20 +198,14 @@ public class HomeActivity extends BaseActivity {
|
||||
LinearLayout r = new LinearLayout(this);
|
||||
r.setOrientation(LinearLayout.HORIZONTAL);
|
||||
int p = Ui.dp(this, 8);
|
||||
r.setPadding(p, p / 2, p, p / 2);
|
||||
r.setPadding(p, p / 4, p, p / 4);
|
||||
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);
|
||||
t.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Ui.dimen(this, R.dimen.tile)));
|
||||
Ui.icon((ImageView) t.findViewById(R.id.tile_icon), glyph, getResources().getColor(R.color.accent), 26);
|
||||
((TextView) t.findViewById(R.id.tile_title)).setText(title);
|
||||
((TextView) t.findViewById(R.id.tile_sub)).setText(sub);
|
||||
t.setOnClickListener(l);
|
||||
@@ -249,7 +245,7 @@ public class HomeActivity extends BaseActivity {
|
||||
final TextView loading = new TextView(this);
|
||||
loading.setText(R.string.loading);
|
||||
loading.setTextColor(getResources().getColor(R.color.text2));
|
||||
loading.setTextSize(12);
|
||||
loading.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.text_sub));
|
||||
loading.setPadding(Ui.dp(this, 6), Ui.dp(this, 30), 0, Ui.dp(this, 30));
|
||||
strip.addView(loading);
|
||||
homeContent.addView(hs);
|
||||
@@ -261,7 +257,7 @@ public class HomeActivity extends BaseActivity {
|
||||
strip.removeAllViews();
|
||||
if (items.isEmpty()) { loading.setText(R.string.nothing_here_yet); strip.addView(loading); return; }
|
||||
LayoutInflater li = getLayoutInflater();
|
||||
int art = Ui.dp(HomeActivity.this, 88);
|
||||
int art = Ui.dimen(HomeActivity.this, R.dimen.card_art);
|
||||
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);
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ItemAdapter extends BaseAdapter {
|
||||
ctx = c;
|
||||
this.mode = mode;
|
||||
inflater = LayoutInflater.from(c);
|
||||
artPx = Ui.dp(c, 44);
|
||||
artPx = Ui.dimen(c, R.dimen.row_art);
|
||||
accent = c.getResources().getColor(R.color.accent);
|
||||
text = c.getResources().getColor(R.color.text);
|
||||
app = App.get();
|
||||
|
||||
@@ -59,7 +59,7 @@ public class NowPlayingActivity extends BaseActivity {
|
||||
public void onClick(View v) { startActivity(new Intent(NowPlayingActivity.this, QueueActivity.class)); }
|
||||
});
|
||||
|
||||
playGlyph = Ui.glyph(this, Glyph.PLAY, getResources().getColor(R.color.on_accent), 24);
|
||||
playGlyph = Ui.glyph(this, Glyph.PLAY, getResources().getColor(R.color.on_accent), 28);
|
||||
playGlyph.scale(1f);
|
||||
playBtn.setImageDrawable(playGlyph);
|
||||
playBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -67,8 +67,8 @@ public class NowPlayingActivity extends BaseActivity {
|
||||
});
|
||||
ImageButton prev = (ImageButton) findViewById(R.id.np_prev);
|
||||
ImageButton next = (ImageButton) findViewById(R.id.np_next);
|
||||
Ui.icon(prev, Glyph.PREV, white, 22);
|
||||
Ui.icon(next, Glyph.NEXT, white, 22);
|
||||
Ui.icon(prev, Glyph.PREV, white, 26);
|
||||
Ui.icon(next, Glyph.NEXT, white, 26);
|
||||
prev.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) { PlayerService.send(NowPlayingActivity.this, PlayerService.ACTION_PREV); }
|
||||
});
|
||||
@@ -76,12 +76,12 @@ public class NowPlayingActivity extends BaseActivity {
|
||||
public void onClick(View v) { PlayerService.send(NowPlayingActivity.this, PlayerService.ACTION_NEXT); }
|
||||
});
|
||||
|
||||
shuffleGlyph = Ui.glyph(this, Glyph.SHUFFLE, dim, 20); shuffleGlyph.scale(1f);
|
||||
shuffleGlyph = Ui.glyph(this, Glyph.SHUFFLE, dim, 25); shuffleGlyph.scale(1f);
|
||||
shuffleBtn.setImageDrawable(shuffleGlyph);
|
||||
shuffleBtn.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) { PlayQueue.get().setShuffle(!PlayQueue.get().shuffle()); }
|
||||
});
|
||||
repeatGlyph = Ui.glyph(this, Glyph.REPEAT, dim, 20); repeatGlyph.scale(1f);
|
||||
repeatGlyph = Ui.glyph(this, Glyph.REPEAT, dim, 25); repeatGlyph.scale(1f);
|
||||
repeatBtn.setImageDrawable(repeatGlyph);
|
||||
repeatBtn.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
@@ -90,7 +90,7 @@ public class NowPlayingActivity extends BaseActivity {
|
||||
toast(str(r == PlayQueue.REPEAT_ALL ? R.string.repeat_queue : r == PlayQueue.REPEAT_ONE ? R.string.repeat_track : R.string.repeat_off));
|
||||
}
|
||||
});
|
||||
loveGlyph = Ui.glyph(this, Glyph.HEART_OUTLINE, white, 20); loveGlyph.scale(1f);
|
||||
loveGlyph = Ui.glyph(this, Glyph.HEART_OUTLINE, white, 25); loveGlyph.scale(1f);
|
||||
loveBtn.setImageDrawable(loveGlyph);
|
||||
loveBtn.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
@@ -99,7 +99,7 @@ public class NowPlayingActivity extends BaseActivity {
|
||||
}
|
||||
});
|
||||
|
||||
Ui.icon((ImageView) findViewById(R.id.np_sleep_icon), Glyph.MOON, 0xB3FFFFFF, 16);
|
||||
Ui.icon((ImageView) findViewById(R.id.np_sleep_icon), Glyph.MOON, 0xCCFFFFFF, 20);
|
||||
findViewById(R.id.np_sleep).setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) { sleepDialog(); }
|
||||
});
|
||||
@@ -117,6 +117,15 @@ public class NowPlayingActivity extends BaseActivity {
|
||||
}
|
||||
});
|
||||
|
||||
// knob drawn here, not from XML: Android 2.x caches XML shapes at the size they were first loaded
|
||||
int knob = Ui.dimen(this, R.dimen.seek_thumb);
|
||||
GradientDrawable thumb = new GradientDrawable();
|
||||
thumb.setShape(GradientDrawable.OVAL);
|
||||
thumb.setColor(white);
|
||||
thumb.setSize(knob, knob);
|
||||
seek.setThumb(thumb);
|
||||
seek.setThumbOffset(knob / 2);
|
||||
|
||||
seek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
public void onProgressChanged(SeekBar s, int p, boolean user) {
|
||||
if (user) {
|
||||
|
||||
@@ -26,7 +26,12 @@ public final class Ui {
|
||||
}
|
||||
|
||||
public static void icon(ImageButton v, int type, int color) {
|
||||
icon(v, type, color, 20);
|
||||
icon(v, type, color, 24);
|
||||
}
|
||||
|
||||
/** A size from res/values/dimens.xml, in pixels. */
|
||||
public static int dimen(Context c, int id) {
|
||||
return c.getResources().getDimensionPixelSize(id);
|
||||
}
|
||||
|
||||
public static String time(long ms) {
|
||||
|
||||
Reference in new issue
Block a user