8 Commits

Author SHA1 Message Date
litecn
619901ef69 Bump version from 1.3.12 to 1.3.13 2026-02-05 11:04:58 +08:00
litecn
8523e5f157 Merge pull request #266 from litecn/cokvr
fix: set login state and show login modal on authentication failure
2026-02-05 10:38:47 +08:00
James Chen
29ad5a5e75 fix: set login state and show login modal on authentication failure 2026-02-05 10:32:05 +08:00
litecn
cf854c3c9f Bump version from 1.3.11 to 1.3.12 2025-12-17 16:29:59 +08:00
litecn
a86eb8ca5c Merge pull request #257 from litecn/cokvr
fix: 修复显示“认证失败,请重新登录”,却找不到登录框 #247 #255
2025-12-17 16:27:30 +08:00
James Chen
487c15d8b6 fix: 修复显示“认证失败,请重新登录”,却找不到登录框 #247 #255 2025-12-13 22:26:00 +08:00
litecn
3526189e32 Merge pull request #240 from litecn/cokvr
fix: installApk error: exposed beyond app through Intent.getData()
2025-10-12 16:19:13 +08:00
James Chen
c473581c26 fix: installApk error: exposed beyond app through Intent.getData() 2025-10-12 15:33:30 +08:00
4 changed files with 22 additions and 19 deletions

View File

@@ -2,7 +2,7 @@
"name": "OrionTV",
"private": true,
"main": "expo-router/entry",
"version": "1.3.11",
"version": "1.3.13",
"scripts": {
"start": "EXPO_TV=1 EXPO_USE_METRO_WORKSPACE_ROOT=1 expo start",
"android": "EXPO_TV=1 EXPO_USE_METRO_WORKSPACE_ROOT=1 expo run:android",

View File

@@ -167,32 +167,29 @@ class UpdateService {
* 4⃣ 安装 APK只在 Android 可用,使用 expo-intent-launcher
* --------------------------------------------------------------- */
async installApk(fileUri: string): Promise<void> {
// if (!Device.isDevice) {
// // 在模拟器里打开文件会报错,直接给用户提示
// Toast.show({
// type: 'error',
// text1: '安装失败',
// text2: '模拟器不支持直接安装 APK请在真机上操作',
// });
// throw new Error('Cannot install on simulator');
// }
// ① 先确认文件存在
const exists = await FileSystem.getInfoAsync(fileUri);
if (!exists.exists) {
throw new Error(`APK not found at ${fileUri}`);
}
// Android 需要给 Intent 设置 mime 类型,并且使用 ACTION_VIEW
// ② 把 file:// 转成 content://ExpoFileSystem 已经实现了 FileProvider
const contentUri = await FileSystem.getContentUriAsync(fileUri);
// ③ 只在 Android 里执行
if (Platform.OS === 'android') {
try {
// Android 7+ 需要给出 URI 权限FileProviderExpoIntentLauncher 已经在内部使用了
// FLAG_ACTIVITY_NEW_TASK = 0x10000000 (1)
// FLAG_GRANT_READ_URI_PERMISSION = 0x00000010
const flags = 1 | 0x00000010; // 1 | 16
await IntentLauncher.startActivityAsync('android.intent.action.VIEW', {
data: fileUri,
type: ANDROID_MIME_TYPE,
flags: 1, // FLAG_ACTIVITY_NEW_TASK
data: contentUri, // 必须是 content://
type: ANDROID_MIME_TYPE, // application/vnd.android.package-archive
flags,
});
} catch (e: any) {
// 常见错误:没有“未知来源”权限、或没有安装包管理器
// 统一错误提示
if (e.message?.includes('Activity not found')) {
Toast.show({
type: 'error',
@@ -215,7 +212,7 @@ class UpdateService {
throw e;
}
} else {
// iOS 不支持的,直接提示用户
// iOS 设备不支持直接安装 APK
Toast.show({
type: 'error',
text1: '安装失败',

View File

@@ -254,6 +254,7 @@ const useHomeStore = create<HomeState>((set, get) => ({
errorMessage = "请点击右上角设置按钮,配置您的服务器地址";
} else if (err.message === "UNAUTHORIZED") {
errorMessage = "认证失败,请重新登录";
useAuthStore.setState({ isLoggedIn: false, isLoginModalVisible: true });
} else if (err.message.includes("Network")) {
errorMessage = "网络连接失败,请检查网络连接";
} else if (err.message.includes("timeout")) {

View File

@@ -2,6 +2,7 @@ import { create } from "zustand";
import { SettingsManager } from "@/services/storage";
import { api, ServerConfig } from "@/services/api";
import { storageConfig } from "@/services/storageConfig";
import AsyncStorage from "@react-native-async-storage/async-storage";
import Logger from "@/utils/Logger";
const logger = Logger.withTag('SettingsStore');
@@ -79,7 +80,8 @@ export const useSettingsStore = create<SettingsState>((set, get) => ({
setVideoSource: (config) => set({ videoSource: config }),
saveSettings: async () => {
const { apiBaseUrl, m3uUrl, remoteInputEnabled, videoSource } = get();
const currentSettings = await SettingsManager.get()
const currentApiBaseUrl = currentSettings.apiBaseUrl;
let processedApiBaseUrl = apiBaseUrl.trim();
if (processedApiBaseUrl.endsWith("/")) {
processedApiBaseUrl = processedApiBaseUrl.slice(0, -1);
@@ -105,6 +107,9 @@ export const useSettingsStore = create<SettingsState>((set, get) => ({
remoteInputEnabled,
videoSource,
});
if ( currentApiBaseUrl !== processedApiBaseUrl) {
await AsyncStorage.setItem('authCookies', '');
}
api.setBaseUrl(processedApiBaseUrl);
// Also update the URL in the state so the input field shows the processed URL
set({ isModalVisible: false, apiBaseUrl: processedApiBaseUrl });