Add stored clock patterns
This commit is contained in:
@@ -107,6 +107,7 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED =
|
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED =
|
||||||
"drawer_date_custom_format_enabled";
|
"drawer_date_custom_format_enabled";
|
||||||
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT = "drawer_date_custom_format";
|
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT = "drawer_date_custom_format";
|
||||||
|
public static final String KEY_CLOCK_STORED_PATTERNS = "clock_stored_patterns";
|
||||||
public static final String KEY_CLOCK_IGNORE_UNDER_DISPLAY_CAMERAS = "clock_ignore_under_display_cameras";
|
public static final String KEY_CLOCK_IGNORE_UNDER_DISPLAY_CAMERAS = "clock_ignore_under_display_cameras";
|
||||||
public static final String KEY_CLOCK_CAMERA_TYPE_OVERRIDES = "clock_camera_type_overrides";
|
public static final String KEY_CLOCK_CAMERA_TYPE_OVERRIDES = "clock_camera_type_overrides";
|
||||||
public static final String KEY_LAYOUT_PADDING_CUTOUT_PX = "layout_padding_cutout_px";
|
public static final String KEY_LAYOUT_PADDING_CUTOUT_PX = "layout_padding_cutout_px";
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import android.content.ClipData;
|
|||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
@@ -31,26 +34,44 @@ import android.widget.Toast;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public final class ClockFragment extends Fragment {
|
public final class ClockFragment extends Fragment {
|
||||||
|
private static final String PATTERN_ORIGIN_STATUSBAR = "statusbar";
|
||||||
|
private static final String PATTERN_ORIGIN_DRAWER_CLOCK = "drawer_clock";
|
||||||
|
private static final String PATTERN_ORIGIN_DRAWER_DATE = "drawer_date";
|
||||||
|
private static final int COLOR_STATUSBAR = 0xffe85050;
|
||||||
|
private static final int COLOR_DRAWER_CLOCK = 0xff4caf50;
|
||||||
|
private static final int COLOR_DRAWER_DATE = 0xff4f8cff;
|
||||||
|
private static final int CARD_STROKE_DP = 2;
|
||||||
|
|
||||||
private final ArrayList<PreviewBinding> previewBindings = new ArrayList<>();
|
private final ArrayList<PreviewBinding> previewBindings = new ArrayList<>();
|
||||||
|
private final ArrayList<StoredPatternBinding> storedPatternBindings = new ArrayList<>();
|
||||||
|
private final ArrayList<StoredPatternRowBinding> storedPatternRowBindings = new ArrayList<>();
|
||||||
private final Runnable previewTicker = new Runnable() {
|
private final Runnable previewTicker = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean needsNextTick = false;
|
boolean needsNextTick = false;
|
||||||
|
HashMap<String, ClockPatternPreviewRenderer.Result> renderCache = new HashMap<>();
|
||||||
for (PreviewBinding binding : previewBindings) {
|
for (PreviewBinding binding : previewBindings) {
|
||||||
needsNextTick |= updatePreview(binding);
|
needsNextTick |= updatePreview(binding);
|
||||||
}
|
}
|
||||||
|
for (StoredPatternRowBinding binding : storedPatternRowBindings) {
|
||||||
|
needsNextTick |= updateStoredPatternPreview(binding, renderCache);
|
||||||
|
}
|
||||||
if (needsNextTick && getView() != null) {
|
if (needsNextTick && getView() != null) {
|
||||||
getView().postDelayed(this, nextSecondDelayMillis());
|
getView().postDelayed(this, nextSecondDelayMillis());
|
||||||
}
|
}
|
||||||
@@ -66,11 +87,14 @@ public final class ClockFragment extends Fragment {
|
|||||||
SharedPreferences prefs = requireContext()
|
SharedPreferences prefs = requireContext()
|
||||||
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
MaterialCardView customFormatCard = root.findViewById(R.id.clock_custom_format_card);
|
||||||
CheckBox customFormatEnabled = root.findViewById(R.id.clock_custom_format_enabled);
|
CheckBox customFormatEnabled = root.findViewById(R.id.clock_custom_format_enabled);
|
||||||
View customFormatContainer = root.findViewById(R.id.clock_custom_format_container);
|
View customFormatContainer = root.findViewById(R.id.clock_custom_format_container);
|
||||||
EditText customFormatInput = root.findViewById(R.id.clock_custom_format_input);
|
EditText customFormatInput = root.findViewById(R.id.clock_custom_format_input);
|
||||||
ClockPatternPreviewView customFormatPreview =
|
ClockPatternPreviewView customFormatPreview =
|
||||||
root.findViewById(R.id.clock_custom_format_preview);
|
root.findViewById(R.id.clock_custom_format_preview);
|
||||||
|
MaterialCardView drawerClockCustomFormatCard =
|
||||||
|
root.findViewById(R.id.drawer_clock_custom_format_card);
|
||||||
CheckBox drawerClockCustomFormatEnabled =
|
CheckBox drawerClockCustomFormatEnabled =
|
||||||
root.findViewById(R.id.drawer_clock_custom_format_enabled);
|
root.findViewById(R.id.drawer_clock_custom_format_enabled);
|
||||||
View drawerClockCustomFormatContainer =
|
View drawerClockCustomFormatContainer =
|
||||||
@@ -79,6 +103,8 @@ public final class ClockFragment extends Fragment {
|
|||||||
root.findViewById(R.id.drawer_clock_custom_format_input);
|
root.findViewById(R.id.drawer_clock_custom_format_input);
|
||||||
ClockPatternPreviewView drawerClockCustomFormatPreview =
|
ClockPatternPreviewView drawerClockCustomFormatPreview =
|
||||||
root.findViewById(R.id.drawer_clock_custom_format_preview);
|
root.findViewById(R.id.drawer_clock_custom_format_preview);
|
||||||
|
MaterialCardView drawerDateCustomFormatCard =
|
||||||
|
root.findViewById(R.id.drawer_date_custom_format_card);
|
||||||
CheckBox drawerDateCustomFormatEnabled =
|
CheckBox drawerDateCustomFormatEnabled =
|
||||||
root.findViewById(R.id.drawer_date_custom_format_enabled);
|
root.findViewById(R.id.drawer_date_custom_format_enabled);
|
||||||
View drawerDateCustomFormatContainer =
|
View drawerDateCustomFormatContainer =
|
||||||
@@ -88,6 +114,8 @@ public final class ClockFragment extends Fragment {
|
|||||||
ClockPatternPreviewView drawerDateCustomFormatPreview =
|
ClockPatternPreviewView drawerDateCustomFormatPreview =
|
||||||
root.findViewById(R.id.drawer_date_custom_format_preview);
|
root.findViewById(R.id.drawer_date_custom_format_preview);
|
||||||
MaterialButton fontPickerButton = root.findViewById(R.id.clock_font_picker_button);
|
MaterialButton fontPickerButton = root.findViewById(R.id.clock_font_picker_button);
|
||||||
|
MaterialButton restoreStoredPatternsButton =
|
||||||
|
root.findViewById(R.id.clock_restore_stored_patterns_button);
|
||||||
TextView customFormatHelpToggle = root.findViewById(R.id.clock_custom_format_help_toggle);
|
TextView customFormatHelpToggle = root.findViewById(R.id.clock_custom_format_help_toggle);
|
||||||
View customFormatHelpContainer = root.findViewById(R.id.clock_custom_format_help_container);
|
View customFormatHelpContainer = root.findViewById(R.id.clock_custom_format_help_container);
|
||||||
TextView customFormatHelpCommon = root.findViewById(R.id.clock_custom_format_help);
|
TextView customFormatHelpCommon = root.findViewById(R.id.clock_custom_format_help);
|
||||||
@@ -115,12 +143,17 @@ public final class ClockFragment extends Fragment {
|
|||||||
SbtDefaults.DRAWER_DATE_CUSTOM_FORMAT_DEFAULT);
|
SbtDefaults.DRAWER_DATE_CUSTOM_FORMAT_DEFAULT);
|
||||||
|
|
||||||
previewBindings.clear();
|
previewBindings.clear();
|
||||||
|
storedPatternBindings.clear();
|
||||||
|
ensureDefaultStoredPatterns(prefs);
|
||||||
bindCustomFormat(
|
bindCustomFormat(
|
||||||
prefs,
|
prefs,
|
||||||
|
customFormatCard,
|
||||||
customFormatEnabled,
|
customFormatEnabled,
|
||||||
customFormatContainer,
|
customFormatContainer,
|
||||||
customFormatInput,
|
customFormatInput,
|
||||||
customFormatPreview,
|
customFormatPreview,
|
||||||
|
PATTERN_ORIGIN_STATUSBAR,
|
||||||
|
COLOR_STATUSBAR,
|
||||||
SbtSettings.KEY_CLOCK_CUSTOM_FORMAT_ENABLED,
|
SbtSettings.KEY_CLOCK_CUSTOM_FORMAT_ENABLED,
|
||||||
SbtSettings.KEY_CLOCK_CUSTOM_FORMAT,
|
SbtSettings.KEY_CLOCK_CUSTOM_FORMAT,
|
||||||
customFormatEnabledValue,
|
customFormatEnabledValue,
|
||||||
@@ -129,10 +162,13 @@ public final class ClockFragment extends Fragment {
|
|||||||
true);
|
true);
|
||||||
bindCustomFormat(
|
bindCustomFormat(
|
||||||
prefs,
|
prefs,
|
||||||
|
drawerClockCustomFormatCard,
|
||||||
drawerClockCustomFormatEnabled,
|
drawerClockCustomFormatEnabled,
|
||||||
drawerClockCustomFormatContainer,
|
drawerClockCustomFormatContainer,
|
||||||
drawerClockCustomFormatInput,
|
drawerClockCustomFormatInput,
|
||||||
drawerClockCustomFormatPreview,
|
drawerClockCustomFormatPreview,
|
||||||
|
PATTERN_ORIGIN_DRAWER_CLOCK,
|
||||||
|
COLOR_DRAWER_CLOCK,
|
||||||
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT_ENABLED,
|
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT_ENABLED,
|
||||||
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT,
|
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT,
|
||||||
drawerClockCustomFormatEnabledValue,
|
drawerClockCustomFormatEnabledValue,
|
||||||
@@ -141,10 +177,13 @@ public final class ClockFragment extends Fragment {
|
|||||||
false);
|
false);
|
||||||
bindCustomFormat(
|
bindCustomFormat(
|
||||||
prefs,
|
prefs,
|
||||||
|
drawerDateCustomFormatCard,
|
||||||
drawerDateCustomFormatEnabled,
|
drawerDateCustomFormatEnabled,
|
||||||
drawerDateCustomFormatContainer,
|
drawerDateCustomFormatContainer,
|
||||||
drawerDateCustomFormatInput,
|
drawerDateCustomFormatInput,
|
||||||
drawerDateCustomFormatPreview,
|
drawerDateCustomFormatPreview,
|
||||||
|
PATTERN_ORIGIN_DRAWER_DATE,
|
||||||
|
COLOR_DRAWER_DATE,
|
||||||
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED,
|
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED,
|
||||||
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT,
|
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT,
|
||||||
drawerDateCustomFormatEnabledValue,
|
drawerDateCustomFormatEnabledValue,
|
||||||
@@ -169,6 +208,12 @@ public final class ClockFragment extends Fragment {
|
|||||||
if (fontPickerButton != null) {
|
if (fontPickerButton != null) {
|
||||||
fontPickerButton.setOnClickListener(v -> showFontPickerDialog());
|
fontPickerButton.setOnClickListener(v -> showFontPickerDialog());
|
||||||
}
|
}
|
||||||
|
if (restoreStoredPatternsButton != null) {
|
||||||
|
restoreStoredPatternsButton.setOnClickListener(v -> {
|
||||||
|
persistStoredPatterns(prefs, mergeDefaultPatterns(loadStoredPatterns(prefs)));
|
||||||
|
renderAllStoredPatternLists(prefs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@@ -181,10 +226,13 @@ public final class ClockFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bindCustomFormat(SharedPreferences prefs,
|
private void bindCustomFormat(SharedPreferences prefs,
|
||||||
|
MaterialCardView card,
|
||||||
CheckBox enabledView,
|
CheckBox enabledView,
|
||||||
View container,
|
View container,
|
||||||
EditText input,
|
EditText input,
|
||||||
ClockPatternPreviewView preview,
|
ClockPatternPreviewView preview,
|
||||||
|
String origin,
|
||||||
|
int accentColor,
|
||||||
String enabledKey,
|
String enabledKey,
|
||||||
String formatKey,
|
String formatKey,
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
@@ -194,6 +242,9 @@ public final class ClockFragment extends Fragment {
|
|||||||
if (enabledView == null || input == null) {
|
if (enabledView == null || input == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
styleClockCard(card, accentColor);
|
||||||
|
styleBorderedView(input, accentColor, 6, 8, 6);
|
||||||
|
styleBorderedView(preview, accentColor, 8, 10, 8);
|
||||||
enabledView.setChecked(enabled);
|
enabledView.setChecked(enabled);
|
||||||
input.setText(format != null ? format : "");
|
input.setText(format != null ? format : "");
|
||||||
if (preview != null) {
|
if (preview != null) {
|
||||||
@@ -204,6 +255,17 @@ public final class ClockFragment extends Fragment {
|
|||||||
updateCustomFormatVisibility(container, enabled);
|
updateCustomFormatVisibility(container, enabled);
|
||||||
PreviewBinding binding = new PreviewBinding(enabledView, input, preview, multilinePreview);
|
PreviewBinding binding = new PreviewBinding(enabledView, input, preview, multilinePreview);
|
||||||
previewBindings.add(binding);
|
previewBindings.add(binding);
|
||||||
|
StoredPatternBinding storedBinding = createStoredPatternSection(
|
||||||
|
prefs,
|
||||||
|
container,
|
||||||
|
input,
|
||||||
|
origin,
|
||||||
|
accentColor,
|
||||||
|
multilinePreview);
|
||||||
|
if (storedBinding != null) {
|
||||||
|
storedPatternBindings.add(storedBinding);
|
||||||
|
renderStoredPatternList(prefs, storedBinding);
|
||||||
|
}
|
||||||
updatePreview(binding);
|
updatePreview(binding);
|
||||||
enabledView.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
enabledView.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
updateCustomFormatVisibility(container, isChecked);
|
updateCustomFormatVisibility(container, isChecked);
|
||||||
@@ -233,6 +295,369 @@ public final class ClockFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void styleClockCard(MaterialCardView card, int accentColor) {
|
||||||
|
if (card == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
card.setStrokeWidth(ViewTreeSupport.dp(card.getContext(), CARD_STROKE_DP));
|
||||||
|
card.setStrokeColor(accentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void styleBorderedView(View view,
|
||||||
|
int accentColor,
|
||||||
|
int radiusDp,
|
||||||
|
int horizontalPaddingDp,
|
||||||
|
int verticalPaddingDp) {
|
||||||
|
if (view == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Context context = view.getContext();
|
||||||
|
GradientDrawable background = new GradientDrawable();
|
||||||
|
background.setColor(Color.TRANSPARENT);
|
||||||
|
background.setCornerRadius(ViewTreeSupport.dp(context, radiusDp));
|
||||||
|
background.setStroke(ViewTreeSupport.dp(context, CARD_STROKE_DP), accentColor);
|
||||||
|
view.setBackground(background);
|
||||||
|
view.setPadding(
|
||||||
|
ViewTreeSupport.dp(context, horizontalPaddingDp),
|
||||||
|
ViewTreeSupport.dp(context, verticalPaddingDp),
|
||||||
|
ViewTreeSupport.dp(context, horizontalPaddingDp),
|
||||||
|
ViewTreeSupport.dp(context, verticalPaddingDp));
|
||||||
|
}
|
||||||
|
|
||||||
|
private StoredPatternBinding createStoredPatternSection(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
View container,
|
||||||
|
EditText input,
|
||||||
|
String origin,
|
||||||
|
int accentColor,
|
||||||
|
boolean multilinePreview
|
||||||
|
) {
|
||||||
|
if (!(container instanceof LinearLayout parent) || input == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Context context = parent.getContext();
|
||||||
|
MaterialButton toggle = new MaterialButton(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
toggle.setText(R.string.clock_stored_patterns_show);
|
||||||
|
LinearLayout.LayoutParams toggleParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
toggleParams.topMargin = ViewTreeSupport.dp(context, 12);
|
||||||
|
parent.addView(toggle, toggleParams);
|
||||||
|
|
||||||
|
LinearLayout section = new LinearLayout(context);
|
||||||
|
section.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
section.setVisibility(View.GONE);
|
||||||
|
parent.addView(section, new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
TextView hint = new TextView(context);
|
||||||
|
hint.setText(R.string.clock_stored_patterns_hint);
|
||||||
|
hint.setTextAppearance(android.R.style.TextAppearance_Material_Body2);
|
||||||
|
LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
hintParams.topMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
section.addView(hint, hintParams);
|
||||||
|
|
||||||
|
LinearLayout list = new LinearLayout(context);
|
||||||
|
list.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
section.addView(list, new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
MaterialButton storeButton = new MaterialButton(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
storeButton.setText(R.string.clock_store_current_pattern);
|
||||||
|
LinearLayout.LayoutParams storeParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
storeParams.topMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
section.addView(storeButton, storeParams);
|
||||||
|
|
||||||
|
StoredPatternBinding binding = new StoredPatternBinding(
|
||||||
|
input,
|
||||||
|
origin,
|
||||||
|
accentColor,
|
||||||
|
multilinePreview,
|
||||||
|
toggle,
|
||||||
|
section,
|
||||||
|
list);
|
||||||
|
toggle.setOnClickListener(v -> {
|
||||||
|
boolean show = section.getVisibility() != View.VISIBLE;
|
||||||
|
section.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
|
toggle.setText(show
|
||||||
|
? R.string.clock_stored_patterns_hide
|
||||||
|
: R.string.clock_stored_patterns_show);
|
||||||
|
if (show) {
|
||||||
|
renderStoredPatternList(prefs, binding);
|
||||||
|
} else {
|
||||||
|
removeStoredPatternRowsFor(binding);
|
||||||
|
list.removeAllViews();
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
storeButton.setOnClickListener(v -> {
|
||||||
|
String pattern = input.getText() != null ? input.getText().toString() : "";
|
||||||
|
storePattern(prefs, pattern, origin);
|
||||||
|
renderAllStoredPatternLists(prefs);
|
||||||
|
});
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderAllStoredPatternLists(SharedPreferences prefs) {
|
||||||
|
for (StoredPatternBinding binding : storedPatternBindings) {
|
||||||
|
renderStoredPatternList(prefs, binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderStoredPatternList(SharedPreferences prefs, StoredPatternBinding binding) {
|
||||||
|
if (binding == null || binding.listContainer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Context context = binding.listContainer.getContext();
|
||||||
|
removeStoredPatternRowsFor(binding);
|
||||||
|
binding.listContainer.removeAllViews();
|
||||||
|
if (binding.section == null || binding.section.getVisibility() != View.VISIBLE) {
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<StoredPattern> patterns = loadStoredPatterns(prefs);
|
||||||
|
for (StoredPattern pattern : patterns) {
|
||||||
|
binding.listContainer.addView(createStoredPatternRow(context, prefs, binding, pattern));
|
||||||
|
}
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private View createStoredPatternRow(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
StoredPatternBinding binding,
|
||||||
|
StoredPattern pattern
|
||||||
|
) {
|
||||||
|
LinearLayout row = new LinearLayout(context);
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||||
|
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
rowParams.topMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
row.setLayoutParams(rowParams);
|
||||||
|
|
||||||
|
ClockPatternPreviewView preview = new ClockPatternPreviewView(context);
|
||||||
|
preview.setPreviewTextAppearance(
|
||||||
|
requireContext().getColor(R.color.sbt_on_surface),
|
||||||
|
previewTextSizeForOrigin(pattern.origin));
|
||||||
|
styleBorderedView(preview, colorForOrigin(pattern.origin), 8, 10, 8);
|
||||||
|
StoredPatternRowBinding rowBinding = new StoredPatternRowBinding(
|
||||||
|
binding,
|
||||||
|
preview,
|
||||||
|
pattern.pattern,
|
||||||
|
normaliseOrigin(pattern.origin),
|
||||||
|
binding.multilinePreview);
|
||||||
|
storedPatternRowBindings.add(rowBinding);
|
||||||
|
updateStoredPatternPreview(rowBinding, new HashMap<>());
|
||||||
|
preview.setOnClickListener(v -> {
|
||||||
|
binding.input.setText(pattern.pattern);
|
||||||
|
binding.input.setSelection(binding.input.getText() != null
|
||||||
|
? binding.input.getText().length()
|
||||||
|
: 0);
|
||||||
|
});
|
||||||
|
row.addView(preview, new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
1f));
|
||||||
|
|
||||||
|
MaterialButton deleteButton = new MaterialButton(context);
|
||||||
|
deleteButton.setText("×");
|
||||||
|
deleteButton.setTextColor(Color.WHITE);
|
||||||
|
deleteButton.setBackgroundTintList(android.content.res.ColorStateList.valueOf(
|
||||||
|
ContextCompat.getColor(context, R.color.sbt_danger)));
|
||||||
|
deleteButton.setMinWidth(0);
|
||||||
|
deleteButton.setMinHeight(0);
|
||||||
|
deleteButton.setPadding(0, 0, 0, 0);
|
||||||
|
deleteButton.setOnClickListener(v -> {
|
||||||
|
deleteStoredPattern(prefs, pattern.pattern);
|
||||||
|
renderAllStoredPatternLists(prefs);
|
||||||
|
});
|
||||||
|
LinearLayout.LayoutParams deleteParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewTreeSupport.dp(context, 44),
|
||||||
|
ViewTreeSupport.dp(context, 44));
|
||||||
|
deleteParams.leftMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
row.addView(deleteButton, deleteParams);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeStoredPatternRowsFor(StoredPatternBinding binding) {
|
||||||
|
for (int i = storedPatternRowBindings.size() - 1; i >= 0; i--) {
|
||||||
|
if (storedPatternRowBindings.get(i).owner == binding) {
|
||||||
|
storedPatternRowBindings.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureDefaultStoredPatterns(SharedPreferences prefs) {
|
||||||
|
if (prefs == null || prefs.contains(SbtSettings.KEY_CLOCK_STORED_PATTERNS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
persistStoredPatterns(prefs, defaultStoredPatterns());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<StoredPattern> mergeDefaultPatterns(ArrayList<StoredPattern> current) {
|
||||||
|
ArrayList<StoredPattern> result = current != null ? current : new ArrayList<>();
|
||||||
|
for (StoredPattern pattern : defaultStoredPatterns()) {
|
||||||
|
upsertPattern(result, pattern.pattern, pattern.origin);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<StoredPattern> defaultStoredPatterns() {
|
||||||
|
ArrayList<StoredPattern> patterns = new ArrayList<>();
|
||||||
|
patterns.add(new StoredPattern("H:mm:ss", PATTERN_ORIGIN_STATUSBAR));
|
||||||
|
patterns.add(new StoredPattern("{big}H:mm:{/big color(#batterybar)}ss", PATTERN_ORIGIN_STATUSBAR));
|
||||||
|
patterns.add(new StoredPattern(
|
||||||
|
"{@sans-serif-condensed-light color(#fff;#000)}|d{\\n30 big}H:mm:|{/big color(#batterybar invRGB 0.5 *RGB invRGB;#batterybar 0.5 *RGB)}ss",
|
||||||
|
PATTERN_ORIGIN_STATUSBAR));
|
||||||
|
patterns.add(new StoredPattern("{@sans-serif-condensed-light}H:mm:{small}ss", PATTERN_ORIGIN_DRAWER_CLOCK));
|
||||||
|
patterns.add(new StoredPattern("EEE, d MMM", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
patterns.add(new StoredPattern("EEE, MMM d", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
patterns.add(new StoredPattern("{@sans-serif-condensed-light}EEEE, MMMM d", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
patterns.add(new StoredPattern("{@sans-serif-condensed-light}EEEE, d MMMM", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storePattern(SharedPreferences prefs, String pattern, String origin) {
|
||||||
|
String value = pattern != null ? pattern.trim() : "";
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<StoredPattern> patterns = loadStoredPatterns(prefs);
|
||||||
|
upsertPattern(patterns, value, origin);
|
||||||
|
persistStoredPatterns(prefs, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upsertPattern(ArrayList<StoredPattern> patterns, String pattern, String origin) {
|
||||||
|
if (patterns == null || pattern == null || pattern.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String value = pattern.trim();
|
||||||
|
String resolvedOrigin = normaliseOrigin(origin);
|
||||||
|
for (int i = 0; i < patterns.size(); i++) {
|
||||||
|
StoredPattern existing = patterns.get(i);
|
||||||
|
if (existing != null && value.equals(existing.pattern)) {
|
||||||
|
patterns.set(i, new StoredPattern(value, resolvedOrigin));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patterns.add(new StoredPattern(value, resolvedOrigin));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteStoredPattern(SharedPreferences prefs, String pattern) {
|
||||||
|
ArrayList<StoredPattern> patterns = loadStoredPatterns(prefs);
|
||||||
|
for (int i = patterns.size() - 1; i >= 0; i--) {
|
||||||
|
StoredPattern existing = patterns.get(i);
|
||||||
|
if (existing != null && existing.pattern.equals(pattern)) {
|
||||||
|
patterns.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
persistStoredPatterns(prefs, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<StoredPattern> loadStoredPatterns(SharedPreferences prefs) {
|
||||||
|
ArrayList<StoredPattern> patterns = new ArrayList<>();
|
||||||
|
if (prefs == null) {
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
String encoded = prefs.getString(SbtSettings.KEY_CLOCK_STORED_PATTERNS, "");
|
||||||
|
if (encoded == null || encoded.isEmpty()) {
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
String[] lines = encoded.split("\\n", -1);
|
||||||
|
for (String line : lines) {
|
||||||
|
StoredPattern pattern = decodeStoredPattern(line);
|
||||||
|
if (pattern == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
upsertPattern(patterns, pattern.pattern, pattern.origin);
|
||||||
|
}
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistStoredPatterns(SharedPreferences prefs, ArrayList<StoredPattern> patterns) {
|
||||||
|
if (prefs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StringBuilder encoded = new StringBuilder();
|
||||||
|
if (patterns != null) {
|
||||||
|
for (StoredPattern pattern : patterns) {
|
||||||
|
if (pattern == null || pattern.pattern == null || pattern.pattern.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (encoded.length() > 0) {
|
||||||
|
encoded.append('\n');
|
||||||
|
}
|
||||||
|
encoded.append(normaliseOrigin(pattern.origin))
|
||||||
|
.append('|')
|
||||||
|
.append(Uri.encode(pattern.pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefs.edit().putString(SbtSettings.KEY_CLOCK_STORED_PATTERNS, encoded.toString()).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private StoredPattern decodeStoredPattern(String line) {
|
||||||
|
if (line == null || line.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int split = line.indexOf('|');
|
||||||
|
if (split <= 0 || split >= line.length() - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String origin = normaliseOrigin(line.substring(0, split));
|
||||||
|
String pattern = Uri.decode(line.substring(split + 1));
|
||||||
|
if (pattern == null || pattern.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new StoredPattern(pattern.trim(), origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normaliseOrigin(String origin) {
|
||||||
|
if (PATTERN_ORIGIN_DRAWER_CLOCK.equals(origin)
|
||||||
|
|| PATTERN_ORIGIN_DRAWER_DATE.equals(origin)
|
||||||
|
|| PATTERN_ORIGIN_STATUSBAR.equals(origin)) {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
return PATTERN_ORIGIN_STATUSBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int colorForOrigin(String origin) {
|
||||||
|
switch (normaliseOrigin(origin)) {
|
||||||
|
case PATTERN_ORIGIN_DRAWER_CLOCK:
|
||||||
|
return COLOR_DRAWER_CLOCK;
|
||||||
|
case PATTERN_ORIGIN_DRAWER_DATE:
|
||||||
|
return COLOR_DRAWER_DATE;
|
||||||
|
case PATTERN_ORIGIN_STATUSBAR:
|
||||||
|
default:
|
||||||
|
return COLOR_STATUSBAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float previewTextSizeForOrigin(String origin) {
|
||||||
|
switch (normaliseOrigin(origin)) {
|
||||||
|
case PATTERN_ORIGIN_DRAWER_CLOCK:
|
||||||
|
return 24f;
|
||||||
|
case PATTERN_ORIGIN_DRAWER_DATE:
|
||||||
|
return 16f;
|
||||||
|
case PATTERN_ORIGIN_STATUSBAR:
|
||||||
|
default:
|
||||||
|
return 18f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean updatePreview(PreviewBinding binding) {
|
private boolean updatePreview(PreviewBinding binding) {
|
||||||
if (binding == null || binding.preview == null) {
|
if (binding == null || binding.preview == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -260,6 +685,36 @@ public final class ClockFragment extends Fragment {
|
|||||||
return result.containsSeconds();
|
return result.containsSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean updateStoredPatternPreview(
|
||||||
|
StoredPatternRowBinding binding,
|
||||||
|
Map<String, ClockPatternPreviewRenderer.Result> cache
|
||||||
|
) {
|
||||||
|
if (binding == null
|
||||||
|
|| binding.preview == null
|
||||||
|
|| binding.owner == null
|
||||||
|
|| binding.owner.section == null
|
||||||
|
|| binding.owner.section.getVisibility() != View.VISIBLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String key = binding.origin + "\u0000" + binding.multilinePreview + "\u0000" + binding.pattern;
|
||||||
|
ClockPatternPreviewRenderer.Result result = cache != null ? cache.get(key) : null;
|
||||||
|
if (result == null) {
|
||||||
|
result = ClockPatternPreviewRenderer.render(
|
||||||
|
binding.preview.getContext(),
|
||||||
|
binding.pattern,
|
||||||
|
binding.preview.getPreviewTextColor());
|
||||||
|
if (!binding.multilinePreview) {
|
||||||
|
result = flattenRows(result);
|
||||||
|
}
|
||||||
|
if (cache != null) {
|
||||||
|
cache.put(key, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.preview.setPreview(result);
|
||||||
|
binding.preview.setContentDescription(result.contentDescription());
|
||||||
|
return result.containsSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
private ClockPatternPreviewRenderer.Result flattenRows(ClockPatternPreviewRenderer.Result result) {
|
private ClockPatternPreviewRenderer.Result flattenRows(ClockPatternPreviewRenderer.Result result) {
|
||||||
if (result == null || result.rows() == null || result.rows().size() <= 1) {
|
if (result == null || result.rows() == null || result.rows().size() <= 1) {
|
||||||
return result;
|
return result;
|
||||||
@@ -324,6 +779,8 @@ public final class ClockFragment extends Fragment {
|
|||||||
view.removeCallbacks(previewTicker);
|
view.removeCallbacks(previewTicker);
|
||||||
}
|
}
|
||||||
previewBindings.clear();
|
previewBindings.clear();
|
||||||
|
storedPatternBindings.clear();
|
||||||
|
storedPatternRowBindings.clear();
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,6 +935,29 @@ public final class ClockFragment extends Fragment {
|
|||||||
private record PreviewBinding(CheckBox enabled, EditText input, ClockPatternPreviewView preview, boolean multilinePreview) {
|
private record PreviewBinding(CheckBox enabled, EditText input, ClockPatternPreviewView preview, boolean multilinePreview) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record StoredPatternBinding(
|
||||||
|
EditText input,
|
||||||
|
String origin,
|
||||||
|
int accentColor,
|
||||||
|
boolean multilinePreview,
|
||||||
|
MaterialButton toggle,
|
||||||
|
View section,
|
||||||
|
LinearLayout listContainer
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record StoredPatternRowBinding(
|
||||||
|
StoredPatternBinding owner,
|
||||||
|
ClockPatternPreviewView preview,
|
||||||
|
String pattern,
|
||||||
|
String origin,
|
||||||
|
boolean multilinePreview
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record StoredPattern(String pattern, String origin) {
|
||||||
|
}
|
||||||
|
|
||||||
private static final class FontFamilyAdapter extends BaseAdapter {
|
private static final class FontFamilyAdapter extends BaseAdapter {
|
||||||
private static final String SAMPLE_PATTERN = "EEEE HH:mm:ss";
|
private static final String SAMPLE_PATTERN = "EEEE HH:mm:ss";
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/clock_custom_format_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -79,6 +80,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/drawer_clock_custom_format_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -140,6 +142,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/drawer_date_custom_format_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -231,6 +234,14 @@
|
|||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/clock_font_picker_button" />
|
android:text="@string/clock_font_picker_button" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/clock_restore_stored_patterns_button"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/clock_restore_stored_patterns" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/clock_custom_format_help_toggle"
|
android:id="@+id/clock_custom_format_help_toggle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -252,6 +252,11 @@
|
|||||||
<string name="clock_custom_format_help_styling">Shorthand blocks:\n\u2022 {#FA0 big @sans-serif-condensed} is shorthand for opening multiple tags in order\n\u2022 {#FA0} is shorthand for {color(#FA0)}\n\u2022 In shorthand: / closes a tag, color(...) sets font colour, @ sets font face\n\u2022 Each whitespace-separated token inside {} becomes its own tag, except color(...) which may contain spaces\n\nLine breaks:\n\u2022 Use \\n, {\\n} or {\\n15}; the number is the line-height delta in statusbar units\n\u2022 Omit the number to use the default row spacing\n\u2022 { /\\n } style line breaks are written without spaces: {/\\n} or {/\\n15}\n\u2022 Normal line breaks propagate open tags onto the next row\n\u2022 A / line break ends all currently open tags before the next row starts\n\nPipes and grouping:\n\u2022 Maximum two pipes per row; more than two is a syntax error for that row only\n\u2022 Pipes inside single quotes or {} do not split the row\n\u2022 One pipe splits into left and right segments\n\u2022 Two pipes remove the middle section, then split using the outer pipes\n\u2022 Tags are logically continued across pipe splits, even if the middle section is ignored\n\u2022 Rows without pipes are independent; rows with pipes share one common pipe group\n\u2022 A syntax-error row becomes the text Syntax error, with no whole-clock fallback\n\nColours:\n\u2022 Supported operands: #RGB, #ARGB, #RRGGBB, #AARRGGBB, #batterybar, [R,G,B], [A,R,G,B], and numbers\n\u2022 Colour expressions use reverse Polish notation: operands first, operator last\n\u2022 Use ; to separate bright/dark variants; the dark side starts with the raw result from the bright side on the stack\n\u2022 Operators: + - * / ^ inv min max sqrt clip dup\n\u2022 Operators can be channel-filtered, for example +A, *RGB, invRGB, maxRGB, clipA\n\u2022 Final values are clipped to 0..255; a final number becomes opaque grayscale</string>
|
<string name="clock_custom_format_help_styling">Shorthand blocks:\n\u2022 {#FA0 big @sans-serif-condensed} is shorthand for opening multiple tags in order\n\u2022 {#FA0} is shorthand for {color(#FA0)}\n\u2022 In shorthand: / closes a tag, color(...) sets font colour, @ sets font face\n\u2022 Each whitespace-separated token inside {} becomes its own tag, except color(...) which may contain spaces\n\nLine breaks:\n\u2022 Use \\n, {\\n} or {\\n15}; the number is the line-height delta in statusbar units\n\u2022 Omit the number to use the default row spacing\n\u2022 { /\\n } style line breaks are written without spaces: {/\\n} or {/\\n15}\n\u2022 Normal line breaks propagate open tags onto the next row\n\u2022 A / line break ends all currently open tags before the next row starts\n\nPipes and grouping:\n\u2022 Maximum two pipes per row; more than two is a syntax error for that row only\n\u2022 Pipes inside single quotes or {} do not split the row\n\u2022 One pipe splits into left and right segments\n\u2022 Two pipes remove the middle section, then split using the outer pipes\n\u2022 Tags are logically continued across pipe splits, even if the middle section is ignored\n\u2022 Rows without pipes are independent; rows with pipes share one common pipe group\n\u2022 A syntax-error row becomes the text Syntax error, with no whole-clock fallback\n\nColours:\n\u2022 Supported operands: #RGB, #ARGB, #RRGGBB, #AARRGGBB, #batterybar, [R,G,B], [A,R,G,B], and numbers\n\u2022 Colour expressions use reverse Polish notation: operands first, operator last\n\u2022 Use ; to separate bright/dark variants; the dark side starts with the raw result from the bright side on the stack\n\u2022 Operators: + - * / ^ inv min max sqrt clip dup\n\u2022 Operators can be channel-filtered, for example +A, *RGB, invRGB, maxRGB, clipA\n\u2022 Final values are clipped to 0..255; a final number becomes opaque grayscale</string>
|
||||||
<string name="clock_custom_format_help_examples">Examples:\n\u2022 <small>EEE d MMM</small> HH:mm\n\u2022 HH<small>:mm</small>\n\u2022 <font color=\'#FA0\'>ss</font>\n\u2022 {color(#FA0 ; invRGB 0.8 *RGB)}ss\n\u2022 {color(#batterybar 1.2 * ; invRGB 0.5 *RGB) big u @sans-serif-condensed}HH:mm\n\u2022 {color([255, 255, 170, 0] ; invRGB)}HH:mm\n\u2022 HH:mm{\\n12 small}EEE d MMM\n\u2022 HH|mm aligns the pipe position across every piped row\n\u2022 left||right removes the middle section before split alignment\n\u2022 {/font}{/\\n big}HH:mm starts a new row without carrying the previous font tags forward</string>
|
<string name="clock_custom_format_help_examples">Examples:\n\u2022 <small>EEE d MMM</small> HH:mm\n\u2022 HH<small>:mm</small>\n\u2022 <font color=\'#FA0\'>ss</font>\n\u2022 {color(#FA0 ; invRGB 0.8 *RGB)}ss\n\u2022 {color(#batterybar 1.2 * ; invRGB 0.5 *RGB) big u @sans-serif-condensed}HH:mm\n\u2022 {color([255, 255, 170, 0] ; invRGB)}HH:mm\n\u2022 HH:mm{\\n12 small}EEE d MMM\n\u2022 HH|mm aligns the pipe position across every piped row\n\u2022 left||right removes the middle section before split alignment\n\u2022 {/font}{/\\n big}HH:mm starts a new row without carrying the previous font tags forward</string>
|
||||||
<string name="clock_font_picker_button">Browse font families</string>
|
<string name="clock_font_picker_button">Browse font families</string>
|
||||||
|
<string name="clock_restore_stored_patterns">Restore stored patterns</string>
|
||||||
|
<string name="clock_stored_patterns_show">Show stored patterns</string>
|
||||||
|
<string name="clock_stored_patterns_hide">Hide stored patterns</string>
|
||||||
|
<string name="clock_stored_patterns_hint">Tap a preview to use it</string>
|
||||||
|
<string name="clock_store_current_pattern">Store current pattern</string>
|
||||||
<string name="clock_custom_format_help_show">▾ Show pattern instructions</string>
|
<string name="clock_custom_format_help_show">▾ Show pattern instructions</string>
|
||||||
<string name="clock_custom_format_help_hide">▴ Hide pattern instructions</string>
|
<string name="clock_custom_format_help_hide">▴ Hide pattern instructions</string>
|
||||||
<string name="clock_font_picker_title">Font families (tap to copy)</string>
|
<string name="clock_font_picker_title">Font families (tap to copy)</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user