류윤주 류윤주 07-18
240718 류윤주 수정
@e8883d122802a77fb55eee086842fdbf9470349b
android/app/src/main/AndroidManifest.xml
--- android/app/src/main/AndroidManifest.xml
+++ android/app/src/main/AndroidManifest.xml
@@ -1,4 +1,5 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+package="com.send_location_ta">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -16,6 +17,7 @@
       android:allowBackup="false"
       android:theme="@style/AppTheme"
       android:exported="true"
+      android:requestLegacyExternalStorage="true"
       android:usesCleartextTraffic="true">
       <activity
         android:name=".MainActivity"
android/app/src/main/assets/index.android.bundle
--- android/app/src/main/assets/index.android.bundle
+++ android/app/src/main/assets/index.android.bundle
This diff is too big to display.
package-lock.json
--- package-lock.json
+++ package-lock.json
This diff is too big to display.
src/screens/LoginScreen.tsx
--- src/screens/LoginScreen.tsx
+++ src/screens/LoginScreen.tsx
@@ -12,6 +12,7 @@
 import {useLoginAPI} from '../utils/loginUtils';
 import {RootStackParam} from '../utils/RootStackParam';
 import {useFocusEffect} from '@react-navigation/native';
+import AsyncStorage from '@react-native-async-storage/async-storage';
 
 export default function LoginScreen() {
   const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
@@ -43,6 +44,7 @@
     console.log(loginData); //
     try {
       const response = await loginAPI(loginData);
+      await AsyncStorage.setItem('user_id', loginData.id);
       console.log('Response from server:', response);
     } catch (error) {
       console.error('Error submitting agreement:', error);
src/screens/SendLocation.tsx
--- src/screens/SendLocation.tsx
+++ src/screens/SendLocation.tsx
@@ -1,4 +1,4 @@
-import React, {useEffect, useRef, useState} from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import {
   StyleSheet,
   View,
@@ -6,58 +6,97 @@
   TouchableOpacity,
   Dimensions,
 } from 'react-native';
-import {Shadow} from 'react-native-shadow-2';
+import { Shadow } from 'react-native-shadow-2';
 import Icon from 'react-native-vector-icons/FontAwesome5';
-import {getCurrentPosition} from '../utils/useGeolocationAPI';
-import {useNavigation} from '@react-navigation/native';
-import {NativeStackNavigationProp} from '@react-navigation/native-stack';
-import {RootStackParam} from '../utils/RootStackParam';
+import Geolocation from '@react-native-community/geolocation';
+import { sendLocationData } from '../utils/useGeolocationAPI';
+import { useNavigation } from '@react-navigation/native';
+import { NativeStackNavigationProp } from '@react-navigation/native-stack';
+import { RootStackParam } from '../utils/RootStackParam';
 import AsyncStorage from '@react-native-async-storage/async-storage';
 import BackgroundService from 'react-native-background-actions';
 import crypto from 'crypto-js';
 
+let intervalId: NodeJS.Timeout | null = null;
+
 export default function SendLocation() {
   const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
-
   const [time, setTime] = useState<number>(0);
   const [time2, setTime2] = useState<number>(0);
   const [isRunning, setIsRunning] = useState<boolean>(false);
-
+  const [locations, setLocations] = useState<{
+    longitudes: string[],
+    latitudes: string[],
+    timestamps: string[]
+  }>({ longitudes: [], latitudes: [], timestamps: [] });
+  const locationsRef = useRef(locations);
   const [hash, setHash] = useState("");
-
   const tripIdRef = useRef<string>(hash);
+  const userIdRef = useRef<string>('');
+  const [watchId, setWatchId] = useState<number | null>(null);
 
-  // 해시
+  // `hash`가 업데이트될 때마다 `tripIdRef`도 업데이트
   useEffect(() => {
     tripIdRef.current = hash;
   }, [hash]);
 
+  // `locations`가 업데이트될 때마다 `locationsRef`도 업데이트
   useEffect(() => {
-     var rand = Math.random().toString();
-     var date = new Date();
-     var sha256Hash = crypto.SHA256(rand + ',' + date.toString()).toString(crypto.enc.Hex);
-     console.log(rand, date, sha256Hash);
-     setHash(sha256Hash);
+    locationsRef.current = locations;
+  }, [locations]);
 
-    if (isRunning) {
-      startBackgroundService();
-    } else {
-      stopBackgroundService();
-    }
+  // 컴포넌트 초기화 및 `isRunning` 상태 변경 시 초기화
+  useEffect(() => {
+    const initialize = async () => {
+      // `user_id`를 AsyncStorage에서 가져옴
+      const userId = await AsyncStorage.getItem('user_id');
+      if (userId) {
+        userIdRef.current = userId;
+      }
+
+      // 해시 값을 생성
+      const rand = Math.random().toString();
+      const date = new Date();
+      const sha256Hash = crypto.SHA256(rand + ',' + date.toString()).toString(crypto.enc.Hex);
+      setHash(sha256Hash);
+
+      // `isRunning` 상태에 따라 백그라운드 서비스 시작 또는 중지
+      if (isRunning) {
+        await startBackgroundService();
+      } else {
+        await stopBackgroundService();
+      }
+    };
+
+    initialize();
   }, [isRunning]);
 
+  // `time2` 밀리초 동안 대기하는 함수
   const sleep = (time2: number) => new Promise(resolve => setTimeout(() => resolve(true), time2));
 
+  // 위치 데이터를 서버로 전송하는 함수
+  const sendData = async () => {
+    const currentLocations = { ...locationsRef.current };
+    // 전송할 위치 데이터가 있는지 확인
+    if (currentLocations.longitudes.length === 0 || currentLocations.latitudes.length === 0 || currentLocations.timestamps.length === 0) {
+      console.log("No data to send.");
+      return;
+    }
+    await sendLocationData(userIdRef.current, tripIdRef.current, currentLocations, navigation);
+    setLocations({ longitudes: [], latitudes: [], timestamps: [] });
+  };
+
+  // 백그라운드 서비스에서 실행될 함수
   const veryIntensiveTask = async (taskDataArguments: any) => {
     const { delay } = taskDataArguments;
 
     while (BackgroundService.isRunning()) {
-      setTime(prevTime => prevTime + 1);
-      getCurrentPosition(tripIdRef, navigation);
       await sleep(delay);
+      await sendData();
     }
   };
 
+  // 백그라운드 서비스 옵션
   const options = {
     taskName: '측정 서비스',
     taskTitle: '측정 중',
@@ -69,42 +108,184 @@
     color: '#ff00ff',
     linkingURI: 'sendLocation',
     parameters: {
-      delay: 1000,
+      delay: 15000,
     },
   };
 
+  // 백그라운드 서비스를 시작하는 함수
   const startBackgroundService = async () => {
-    await BackgroundService.start(veryIntensiveTask, options);
+    return new Promise<void>((resolve, reject) => {
+      let latestPosition: GeolocationPosition | null = null; // 마지막 위치를 저장할 변수
+      let locationCheckInterval: NodeJS.Timeout | null = null; // 위치 체크 인터벌을 저장할 변수
+      let watchId: number | null = null; // 위치 감시 ID를 저장할 변수
+      let lastUpdateTime = Date.now(); // 마지막 업데이트 시간을 저장할 변수
+  
+      // 위치 업데이트를 처리하는 함수
+      const handleLocationUpdate = (position: GeolocationPosition) => {
+        latestPosition = position; // 최신 위치를 업데이트
+        lastUpdateTime = Date.now(); // 마지막 업데이트 시간 갱신
+  
+        const { longitude, latitude } = position.coords;
+        const time = new Date().toISOString();
+  
+        // 한국 시간으로 변환하는 함수
+        const formatDate = (time) => {
+          const date = new Date(time);
+          const year = date.getFullYear();
+          const month = `0${date.getMonth() + 1}`.slice(-2);
+          const day = `0${date.getDate()}`.slice(-2);
+          const hours = `0${date.getHours()}`.slice(-2);
+          const minutes = `0${date.getMinutes()}`.slice(-2);
+          const seconds = `0${date.getSeconds()}`.slice(-2);
+          const milliseconds = `00${date.getMilliseconds()}`.slice(-3);
+  
+          return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
+        };
+  
+        const timestamp = formatDate(time);
+  
+        // 위치 데이터를 상태에 추가
+        setLocations(prevData => ({
+          longitudes: [...prevData.longitudes, longitude.toString()],
+          latitudes: [...prevData.latitudes, latitude.toString()],
+          timestamps: [...prevData.timestamps, timestamp]
+        }));
+      };
+  
+      // 1초마다 위치 업데이트를 확인하고 3초 동안 업데이트가 없을 경우 마지막 위치를 추가
+      locationCheckInterval = setInterval(() => {
+        if (latestPosition && (Date.now() - lastUpdateTime > 3000)) {
+          const { longitude, latitude } = latestPosition.coords;
+          const time = new Date().toISOString();
+          const timestamp = formatDate(time);
+  
+          setLocations(prevData => ({
+            longitudes: [...prevData.longitudes, longitude.toString()],
+            latitudes: [...prevData.latitudes, latitude.toString()],
+            timestamps: [...prevData.timestamps, timestamp]
+          }));
+          lastUpdateTime = Date.now();
+        }
+      }, 1000);
+  
+      // 위치 업데이트를 시작하는 함수
+      const startWatchingPosition = () => {
+        watchId = Geolocation.watchPosition(
+          handleLocationUpdate,
+          error => {
+            console.log('watchPosition Error:', error);
+            reject(error);
+          },
+          {
+            enableHighAccuracy: false, // 높은 정확도를 사용할지 여부
+            distanceFilter: 0, // 최소 거리 필터
+            interval: 1000, // 업데이트 간격 (밀리초)
+          }
+        );
+      };
+  
+      // watchId가 유효한 경우 백그라운드 서비스 시작
+      if (watchId === null) {
+        (async () => {
+          try {
+            await BackgroundService.start(veryIntensiveTask, options);
+            startWatchingPosition(); // 위치 업데이트 시작
+            resolve();
+          } catch (error) {
+            reject(error);
+          }
+        })();
+      }
+  
+      // 백그라운드 서비스를 중지할 때의 정리 함수
+      return () => {
+        if (locationCheckInterval !== null) {
+          clearInterval(locationCheckInterval);
+          locationCheckInterval = null;
+        }
+        if (watchId !== null) {
+          Geolocation.clearWatch(watchId);
+          watchId = null;
+        }
+        BackgroundService.stop();
+      };
+    });
   };
 
+  // 백그라운드 서비스를 중지하는 함수
   const stopBackgroundService = async () => {
+    if (watchId !== null) {
+      Geolocation.clearWatch(watchId);
+      setWatchId(null);
+    }
+    if (intervalId) {
+      clearInterval(intervalId);
+      intervalId = null;
+    }
     await BackgroundService.stop();
   };
 
-  // 측정 시작
+  // 측정을 시작하는 함수
   const handleStart = () => {
     setIsRunning(true);
+    intervalId = setInterval(() => {
+      setTime(prevTime => prevTime + 1);
+    }, 1000);
   };
 
-  // 측정 종료
+  // 측정을 중지하는 함수
   const handleStop = () => {
+    // `watchPosition` 제거
+    if (watchId !== null) {
+      Geolocation.clearWatch(watchId); // watchId 제거
+      setWatchId(null); // watchId 초기화
+    }
+
+    // `intervalId` 제거
+    if (intervalId) {
+      clearInterval(intervalId);
+      intervalId = null;
+    }
+
+    // 상태 초기화
     setTime(0);
     setIsRunning(false);
+
+    // 백그라운드 서비스가 실행 중인지 확인
+    if (BackgroundService.isRunning()) {
+      // 백그라운드 서비스 중지
+      BackgroundService.stop().then(() => {
+        console.log("Background service stopped successfully.");
+        // 중지 후 즉시 데이터 전송
+        sendData().then(() => {
+          console.log("Data sent successfully after stopping background service.");
+        }).catch(error => {
+          console.log("Error sending data after stopping background service:", error);
+        });
+      }).catch(error => {
+        console.log("Error stopping background service:", error);
+      });
+    } else {
+      // 백그라운드 서비스가 실행 중이 아니면 데이터만 전송
+      sendData().then(() => {
+        console.log("Data sent successfully when background service is not running.");
+      }).catch(error => {
+        console.log("Error sending data when background service is not running:", error);
+      });
+    }
   };
 
-  // 시간 포맷
+  // 시간을 형식화하는 함수
   const formatTime = (time: number): string => {
-  console.log(time)
-      const hours = Math.floor(time / 3600);
+    const hours = Math.floor(time / 3600);
     const minutes = Math.floor((time % 3600) / 60);
     const seconds = time % 60;
-    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds
-      .toString().padStart(2, '0')}`;
+    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
   };
 
   return (
     <View style={styles.container}>
-      <View style={{alignItems: 'center'}}>
+      <View style={{ alignItems: 'center' }}>
         <Shadow
           distance={8}
           startColor={'#3872ef33'}
@@ -112,7 +293,7 @@
           offset={[5, 6]}
           containerStyle={styles.watchContainer}>
           <View style={styles.watchContainer}>
-            <Text style={{fontWeight: 'bold', fontSize: 20}}>
+            <Text style={{ fontWeight: 'bold', fontSize: 20 }}>
               측정 경과 시간
             </Text>
             <Text style={styles.time}> {formatTime(time)} </Text>
src/utils/useGeolocationAPI.ts
--- src/utils/useGeolocationAPI.ts
+++ src/utils/useGeolocationAPI.ts
@@ -1,18 +1,16 @@
 import React from 'react';
-//import {Alert} from 'react-native';
-import Geolocation from '@react-native-community/geolocation';
-
-import {getTokenFromStorage, verifyTokens} from './loginUtils';
+import { getTokenFromStorage, verifyTokens } from './loginUtils';
 import AsyncStorage from '@react-native-async-storage/async-storage';
-
-import {NativeStackNavigationProp} from '@react-navigation/native-stack';
-import {RootStackParam} from '../utils/RootStackParam';
+import { NativeStackNavigationProp } from '@react-navigation/native-stack';
+import { RootStackParam } from '../utils/RootStackParam';
 
 // API 주소
 const url = 'http://takensoftai.iptime.org:15857/action/gps_update';
 
-export const getCurrentPosition = async (
-  tripId:String,
+export const sendLocationData = async (
+  userId: string,
+  tripId: string,
+  locations: { longitudes: string[], latitudes: string[], timestamps: string[] },
   navigation: NativeStackNavigationProp<RootStackParam>,
 ) => {
   try {
@@ -23,71 +21,42 @@
       return;
     }
 
-    let initialTimeout = 10000;
-    Geolocation.getCurrentPosition(
-      async pos => {
-        const longitude = JSON.stringify(pos.coords.longitude);
-        const latitude = JSON.stringify(pos.coords.latitude);
-        const date = new Date();
-
-        const year = date.getFullYear();
-        const month = String(date.getMonth() + 1).padStart(2, '0');
-        const day = String(date.getDate()).padStart(2, '0');
-        const hours = String(date.getHours()).padStart(2, '0');
-        const minutes = String(date.getMinutes()).padStart(2, '0');
-        const seconds = String(date.getSeconds()).padStart(2, '0');
-        const milliSeconds = date.getMilliseconds().toString().padStart(3, '0');
-
-        const date_now = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliSeconds}`;
-
-        console.log('trip_id:', tripId, '현재 위치: ', longitude, latitude, '현재 시간:', date_now);
-        try {
-          const response = await fetch(url, {
-            method: 'POST',
-            headers: {
-              'Content-Type': 'application/json',
-              Authorization: `${token}`,
-            },
-            body: JSON.stringify({
-              trip_id: tripId,
-              location_x: longitude,
-              location_y: latitude,
-              timestamp: date_now
-            }),
-          });
-
-          const data = await response.json();
-          console.log('대답 : ', data);
-
-          if (!response.ok) {
-            console.log('서버 응답 실패 : ', data.msg || response.statusText);
-          }
-          if (data.result === 'fail') {
-            await AsyncStorage.removeItem('token');
-          }
-        } catch (error) {
-          console.log('서버 요청 오류:', error);
-        }
+    const requestBody = {
+      user_id: userId,
+      trip_id: tripId,
+      trip_log: {
+        latitude: locations.latitudes,
+        longitude: locations.longitudes,
+        timestamp: locations.timestamps,
       },
-      //error => Alert.alert('GetCurrentPosition Error', JSON.stringify(error)),
-      error => {
-        console.log('GetCurrentPosition Error:', error);
-      },
-      {
-        enableHighAccuracy: false,
-        timeout: initialTimeout,
-        maximumAge: 10000,
-      },
-    );
+    };
+
+    console.log('Request body:', requestBody);
+
+    try {
+      const response = await fetch(url, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+          Authorization: `${token}`,
+        },
+        body: JSON.stringify(requestBody),
+      });
+
+      const data = await response.json();
+      console.log('Response:', data);
+
+      if (!response.ok) {
+        console.log('Server response error:', data.msg || response.statusText);
+      }
+      if (data.result === 'fail') {
+        await AsyncStorage.removeItem('token');
+      }
+    } catch (error) {
+      console.log('Server request error:', error);
+    }
   } catch (error) {
-    console.log('토큰 가져오기 오류:', error);
+    console.log('Token retrieval error:', error);
   }
 };
 
-export default function GetCurrentLocation() {
-  React.useEffect(() => {
-    Geolocation.requestAuthorization();
-  }, []);
-
-  return null;
-}
Add a comment
List