Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d2c3041b0a | |||
| 77c417117a |
@@ -8,8 +8,8 @@ android {
|
|||||||
applicationId "com.streamplayer"
|
applicationId "com.streamplayer"
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 94200
|
versionCode 94400
|
||||||
versionName "9.4.2"
|
versionName "9.4.4"
|
||||||
buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"'
|
buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,21 @@ public class EventRepository {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void prefetchEvents(Context context) {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
String json = downloadJson();
|
||||||
|
parseEvents(json);
|
||||||
|
prefs.edit()
|
||||||
|
.putString(KEY_JSON, json)
|
||||||
|
.putLong(KEY_TIMESTAMP, System.currentTimeMillis())
|
||||||
|
.apply();
|
||||||
|
} catch (IOException | JSONException ignored) {
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
private String downloadJson() throws IOException {
|
private String downloadJson() throws IOException {
|
||||||
URL url = new URL(EVENTS_URL);
|
URL url = new URL(EVENTS_URL);
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
@@ -24,14 +27,18 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final long EVENT_PREFETCH_INTERVAL_MS = TimeUnit.HOURS.toMillis(1);
|
||||||
|
|
||||||
private RecyclerView sectionList;
|
private RecyclerView sectionList;
|
||||||
private RecyclerView contentList;
|
private RecyclerView contentList;
|
||||||
private ProgressBar loadingIndicator;
|
private ProgressBar loadingIndicator;
|
||||||
private TextView messageView;
|
private TextView messageView;
|
||||||
private TextView contentTitle;
|
private TextView contentTitle;
|
||||||
|
private Button eventsRefreshButton;
|
||||||
|
|
||||||
private ChannelAdapter channelAdapter;
|
private ChannelAdapter channelAdapter;
|
||||||
private EventAdapter eventAdapter;
|
private EventAdapter eventAdapter;
|
||||||
@@ -46,6 +53,9 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private AlertDialog updateDialog;
|
private AlertDialog updateDialog;
|
||||||
private AlertDialog blockedDialog;
|
private AlertDialog blockedDialog;
|
||||||
private DeviceRegistry deviceRegistry;
|
private DeviceRegistry deviceRegistry;
|
||||||
|
private Handler eventPrefetchHandler;
|
||||||
|
private Runnable eventPrefetchRunnable;
|
||||||
|
private boolean isEventsRefreshing;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -57,6 +67,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
loadingIndicator = findViewById(R.id.loading_indicator);
|
loadingIndicator = findViewById(R.id.loading_indicator);
|
||||||
messageView = findViewById(R.id.message_view);
|
messageView = findViewById(R.id.message_view);
|
||||||
contentTitle = findViewById(R.id.content_title);
|
contentTitle = findViewById(R.id.content_title);
|
||||||
|
eventsRefreshButton = findViewById(R.id.events_refresh_button);
|
||||||
|
eventsRefreshButton.setOnClickListener(v -> manualRefreshEvents());
|
||||||
|
|
||||||
channelAdapter = new ChannelAdapter(
|
channelAdapter = new ChannelAdapter(
|
||||||
channel -> openPlayer(channel.getName(), channel.getPageUrl()));
|
channel -> openPlayer(channel.getName(), channel.getPageUrl()));
|
||||||
@@ -113,6 +125,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
startEventPrefetchScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -138,6 +152,12 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (deviceRegistry != null) {
|
if (deviceRegistry != null) {
|
||||||
deviceRegistry.release();
|
deviceRegistry.release();
|
||||||
}
|
}
|
||||||
|
stopEventPrefetchScheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
closeAppCompletely();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectSection(int index) {
|
private void selectSection(int index) {
|
||||||
@@ -157,6 +177,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showChannels(SectionEntry section) {
|
private void showChannels(SectionEntry section) {
|
||||||
|
updateEventsRefreshVisibility(false);
|
||||||
contentTitle.setText(section.title);
|
contentTitle.setText(section.title);
|
||||||
contentList.setLayoutManager(channelLayoutManager);
|
contentList.setLayoutManager(channelLayoutManager);
|
||||||
contentList.setAdapter(channelAdapter);
|
contentList.setAdapter(channelAdapter);
|
||||||
@@ -172,6 +193,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showEvents() {
|
private void showEvents() {
|
||||||
|
updateEventsRefreshVisibility(true);
|
||||||
contentTitle.setText(currentSection != null ? currentSection.title : getString(R.string.section_events));
|
contentTitle.setText(currentSection != null ? currentSection.title : getString(R.string.section_events));
|
||||||
contentList.setLayoutManager(eventLayoutManager);
|
contentList.setLayoutManager(eventLayoutManager);
|
||||||
contentList.setAdapter(eventAdapter);
|
contentList.setAdapter(eventAdapter);
|
||||||
@@ -183,6 +205,9 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadEvents(boolean forceRefresh) {
|
private void loadEvents(boolean forceRefresh) {
|
||||||
|
if (currentSection != null && currentSection.type == SectionEntry.Type.EVENTS) {
|
||||||
|
setEventsRefreshing(true);
|
||||||
|
}
|
||||||
loadingIndicator.setVisibility(View.VISIBLE);
|
loadingIndicator.setVisibility(View.VISIBLE);
|
||||||
messageView.setVisibility(View.GONE);
|
messageView.setVisibility(View.GONE);
|
||||||
eventRepository.loadEvents(this, forceRefresh, new EventRepository.Callback() {
|
eventRepository.loadEvents(this, forceRefresh, new EventRepository.Callback() {
|
||||||
@@ -196,6 +221,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
} else {
|
} else {
|
||||||
loadingIndicator.setVisibility(View.GONE);
|
loadingIndicator.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
setEventsRefreshing(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,11 +231,48 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
loadingIndicator.setVisibility(View.GONE);
|
loadingIndicator.setVisibility(View.GONE);
|
||||||
messageView.setVisibility(View.VISIBLE);
|
messageView.setVisibility(View.VISIBLE);
|
||||||
messageView.setText(getString(R.string.message_events_error, message));
|
messageView.setText(getString(R.string.message_events_error, message));
|
||||||
|
setEventsRefreshing(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void manualRefreshEvents() {
|
||||||
|
if (isEventsRefreshing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentSection == null || currentSection.type != SectionEntry.Type.EVENTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadEvents(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEventsRefreshVisibility(boolean visible) {
|
||||||
|
if (eventsRefreshButton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eventsRefreshButton.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
if (visible) {
|
||||||
|
eventsRefreshButton.setEnabled(!isEventsRefreshing);
|
||||||
|
eventsRefreshButton.setText(isEventsRefreshing
|
||||||
|
? getString(R.string.events_refreshing)
|
||||||
|
: getString(R.string.events_refresh_action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setEventsRefreshing(boolean refreshing) {
|
||||||
|
isEventsRefreshing = refreshing;
|
||||||
|
if (eventsRefreshButton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eventsRefreshButton.getVisibility() == View.VISIBLE) {
|
||||||
|
eventsRefreshButton.setEnabled(!refreshing);
|
||||||
|
eventsRefreshButton.setText(refreshing
|
||||||
|
? getString(R.string.events_refreshing)
|
||||||
|
: getString(R.string.events_refresh_action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void displayEvents() {
|
private void displayEvents() {
|
||||||
loadingIndicator.setVisibility(View.GONE);
|
loadingIndicator.setVisibility(View.GONE);
|
||||||
if (cachedEvents.isEmpty()) {
|
if (cachedEvents.isEmpty()) {
|
||||||
@@ -254,7 +317,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
builder.setCancelable(false);
|
builder.setCancelable(false);
|
||||||
builder.setNegativeButton(R.string.update_action_close_app,
|
builder.setNegativeButton(R.string.update_action_close_app,
|
||||||
(dialog, which) -> finish());
|
(dialog, which) -> closeAppCompletely());
|
||||||
} else {
|
} else {
|
||||||
builder.setNegativeButton(R.string.update_action_later, null);
|
builder.setNegativeButton(R.string.update_action_later, null);
|
||||||
}
|
}
|
||||||
@@ -338,7 +401,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
.setView(dialogView)
|
.setView(dialogView)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setPositiveButton(R.string.device_blocked_close,
|
.setPositiveButton(R.string.device_blocked_close,
|
||||||
(dialog, which) -> finish());
|
(dialog, which) -> closeAppCompletely());
|
||||||
if (hasToken) {
|
if (hasToken) {
|
||||||
builder.setNeutralButton(R.string.device_blocked_copy_token,
|
builder.setNeutralButton(R.string.device_blocked_copy_token,
|
||||||
(dialog, which) -> copyTokenToClipboard(tokenPart));
|
(dialog, which) -> copyTokenToClipboard(tokenPart));
|
||||||
@@ -358,6 +421,40 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
Toast.makeText(this, R.string.device_blocked_copy_success, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.device_blocked_copy_success, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startEventPrefetchScheduler() {
|
||||||
|
if (eventPrefetchHandler == null) {
|
||||||
|
eventPrefetchHandler = new Handler(Looper.getMainLooper());
|
||||||
|
}
|
||||||
|
if (eventPrefetchRunnable == null) {
|
||||||
|
eventPrefetchRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (eventRepository != null) {
|
||||||
|
eventRepository.prefetchEvents(getApplicationContext());
|
||||||
|
}
|
||||||
|
if (eventPrefetchHandler != null) {
|
||||||
|
eventPrefetchHandler.postDelayed(this, EVENT_PREFETCH_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
eventPrefetchHandler.removeCallbacks(eventPrefetchRunnable);
|
||||||
|
}
|
||||||
|
eventPrefetchHandler.post(eventPrefetchRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopEventPrefetchScheduler() {
|
||||||
|
if (eventPrefetchHandler != null && eventPrefetchRunnable != null) {
|
||||||
|
eventPrefetchHandler.removeCallbacks(eventPrefetchRunnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeAppCompletely() {
|
||||||
|
finishAffinity();
|
||||||
|
android.os.Process.killProcess(android.os.Process.myPid());
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
private int getSpanCount() {
|
private int getSpanCount() {
|
||||||
return getResources().getInteger(R.integer.channel_grid_span);
|
return getResources().getInteger(R.integer.channel_grid_span);
|
||||||
}
|
}
|
||||||
|
|||||||
10
app/src/main/res/drawable/bg_dialog_dark.xml
Normal file
10
app/src/main/res/drawable/bg_dialog_dark.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/dialog_background" />
|
||||||
|
<corners android:radius="18dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="16dp"
|
||||||
|
android:left="16dp"
|
||||||
|
android:right="16dp"
|
||||||
|
android:top="16dp" />
|
||||||
|
</shape>
|
||||||
10
app/src/main/res/drawable/bg_events_refresh_button.xml
Normal file
10
app/src/main/res/drawable/bg_events_refresh_button.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/accent_blue" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="8dp"
|
||||||
|
android:left="20dp"
|
||||||
|
android:right="20dp"
|
||||||
|
android:top="8dp" />
|
||||||
|
</shape>
|
||||||
@@ -72,14 +72,34 @@
|
|||||||
app:layout_constraintStart_toEndOf="@id/divider"
|
app:layout_constraintStart_toEndOf="@id/divider"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/content_title"
|
android:id="@+id/content_header"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="@color/white"
|
android:gravity="center_vertical"
|
||||||
android:textSize="18sp"
|
android:orientation="horizontal">
|
||||||
android:textStyle="bold"
|
|
||||||
tools:text="Canales" />
|
<TextView
|
||||||
|
android:id="@+id/content_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="Canales" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/events_refresh_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="@drawable/bg_events_refresh_button"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:text="@string/events_refresh_action"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/loading_indicator"
|
android:id="@+id/loading_indicator"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
android:id="@+id/blocked_message_text"
|
android:id="@+id/blocked_message_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="@android:color/black"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/device_blocked_token_label"
|
android:text="@string/device_blocked_token_label"
|
||||||
android:textColor="@android:color/black"
|
android:textColor="@color/white"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:textColor="@android:color/black"
|
android:textColor="@color/white"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -3,4 +3,6 @@
|
|||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
<color name="text_secondary">#B3FFFFFF</color>
|
<color name="text_secondary">#B3FFFFFF</color>
|
||||||
|
<color name="accent_blue">#FF1E88E5</color>
|
||||||
|
<color name="dialog_background">#FF121212</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -46,4 +46,6 @@
|
|||||||
<string name="device_blocked_copy_success">Código copiado al portapapeles</string>
|
<string name="device_blocked_copy_success">Código copiado al portapapeles</string>
|
||||||
<string name="device_blocked_copy_error">No se pudo copiar el código</string>
|
<string name="device_blocked_copy_error">No se pudo copiar el código</string>
|
||||||
<string name="device_registry_error">No se pudo registrar el dispositivo (%1$s)</string>
|
<string name="device_registry_error">No se pudo registrar el dispositivo (%1$s)</string>
|
||||||
|
<string name="events_refresh_action">Actualizar ahora</string>
|
||||||
|
<string name="events_refreshing">Actualizando...</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -8,8 +8,16 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="ThemeOverlay.StreamPlayer.AlertDialog" parent="ThemeOverlay.AppCompat.Dialog.Alert">
|
<style name="ThemeOverlay.StreamPlayer.AlertDialog" parent="ThemeOverlay.AppCompat.Dialog.Alert">
|
||||||
|
<item name="android:windowBackground">@drawable/bg_dialog_dark</item>
|
||||||
|
<item name="android:colorBackground">@color/dialog_background</item>
|
||||||
|
<item name="colorBackgroundFloating">@color/dialog_background</item>
|
||||||
<item name="android:textColorPrimary">@color/white</item>
|
<item name="android:textColorPrimary">@color/white</item>
|
||||||
<item name="android:textColorSecondary">@color/white</item>
|
<item name="android:textColorSecondary">@color/text_secondary</item>
|
||||||
<item name="colorAccent">@color/white</item>
|
<item name="colorAccent">@color/accent_blue</item>
|
||||||
|
<item name="android:buttonStyle">@style/StreamPlayer.DialogButton</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="StreamPlayer.DialogButton" parent="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog">
|
||||||
|
<item name="android:textColor">@color/accent_blue</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
"model": "SM-S928B",
|
"model": "SM-S928B",
|
||||||
"manufacturer": "Samsung",
|
"manufacturer": "Samsung",
|
||||||
"osVersion": "16 (API 36)",
|
"osVersion": "16 (API 36)",
|
||||||
"appVersionName": "9.4.1",
|
"appVersionName": "9.4.4",
|
||||||
"appVersionCode": 94100,
|
"appVersionCode": 94400,
|
||||||
"firstSeen": "2025-11-23T22:31:13.359Z",
|
"firstSeen": "2025-11-23T22:31:13.359Z",
|
||||||
"lastSeen": "2025-11-23T23:11:07.215Z",
|
"lastSeen": "2025-11-23T23:11:07.215Z",
|
||||||
"blocked": false,
|
"blocked": false,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"versionCode": 94100,
|
"versionCode": 94400,
|
||||||
"versionName": "9.4.1",
|
"versionName": "9.4.4",
|
||||||
"minSupportedVersionCode": 91000,
|
"minSupportedVersionCode": 91000,
|
||||||
"forceUpdate": false,
|
"forceUpdate": false,
|
||||||
"downloadUrl": "https://gitea.cbcren.online/renato97/app/releases/download/v9.4.1/StreamPlayer-v9.4.1.apk",
|
"downloadUrl": "https://gitea.cbcren.online/renato97/app/releases/download/v9.4.4/StreamPlayer-v9.4.4.apk",
|
||||||
"fileName": "StreamPlayer-v9.4.1.apk",
|
"fileName": "StreamPlayer-v9.4.4.apk",
|
||||||
"sizeBytes": 5944680,
|
"sizeBytes": 5948849,
|
||||||
"notes": "StreamPlayer v9.4.1\n\nMejoras en esta versión:\n\n- Experiencia de reproducción optimizada e ininterrumpida\n- Mejores controles de administración y gestión de dispositivos\n- Funcionalidad de eliminación de registros con confirmación segura\n- Optimización de energía durante el uso de la aplicación\n- Interfaz administrativa mejorada con más opciones\n- Flujo de trabajo más eficiente para la gestión\n- Mejor respuesta y estabilidad general\n- Correcciones de usabilidad menores\n\nEsta actualización mejora tanto la experiencia de visualización como las herramientas de administración para un mejor control y uso de la aplicación."
|
"notes": "StreamPlayer v9.4.4\n\nCorrecciones y mejoras:\n\n- Los botones de actualización y cierre en los diálogos ahora se ven correctamente en temas claros y oscuros.\n- Nuevo estilo oscuro en los diálogos para mejorar el contraste del texto y acciones.\n- Botón \"Actualizar ahora\" de eventos con diseño sólido para que destaque en cualquier fondo.\n- Se mantiene la actualización silenciosa y el caché de eventos para que siempre encuentres la grilla fresca al reiniciar.\n\nRecomendamos actualizar para asegurar la mejor experiencia con la sección de eventos y el sistema de actualizaciones."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user