Add dark mode support and improve language handling in SettingsActivity

- Refactored color constants to support both light and dark modes.
- Implemented a toggle for dark mode in SettingsActivity.
- Updated language selection functionality to correctly display "Español".
- Enhanced prayer management in ExtrasActivity with a detailed view.
- Added new styles for dark mode in styles.xml.
This commit is contained in:
jahruz67
2026-07-21 17:35:42 -07:00
parent db610e4095
commit 782ede4254
6 changed files with 1015 additions and 49 deletions
+478
View File
@@ -0,0 +1,478 @@
package com.bibliadiaria.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Switch;
import android.widget.TextView;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SettingsActivity extends Activity {
// Light mode colors
private static final int COLOR_BG_LIGHT = Color.rgb(247, 248, 246);
private static final int COLOR_CARD_LIGHT = Color.WHITE;
private static final int COLOR_INK_LIGHT = Color.rgb(25, 31, 31);
private static final int COLOR_MUTED_LIGHT = Color.rgb(91, 99, 98);
private static final int COLOR_STROKE_LIGHT = Color.rgb(219, 226, 222);
private static final int COLOR_CARD_STROKE_LIGHT = Color.rgb(229, 232, 230);
// Dark mode colors
private static final int COLOR_BG_DARK = Color.rgb(26, 28, 30);
private static final int COLOR_CARD_DARK = Color.rgb(38, 41, 44);
private static final int COLOR_INK_DARK = Color.rgb(224, 226, 219);
private static final int COLOR_MUTED_DARK = Color.rgb(158, 163, 160);
private static final int COLOR_STROKE_DARK = Color.rgb(48, 52, 55);
private static final int COLOR_CARD_STROKE_DARK = Color.rgb(58, 62, 65);
private static final int COLOR_ACCENT = Color.rgb(0, 107, 90);
private static final int COLOR_WARM = Color.rgb(217, 75, 61);
private ExecutorService executor;
private TextView statusText;
private TextView installedVersionText;
private TextView checkButton;
private TextView downloadButton;
private TextView voiceSubtitleText;
private TextView voiceButton;
private UpdateManager.UpdateInfo latestInfo;
private boolean isEnglish;
private boolean isDarkMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isEnglish = UpdateManager.isEnglish(this);
isDarkMode = UpdateManager.isDarkMode(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(bg());
getWindow().setNavigationBarColor(bg());
}
executor = Executors.newSingleThreadExecutor();
setContentView(createScreen());
updateInstalledVersionText();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (executor != null) {
executor.shutdownNow();
}
}
private int bg() { return isDarkMode ? COLOR_BG_DARK : COLOR_BG_LIGHT; }
private int card() { return isDarkMode ? COLOR_CARD_DARK : COLOR_CARD_LIGHT; }
private int ink() { return isDarkMode ? COLOR_INK_DARK : COLOR_INK_LIGHT; }
private int muted() { return isDarkMode ? COLOR_MUTED_DARK : COLOR_MUTED_LIGHT; }
private int stroke() { return isDarkMode ? COLOR_STROKE_DARK : COLOR_STROKE_LIGHT; }
private int cardStroke() { return isDarkMode ? COLOR_CARD_STROKE_DARK : COLOR_CARD_STROKE_LIGHT; }
private View createScreen() {
FrameLayout root = new FrameLayout(this);
root.setBackgroundColor(bg());
ScrollView scrollView = new ScrollView(this);
scrollView.setFillViewport(true);
scrollView.setClipToPadding(false);
LinearLayout content = new LinearLayout(this);
content.setOrientation(LinearLayout.VERTICAL);
content.setPadding(dp(20), dp(28), dp(20), dp(28));
scrollView.addView(content, new ScrollView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
content.addView(createTopBar());
TextView title = textView("Settings", 34, ink(), Typeface.BOLD);
title.setIncludeFontPadding(false);
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
titleParams.setMargins(0, dp(18), 0, dp(18));
content.addView(title, titleParams);
content.addView(createDarkModeCard());
content.addView(createLanguageCard());
content.addView(createVoiceCard());
content.addView(createVersionCard());
checkButton = actionButton(isEnglish ? "Check now" : "Comprobar", COLOR_ACCENT, Color.WHITE);
checkButton.setOnClickListener(view -> checkForUpdate());
LinearLayout.LayoutParams checkParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
dp(46)
);
checkParams.setMargins(0, dp(16), 0, 0);
content.addView(checkButton, checkParams);
downloadButton = actionButton("Download latest APK", COLOR_WARM, Color.WHITE);
downloadButton.setVisibility(View.GONE);
downloadButton.setOnClickListener(view -> {
String url = latestInfo == null ? UpdateManager.APK_URL : latestInfo.apkUrl;
UpdateManager.openDownload(this, url);
});
LinearLayout.LayoutParams downloadParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
dp(46)
);
downloadParams.setMargins(0, dp(10), 0, 0);
content.addView(downloadButton, downloadParams);
root.addView(scrollView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
root.setOnApplyWindowInsetsListener((view, insets) -> {
content.setPadding(
dp(20),
dp(28) + insets.getSystemWindowInsetTop(),
dp(20),
dp(28) + insets.getSystemWindowInsetBottom()
);
return insets;
});
root.requestApplyInsets();
return root;
}
private View createTopBar() {
LinearLayout topBar = new LinearLayout(this);
topBar.setOrientation(LinearLayout.HORIZONTAL);
topBar.setGravity(Gravity.CENTER_VERTICAL);
TextView label = textView("APP", 12, COLOR_ACCENT, Typeface.BOLD);
topBar.addView(label, new LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
));
TextView back = textView(isEnglish ? "Extras" : "Extras", 15, COLOR_ACCENT, Typeface.BOLD);
back.setGravity(Gravity.CENTER);
back.setPadding(dp(14), 0, dp(14), 0);
back.setBackground(roundedRect(card(), dp(8), stroke(), dp(1)));
back.setOnClickListener(view -> finish());
topBar.addView(back, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
dp(40)
));
return topBar;
}
private View createDarkModeCard() {
LinearLayout card = createCard();
card.setOrientation(LinearLayout.HORIZONTAL);
card.setGravity(Gravity.CENTER_VERTICAL);
LinearLayout copy = new LinearLayout(this);
copy.setOrientation(LinearLayout.VERTICAL);
TextView title = textView(isEnglish ? "Dark Mode" : "Modo oscuro", 20, ink(), Typeface.BOLD);
title.setIncludeFontPadding(false);
copy.addView(title);
TextView subtitle = textView(
isEnglish ? "Use dark colors for the interface" : "Usar colores oscuros para la interfaz",
14, muted(), Typeface.NORMAL);
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
subtitleParams.setMargins(0, dp(6), 0, 0);
copy.addView(subtitle, subtitleParams);
card.addView(copy, new LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
));
final Switch darkToggle = new Switch(this);
darkToggle.setChecked(isDarkMode);
darkToggle.setOnCheckedChangeListener((buttonView, isChecked) -> {
UpdateManager.setDarkMode(this, isChecked);
isDarkMode = isChecked;
recreate();
});
card.addView(darkToggle, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return card;
}
private View createLanguageCard() {
LinearLayout card = createCard();
card.setOrientation(LinearLayout.HORIZONTAL);
card.setGravity(Gravity.CENTER_VERTICAL);
LinearLayout copy = new LinearLayout(this);
copy.setOrientation(LinearLayout.VERTICAL);
TextView title = textView("Language", 20, ink(), Typeface.BOLD);
title.setIncludeFontPadding(false);
copy.addView(title);
TextView subtitle = textView("Select the language for daily readings", 14, muted(), Typeface.NORMAL);
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
subtitleParams.setMargins(0, dp(6), 0, 0);
copy.addView(subtitle, subtitleParams);
card.addView(copy, new LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
));
TextView langToggle = actionButton(UpdateManager.isEnglish(this) ? "English" : "Español", COLOR_ACCENT, Color.WHITE);
langToggle.setOnClickListener(view -> {
boolean current = UpdateManager.isEnglish(this);
UpdateManager.setEnglish(this, !current);
langToggle.setText(!current ? "English" : "Español");
updateVoiceCard();
});
card.addView(langToggle, new LinearLayout.LayoutParams(dp(100), dp(40)));
return card;
}
private View createVoiceCard() {
LinearLayout card = createCard();
card.setOrientation(LinearLayout.VERTICAL);
LinearLayout copy = new LinearLayout(this);
copy.setOrientation(LinearLayout.VERTICAL);
TextView title = textView("Voice", 20, ink(), Typeface.BOLD);
title.setIncludeFontPadding(false);
copy.addView(title);
voiceSubtitleText = textView("", 14, muted(), Typeface.NORMAL);
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
subtitleParams.setMargins(0, dp(6), 0, 0);
copy.addView(voiceSubtitleText, subtitleParams);
card.addView(copy, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
voiceButton = actionButton("", COLOR_ACCENT, Color.WHITE);
voiceButton.setOnClickListener(view -> showVoicePicker());
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
dp(42)
);
buttonParams.setMargins(0, dp(12), 0, 0);
card.addView(voiceButton, buttonParams);
updateVoiceCard();
return card;
}
private View createVersionCard() {
LinearLayout card = createCard();
card.setOrientation(LinearLayout.VERTICAL);
TextView title = textView("Version", 20, ink(), Typeface.BOLD);
title.setIncludeFontPadding(false);
card.addView(title);
installedVersionText = textView("", 15, muted(), Typeface.BOLD);
LinearLayout.LayoutParams installedParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
installedParams.setMargins(0, dp(10), 0, 0);
card.addView(installedVersionText, installedParams);
statusText = textView("Ready to check for updates.", 15, muted(), Typeface.NORMAL);
statusText.setLineSpacing(dp(3), 1.1f);
LinearLayout.LayoutParams statusParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
statusParams.setMargins(0, dp(8), 0, 0);
card.addView(statusText, statusParams);
return card;
}
private void checkForUpdate() {
if (executor == null || executor.isShutdown()) {
return;
}
latestInfo = null;
downloadButton.setVisibility(View.GONE);
checkButton.setEnabled(false);
checkButton.setAlpha(0.62f);
statusText.setText("Checking for updates...");
executor.submit(() -> {
try {
UpdateManager.UpdateInfo info = UpdateManager.fetchLatestUpdate(this);
runOnUiThread(() -> showUpdateResult(info));
} catch (Exception error) {
runOnUiThread(() -> showUpdateError(error));
}
});
}
private void showUpdateResult(UpdateManager.UpdateInfo info) {
latestInfo = info;
checkButton.setEnabled(true);
checkButton.setAlpha(1f);
if (info.updateAvailable) {
String version = info.latestVersionName.isEmpty()
? String.valueOf(info.latestVersionCode)
: info.latestVersionName + " (" + info.latestVersionCode + ")";
statusText.setText("Update available: " + version);
downloadButton.setVisibility(View.VISIBLE);
return;
}
statusText.setText("You are on the latest build: "
+ info.currentVersionName + " (" + info.currentVersionCode + ")");
}
private void showUpdateError(Exception error) {
checkButton.setEnabled(true);
checkButton.setAlpha(1f);
statusText.setText("Could not check for updates. " + cleanMessage(error));
}
private void showVoicePicker() {
boolean english = UpdateManager.isEnglish(this);
UpdateManager.VoiceOption[] options = UpdateManager.getTtsVoiceOptions(english);
String[] labels = new String[options.length];
for (int index = 0; index < options.length; index++) {
labels[index] = options[index].label;
}
int selectedIndex = UpdateManager.getTtsVoiceIndex(
english,
UpdateManager.getTtsVoice(this, english)
);
new AlertDialog.Builder(this)
.setTitle(english ? "English voice" : "Spanish voice")
.setSingleChoiceItems(labels, selectedIndex, (dialog, which) -> {
UpdateManager.setTtsVoice(this, english, options[which].id);
updateVoiceCard();
dialog.dismiss();
})
.setNegativeButton("Cancel", null)
.show();
}
private void updateVoiceCard() {
if (voiceSubtitleText == null || voiceButton == null) {
return;
}
boolean english = UpdateManager.isEnglish(this);
String voiceId = UpdateManager.getTtsVoice(this, english);
voiceSubtitleText.setText(english
? "English voices are shown while English is selected"
: "Las voces en español se muestran mientras el español esté seleccionado");
voiceButton.setText(UpdateManager.getTtsVoiceLabel(english, voiceId));
}
private void updateInstalledVersionText() {
try {
installedVersionText.setText(
(isEnglish ? "Installed: " : "Instalada: ")
+ UpdateManager.getInstalledVersionName(this)
+ " (" + UpdateManager.getInstalledVersionCode(this) + ")");
} catch (Exception error) {
installedVersionText.setText(isEnglish ? "Installed version unavailable." : "Versión instalada no disponible.");
}
}
private String cleanMessage(Exception error) {
String message = error.getMessage();
if (message == null || message.trim().isEmpty()) {
return "Unknown error.";
}
return message.trim();
}
private LinearLayout createCard() {
LinearLayout card = new LinearLayout(this);
card.setPadding(dp(18), dp(16), dp(18), dp(16));
card.setBackground(roundedRect(card(), dp(8), cardStroke(), dp(1)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
card.setElevation(dp(1));
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
params.setMargins(0, 0, 0, dp(14));
card.setLayoutParams(params);
return card;
}
private TextView actionButton(String text, int backgroundColor, int textColor) {
TextView button = textView(text, 15, textColor, Typeface.BOLD);
button.setGravity(Gravity.CENTER);
button.setPadding(dp(14), 0, dp(14), 0);
button.setBackground(roundedRect(backgroundColor, dp(8), Color.TRANSPARENT, 0));
return button;
}
private TextView textView(String text, int sizeSp, int color, int style) {
TextView textView = new TextView(this);
textView.setText(text);
textView.setTextSize(sizeSp);
textView.setTextColor(color);
textView.setTypeface(Typeface.DEFAULT, style);
return textView;
}
private GradientDrawable roundedRect(int color, int radius, int strokeColor, int strokeWidth) {
GradientDrawable drawable = new GradientDrawable();
drawable.setShape(GradientDrawable.RECTANGLE);
drawable.setColor(color);
drawable.setCornerRadius(radius);
if (strokeWidth > 0) {
drawable.setStroke(strokeWidth, strokeColor);
}
return drawable;
}
private int dp(int value) {
return Math.round(value * getResources().getDisplayMetrics().density);
}
}
+424
View File
@@ -0,0 +1,424 @@
package com.bibliadiaria.app;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
public class ExtrasActivity extends Activity {
// Light mode colors
private static final int COLOR_BG_LIGHT = Color.rgb(247, 248, 246);
private static final int COLOR_CARD_LIGHT = Color.WHITE;
private static final int COLOR_INK_LIGHT = Color.rgb(25, 31, 31);
private static final int COLOR_MUTED_LIGHT = Color.rgb(91, 99, 98);
private static final int COLOR_STROKE_LIGHT = Color.rgb(219, 226, 222);
private static final int COLOR_CARD_STROKE_LIGHT = Color.rgb(229, 232, 230);
// Dark mode colors
private static final int COLOR_BG_DARK = Color.rgb(26, 28, 30);
private static final int COLOR_CARD_DARK = Color.rgb(38, 41, 44);
private static final int COLOR_INK_DARK = Color.rgb(224, 226, 219);
private static final int COLOR_MUTED_DARK = Color.rgb(158, 163, 160);
private static final int COLOR_STROKE_DARK = Color.rgb(48, 52, 55);
private static final int COLOR_CARD_STROKE_DARK = Color.rgb(58, 62, 65);
private static final int COLOR_ACCENT = Color.rgb(0, 107, 90);
private static final int COLOR_WARM = Color.rgb(217, 75, 61);
private boolean isEnglish;
private boolean isDarkMode;
private static final String VENI_CREATOR_PRAYER =
"Ven, Esp\u00edritu Creador,\n"
+ "visita las almas de tus fieles\n"
+ "y llena de la gracia divina\n"
+ "los corazones que t\u00fa mismo creaste.\n\n"
+ "T\u00fa eres nuestro Consolador,\n"
+ "don del Dios alt\u00edsimo,\n"
+ "fuente viva, fuego, caridad\n"
+ "y espiritual unci\u00f3n.\n\n"
+ "T\u00fa derramas sobre nosotros\n"
+ "los siete dones;\n"
+ "t\u00fa eres el dedo de la diestra del Padre,\n"
+ "promesa solemne del Padre,\n"
+ "que pones en nuestros labios la palabra.\n\n"
+ "Enciende tu luz en nuestras mentes,\n"
+ "infunde tu amor en nuestros corazones\n"
+ "y fortalece con tu fuerza constante\n"
+ "la debilidad de nuestro cuerpo.\n\n"
+ "Aleja de nosotros al enemigo,\n"
+ "danos pronto la paz;\n"
+ "siendo t\u00fa nuestro gu\u00eda,\n"
+ "evitaremos todo mal.\n\n"
+ "Por ti conozcamos al Padre,\n"
+ "y tambi\u00e9n al Hijo;\n"
+ "y que en ti, Esp\u00edritu de ambos,\n"
+ "creamos en todo tiempo.\n\n"
+ "Gloria a Dios Padre,\n"
+ "y al Hijo que resucit\u00f3,\n"
+ "y al Esp\u00edritu Consolador,\n"
+ "por los siglos de los siglos.\n\n"
+ "Am\u00e9n.";
private static final String ANGELUS_PRAYER =
"El \u00c1ngel del Se\u00f1or anunci\u00f3 a Mar\u00eda.\n"
+ "Y concibi\u00f3 por obra y gracia del Esp\u00edritu Santo.\n\n"
+ "Dios te salve, Mar\u00eda, llena eres de gracia,\n"
+ "el Se\u00f1or es contigo.\n"
+ "Bendita t\u00fa eres entre todas las mujeres,\n"
+ "y bendito es el fruto de tu vientre, Jes\u00fas.\n"
+ "Santa Mar\u00eda, Madre de Dios,\n"
+ "ruega por nosotros, pecadores,\n"
+ "ahora y en la hora de nuestra muerte.\n"
+ "Am\u00e9n.\n\n"
+ "He aqu\u00ed la esclava del Se\u00f1or.\n"
+ "H\u00e1gase en m\u00ed seg\u00fan tu palabra.\n\n"
+ "Dios te salve, Mar\u00eda...\n\n"
+ "Y el Verbo se hizo carne.\n"
+ "Y habit\u00f3 entre nosotros.\n\n"
+ "Dios te salve, Mar\u00eda...\n\n"
+ "Ruega por nosotros, Santa Madre de Dios.\n"
+ "Para que seamos dignos de alcanzar las promesas de Cristo.\n\n"
+ "Infunde, Se\u00f1or, tu gracia en nuestros corazones,\n"
+ "para que quienes hemos conocido por el anuncio del \u00c1ngel\n"
+ "la encarnaci\u00f3n de tu Hijo Jesucristo,\n"
+ "por su pasi\u00f3n y cruz seamos llevados\n"
+ "a la gloria de su resurrecci\u00f3n.\n"
+ "Por Jesucristo nuestro Se\u00f1or.\n\n"
+ "Am\u00e9n.";
private static final String SALVE_REGINA_PRAYER =
"Dios te salve, Reina y Madre de misericordia,\n"
+ "vida, dulzura y esperanza nuestra;\n"
+ "Dios te salve.\n\n"
+ "A ti llamamos los desterrados hijos de Eva;\n"
+ "a ti suspiramos, gimiendo y llorando\n"
+ "en este valle de l\u00e1grimas.\n\n"
+ "Ea, pues, Se\u00f1ora, abogada nuestra,\n"
+ "vuelve a nosotros esos tus ojos misericordiosos;\n"
+ "y despu\u00e9s de este destierro,\n"
+ "mu\u00e9stranos a Jes\u00fas,\n"
+ "fruto bendito de tu vientre.\n\n"
+ "\u00a1Oh clemente, oh piadosa,\n"
+ "oh dulce Virgen Mar\u00eda!\n\n"
+ "Ruega por nosotros, Santa Madre de Dios,\n"
+ "para que seamos dignos de alcanzar\n"
+ "las promesas de nuestro Se\u00f1or Jesucristo.\n\n"
+ "Am\u00e9n.";
private static final String MEMORARE_PRAYER =
"Acordaos, oh piados\u00edsima Virgen Mar\u00eda,\n"
+ "que jam\u00e1s se ha o\u00eddo decir\n"
+ "que ninguno de los que han acudido a vuestra protecci\u00f3n,\n"
+ "implorado vuestra asistencia\n"
+ "y reclamado vuestro socorro,\n"
+ "haya sido abandonado de vos.\n\n"
+ "Animado con esta confianza,\n"
+ "a vos tambi\u00e9n acudo,\n"
+ "oh Madre, Virgen de las v\u00edrgenes;\n"
+ "y aunque gimiendo bajo el peso de mis pecados,\n"
+ "me atrevo a comparecer ante vuestra presencia soberana.\n\n"
+ "No desech\u00e9is, oh Madre de Dios,\n"
+ "mis humildes s\u00faplicas;\n"
+ "antes bien, escuchadlas y acogedlas benignamente.\n\n"
+ "Am\u00e9n.";
private static final PrayerItem[] PRAYERS = {
new PrayerItem("Ven, Esp\u00edritu Creador", "Veni Creator Spiritus", VENI_CREATOR_PRAYER),
new PrayerItem("\u00c1ngelus", "Oraci\u00f3n de la Encarnaci\u00f3n", ANGELUS_PRAYER),
new PrayerItem("Salve Regina", "Dios te salve, Reina", SALVE_REGINA_PRAYER),
new PrayerItem("Acordaos", "Memorare", MEMORARE_PRAYER)
};
private ScrollView scrollView;
private LinearLayout listContainer;
private LinearLayout detailContainer;
private TextView titleText;
private PrayerItem selectedPrayer = PRAYERS[0];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isEnglish = UpdateManager.isEnglish(this);
isDarkMode = UpdateManager.isDarkMode(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(bg());
getWindow().setNavigationBarColor(bg());
}
setContentView(createScreen());
}
@Override
protected void onResume() {
super.onResume();
boolean currentLanguage = UpdateManager.isEnglish(this);
boolean currentDark = UpdateManager.isDarkMode(this);
if (currentLanguage != isEnglish || currentDark != isDarkMode) {
isEnglish = currentLanguage;
isDarkMode = currentDark;
recreate();
}
}
@Override
public void onBackPressed() {
if (detailContainer != null && detailContainer.getVisibility() == View.VISIBLE) {
showPrayerList();
return;
}
super.onBackPressed();
}
private int bg() { return isDarkMode ? COLOR_BG_DARK : COLOR_BG_LIGHT; }
private int card() { return isDarkMode ? COLOR_CARD_DARK : COLOR_CARD_LIGHT; }
private int ink() { return isDarkMode ? COLOR_INK_DARK : COLOR_INK_LIGHT; }
private int muted() { return isDarkMode ? COLOR_MUTED_DARK : COLOR_MUTED_LIGHT; }
private int stroke() { return isDarkMode ? COLOR_STROKE_DARK : COLOR_STROKE_LIGHT; }
private int cardStroke() { return isDarkMode ? COLOR_CARD_STROKE_DARK : COLOR_CARD_STROKE_LIGHT; }
private View createScreen() {
FrameLayout root = new FrameLayout(this);
root.setBackgroundColor(bg());
scrollView = new ScrollView(this);
scrollView.setFillViewport(true);
scrollView.setClipToPadding(false);
LinearLayout content = new LinearLayout(this);
content.setOrientation(LinearLayout.VERTICAL);
content.setPadding(dp(20), dp(28), dp(20), dp(28));
scrollView.addView(content, new ScrollView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
content.addView(createTopBar());
titleText = textView(isEnglish ? "Extras" : "Extras", 34, ink(), Typeface.BOLD);
titleText.setIncludeFontPadding(false);
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
titleParams.setMargins(0, dp(18), 0, dp(18));
content.addView(titleText, titleParams);
listContainer = new LinearLayout(this);
listContainer.setOrientation(LinearLayout.VERTICAL);
for (PrayerItem prayer : PRAYERS) {
listContainer.addView(createPrayerCard(prayer));
}
content.addView(listContainer, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
detailContainer = new LinearLayout(this);
detailContainer.setOrientation(LinearLayout.VERTICAL);
detailContainer.setVisibility(View.GONE);
content.addView(detailContainer, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
root.addView(scrollView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
root.setOnApplyWindowInsetsListener((view, insets) -> {
content.setPadding(
dp(20),
dp(28) + insets.getSystemWindowInsetTop(),
dp(20),
dp(28) + insets.getSystemWindowInsetBottom()
);
return insets;
});
root.requestApplyInsets();
return root;
}
private View createTopBar() {
LinearLayout topBar = new LinearLayout(this);
topBar.setOrientation(LinearLayout.HORIZONTAL);
topBar.setGravity(Gravity.CENTER_VERTICAL);
TextView label = textView(isEnglish ? "PRAYERS" : "ORACIONES", 12, COLOR_ACCENT, Typeface.BOLD);
topBar.addView(label, new LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
));
TextView settings = textView(isEnglish ? "Settings" : "Ajustes", 15, COLOR_ACCENT, Typeface.BOLD);
settings.setGravity(Gravity.CENTER);
settings.setPadding(dp(14), 0, dp(14), 0);
settings.setBackground(roundedRect(card(), dp(8), stroke(), dp(1)));
settings.setOnClickListener(view -> startActivity(new Intent(this, SettingsActivity.class)));
LinearLayout.LayoutParams settingsParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
dp(40)
);
settingsParams.setMargins(0, 0, dp(8), 0);
topBar.addView(settings, settingsParams);
TextView home = textView(isEnglish ? "Home" : "Inicio", 15, COLOR_ACCENT, Typeface.BOLD);
home.setGravity(Gravity.CENTER);
home.setPadding(dp(14), 0, dp(14), 0);
home.setBackground(roundedRect(card(), dp(8), stroke(), dp(1)));
home.setOnClickListener(view -> finish());
topBar.addView(home, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
dp(40)
));
return topBar;
}
private View createPrayerCard(PrayerItem prayer) {
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.HORIZONTAL);
card.setGravity(Gravity.CENTER_VERTICAL);
card.setPadding(dp(18), dp(16), dp(16), dp(16));
card.setBackground(roundedRect(card(), dp(8), cardStroke(), dp(1)));
card.setOnClickListener(view -> showPrayerDetail(prayer));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
card.setElevation(dp(1));
}
LinearLayout copy = new LinearLayout(this);
copy.setOrientation(LinearLayout.VERTICAL);
TextView title = textView(prayer.title, 20, ink(), Typeface.BOLD);
title.setIncludeFontPadding(false);
copy.addView(title);
TextView subtitle = textView(prayer.subtitle, 14, muted(), Typeface.BOLD);
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
subtitleParams.setMargins(0, dp(6), 0, 0);
copy.addView(subtitle, subtitleParams);
card.addView(copy, new LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
));
TextView arrow = textView(">", 22, COLOR_ACCENT, Typeface.BOLD);
arrow.setGravity(Gravity.CENTER);
card.addView(arrow, new LinearLayout.LayoutParams(
dp(28),
ViewGroup.LayoutParams.WRAP_CONTENT
));
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
cardParams.setMargins(0, 0, 0, dp(12));
card.setLayoutParams(cardParams);
return card;
}
private void renderPrayerDetail() {
detailContainer.removeAllViews();
TextView subtitle = textView(selectedPrayer.subtitle, 14, COLOR_WARM, Typeface.BOLD);
detailContainer.addView(subtitle);
TextView prayer = textView(selectedPrayer.body, 21, ink(), Typeface.NORMAL);
prayer.setLineSpacing(dp(5), 1.14f);
LinearLayout.LayoutParams prayerParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
prayerParams.setMargins(0, dp(16), 0, dp(18));
detailContainer.addView(prayer, prayerParams);
TextView backToList = textView(isEnglish ? "Back to extras" : "Volver a extras", 15, COLOR_ACCENT, Typeface.BOLD);
backToList.setGravity(Gravity.CENTER);
backToList.setPadding(dp(14), 0, dp(14), 0);
backToList.setBackground(roundedRect(card(), dp(8), stroke(), dp(1)));
backToList.setOnClickListener(view -> showPrayerList());
detailContainer.addView(backToList, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
dp(42)
));
}
private void showPrayerDetail(PrayerItem prayer) {
selectedPrayer = prayer;
titleText.setText(prayer.title);
renderPrayerDetail();
listContainer.setVisibility(View.GONE);
detailContainer.setVisibility(View.VISIBLE);
scrollToTop();
}
private void showPrayerList() {
titleText.setText(isEnglish ? "Extras" : "Extras");
detailContainer.setVisibility(View.GONE);
listContainer.setVisibility(View.VISIBLE);
scrollToTop();
}
private void scrollToTop() {
if (scrollView != null) {
scrollView.post(() -> scrollView.smoothScrollTo(0, 0));
}
}
private TextView textView(String text, int sizeSp, int color, int style) {
TextView textView = new TextView(this);
textView.setText(text);
textView.setTextSize(sizeSp);
textView.setTextColor(color);
textView.setTypeface(Typeface.DEFAULT, style);
return textView;
}
private GradientDrawable roundedRect(int color, int radius, int strokeColor, int strokeWidth) {
GradientDrawable drawable = new GradientDrawable();
drawable.setShape(GradientDrawable.RECTANGLE);
drawable.setColor(color);
drawable.setCornerRadius(radius);
if (strokeWidth > 0) {
drawable.setStroke(strokeWidth, strokeColor);
}
return drawable;
}
private int dp(int value) {
return Math.round(value * getResources().getDisplayMetrics().density);
}
private static class PrayerItem {
final String title;
final String subtitle;
final String body;
PrayerItem(String title, String subtitle, String body) {
this.title = title;
this.subtitle = subtitle;
this.body = body;
}
}
}
@@ -50,10 +50,29 @@ import java.util.concurrent.Executors;
import org.json.JSONObject;
public class MainActivity extends Activity {
private static final int COLOR_BACKGROUND = Color.rgb(247, 248, 246);
private static final int COLOR_CARD = Color.WHITE;
private static final int COLOR_INK = Color.rgb(25, 31, 31);
private static final int COLOR_MUTED = Color.rgb(91, 99, 98);
// Light mode colors
private static final int COLOR_BACKGROUND_LIGHT = Color.rgb(247, 248, 246);
private static final int COLOR_CARD_LIGHT = Color.WHITE;
private static final int COLOR_INK_LIGHT = Color.rgb(25, 31, 31);
private static final int COLOR_MUTED_LIGHT = Color.rgb(91, 99, 98);
private static final int COLOR_STROKE_LIGHT = Color.rgb(229, 232, 230);
private static final int COLOR_CHIP_BG_LIGHT = Color.rgb(219, 226, 222);
private static final int COLOR_SPEAKER_BG_LIGHT = Color.rgb(229, 244, 240);
private static final int COLOR_REFERENCE_BG_LIGHT = Color.rgb(229, 244, 240);
private static final int COLOR_TODAY_BG_LIGHT = Color.rgb(255, 239, 236);
// Dark mode colors
private static final int COLOR_BACKGROUND_DARK = Color.rgb(26, 28, 30);
private static final int COLOR_CARD_DARK = Color.rgb(38, 41, 44);
private static final int COLOR_INK_DARK = Color.rgb(224, 226, 219);
private static final int COLOR_MUTED_DARK = Color.rgb(158, 163, 160);
private static final int COLOR_STROKE_DARK = Color.rgb(58, 62, 65);
private static final int COLOR_CHIP_BG_DARK = Color.rgb(48, 52, 55);
private static final int COLOR_SPEAKER_BG_DARK = Color.rgb(45, 65, 60);
private static final int COLOR_REFERENCE_BG_DARK = Color.rgb(45, 65, 60);
private static final int COLOR_TODAY_BG_DARK = Color.rgb(75, 50, 48);
// Shared colors (no change between modes)
private static final int COLOR_ACCENT = Color.rgb(0, 107, 90);
private static final int COLOR_WARM = Color.rgb(217, 75, 61);
private static final int COLOR_HOLIDAY_BG = Color.rgb(255, 246, 229);
@@ -120,19 +139,21 @@ public class MainActivity extends Activity {
private int ttsGeneration;
private float readingFontSp;
private boolean isEnglish;
private boolean isDarkMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(COLOR_BACKGROUND);
getWindow().setNavigationBarColor(COLOR_BACKGROUND);
getWindow().setStatusBarColor(bg());
getWindow().setNavigationBarColor(bg());
}
preferences = getSharedPreferences("preferencias_lectura", MODE_PRIVATE);
readingFontSp = preferences.getFloat("tamano_letra", 20f);
isEnglish = preferences.getBoolean("is_english", false);
isDarkMode = preferences.getBoolean("dark_mode", false);
selectedDateCalendar = startOfDay(Calendar.getInstance());
visibleMonthCalendar = startOfMonth(selectedDateCalendar);
executor = Executors.newSingleThreadExecutor();
@@ -163,15 +184,28 @@ public class MainActivity extends Activity {
protected void onResume() {
super.onResume();
boolean currentLanguage = UpdateManager.isEnglish(this);
if (currentLanguage != isEnglish) {
boolean currentDark = UpdateManager.isDarkMode(this);
if (currentLanguage != isEnglish || currentDark != isDarkMode) {
isEnglish = currentLanguage;
isDarkMode = currentDark;
recreate();
}
}
// --- Dark mode aware color helpers ---
private int bg() { return isDarkMode ? COLOR_BACKGROUND_DARK : COLOR_BACKGROUND_LIGHT; }
private int card() { return isDarkMode ? COLOR_CARD_DARK : COLOR_CARD_LIGHT; }
private int ink() { return isDarkMode ? COLOR_INK_DARK : COLOR_INK_LIGHT; }
private int muted() { return isDarkMode ? COLOR_MUTED_DARK : COLOR_MUTED_LIGHT; }
private int stroke() { return isDarkMode ? COLOR_STROKE_DARK : COLOR_STROKE_LIGHT; }
private int chipBg() { return isDarkMode ? COLOR_CHIP_BG_DARK : COLOR_CHIP_BG_LIGHT; }
private int speakerBg() { return isDarkMode ? COLOR_SPEAKER_BG_DARK : COLOR_SPEAKER_BG_LIGHT; }
private int referenceBg() { return isDarkMode ? COLOR_REFERENCE_BG_DARK : COLOR_REFERENCE_BG_LIGHT; }
private int todayBg() { return isDarkMode ? COLOR_TODAY_BG_DARK : COLOR_TODAY_BG_LIGHT; }
private View createScreen() {
FrameLayout root = new FrameLayout(this);
root.setBackgroundColor(COLOR_BACKGROUND);
root.setBackgroundColor(bg());
ScrollView scrollView = new ScrollView(this);
scrollView.setFillViewport(true);
@@ -195,7 +229,7 @@ public class MainActivity extends Activity {
ViewGroup.LayoutParams.WRAP_CONTENT
));
sourceText = textView(isEnglish ? "Source: Vatican News" : "Fuente: Vatican News", 13, COLOR_MUTED, Typeface.NORMAL);
sourceText = textView(isEnglish ? "Source: Vatican News" : "Fuente: Vatican News", 13, muted(), Typeface.NORMAL);
LinearLayout.LayoutParams sourceParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
@@ -268,7 +302,7 @@ public class MainActivity extends Activity {
topRow.addView(createDateChip());
header.addView(topRow);
TextView title = textView(isEnglish ? "Word of the day" : "Palabra del día", 34, COLOR_INK, Typeface.BOLD);
TextView title = textView(isEnglish ? "Word of the day" : "Palabra del día", 34, ink(), Typeface.BOLD);
title.setIncludeFontPadding(false);
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -292,7 +326,7 @@ public class MainActivity extends Activity {
calendarParams.setMargins(0, dp(2), 0, dp(14));
header.addView(calendarPanel, calendarParams);
dateText = textView("", 16, COLOR_MUTED, Typeface.BOLD);
dateText = textView("", 16, muted(), Typeface.BOLD);
header.addView(dateText);
holidayTagsRow = new LinearLayout(this);
@@ -306,7 +340,7 @@ public class MainActivity extends Activity {
tagsParams.setMargins(0, dp(8), 0, 0);
header.addView(holidayTagsRow, tagsParams);
liturgyText = textView("", 16, COLOR_INK, Typeface.NORMAL);
liturgyText = textView("", 16, ink(), Typeface.NORMAL);
liturgyText.setLineSpacing(0, 1.12f);
LinearLayout.LayoutParams liturgyParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -339,7 +373,7 @@ public class MainActivity extends Activity {
chip.setOrientation(LinearLayout.HORIZONTAL);
chip.setGravity(Gravity.CENTER_VERTICAL);
chip.setPadding(dp(12), dp(8), dp(10), dp(8));
chip.setBackground(roundedRect(Color.WHITE, dp(8), Color.rgb(219, 226, 222), dp(1)));
chip.setBackground(roundedRect(card(), dp(8), chipBg(), dp(1)));
chip.setContentDescription(isEnglish ? "Choose date" : "Elegir fecha");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
chip.setElevation(dp(1));
@@ -350,11 +384,11 @@ public class MainActivity extends Activity {
labelGroup.setOrientation(LinearLayout.VERTICAL);
labelGroup.setGravity(Gravity.CENTER_VERTICAL);
dateChipPrimary = textView("", 20, COLOR_INK, Typeface.BOLD);
dateChipPrimary = textView("", 20, ink(), Typeface.BOLD);
dateChipPrimary.setIncludeFontPadding(false);
labelGroup.addView(dateChipPrimary);
dateChipSecondary = textView("", 11, COLOR_MUTED, Typeface.BOLD);
dateChipSecondary = textView("", 11, muted(), Typeface.BOLD);
dateChipSecondary.setIncludeFontPadding(false);
labelGroup.addView(dateChipSecondary);
@@ -378,7 +412,7 @@ public class MainActivity extends Activity {
panel.setOrientation(LinearLayout.VERTICAL);
panel.setPadding(dp(14), dp(12), dp(14), dp(14));
panel.setVisibility(View.GONE);
panel.setBackground(roundedRect(Color.WHITE, dp(8), Color.rgb(219, 226, 222), dp(1)));
panel.setBackground(roundedRect(card(), dp(8), chipBg(), dp(1)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
panel.setElevation(dp(4));
}
@@ -389,7 +423,7 @@ public class MainActivity extends Activity {
monthRow.addView(calendarNavButton("<", view -> changeVisibleMonth(-1)));
calendarMonthText = textView("", 18, COLOR_INK, Typeface.BOLD);
calendarMonthText = textView("", 18, ink(), Typeface.BOLD);
calendarMonthText.setGravity(Gravity.CENTER);
monthRow.addView(calendarMonthText, new LinearLayout.LayoutParams(
0,
@@ -418,7 +452,7 @@ public class MainActivity extends Activity {
weekdays.setOrientation(LinearLayout.HORIZONTAL);
String[] labels = {"L", "M", "X", "J", "V", "S", "D"};
for (String label : labels) {
TextView dayLabel = textView(label, 12, COLOR_MUTED, Typeface.BOLD);
TextView dayLabel = textView(label, 12, muted(), Typeface.BOLD);
dayLabel.setGravity(Gravity.CENTER);
weekdays.addView(dayLabel, new LinearLayout.LayoutParams(
0,
@@ -443,7 +477,7 @@ public class MainActivity extends Activity {
private TextView calendarNavButton(String text, View.OnClickListener listener) {
TextView button = textView(text, 22, COLOR_ACCENT, Typeface.BOLD);
button.setGravity(Gravity.CENTER);
button.setBackground(roundedRect(Color.rgb(229, 244, 240), dp(8), Color.TRANSPARENT, 0));
button.setBackground(roundedRect(speakerBg(), dp(8), Color.TRANSPARENT, 0));
button.setOnClickListener(listener);
button.setContentDescription(text.equals("<") ? (isEnglish ? "Previous month" : "Mes anterior") : (isEnglish ? "Next month" : "Mes siguiente"));
return button;
@@ -453,7 +487,7 @@ public class MainActivity extends Activity {
TextView button = textView(text, 13, COLOR_ACCENT, Typeface.BOLD);
button.setGravity(Gravity.CENTER);
button.setPadding(dp(8), 0, dp(8), 0);
button.setBackground(roundedRect(Color.rgb(229, 244, 240), dp(8), Color.TRANSPARENT, 0));
button.setBackground(roundedRect(speakerBg(), dp(8), Color.TRANSPARENT, 0));
button.setOnClickListener(listener);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
@@ -512,7 +546,7 @@ public class MainActivity extends Activity {
Calendar today = startOfDay(Calendar.getInstance());
for (int cell = 0; cell < 42; cell++) {
TextView dayCell = textView("", 16, COLOR_INK, Typeface.BOLD);
TextView dayCell = textView("", 16, ink(), Typeface.BOLD);
dayCell.setGravity(Gravity.CENTER);
dayCell.setIncludeFontPadding(false);
@@ -540,7 +574,7 @@ public class MainActivity extends Activity {
dayCell.setBackground(roundedRect(COLOR_HOLIDAY_BG, dp(8), COLOR_HOLIDAY_BORDER, dp(1)));
} else if (isSameDay(cellDate, today)) {
dayCell.setTextColor(COLOR_WARM);
dayCell.setBackground(roundedRect(Color.rgb(255, 239, 236), dp(8), Color.TRANSPARENT, 0));
dayCell.setBackground(roundedRect(todayBg(), dp(8), Color.TRANSPARENT, 0));
} else {
dayCell.setBackground(roundedRect(Color.TRANSPARENT, dp(8), Color.TRANSPARENT, 0));
}
@@ -983,7 +1017,7 @@ public class MainActivity extends Activity {
statusBlock = new LinearLayout(this);
statusBlock.setOrientation(LinearLayout.VERTICAL);
statusBlock.setPadding(dp(18), dp(16), dp(18), dp(16));
statusBlock.setBackground(roundedRect(COLOR_CARD, dp(8), Color.TRANSPARENT, 0));
statusBlock.setBackground(roundedRect(card(), dp(8), Color.TRANSPARENT, 0));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
statusBlock.setElevation(dp(1));
}
@@ -997,7 +1031,7 @@ public class MainActivity extends Activity {
progressParams.setMargins(0, 0, dp(12), 0);
row.addView(progressBar, progressParams);
statusText = textView(isEnglish ? "Loading today's Word..." : "Cargando la Palabra de hoy...", 16, COLOR_INK, Typeface.NORMAL);
statusText = textView(isEnglish ? "Loading today's Word..." : "Cargando la Palabra de hoy...", 16, ink(), Typeface.NORMAL);
statusText.setLineSpacing(0, 1.16f);
row.addView(statusText, new LinearLayout.LayoutParams(
0,
@@ -1035,7 +1069,7 @@ public class MainActivity extends Activity {
panel.setOrientation(LinearLayout.VERTICAL);
panel.setPadding(dp(16), dp(14), dp(16), dp(14));
panel.setVisibility(View.GONE);
panel.setBackground(roundedRect(Color.WHITE, dp(8), Color.rgb(223, 229, 226), dp(1)));
panel.setBackground(roundedRect(card(), dp(8), stroke(), dp(1)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
panel.setElevation(dp(8));
}
@@ -1043,10 +1077,10 @@ public class MainActivity extends Activity {
LinearLayout labelRow = new LinearLayout(this);
labelRow.setGravity(Gravity.CENTER_VERTICAL);
TextView label = textView(isEnglish ? "Font size" : "Tamaño de letra", 15, COLOR_INK, Typeface.BOLD);
TextView label = textView(isEnglish ? "Font size" : "Tamaño de letra", 15, ink(), Typeface.BOLD);
labelRow.addView(label, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
fontValueText = textView(Math.round(readingFontSp) + " sp", 14, COLOR_MUTED, Typeface.BOLD);
fontValueText = textView(Math.round(readingFontSp) + " sp", 14, muted(), Typeface.BOLD);
labelRow.addView(fontValueText);
panel.addView(labelRow);
@@ -1380,7 +1414,7 @@ public class MainActivity extends Activity {
button.setTextColor(COLOR_ACCENT);
button.setTextSize(14);
button.setAllCaps(false);
button.setBackground(roundedRect(Color.WHITE, dp(8), Color.rgb(219, 226, 222), dp(1)));
button.setBackground(roundedRect(card(), dp(8), stroke(), dp(1)));
return button;
}
@@ -1674,7 +1708,7 @@ public class MainActivity extends Activity {
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER_VERTICAL);
TextView titleText = textView(title, titleSizeSp, COLOR_INK, Typeface.BOLD);
TextView titleText = textView(title, titleSizeSp, ink(), Typeface.BOLD);
titleText.setIncludeFontPadding(false);
row.addView(titleText, new LinearLayout.LayoutParams(
0,
@@ -1686,7 +1720,7 @@ public class MainActivity extends Activity {
speakerButton.setImageResource(R.drawable.ic_speaker_24);
speakerButton.setColorFilter(COLOR_ACCENT);
speakerButton.setPadding(dp(8), dp(8), dp(8), dp(8));
speakerButton.setBackground(roundedRect(Color.rgb(229, 244, 240), dp(8), Color.TRANSPARENT, 0));
speakerButton.setBackground(roundedRect(speakerBg(), dp(8), Color.TRANSPARENT, 0));
speakerButton.setContentDescription((isEnglish ? "Listen to " : "Escuchar ") + title);
speakerButton.setOnClickListener(view -> startTtsConversionForText(speechText, controls));
@@ -1697,7 +1731,7 @@ public class MainActivity extends Activity {
}
private TtsControls createTtsControls() {
TextView status = textView("", 14, COLOR_MUTED, Typeface.BOLD);
TextView status = textView("", 14, muted(), Typeface.BOLD);
status.setVisibility(View.GONE);
LinearLayout playerPanel = new LinearLayout(this);
@@ -1738,7 +1772,7 @@ public class MainActivity extends Activity {
seekParams.setMargins(dp(8), 0, dp(8), 0);
playerPanel.addView(seekBar, seekParams);
TextView timeText = textView("0:00", 13, COLOR_MUTED, Typeface.BOLD);
TextView timeText = textView("0:00", 13, muted(), Typeface.BOLD);
timeText.setGravity(Gravity.CENTER_VERTICAL);
playerPanel.addView(timeText, new LinearLayout.LayoutParams(
dp(86),
@@ -1797,7 +1831,7 @@ public class MainActivity extends Activity {
addTtsControlsToCard(card, ttsControls);
if (!section.introduction.isEmpty()) {
TextView introText = textView(section.introduction, 16, COLOR_MUTED, Typeface.BOLD);
TextView introText = textView(section.introduction, 16, muted(), Typeface.BOLD);
introText.setLineSpacing(0, 1.14f);
resizableTextViews.add(introText);
card.addView(introText);
@@ -1806,7 +1840,7 @@ public class MainActivity extends Activity {
if (!section.reference.isEmpty()) {
TextView referenceText = textView(section.reference, 15, COLOR_ACCENT, Typeface.BOLD);
referenceText.setGravity(Gravity.CENTER_VERTICAL);
referenceText.setBackground(roundedRect(Color.rgb(229, 244, 240), dp(8), Color.TRANSPARENT, 0));
referenceText.setBackground(roundedRect(referenceBg(), dp(8), Color.TRANSPARENT, 0));
referenceText.setPadding(dp(10), dp(6), dp(10), dp(6));
LinearLayout.LayoutParams referenceParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
@@ -1816,7 +1850,7 @@ public class MainActivity extends Activity {
card.addView(referenceText, referenceParams);
}
TextView bodyText = textView(section.body, Math.round(readingFontSp), COLOR_INK, Typeface.NORMAL);
TextView bodyText = textView(section.body, Math.round(readingFontSp), ink(), Typeface.NORMAL);
bodyText.setLineSpacing(dp(4), 1.16f);
resizableTextViews.add(bodyText);
card.addView(bodyText);
@@ -1841,7 +1875,7 @@ public class MainActivity extends Activity {
));
addTtsControlsToCard(card, ttsControls);
TextView body = textView(section.body, Math.round(readingFontSp), COLOR_INK, Typeface.NORMAL);
TextView body = textView(section.body, Math.round(readingFontSp), ink(), Typeface.NORMAL);
body.setLineSpacing(dp(4), 1.16f);
LinearLayout.LayoutParams bodyParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -1858,7 +1892,7 @@ public class MainActivity extends Activity {
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
card.setPadding(padding, padding, padding, padding);
card.setBackground(roundedRect(COLOR_CARD, dp(8), Color.rgb(229, 232, 230), dp(1)));
card.setBackground(roundedRect(card(), dp(8), stroke(), dp(1)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
card.setElevation(dp(1));
}
@@ -2416,4 +2450,4 @@ public class MainActivity extends Activity {
return lines;
}
}
}
}
@@ -20,10 +20,20 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SettingsActivity extends Activity {
private static final int COLOR_BACKGROUND = Color.rgb(247, 248, 246);
private static final int COLOR_CARD = Color.WHITE;
private static final int COLOR_INK = Color.rgb(25, 31, 31);
private static final int COLOR_MUTED = Color.rgb(91, 99, 98);
// Light mode colors
private static final int COLOR_BG_LIGHT = Color.rgb(247, 248, 246);
private static final int COLOR_CARD_LIGHT = Color.WHITE;
private static final int COLOR_INK_LIGHT = Color.rgb(25, 31, 31);
private static final int COLOR_MUTED_LIGHT = Color.rgb(91, 99, 98);
private static final int COLOR_STROKE_LIGHT = Color.rgb(219, 226, 222);
// Dark mode colors
private static final int COLOR_BG_DARK = Color.rgb(26, 28, 30);
private static final int COLOR_CARD_DARK = Color.rgb(38, 41, 44);
private static final int COLOR_INK_DARK = Color.rgb(224, 226, 219);
private static final int COLOR_MUTED_DARK = Color.rgb(158, 163, 160);
private static final int COLOR_STROKE_DARK = Color.rgb(58, 62, 65);
private static final int COLOR_ACCENT = Color.rgb(0, 107, 90);
private static final int COLOR_WARM = Color.rgb(217, 75, 61);
@@ -183,11 +193,11 @@ public class SettingsActivity extends Activity {
1f
));
TextView langToggle = actionButton(UpdateManager.isEnglish(this) ? "English" : "Español", COLOR_ACCENT, Color.WHITE);
TextView langToggle = actionButton(UpdateManager.isEnglish(this) ? "English" : "Español", COLOR_ACCENT, Color.WHITE);
langToggle.setOnClickListener(view -> {
boolean current = UpdateManager.isEnglish(this);
UpdateManager.setEnglish(this, !current);
langToggle.setText(!current ? "English" : "Español");
langToggle.setText(!current ? "English" : "Español");
updateVoiceCard();
});
card.addView(langToggle, new LinearLayout.LayoutParams(dp(100), dp(40)));
@@ -338,7 +348,7 @@ public class SettingsActivity extends Activity {
String voiceId = UpdateManager.getTtsVoice(this, english);
voiceSubtitleText.setText(english
? "English voices are shown while English is selected"
: "Las voces en español se muestran mientras el español esté seleccionado");
: "Las voces en español se muestran mientras el español esté seleccionado");
voiceButton.setText(UpdateManager.getTtsVoiceLabel(english, voiceId));
}
@@ -349,7 +359,7 @@ public class SettingsActivity extends Activity {
+ UpdateManager.getInstalledVersionName(this)
+ " (" + UpdateManager.getInstalledVersionCode(this) + ")");
} catch (Exception error) {
installedVersionText.setText(isEnglish ? "Installed version unavailable." : "Versión instalada no disponible.");
installedVersionText.setText(isEnglish ? "Installed version unavailable." : "Versión instalada no disponible.");
}
}
@@ -24,6 +24,7 @@ final class UpdateManager {
static final String PREFS_NAME = "preferencias_lectura";
static final String KEY_AUTO_UPDATES = "auto_updates_enabled";
static final String KEY_IS_ENGLISH = "is_english";
static final String KEY_DARK_MODE = "dark_mode";
static final String KEY_TTS_VOICE_ENGLISH = "tts_voice_english";
static final String KEY_TTS_VOICE_SPANISH = "tts_voice_spanish";
static final String DEFAULT_TTS_VOICE_ENGLISH = "af_heart";
@@ -89,6 +90,14 @@ final class UpdateManager {
preferences(context).edit().putBoolean(KEY_IS_ENGLISH, enabled).apply();
}
static boolean isDarkMode(Context context) {
return preferences(context).getBoolean(KEY_DARK_MODE, false);
}
static void setDarkMode(Context context, boolean enabled) {
preferences(context).edit().putBoolean(KEY_DARK_MODE, enabled).apply();
}
static VoiceOption[] getTtsVoiceOptions(boolean isEnglish) {
return isEnglish ? ENGLISH_VOICES : SPANISH_VOICES;
}
@@ -207,10 +216,10 @@ final class UpdateManager {
boolean english = isEnglish(activity);
new AlertDialog.Builder(activity)
.setTitle(english ? "Update available" : "Actualización disponible")
.setMessage((english ? "Version " : "Versión ") + version + (english ? " is ready to download." : " está lista para descargar."))
.setTitle(english ? "Update available" : "Actualización disponible")
.setMessage((english ? "Version " : "Versión ") + version + (english ? " is ready to download." : " está lista para descargar."))
.setPositiveButton(english ? "Download" : "Descargar", (dialog, which) -> openDownload(activity, info.apkUrl))
.setNegativeButton(english ? "Later" : "Después", null)
.setNegativeButton(english ? "Later" : "Después", null)
.show();
}
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="android:style/Theme.Material.NoActionBar">
<item name="android:fontFamily">sans</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorAccent">#006B5A</item>
<item name="android:navigationBarColor">#1A1C1E</item>
<item name="android:windowActionModeOverlay">true</item>
</style>
</resources>