mirror of
git://f0xx.org/ac/ac-mobile-android
synced 2026-08-12 18:12:13 +03:00
OTA channel selector with dev/staging/prod presets.
Developer settings spinner prefills manifest URL from selected channel; prod is default.
This commit is contained in:
@@ -18,6 +18,7 @@ import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.foxx.androidcast.display.ExternalDisplayCapturePolicy;
|
||||
import com.foxx.androidcast.ota.OtaDefaults;
|
||||
import com.foxx.androidcast.receiver.buffer.JitterBufferConfig;
|
||||
import com.foxx.androidcast.receiver.buffer.JitterBufferMode;
|
||||
import com.foxx.androidcast.receiver.av.ReceiverAudioPreset;
|
||||
@@ -55,6 +56,7 @@ public final class AppPreferences {
|
||||
private static final String KEY_RECEIVER_PIP_USER_DEFINED = "receiver_pip_user_defined";
|
||||
private static final String KEY_RECEIVER_PIP_ENABLED = "receiver_pip_enabled";
|
||||
private static final String KEY_OTA_CHANNEL_URL = "ota_channel_url";
|
||||
private static final String KEY_OTA_CHANNEL_PRESET = "ota_channel_preset";
|
||||
private static final String KEY_OTA_MANIFEST_URL_LEGACY = "ota_manifest_url";
|
||||
private static final String KEY_OTA_CHECK_ON_LAUNCH = "ota_check_on_launch";
|
||||
private static final String KEY_OTA_LAST_CHECK_MS = "ota_last_check_ms";
|
||||
@@ -312,6 +314,19 @@ public final class AppPreferences {
|
||||
.apply();
|
||||
}
|
||||
|
||||
/** {@code dev}, {@code staging}, or {@code prod} (default). */
|
||||
public static String getOtaChannelPreset(Context context) {
|
||||
return prefs(context).getString(KEY_OTA_CHANNEL_PRESET, OtaDefaults.defaultPreset());
|
||||
}
|
||||
|
||||
public static void setOtaChannelPreset(Context context, String preset) {
|
||||
String v = preset != null ? preset.trim() : "";
|
||||
if (v.isEmpty()) {
|
||||
v = OtaDefaults.defaultPreset();
|
||||
}
|
||||
prefs(context).edit().putString(KEY_OTA_CHANNEL_PRESET, v).apply();
|
||||
}
|
||||
|
||||
public static boolean isOtaCheckOnLaunch(Context context) {
|
||||
return prefs(context).getBoolean(KEY_OTA_CHECK_ON_LAUNCH, true);
|
||||
}
|
||||
|
||||
@@ -107,19 +107,24 @@ public class DeveloperSettingsActivity extends AppCompatActivity {
|
||||
});
|
||||
|
||||
TextView installedVersion = findViewById(R.id.text_ota_installed_version);
|
||||
Spinner otaChannelSpinner = findViewById(R.id.spinner_ota_channel);
|
||||
TextInputEditText manifestUrl = findViewById(R.id.edit_ota_manifest_url);
|
||||
CheckBox otaOnLaunch = findViewById(R.id.check_ota_on_launch);
|
||||
Button checkOta = findViewById(R.id.btn_check_ota);
|
||||
|
||||
refreshOtaUi(installedVersion, manifestUrl, otaOnLaunch);
|
||||
bindOtaChannelSpinner(otaChannelSpinner, manifestUrl);
|
||||
|
||||
refreshOtaUi(installedVersion, otaChannelSpinner, manifestUrl, otaOnLaunch);
|
||||
|
||||
otaOnLaunch.setOnCheckedChangeListener((buttonView, isChecked) ->
|
||||
AppPreferences.setOtaCheckOnLaunch(this, isChecked));
|
||||
|
||||
checkOta.setOnClickListener(v -> {
|
||||
String preset = presetFromSpinner(otaChannelSpinner);
|
||||
AppPreferences.setOtaChannelPreset(this, preset);
|
||||
String url = manifestUrl.getText() != null ? manifestUrl.getText().toString().trim() : "";
|
||||
if (url.isEmpty()) {
|
||||
url = OtaDefaults.effectiveChannelUrl(this);
|
||||
url = OtaDefaults.channelUrlForPreset(preset);
|
||||
}
|
||||
AppPreferences.setOtaChannelUrl(this, url);
|
||||
OtaUpdateCoordinator.requestManualCheck(this);
|
||||
@@ -165,6 +170,7 @@ public class DeveloperSettingsActivity extends AppCompatActivity {
|
||||
bindDiagPingSection();
|
||||
refreshOtaUi(
|
||||
findViewById(R.id.text_ota_installed_version),
|
||||
findViewById(R.id.spinner_ota_channel),
|
||||
findViewById(R.id.edit_ota_manifest_url),
|
||||
findViewById(R.id.check_ota_on_launch));
|
||||
bindAvPresets();
|
||||
@@ -704,14 +710,26 @@ public class DeveloperSettingsActivity extends AppCompatActivity {
|
||||
};
|
||||
}
|
||||
|
||||
private void refreshOtaUi(TextView installedVersion, TextInputEditText manifestUrl, CheckBox otaOnLaunch) {
|
||||
private void refreshOtaUi(TextView installedVersion, Spinner otaChannelSpinner,
|
||||
TextInputEditText manifestUrl, CheckBox otaOnLaunch) {
|
||||
if (installedVersion != null) {
|
||||
installedVersion.setText(getString(R.string.ota_installed_version,
|
||||
OtaUpdateChecker.installedVersionName(this),
|
||||
OtaUpdateChecker.installedVersionCode(this)));
|
||||
}
|
||||
String preset = AppPreferences.getOtaChannelPreset(this);
|
||||
if (otaChannelSpinner != null) {
|
||||
int idx = presetIndex(preset);
|
||||
if (otaChannelSpinner.getSelectedItemPosition() != idx) {
|
||||
otaChannelSpinner.setSelection(idx, false);
|
||||
}
|
||||
}
|
||||
if (manifestUrl != null) {
|
||||
manifestUrl.setText(OtaDefaults.effectiveChannelUrl(this));
|
||||
String url = AppPreferences.getOtaChannelUrl(this);
|
||||
if (url.isEmpty()) {
|
||||
url = OtaDefaults.channelUrlForPreset(preset);
|
||||
}
|
||||
manifestUrl.setText(url);
|
||||
}
|
||||
if (otaOnLaunch != null) {
|
||||
otaOnLaunch.setOnCheckedChangeListener(null);
|
||||
@@ -721,6 +739,62 @@ public class DeveloperSettingsActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void bindOtaChannelSpinner(Spinner spinner, TextInputEditText manifestUrl) {
|
||||
if (spinner == null) {
|
||||
return;
|
||||
}
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
|
||||
android.R.layout.simple_spinner_item,
|
||||
new String[]{
|
||||
getString(R.string.ota_channel_dev),
|
||||
getString(R.string.ota_channel_staging),
|
||||
getString(R.string.ota_channel_prod),
|
||||
});
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(adapter);
|
||||
spinner.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(android.widget.AdapterView<?> parent, android.view.View view,
|
||||
int position, long id) {
|
||||
String preset = presetFromIndex(position);
|
||||
AppPreferences.setOtaChannelPreset(DeveloperSettingsActivity.this, preset);
|
||||
if (manifestUrl != null) {
|
||||
manifestUrl.setText(OtaDefaults.channelUrlForPreset(preset));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(android.widget.AdapterView<?> parent) {}
|
||||
});
|
||||
}
|
||||
|
||||
private static int presetIndex(String preset) {
|
||||
if (OtaDefaults.CHANNEL_DEV.equals(preset)) {
|
||||
return 0;
|
||||
}
|
||||
if (OtaDefaults.CHANNEL_STAGING.equals(preset)) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static String presetFromIndex(int index) {
|
||||
if (index == 0) {
|
||||
return OtaDefaults.CHANNEL_DEV;
|
||||
}
|
||||
if (index == 1) {
|
||||
return OtaDefaults.CHANNEL_STAGING;
|
||||
}
|
||||
return OtaDefaults.CHANNEL_PROD;
|
||||
}
|
||||
|
||||
private static String presetFromSpinner(Spinner spinner) {
|
||||
if (spinner == null) {
|
||||
return OtaDefaults.defaultPreset();
|
||||
}
|
||||
return presetFromIndex(spinner.getSelectedItemPosition());
|
||||
}
|
||||
|
||||
private void refreshFromPreferences(CheckBox debugOverlay, CheckBox codecScores, CheckBox sessionStats,
|
||||
CheckBox streamDump, CheckBox adaptiveDisplay, CheckBox backendNtpSync) {
|
||||
bindDeveloperCheckbox(debugOverlay, AppPreferences.isShowReceiverDebugOverlay(this),
|
||||
|
||||
@@ -7,18 +7,66 @@ import com.foxx.androidcast.BuildConfig;
|
||||
|
||||
/** Built-in OTA channel fallback when prefs / BuildConfig / settings.json omit a URL. */
|
||||
public final class OtaDefaults {
|
||||
/** Canonical stable channel on the public apps host. */
|
||||
/** Public apps host — channel JSON lives under {@code /v0/ota/channel/}. */
|
||||
public static final String OTA_HOST_BASE = "https://apps.f0xx.org";
|
||||
|
||||
public static final String CHANNEL_DEV = "dev";
|
||||
public static final String CHANNEL_STAGING = "staging";
|
||||
public static final String CHANNEL_PROD = "prod";
|
||||
|
||||
/** Legacy alias; prod channel file is {@code stable.json}. */
|
||||
public static final String FALLBACK_CHANNEL_URL =
|
||||
"https://apps.f0xx.org/v0/ota/channel/stable.json";
|
||||
channelUrlForPreset(CHANNEL_PROD);
|
||||
|
||||
private OtaDefaults() {}
|
||||
|
||||
/** Preference → BuildConfig channel → BuildConfig manifest → {@link #FALLBACK_CHANNEL_URL}. */
|
||||
/** Maps UI preset to on-disk channel filename (prod → stable.json). */
|
||||
public static String channelFileForPreset(String preset) {
|
||||
if (CHANNEL_DEV.equals(preset)) {
|
||||
return "dev.json";
|
||||
}
|
||||
if (CHANNEL_STAGING.equals(preset)) {
|
||||
return "staging.json";
|
||||
}
|
||||
return "stable.json";
|
||||
}
|
||||
|
||||
public static String channelUrlForPreset(String preset) {
|
||||
return OTA_HOST_BASE + "/v0/ota/channel/" + channelFileForPreset(preset);
|
||||
}
|
||||
|
||||
public static String defaultPreset() {
|
||||
return CHANNEL_PROD;
|
||||
}
|
||||
|
||||
/** Preset from stored URL when possible (dev/staging/prod). */
|
||||
public static String presetForUrl(String url) {
|
||||
if (url == null || url.isEmpty()) {
|
||||
return defaultPreset();
|
||||
}
|
||||
String lower = url.toLowerCase();
|
||||
if (lower.contains("/channel/dev.json")) {
|
||||
return CHANNEL_DEV;
|
||||
}
|
||||
if (lower.contains("/channel/staging.json")) {
|
||||
return CHANNEL_STAGING;
|
||||
}
|
||||
if (lower.contains("/channel/stable.json")) {
|
||||
return CHANNEL_PROD;
|
||||
}
|
||||
return defaultPreset();
|
||||
}
|
||||
|
||||
/** Preference URL → preset URL → BuildConfig → {@link #FALLBACK_CHANNEL_URL}. */
|
||||
public static String effectiveChannelUrl(Context context) {
|
||||
String url = AppPreferences.getOtaChannelUrl(context);
|
||||
if (!url.isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
String preset = AppPreferences.getOtaChannelPreset(context);
|
||||
if (preset != null && !preset.isEmpty()) {
|
||||
return channelUrlForPreset(preset);
|
||||
}
|
||||
if (BuildConfig.OTA_CHANNEL_URL_DEFAULT != null) {
|
||||
url = BuildConfig.OTA_CHANNEL_URL_DEFAULT.trim();
|
||||
if (!url.isEmpty()) {
|
||||
|
||||
@@ -42,6 +42,9 @@ final class OtaSettingsStore {
|
||||
|
||||
LinkedHashMap<String, Boolean> dedup = new LinkedHashMap<>();
|
||||
addSource(dedup, AppPreferences.getOtaChannelUrl(context));
|
||||
if (AppPreferences.getOtaChannelUrl(context).isEmpty()) {
|
||||
addSource(dedup, OtaDefaults.channelUrlForPreset(AppPreferences.getOtaChannelPreset(context)));
|
||||
}
|
||||
if (BuildConfig.OTA_CHANNEL_URL_DEFAULT != null) {
|
||||
addSource(dedup, BuildConfig.OTA_CHANNEL_URL_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -333,6 +333,19 @@
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/ota_channel_label"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_ota_channel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -397,8 +397,12 @@
|
||||
<string name="dev_av_audio_dolby_51">Dolby 5.1 (stereo headset)</string>
|
||||
<string name="dev_av_audio_dolby_71">Dolby 7.1 (stereo headset)</string>
|
||||
<string name="ota_section_title">App updates (OTA)</string>
|
||||
<string name="ota_section_hint">v0 layout: https://host/v0/ota/channel/stable.json → manifest → APK. Service checks every 1 minute by default (overridable in private settings.json).</string>
|
||||
<string name="ota_manifest_url_hint">OTA channel URL (v0 stable.json)</string>
|
||||
<string name="ota_section_hint">v0 layout: channel JSON → manifest → APK (or bundle). Default channel: production (stable.json). Checks run every 1 minute when enabled.</string>
|
||||
<string name="ota_channel_label">OTA channel</string>
|
||||
<string name="ota_channel_dev">Development (dev)</string>
|
||||
<string name="ota_channel_staging">Staging (QA)</string>
|
||||
<string name="ota_channel_prod">Production (default)</string>
|
||||
<string name="ota_manifest_url_hint">OTA channel URL (full https URL)</string>
|
||||
<string name="ota_check_on_launch">Enable OTA periodic checks (default every 1 minute)</string>
|
||||
<string name="ota_check_now">Check for updates now</string>
|
||||
<string name="ota_installed_version">Installed: %1$s (%2$d)</string>
|
||||
|
||||
Reference in New Issue
Block a user