Remove dashboard/VPS references - make project PR-ready
- Remove DeviceRegistry.java (dashboard integration) - Remove VPS IP from build.gradle - Remove personal Gitea token from UpdateManager - Add configurable UPDATE_CHECK_URL for updates - Clean README to be generic and PR-ready - Clean update manifests - Remove Docker files and .env from repo
This commit is contained in:
4
.env
4
.env
@@ -1,4 +0,0 @@
|
|||||||
GITEA_TOKEN=efeed2af00597883adb04da70bd6a7c2993ae92d
|
|
||||||
GEMINI_API_KEY=AIzaSyDWOgyAJqscuPU6iSpS6gxupWBm4soNw5o
|
|
||||||
TELEGRAM_BOT_TOKEN=8593525164:AAGCX9B_RJGN35_F7tSB72rEZhS_4Zpcszs
|
|
||||||
TELEGRAM_CHAT_ID=692714536
|
|
||||||
57
Dockerfile
57
Dockerfile
@@ -1,57 +0,0 @@
|
|||||||
FROM eclipse-temurin:17-jdk
|
|
||||||
|
|
||||||
# Evitar interactividad durante la instalación
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
# Instalar dependencias necesarias para Android SDK
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
wget \
|
|
||||||
unzip \
|
|
||||||
git \
|
|
||||||
python3 \
|
|
||||||
python3-pip \
|
|
||||||
ncurses-bin \
|
|
||||||
build-essential \
|
|
||||||
lib32z1 \
|
|
||||||
lib32ncurses6 \
|
|
||||||
lib32stdc++6 \
|
|
||||||
zlib1g-dev \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Instalar Android SDK
|
|
||||||
ENV ANDROID_SDK_ROOT=/opt/android-sdk
|
|
||||||
ENV SDKMANAGER="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager"
|
|
||||||
|
|
||||||
RUN mkdir -p $ANDROID_SDK_ROOT/cmdline-tools && \
|
|
||||||
wget -q https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O tools.zip && \
|
|
||||||
unzip -q tools.zip && \
|
|
||||||
mv cmdline-tools $ANDROID_SDK_ROOT/cmdline-tools/latest && \
|
|
||||||
rm tools.zip
|
|
||||||
|
|
||||||
# Aceptar licencias
|
|
||||||
RUN yes | $SDKMANAGER --licenses
|
|
||||||
|
|
||||||
# Instalar componentes necesarios
|
|
||||||
RUN $SDKMANAGER "platform-tools" "platforms;android-33" "build-tools;33.0.2" "platforms;android-31"
|
|
||||||
|
|
||||||
# Instalar Gradle
|
|
||||||
ENV GRADLE_HOME=/opt/gradle
|
|
||||||
RUN wget -q https://services.gradle.org/distributions/gradle-8.2-bin.zip -O gradle.zip && \
|
|
||||||
unzip -q gradle.zip && \
|
|
||||||
mv gradle-8.2 $GRADLE_HOME && \
|
|
||||||
rm gradle.zip
|
|
||||||
|
|
||||||
ENV PATH=$PATH:$GRADLE_HOME/bin:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools
|
|
||||||
|
|
||||||
# Copiar proyecto
|
|
||||||
COPY . /app
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Dar permisos de ejecución a gradlew
|
|
||||||
RUN chmod +x ./gradlew
|
|
||||||
|
|
||||||
# Construir APK
|
|
||||||
RUN ./gradlew assembleRelease
|
|
||||||
|
|
||||||
# Comando para copiar APK a un volumen montado
|
|
||||||
CMD ["cp", "/app/app/build/outputs/apk/release/app-release.apk", "/output/StreamPlayer-v10.0.apk"]
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
FROM ubuntu:22.04
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
wireguard-tools \
|
|
||||||
iputils-ping \
|
|
||||||
dnsutils \
|
|
||||||
iproute2 \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN mkdir -p /etc/wireguard
|
|
||||||
|
|
||||||
RUN echo '[Interface]' > /etc/wireguard/wg0.conf && \
|
|
||||||
echo 'PrivateKey = Tq9/VQ8qdsphS+0nVEFmWgFvMfvJ2FbWGK/Xt9cX4AA=' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo 'Address = 10.8.0.2/32' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo 'DNS = 1.1.1.1' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo '' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo '[Peer]' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo 'PublicKey = 03qeK7CSn6wcMzfqilmVt6Tf81VZIPWnSG04euSkyxM=' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo 'Endpoint = 149.88.104.2:51820' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo 'AllowedIPs = 0.0.0.0/0' >> /etc/wireguard/wg0.conf && \
|
|
||||||
echo 'PersistentKeepalive = 25' >> /etc/wireguard/wg0.conf
|
|
||||||
|
|
||||||
CMD tail -f /dev/null
|
|
||||||
53
README.md
53
README.md
@@ -1,29 +1,27 @@
|
|||||||
# 🏆 Furbo VPN Edition
|
# StreamPlayer VPN Edition
|
||||||
|
|
||||||
[](https://android.com)
|
[](https://android.com)
|
||||||
[]()
|
[](LICENSE)
|
||||||
[](LICENSE)
|
|
||||||
|
|
||||||
**StreamPlayer con VPN integrada** - La única app de streaming con Mullvad VPN built-in.
|
**StreamPlayer con VPN integrada** - App de streaming deportivo con Mullvad VPN built-in.
|
||||||
|
|
||||||
## ✨ Características
|
## Características
|
||||||
|
|
||||||
- 📺 **Streaming**: Reproducción de canales en vivo
|
- **Streaming**: Reproducción de canales en vivo
|
||||||
- 🔒 **VPN Integrada**: Con Mullvad WireGuard - sin app externa
|
- **VPN Integrada**: Con Mullvad WireGuard - sin app externa
|
||||||
- 🌎 **Sin Bloqueos Geográficos**: VPN incluida para evitar restricciones
|
- **Android TV**: Optimizado para Android TV y control remoto
|
||||||
- 📱 **Android TV**: Optimizado para Android TV y control remoto
|
- **DNS over HTTPS**: Múltiples servidores DNS con fallback automático
|
||||||
- ⚡ **Alto Rendimiento**: DNS over HTTPS con fallback
|
|
||||||
|
|
||||||
## 🚀 Instalación
|
## Instalación
|
||||||
|
|
||||||
### Opción 1: Descargar APK
|
### Opción 1: Descargar APK
|
||||||
Descargar el APK más reciente de [Releases](https://gitea.cbcren.online/renato97/furbo-vpn-edition/releases)
|
Descargar el APK desde la sección de Releases de este repositorio.
|
||||||
|
|
||||||
### Opción 2: Compilar
|
### Opción 2: Compilar
|
||||||
```bash
|
```bash
|
||||||
# Clonar repositorio
|
# Clonar repositorio
|
||||||
git clone https://gitea.cbcren.online/renato97/furbo-vpn-edition.git
|
git clone <repo-url>
|
||||||
cd furbo-vpn-edition
|
cd <project-dir>
|
||||||
|
|
||||||
# Compilar
|
# Compilar
|
||||||
./gradlew assembleDebug
|
./gradlew assembleDebug
|
||||||
@@ -31,7 +29,7 @@ cd furbo-vpn-edition
|
|||||||
# APK en: app/build/outputs/apk/debug/app-debug.apk
|
# APK en: app/build/outputs/apk/debug/app-debug.apk
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔧 Uso de la VPN
|
## Uso de la VPN
|
||||||
|
|
||||||
1. **Primera vez**: Tocar el botón "Conectar VPN" en el menú
|
1. **Primera vez**: Tocar el botón "Conectar VPN" en el menú
|
||||||
2. **Permiso**: Aceptar el permiso de VPN cuando se solicite
|
2. **Permiso**: Aceptar el permiso de VPN cuando se solicite
|
||||||
@@ -43,34 +41,35 @@ Para cambiar el servidor Mullvad:
|
|||||||
2. Reemplazar el archivo `app/src/main/res/raw/mullvad.conf`
|
2. Reemplazar el archivo `app/src/main/res/raw/mullvad.conf`
|
||||||
3. Recompilar
|
3. Recompilar
|
||||||
|
|
||||||
## 📋 Requisitos
|
## Requisitos
|
||||||
|
|
||||||
- **Android**: 7.0 (API 24) o superior
|
- **Android**: 7.0 (API 24) o superior
|
||||||
- **VPN**: WireGuard integrado (no requiere app externa)
|
- **VPN**: WireGuard integrado (no requiere app externa)
|
||||||
|
|
||||||
## 🔐 Privacidad
|
## Configuración de Actualizaciones
|
||||||
|
|
||||||
- **DNS**: Múltiples servidores DoH (Google, Cloudflare, AdGuard, Quad9)
|
Para habilitar las actualizaciones automáticas, configura la URL en `app/build.gradle`:
|
||||||
- **VPN**: Mullvad - Sin logs, sin datos personales
|
|
||||||
- **Todo en uno**: No necesita otras apps
|
|
||||||
|
|
||||||
## 📁 Estructura
|
```gradle
|
||||||
|
buildConfigField "String", "UPDATE_CHECK_URL", '"https://your-gitea-instance.com/api/v1/repos/user/repo/releases/latest"'
|
||||||
|
```
|
||||||
|
|
||||||
|
Opcionalmente, configura un token de API en `UpdateManager.java` si tu repositorio es privado.
|
||||||
|
|
||||||
|
## Estructura
|
||||||
|
|
||||||
```
|
```
|
||||||
app/src/main/
|
app/src/main/
|
||||||
├── java/com/streamplayer/
|
├── java/com/streamplayer/
|
||||||
│ ├── MainActivity.java # UI principal
|
│ ├── MainActivity.java # UI principal
|
||||||
│ ├── VpnManager.java # Gestión VPN
|
│ ├── VpnManager.java # Gestión VPN
|
||||||
|
│ ├── UpdateManager.java # Sistema de actualizaciones
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── res/raw/
|
├── res/raw/
|
||||||
│ └── mullvad.conf # Configuración VPN
|
│ └── mullvad.conf # Configuración VPN
|
||||||
└── AndroidManifest.xml
|
└── AndroidManifest.xml
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📞 Soporte
|
## Disclaimer
|
||||||
|
|
||||||
- Issues: [GitHub Issues](https://gitea.cbcren.online/renato97/furbo-vpn-edition/issues)
|
Esta aplicación es para fines educativos. El usuario es responsable de cumplir con los términos de servicio de las plataformas de streaming.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
⚠️ **Disclaimer**: Esta aplicación es para fines educativos. El usuario es responsable de cumplir con los términos de servicio de las plataformas de streaming.
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ android {
|
|||||||
targetSdk 35
|
targetSdk 35
|
||||||
versionCode 100111
|
versionCode 100111
|
||||||
versionName "10.1.11"
|
versionName "10.1.11"
|
||||||
buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"'
|
buildConfigField "String", "UPDATE_CHECK_URL", '""'
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdkVersion 28
|
|
||||||
buildToolsVersion "28.0.3"
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "com.streamplayer"
|
|
||||||
minSdkVersion 21
|
|
||||||
targetSdkVersion 28
|
|
||||||
versionCode 1
|
|
||||||
versionName "1.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation 'com.google.android.exoplayer:exoplayer:2.8.4'
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
package com.streamplayer;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import okhttp3.MediaType;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okhttp3.Response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Informa al dashboard qué dispositivos tienen instalada la app y permite bloquearlos remotamente.
|
|
||||||
*/
|
|
||||||
public class DeviceRegistry {
|
|
||||||
|
|
||||||
public interface Callback {
|
|
||||||
void onAllowed();
|
|
||||||
|
|
||||||
void onBlocked(String reason, String tokenPart);
|
|
||||||
|
|
||||||
void onError(String message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String TAG = "DeviceRegistry";
|
|
||||||
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
|
|
||||||
|
|
||||||
private final Context appContext;
|
|
||||||
private final OkHttpClient httpClient;
|
|
||||||
private final ExecutorService executorService;
|
|
||||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
||||||
|
|
||||||
public DeviceRegistry(Context context) {
|
|
||||||
this.appContext = context.getApplicationContext();
|
|
||||||
// Usar NetworkUtils para obtener cliente con DNS over HTTPS configurado
|
|
||||||
this.httpClient = NetworkUtils.getClient();
|
|
||||||
this.executorService = Executors.newSingleThreadExecutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void syncDevice(Callback callback) {
|
|
||||||
if (TextUtils.isEmpty(BuildConfig.DEVICE_REGISTRY_URL)) {
|
|
||||||
postAllowed(callback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
executorService.execute(() -> {
|
|
||||||
try {
|
|
||||||
JSONObject payload = new JSONObject();
|
|
||||||
payload.put("deviceId", getDeviceId());
|
|
||||||
payload.put("deviceName", Build.MODEL);
|
|
||||||
payload.put("model", Build.MODEL);
|
|
||||||
payload.put("manufacturer", capitalize(Build.MANUFACTURER));
|
|
||||||
payload.put("osVersion", Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ")");
|
|
||||||
payload.put("appVersionName", BuildConfig.VERSION_NAME);
|
|
||||||
payload.put("appVersionCode", BuildConfig.VERSION_CODE);
|
|
||||||
|
|
||||||
String endpoint = sanitizeBaseUrl(BuildConfig.DEVICE_REGISTRY_URL) + "/api/devices/register";
|
|
||||||
RequestBody body = RequestBody.create(payload.toString(), JSON);
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(endpoint)
|
|
||||||
.post(body)
|
|
||||||
.build();
|
|
||||||
try (Response response = httpClient.newCall(request).execute()) {
|
|
||||||
if (!response.isSuccessful() || response.body() == null) {
|
|
||||||
throw new IOException("HTTP " + response.code());
|
|
||||||
}
|
|
||||||
String responseText = response.body().string();
|
|
||||||
|
|
||||||
// Validar que no sea HTML antes de parsear
|
|
||||||
if (responseText != null) {
|
|
||||||
String trimmed = responseText.trim();
|
|
||||||
if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
|
|
||||||
throw new IOException("El servidor devolvió HTML en lugar de JSON");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject(responseText);
|
|
||||||
JSONObject deviceJson = json.optJSONObject("device");
|
|
||||||
JSONObject verificationJson = json.optJSONObject("verification");
|
|
||||||
boolean blocked = json.optBoolean("blocked", false);
|
|
||||||
String reason = json.optString("message");
|
|
||||||
if (TextUtils.isEmpty(reason) && deviceJson != null) {
|
|
||||||
reason = deviceJson.optString("notes", "");
|
|
||||||
}
|
|
||||||
String tokenPart = "";
|
|
||||||
if (verificationJson != null) {
|
|
||||||
boolean verificationRequired = verificationJson.optBoolean("required", false);
|
|
||||||
blocked = blocked || verificationRequired;
|
|
||||||
tokenPart = verificationJson.optString("clientTokenPart", "");
|
|
||||||
}
|
|
||||||
if (blocked) {
|
|
||||||
postBlocked(callback, reason, tokenPart);
|
|
||||||
} else {
|
|
||||||
postAllowed(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException | JSONException e) {
|
|
||||||
Log.w(TAG, "Device sync error", e);
|
|
||||||
postError(callback, e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private String sanitizeBaseUrl(String base) {
|
|
||||||
if (TextUtils.isEmpty(base)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (base.endsWith("/")) {
|
|
||||||
return base.substring(0, base.length() - 1);
|
|
||||||
}
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDeviceId() {
|
|
||||||
String id = Settings.Secure.getString(appContext.getContentResolver(),
|
|
||||||
Settings.Secure.ANDROID_ID);
|
|
||||||
if (TextUtils.isEmpty(id)) {
|
|
||||||
id = Build.MODEL + "-" + Build.BOARD + "-" + BuildConfig.VERSION_CODE;
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String capitalize(String value) {
|
|
||||||
if (TextUtils.isEmpty(value)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return value.substring(0, 1).toUpperCase(Locale.getDefault())
|
|
||||||
+ value.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void release() {
|
|
||||||
executorService.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postAllowed(Callback callback) {
|
|
||||||
if (callback == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mainHandler.post(callback::onAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postBlocked(Callback callback, String reason, String tokenPart) {
|
|
||||||
if (callback == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String reasonText = reason == null ? "" : reason;
|
|
||||||
String token = tokenPart == null ? "" : tokenPart;
|
|
||||||
mainHandler.post(() -> callback.onBlocked(reasonText, token));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postError(Callback callback, String message) {
|
|
||||||
if (callback == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mainHandler.post(() -> callback.onError(message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -52,8 +52,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private SectionEntry currentSection;
|
private SectionEntry currentSection;
|
||||||
private UpdateManager updateManager;
|
private UpdateManager updateManager;
|
||||||
private AlertDialog updateDialog;
|
private AlertDialog updateDialog;
|
||||||
private AlertDialog blockedDialog;
|
|
||||||
private DeviceRegistry deviceRegistry;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -122,28 +120,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
deviceRegistry = new DeviceRegistry(this);
|
|
||||||
deviceRegistry.syncDevice(new DeviceRegistry.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onAllowed() {
|
|
||||||
// Device authorized, continue normally.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlocked(String reason, String tokenPart) {
|
|
||||||
showBlockedDialog(reason, tokenPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(String message) {
|
|
||||||
if (!TextUtils.isEmpty(message)) {
|
|
||||||
Toast.makeText(MainActivity.this,
|
|
||||||
getString(R.string.device_registry_error, message),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -160,15 +136,9 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (updateDialog != null && updateDialog.isShowing()) {
|
if (updateDialog != null && updateDialog.isShowing()) {
|
||||||
updateDialog.dismiss();
|
updateDialog.dismiss();
|
||||||
}
|
}
|
||||||
if (blockedDialog != null && blockedDialog.isShowing()) {
|
|
||||||
blockedDialog.dismiss();
|
|
||||||
}
|
|
||||||
if (updateManager != null) {
|
if (updateManager != null) {
|
||||||
updateManager.release();
|
updateManager.release();
|
||||||
}
|
}
|
||||||
if (deviceRegistry != null) {
|
|
||||||
deviceRegistry.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectSection(int index) {
|
private void selectSection(int index) {
|
||||||
@@ -348,54 +318,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showBlockedDialog(String reason, String tokenPart) {
|
|
||||||
if (isFinishing()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String finalReason = TextUtils.isEmpty(reason)
|
|
||||||
? getString(R.string.device_blocked_default_reason)
|
|
||||||
: reason;
|
|
||||||
if (blockedDialog != null && blockedDialog.isShowing()) {
|
|
||||||
blockedDialog.dismiss();
|
|
||||||
}
|
|
||||||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_blocked, null);
|
|
||||||
TextView messageText = dialogView.findViewById(R.id.blocked_message_text);
|
|
||||||
View tokenContainer = dialogView.findViewById(R.id.blocked_token_container);
|
|
||||||
TextView tokenValue = dialogView.findViewById(R.id.blocked_token_value);
|
|
||||||
messageText.setText(getString(R.string.device_blocked_message, finalReason));
|
|
||||||
boolean hasToken = !TextUtils.isEmpty(tokenPart);
|
|
||||||
if (hasToken) {
|
|
||||||
tokenContainer.setVisibility(View.VISIBLE);
|
|
||||||
tokenValue.setText(tokenPart);
|
|
||||||
tokenValue.setOnClickListener(v -> copyTokenToClipboard(tokenPart));
|
|
||||||
} else {
|
|
||||||
tokenContainer.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.ThemeOverlay_StreamPlayer_AlertDialog)
|
|
||||||
.setTitle(R.string.device_blocked_title)
|
|
||||||
.setView(dialogView)
|
|
||||||
.setCancelable(false)
|
|
||||||
.setPositiveButton(R.string.device_blocked_close,
|
|
||||||
(dialog, which) -> finish());
|
|
||||||
if (hasToken) {
|
|
||||||
builder.setNeutralButton(R.string.device_blocked_copy_token,
|
|
||||||
(dialog, which) -> copyTokenToClipboard(tokenPart));
|
|
||||||
}
|
|
||||||
blockedDialog = builder.create();
|
|
||||||
blockedDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void copyTokenToClipboard(String tokenPart) {
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
if (clipboard == null) {
|
|
||||||
Toast.makeText(this, R.string.device_blocked_copy_error, Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ClipData data = ClipData.newPlainText("token", tokenPart);
|
|
||||||
clipboard.setPrimaryClip(data);
|
|
||||||
Toast.makeText(this, R.string.device_blocked_copy_success, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleVpn() {
|
private void toggleVpn() {
|
||||||
VpnManager vpnManager = VpnManager.getInstance();
|
VpnManager vpnManager = VpnManager.getInstance();
|
||||||
if (vpnManager.isConnected()) {
|
if (vpnManager.isConnected()) {
|
||||||
|
|||||||
@@ -43,9 +43,12 @@ import okhttp3.Response;
|
|||||||
public class UpdateManager {
|
public class UpdateManager {
|
||||||
|
|
||||||
private static final String TAG = "UpdateManager";
|
private static final String TAG = "UpdateManager";
|
||||||
|
// NOTE: Configure tu propia URL de releases en build.gradle o aquí
|
||||||
private static final String LATEST_RELEASE_URL =
|
private static final String LATEST_RELEASE_URL =
|
||||||
"https://gitea.cbcren.online/api/v1/repos/renato97/app/releases/latest";
|
BuildConfig.UPDATE_CHECK_URL.isEmpty()
|
||||||
private static final String GITEA_TOKEN = "4b94b3610136529861af0821040a801906821a0f";
|
? "https://your-gitea-instance.com/api/v1/repos/your-user/your-repo/releases/latest"
|
||||||
|
: BuildConfig.UPDATE_CHECK_URL;
|
||||||
|
private static final String GITEA_TOKEN = ""; // Configura tu token si es necesario
|
||||||
|
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"versionCode": 90000,
|
"versionCode": 100111,
|
||||||
"versionName": "9.0.0",
|
"versionName": "10.1.11",
|
||||||
"minSupportedVersionCode": 80000,
|
"minSupportedVersionCode": 100000,
|
||||||
"forceUpdate": false,
|
"forceUpdate": false,
|
||||||
"downloadUrl": "https://gitea.cbcren.online/renato97/app/releases/download/v9.0/StreamPlayer-v9.0-DefinitiveEdition.apk",
|
"downloadUrl": "https://your-gitea-instance.com/your-user/your-repo/releases/download/v10.1.11/StreamPlayer-v10.1.11.apk",
|
||||||
"fileName": "StreamPlayer-v9.0-DefinitiveEdition.apk",
|
"fileName": "StreamPlayer-v10.1.11.apk",
|
||||||
"sizeBytes": 12000000,
|
"sizeBytes": 20000000,
|
||||||
"notes": "Texto opcional si necesitas personalizar las notas que verá el usuario"
|
"notes": "StreamPlayer VPN Edition\n\nConfigura UPDATE_CHECK_URL en app/build.gradle para habilitar actualizaciones automáticas."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"versionCode": 100110,
|
"versionCode": 100111,
|
||||||
"versionName": "10.1.10",
|
"versionName": "10.1.11",
|
||||||
"minSupportedVersionCode": 0,
|
"minSupportedVersionCode": 100000,
|
||||||
"forceUpdate": false,
|
"forceUpdate": false,
|
||||||
"downloadUrl": "http://gitea.cbcren.online/renato97/app/releases/download/v10.1.10/StreamPlayer-v10.1.10-debug.apk",
|
"downloadUrl": "",
|
||||||
"fileName": "StreamPlayer-v10.1.10-debug.apk",
|
"fileName": "",
|
||||||
"sizeBytes": 9113609,
|
"sizeBytes": 0,
|
||||||
"notes": "Cambiar a HTTP para evitar errores de certificado"
|
"notes": "StreamPlayer VPN Edition - Configura tu propia URL de actualizaciones en build.gradle"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user