feat: add Android notification permission handling and improve error logging for live notifications

This commit is contained in:
harukin-expo-dev-env
2026-03-22 23:06:56 +00:00
parent 0d45732d66
commit 0a2333a201
3 changed files with 42 additions and 12 deletions

View File

@@ -11,6 +11,9 @@ import {
Image,
LayoutAnimation,
ScrollView,
Platform,
PermissionsAndroid,
Alert,
} from "react-native";
import { getTrainType } from "@/lib/getTrainType";
import { trainDataType, trainPosition } from "@/lib/trainPositionTextArray";
@@ -362,10 +365,20 @@ export const FixedTrain: FC<props> = ({ trainID }) => {
const toggleLiveNotification = async () => {
if (liveNotifyId) {
await endTrainFollowActivity(liveNotifyId).catch(() => {});
await endTrainFollowActivity(liveNotifyId).catch((e) => console.warn('[LiveNotify] stop error:', e));
setLiveNotifyId(null);
setLiveNotificationActive(false);
} else {
// Android 13+ requires POST_NOTIFICATIONS runtime permission
if (Platform.OS === 'android' && Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
Alert.alert('通知の許可が必要です', '設定から通知を許可してください');
return;
}
}
const delayNum =
train?.delay === "入線" ? 0 : parseInt(train?.delay) || 0;
const delayStatus = delayNum > 0 ? `${delayNum}分遅れ` : "定刻";
@@ -390,7 +403,10 @@ export const FixedTrain: FC<props> = ({ trainID }) => {
});
setLiveNotifyId(id);
setLiveNotificationActive(true);
} catch (_e) {}
} catch (e) {
console.warn('[LiveNotify] start error:', e);
Alert.alert('通知の開始に失敗しました', String(e));
}
}
};

View File

@@ -1,7 +1,22 @@
const { getDefaultConfig } = require("expo/metro-config");
const path = require("path");
const config = getDefaultConfig(__dirname);
// Ensure local Expo modules in modules/ are watched and resolved directly
config.watchFolders = [
...(config.watchFolders || []),
path.resolve(__dirname, "modules"),
];
config.resolver = {
...config.resolver,
nodeModulesPaths: [
path.resolve(__dirname, "modules"),
...(config.resolver?.nodeModulesPaths || []),
],
};
config.transformer = {
...config.transformer,
experimentalImportSupport: true,

View File

@@ -229,15 +229,15 @@ export async function startTrainFollowActivity(
): Promise<string> {
if (!ExpoLiveActivityModule) throw new Error('ExpoLiveActivity is not available');
if (Platform.OS === 'android') {
const title = buildTrainFollowTitle(params);
const body = buildTrainFollowBody(params);
const color = params.trainTypeColor || '#00B8FF';
const progressCurrent = params.nextStationIndex != null && params.nextStationIndex >= 0
? params.nextStationIndex + 1 : 0;
const progressTotal = params.stationStops?.length ?? 0;
console.log('[LiveActivity] start args:', { title, body, color, progressCurrent, progressTotal });
return await ExpoLiveActivityModule.startTrainFollowNotification(
buildTrainFollowTitle(params),
buildTrainFollowBody(params),
params.trainTypeColor || '#00B8FF',
progressCurrent,
progressTotal
title, body, color, progressCurrent, progressTotal
);
}
return await ExpoLiveActivityModule.startTrainFollowActivity(params);
@@ -252,15 +252,14 @@ export async function updateTrainFollowActivity(
): Promise<void> {
if (!ExpoLiveActivityModule) throw new Error('ExpoLiveActivity is not available');
if (Platform.OS === 'android') {
const title = buildTrainFollowTitle(state);
const body = buildTrainFollowBody(state);
const color = state.trainTypeColor || '#00B8FF';
const progressCurrent = state.nextStationIndex != null && state.nextStationIndex >= 0
? state.nextStationIndex + 1 : 0;
const progressTotal = state.stationStops?.length ?? 0;
await ExpoLiveActivityModule.updateTrainFollowNotification(
buildTrainFollowTitle(state),
buildTrainFollowBody(state),
state.trainTypeColor || '#00B8FF',
progressCurrent,
progressTotal
title, body, color, progressCurrent, progressTotal
);
return;
}