Initial commit

This commit is contained in:
pjkim 2025-03-25 15:48:12 +09:00
commit 4eb9557146
140 changed files with 10229 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

1
.idea/.name generated Normal file
View File

@ -0,0 +1 @@
smart119

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidTestResultsUserPreferences">
<option name="androidTestResultsTableState">
<map>
<entry key="28629151">
<value>
<AndroidTestResultsTableState>
<option name="preferredColumnWidths">
<map>
<entry key="Duration" value="90" />
<entry key="Tests" value="360" />
<entry key="samsung SM-P615N" value="120" />
</map>
</option>
</AndroidTestResultsTableState>
</value>
</entry>
</map>
</option>
</component>
</project>

123
.idea/codeStyles/Project.xml generated Normal file
View File

@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

21
.idea/deploymentTargetSelector.xml generated Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-03-17T01:37:05.045672200Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\pj\.android\avd\Galaxy_Tab_S6_API_35.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="All Tests">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

20
.idea/gradle.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

6
.idea/kotlinc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.24" />
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

9
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

17
.idea/runConfigurations.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

89
app/build.gradle.kts Normal file
View File

@ -0,0 +1,89 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.example.smart119"
compileSdk = 34
signingConfigs {
create("release") {
storeFile = file("D:\\work\\Android\\release\\release.jks") // 키스토어 파일 경로
storePassword = "rnrmq1!" // 키스토어 비밀번호
keyAlias = "release_key" // 키 별칭
keyPassword = "admin123#" // 키 비밀번호
}
}
buildFeatures {
buildConfig = true
}
defaultConfig {
applicationId = "com.example.smart119"
minSdk = 30
targetSdk = 34
versionCode = 2
versionName = "2.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "APP_VERSION", "\"${versionName}\"")
}
buildTypes {
debug {
isDebuggable = true // ✅ 디버깅 활성화
}
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
signingConfig = signingConfigs.getByName("release") // 🔹 릴리즈 서명 추가
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.androidx.databinding.runtime)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// Gson 라이브러리
implementation("com.google.code.gson:gson:2.8.9")
// RxJava Core 라이브러리
implementation("io.reactivex.rxjava3:rxjava:3.1.5")
// RxAndroid (Android-specific extensions)
implementation("io.reactivex.rxjava3:rxandroid:3.0.2")
// gps, Fused Location Provider API
implementation("com.google.android.gms:play-services-location:21.0.1")
implementation("com.google.android.gms:play-services-location:21.0.1")
//view pager2
implementation("androidx.viewpager2:viewpager2:1.1.0")
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.example.smart119",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "2.1",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 30
}

Binary file not shown.

View File

@ -0,0 +1,24 @@
package com.example.smart119
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.smart119", appContext.packageName)
}
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name="com.example.smart119.App"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Smart119"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustPan"
android:theme="@style/Theme.Smart119">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".LocationService"
android:exported="false"
android:foregroundServiceType="location" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

@ -0,0 +1,183 @@
package com.example.smart119
import android.os.Handler
import android.os.Looper
import android.widget.Toast
import com.google.gson.Gson
import com.google.gson.JsonObject
import kotlin.math.*
object API_Helper {
fun ETA_SEC_TO_STRING(duration:Int):String {
var ret_string = "";
val minutes = duration / 60 // 분 계산
val seconds = duration % 60 // 초 계산
if (duration < 60) {
ret_string = "${seconds}"
}
else {
ret_string = "${minutes}${seconds}"
}
return ret_string
}
fun DISTANCE_FORMAT_METER_TO_KM(meters: String): String {
val km = meters.toDoubleOrNull()?.div(1000) ?: return "Invalid input"
return String.format("%.2f km", km)
}
fun lidar_levelState(level:Int): String {
return when (level) {
-1 -> "정보 없음"
0 -> "원활"
1 -> "보통"
2 -> "약간 혼잡"
3 -> "혼잡"
4 -> "매우 혼잡"
else -> "잘못된 레벨"
}
}
fun lidar_levelState_Color(level:String):String {
return when (level) {
"정보 없음" -> "#87CEEB"
"원활" -> "#4CAF50"
"보통" -> "#FFEB3B"
"약간 혼잡" -> "#FF9800"
"혼잡" -> "#F44336"
"매우 혼잡" -> "#B71C1C"
else -> "#000000" // 잘못된 레벨
}
}
fun make_findpath_param(org:String, dest:String, summary:Boolean = true):Map<String, String> {
return mapOf(
"origin" to "${org}", //126.705098, 37.412087 출발지 경도, 위도
"destination" to "${dest}", //126.696239, 37.395276 도착지 경도, 위도
"priority" to "DISTANCE", // 'RECOMMEND' : 추천경로, 'TIME' : 최단시간, 'DISTANCE' : 최단경로
"car_type" to "1", //자동차 타입 '1' : 소형(예시) 승용차, 16인승 이하 승합차, 2.5 톤 미만 화물차), '2' : 중형, '3' : 대형, '4' : 대형 화물, '5' : 특수 화물, '6' : 경차, '7' : 이륜차
"car_fuel" to "DIESEL", //연료 종류
"car_hipass" to "true", // 하이패스 장착 여부
"summary" to "${summary}", //요약 전달(true 요약정보 제공, false 미제공)
)
/*
car_type : [1,
*/
}
fun make_getAddr_param(lon:String, lat:String):Map<String, String> {
return mapOf(
"x" to "${lon}", //126.705098,
"y" to "${lat}" //37.395276
)
/*
car_type : [1,
*/
}
fun make_addressHosp_param(
Q0:String, // 광역시/도
Q1:String, // 시군구
QD:String, // 진료과
QZ:String, // 병원분류코드
QN:String, // 관련검색어
page:String = "1", // 페이지
row:String = "10", // 결과 개수
):JsonObject {
var _param = mapOf(
"Q0" to Q0,
"Q1" to Q1,
"QD" to QD,
"QZ" to QZ,
"QN" to QN,
"page" to page,
"row" to row,
)
val gson = Gson()
val json = gson.toJsonTree(_param).asJsonObject
return json
}
fun make_arroundHosp_param(
lon:String,
lat:String,
page: String = "1",
dgids:List<String> = emptyList(),
row: String = "10",
radius: String = "5"
): JsonObject {
var order = "distance"
var _param = mapOf(
"lon" to lon, //126.696239 경도
"lat" to lat, //37.395276 위도
"radius" to radius, // 경/위도 기준 meter 단위 반경내의 병원
"order" to order, // 정렬 기준 "distance" 고정
"row" to row, // 결과 row 개수
"page" to page, // 결과 page 번째
"dgidIdName" to dgids, // 진료과에 대한 항목들
)
val gson = Gson()
val json = gson.toJsonTree(_param).asJsonObject
return json
}
fun make_arroundHosp_param2(
lon:String,
lat:String,
emogemdv:String = "", // 필수
prtcode:String = "", // 필수
emogdesc:String = "", // 필수 아님
radius: String = "3",
row: String = "10",
page: String = "1",
emergency: String = "Y"
): JsonObject {
var order = "distance"
var _param = mapOf(
"lon" to lon, //126.696239 경도
"lat" to lat, //37.395276 위도
"emogemdv" to emogemdv, // 의료기관번호
"prtcode" to prtcode, // 진료과코드
"emogdesc" to emogdesc, // 연관검색어
"radius" to radius, // 반경(km단위, 숫자만 입력)
"row" to row, // 행
"page" to page, // 페이지
"emergency" to emergency // 응급실 여부("Y", "N")
)
val gson = Gson()
val json = gson.toJsonTree(_param).asJsonObject
return json
}
fun make_detail_hosp_param(
hpid: String
) :JsonObject {
var _param = mapOf(
"hpid" to hpid, // 병원 아이디
)
val gson = Gson()
val json = gson.toJsonTree(_param).asJsonObject
return json
}
fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
val R = 6371.0 // 지구 반지름 (단위: km)
val dLat = Math.toRadians(lat2 - lat1)
val dLon = Math.toRadians(lon2 - lon1)
val a = sin(dLat / 2) * sin(dLat / 2) +
cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) *
sin(dLon / 2) * sin(dLon / 2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return R * c // 거리 반환 (단위: km)
}
}

View File

@ -0,0 +1,138 @@
package com.example.smart119
import android.app.Activity
import android.content.Context
import android.text.SpannableStringBuilder
import android.util.TypedValue
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
object AlertDialogUtil {
// 기본 AlertDialog 생성 함수
fun showAlert(
context: Context,
title: String,
message: String,
positiveText: String = "확인",
negativeText: String = "취소",
onPositiveClick: (() -> Unit)? = null,
onNegativeClick: (() -> Unit)? = null
) {
val builder = AlertDialog.Builder(context)
builder.setTitle(title)
// builder.setMessage(message)
val messageTextView = TextView(context).apply {
text = message
setPadding(40, 40, 40, 0)
textSize = 16f
setLineSpacing(10f, 1.2f) // 첫 번째 인자는 추가 간격(px), 두 번째 인자는 배수
}
builder.setView(messageTextView)
// Positive 버튼 설정
builder.setPositiveButton(positiveText) { dialog, _ ->
onPositiveClick?.invoke() // Positive 버튼 클릭 동작
dialog.dismiss()
}
// Negative 버튼 설정
builder.setNegativeButton(negativeText) { dialog, _ ->
onNegativeClick?.invoke() // Negative 버튼 클릭 동작
dialog.dismiss()
}
// 팝업 표시
val dialog = builder.create()
dialog.show()
}
fun showAlert_OK(
context: Context,
title: String,
message: String,
positiveText: String = "확인",
negativeText: String = "취소",
onPositiveClick: (() -> Unit)? = null
// onNegativeClick: (() -> Unit)? = null
) {
val builder = AlertDialog.Builder(context)
builder.setTitle(title)
// builder.setMessage(message)
val messageTextView = TextView(context).apply {
text = message
setPadding(40, 40, 40, 0)
textSize = 16f
setLineSpacing(10f, 1.2f) // 첫 번째 인자는 추가 간격(px), 두 번째 인자는 배수
}
builder.setView(messageTextView)
// Positive 버튼 설정
builder.setPositiveButton(positiveText) { dialog, _ ->
onPositiveClick?.invoke() // Positive 버튼 클릭 동작
dialog.dismiss()
}
// Negative 버튼 설정
// builder.setNegativeButton(negativeText) { dialog, _ ->
// onNegativeClick?.invoke() // Negative 버튼 클릭 동작
// dialog.dismiss()
// }
// 팝업 표시
val dialog = builder.create()
dialog.show()
}
fun showAlert_CUSTOM(
context: Context,
title: String,
sppStr: SpannableStringBuilder,
positiveText: String = "확인",
negativeText: String = "취소",
onPositiveClick: (() -> Unit)? = null,
onNegativeClick: (() -> Unit)? = null
) {
val builder = AlertDialog.Builder(context)
builder.setTitle(title)
/*
val spannableMessage = SpannableStringBuilder().apply {
val boldText = "확인"
append(boldText)
setSpan(StyleSpan(Typeface.BOLD), 0, boldText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
append(" 선택시 환자정보가 전송됩니다.")
}
*/
val textView = TextView(context).apply {
text = sppStr
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
setLineSpacing(12f, 1.3f) // 줄간 간격 조절 (추가간격 px, 배수)
setPadding(40, 40, 40, 0) // 내부 여백도 함께 설정
}
// builder.setMessage(sppStr)
builder.setView(textView)
// Positive 버튼 설정
builder.setPositiveButton(positiveText) { dialog, _ ->
onPositiveClick?.invoke() // Positive 버튼 클릭 동작
dialog.dismiss()
}
// Negative 버튼 설정
builder.setNegativeButton(negativeText) { dialog, _ ->
onNegativeClick?.invoke() // Negative 버튼 클릭 동작
dialog.dismiss()
}
// 팝업 표시
val dialog = builder.create()
dialog.show()
}
fun getActivityContext(): Activity? {
return GlobalActivityTracker.currentActivity
}
}

View File

@ -0,0 +1,18 @@
package com.example.smart119
import android.app.Application
import android.util.Log
class App : Application() {
companion object {
lateinit var instance: App
private set
}
override fun onCreate() {
super.onCreate()
Log.d("App", "App.onCreate 실행됨")
registerActivityLifecycleCallbacks(GlobalActivityTracker)
instance = this
}
}

View File

@ -0,0 +1,27 @@
package com.example.smart119
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.util.Log
object GlobalActivityTracker : Application.ActivityLifecycleCallbacks {
var currentActivity: Activity? = null
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
Log.d("GlobalActivityTracker", "현재 액티비티: ${activity.localClassName}")
}
override fun onActivityPaused(activity: Activity) {
if (currentActivity === activity) {
currentActivity = null
}
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
}

View File

@ -0,0 +1,14 @@
package com.example.smart119
import android.os.Handler
import android.os.Looper
object HelpUtility {
private val delayHandler = Handler(Looper.getMainLooper())
fun delay_executor(delayMillis: Long = 1000, callback: () -> Unit) {
delayHandler.postDelayed({
callback()
}, delayMillis)
}
}

View File

@ -0,0 +1,6 @@
package com.example.smart119
sealed class ListItem {
data class Header(val columns: List<String>) : ListItem()
data class Row(val values: List<String>) : ListItem()
}

View File

@ -0,0 +1,99 @@
package com.example.smart119
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.os.IBinder
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
class LocationService : Service() {
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationCallback: LocationCallback
override fun onCreate() {
super.onCreate()
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
locationCallback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
for (location: Location in result.locations) {
// Log.d("LocationService", "위치: ${location.latitude}, ${location.longitude}")
// TODO: 서버 전송 또는 처리 로직 추가
val lat = location.latitude
val lon = location.longitude
// ✅ LocationHelper에 위치 저장
LocationHelper.setCurrentLocation(lat, lon)
println("[GPS_UPDATE] lon:${lon}, lat:${lat}")
// ✅ Broadcast도 필요 시 같이 전송
// broadcastLocation(lat, lon)
}
}
}
startLocationUpdates()
}
private fun startLocationUpdates() {
val request = LocationRequest.create().apply {
interval = 5_000
fastestInterval = 3_000
priority = Priority.PRIORITY_HIGH_ACCURACY
}
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) return
fusedLocationClient.requestLocationUpdates(request, locationCallback, Looper.getMainLooper())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForegroundServiceWithNotification()
return START_STICKY
}
private fun startForegroundServiceWithNotification() {
val channelId = "location_service_channel"
val channel = NotificationChannel(
channelId,
"Location Service",
NotificationManager.IMPORTANCE_LOW
)
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(channel)
val notification = NotificationCompat.Builder(this, channelId)
.setContentTitle("위치 추적 중")
.setContentText("백그라운드에서 위치를 추적하고 있습니다.")
.setSmallIcon(R.drawable.ic_location_on)
.build()
try {
startForeground(1, notification)
} catch (e: Exception) {
println(e.message)
}
}
override fun onDestroy() {
super.onDestroy()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
override fun onBind(intent: Intent?): IBinder? = null
}

View File

@ -0,0 +1,179 @@
package com.example.smart119
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.net.Uri
import android.provider.Settings
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.gson.JsonObject
object LocationHelper {
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val LOCATION_PERMISSION_REQUEST_CODE = 100
// ✅ 실시간 위치 저장용 변수
private var currentLat: Double = 0.0
private var currentLon: Double = 0.0
fun startLocationService(ctx: Context, onFinish: () -> Unit = {}) {
if (!checkAndRequestLocationPermission(ctx, onFinish)) return
if (!isGpsEnabled(ctx)) {
promptEnableGps(ctx)
return
}
val serviceIntent = Intent(ctx, LocationService::class.java)
ContextCompat.startForegroundService(ctx, serviceIntent)
println("[startLocationService] - start")
}
fun stopLocationService(ctx: Context) {
val serviceIntent = Intent(ctx, LocationService::class.java)
ctx.stopService(serviceIntent)
}
fun setCurrentLocation(lat: Double, lon: Double) {
currentLat = lat
currentLon = lon
}
fun getCurrentLat(): Double = currentLat
fun getCurrentLon(): Double = currentLon
fun init(
ctx: Context,
onFinish:() -> Unit
) {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(ctx)
if (!checkAndRequestLocationPermission(ctx, onFinish)) {
return
}
handleGpsState(ctx)
}
fun handleGpsState(ctx: Context) {
if (isGpsEnabled(ctx)) {
//Toast.makeText(this, "GPS is already enabled", Toast.LENGTH_SHORT).show()
println("GPS Enabled")
startLocationService(ctx)
} else {
println("GPS not Enabled")
promptEnableGps(ctx)
}
}
// fun permissionResultInvoke(
// ctx: Context,
// requestCode: Int,
// permissions: Array<out String>,
// grantResults: IntArray,
// onFinish: () -> Unit
// ) {
// if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
// if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// LocationHelper.handleGpsState(ctx)
// } else {
// // "다시 묻지 않음" 여부 확인
// if (!ActivityCompat.shouldShowRequestPermissionRationale(
// ctx as Activity, Manifest.permission.ACCESS_FINE_LOCATION
// )
// ) {
// // 설정 화면으로 이동 안내(MainActivity에 구현)
// showSettingsDialog(ctx, onFinish)
// } else {
// Toast.makeText(ctx, "권한이 거부되었습니다. 앱을 종료합니다.", Toast.LENGTH_SHORT).show()
// onFinish();
// }
// }
// }
// }
private fun showSettingsDialog(ctx: Context, onFinish: () -> Unit) {
AlertDialog.Builder(ctx)
.setTitle("권한 필요")
.setMessage("위치 권한이 필요합니다. 설정에서 권한을 허용해주세요.")
.setPositiveButton("설정으로 이동") { _, _ ->
goToAppSettings(ctx)
}
.setNegativeButton("취소") { _, _ ->
Toast.makeText(ctx, "권한이 거부되었습니다. 앱을 종료합니다.", Toast.LENGTH_SHORT).show()
onFinish()
}
.show()
}
private fun goToAppSettings(context: Context) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", context.packageName, null)
}
context.startActivity(intent)
}
//1.현재 gps 상태 확인
private fun isGpsEnabled(ctx: Context): Boolean {
val locationManager = ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
}
//2.gps 활성화 요청
private fun promptEnableGps(ctx: Context) {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
ctx.startActivity(intent)
}
//3. 권한 요청 코드
private fun checkAndRequestLocationPermission(ctx: Context, onFinish: () -> Unit): Boolean {
if (ContextCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
ctx as Activity,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
// 권한 요청 설명
showPermissionRationale(ctx, onFinish)
} else {
// 권한 요청
ActivityCompat.requestPermissions(
ctx,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
}
return false
}
return true
}
private fun showPermissionRationale(ctx: Context, onFinish: () -> Unit) {
AlertDialog.Builder(ctx)
.setTitle("위치 권한 필요")
.setMessage("앱에서 정확한 위치 서비스를 제공하려면 위치 권한이 필요합니다.")
.setPositiveButton("권한 허용") { _, _ ->
ActivityCompat.requestPermissions(
ctx as Activity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
}
.setNegativeButton("취소") { _, _ ->
Toast.makeText(ctx, "권한이 거부되었습니다. 앱을 종료합니다.", Toast.LENGTH_SHORT).show()
onFinish()
}
.show()
}
}

View File

@ -0,0 +1,32 @@
package com.example.smart119
import com.google.gson.JsonObject
object LoginManager {
private var isLoggedIn: Boolean = false // 기본값 설정
var tryLoginBody: JsonObject = JsonObject().apply {
addProperty("userId", "")
addProperty("carNo", "")
addProperty("ver", "")
}
// 로그인 상태 확인
fun isLoggedIn(): Boolean {
return isLoggedIn
}
// 로그인 상태 업데이트
fun setLoggedIn(status: Boolean) {
isLoggedIn = status
}
fun getLoggedInfo() :JsonObject{
return tryLoginBody;
}
fun setLoggedInfo(userId:String, carNo:String, ver:String) {
tryLoginBody.addProperty("userId", userId)
tryLoginBody.addProperty("carNo", carNo)
tryLoginBody.addProperty("ver", ver)
}
}

View File

@ -0,0 +1,335 @@
package com.example.smart119
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.viewModels
import com.example.smart119.databinding.ActivityMainBinding
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import com.example.smart119.datas.HospitalSearch
import com.example.smart119.datas.SaveLogin
import com.example.smart119.interfaces.RetrofitClient
import com.example.smart119.viewModel.TransferViewModel
import com.example.smart119.views.LoadingDialog
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.textfield.TextInputEditText
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val LOCATION_PERMISSION_REQUEST_CODE = 100
private val viewModel by viewModels<TransferViewModel>()
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// 권한이 허용되면 서비스 시작
LocationHelper.startLocationService(this)
} else {
// 권한 거부됨 -> 필요한 안내 처리
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
// 권한 요청 흐름 시작
// if (!checkAndRequestLocationPermission()) {
// return
// }
// LocationHelper.init(this, {
// finish()
// });
LocationHelper.startLocationService(this)
SaveLogin.init(this);
HospitalSearch.init_hospital_req(this)
// LocationHelper.getLastKnownLocation_TOASTMSG(this)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNavigationView.setupWithNavController(navController)
//이송 초기화
TransferManager.setIdle()
//네비게이션하고 fragment 연결
bottomNavigationView.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.emgLogin -> {
if (LoginManager.isLoggedIn()) {
val _inputId = SaveLogin.getUserId()
val _inputCarno = SaveLogin.getCarNo()
val message = """
현재 로그인된 정보입니다.
아이디: $_inputId
차량번호: $_inputCarno
로그아웃하시겠습니까?
""".trimIndent()
AlertDialogUtil.showAlert(
context = this,
title = "로그인 상태 확인",
message = message,
onPositiveClick = {
//로그아웃 처리
println("logout")
navController.navigate(R.id.LoginFragment)
SaveLogin.setLogin(false)
TransferManager.clear_state()
},
onNegativeClick = {
}
)
false
}
else {
navController.navigate(R.id.LoginFragment)
true
}
}
R.id.hospSearch -> {
navController.navigate(R.id.HospSearchFragment)
true
}
R.id.hospTransfer -> {
navController.navigate(R.id.TransferFragment)
true
}
else -> false
}
}
TransferManager.registerListener("transfer_update_event", ::TransferUpdateEvent)
TransferManager.registerListener("transfer_dialogmsg_event", ::TransferDialogMsgEvent)
MedicalInfo.init()
// viewModel.requestData()
// viewModel.someLiveData.observe(viewLifecycleOwner) { result ->
// // UI 업데이트
// }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_content_main)
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
//권한 확인
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
LocationHelper.handleGpsState(this)
} else {
// "다시 묻지 않음" 여부 확인
if (!ActivityCompat.shouldShowRequestPermissionRationale(
this as Activity, Manifest.permission.ACCESS_FINE_LOCATION
)
) {
// 설정 화면으로 이동 안내(MainActivity에 구현)
showSettingsDialog(this@MainActivity) { finish() }
} else {
Toast.makeText(this, "권한이 거부되었습니다. 앱을 종료합니다.", Toast.LENGTH_SHORT).show()
finish();
}
}
}
else {
println("gps 권한 여부 코드 : ${requestCode}")
}
}
private fun showSettingsDialog(activity: Activity, onFinish: () -> Unit) {
AlertDialog.Builder(activity)
.setTitle("권한 필요")
.setMessage("위치 권한이 필요합니다. 설정에서 권한을 허용해주세요.")
.setPositiveButton("설정으로 이동") { _, _ ->
goToAppSettings(activity)
}
.setNegativeButton("취소") { _, _ ->
Toast.makeText(activity, "권한이 거부되었습니다. 앱을 종료합니다.", Toast.LENGTH_SHORT).show()
onFinish()
}
.show()
}
private fun goToAppSettings(activity: Activity) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", activity.packageName, null)
}
activity.startActivity(intent)
}
private fun onAccessButtonClick() {
Toast.makeText(this, "Button clicked!", Toast.LENGTH_SHORT).show()
}
fun TransferUpdateEvent(data: JsonObject?) {
}
fun TransferDialogMsgEvent(data: JsonObject?) {
try {
var _msgObj = data?.asJsonObject!!;
AlertDialogUtil.showAlert(
context = this,
title = _msgObj["title"].toString().trim('"'),
message = _msgObj["msg"].toString().trim('"'),
onPositiveClick = {
val activity = AlertDialogUtil.getActivityContext()
val bottomNav = activity?.findViewById<BottomNavigationView>(R.id.bottom_navigation)
if (bottomNav != null &&
bottomNav.selectedItemId != R.id.hospSearch
){
bottomNav.selectedItemId = R.id.hospSearch
}
},
onNegativeClick = {
val activity = AlertDialogUtil.getActivityContext()
val bottomNav = activity?.findViewById<BottomNavigationView>(R.id.bottom_navigation)
if (bottomNav != null &&
bottomNav.selectedItemId != R.id.hospSearch
) {
bottomNav.selectedItemId = R.id.hospSearch
}
}
)
} catch (e: Exception) {
//TODO("Not yet implemented")
}
}
//3. 권한 요청 코드
private fun checkAndRequestLocationPermission(): Boolean {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
// 권한 요청 설명
showPermissionRationale()
} else {
// 권한 요청
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
}
return false
}
return true
}
private fun showPermissionRationale() {
AlertDialog.Builder(this)
.setTitle("위치 권한 필요")
.setMessage("앱에서 정확한 위치 서비스를 제공하려면 위치 권한이 필요합니다.")
.setPositiveButton("권한 허용") { _, _ ->
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
}
.setNegativeButton("취소") { _, _ ->
Toast.makeText(this, "권한이 거부되었습니다. 앱을 종료합니다.", Toast.LENGTH_SHORT).show()
finish()
}
.show()
}
private fun showSettingsDialog() {
AlertDialog.Builder(this)
.setTitle("권한 필요")
.setMessage("위치 권한이 필요합니다. 설정에서 권한을 허용해주세요.")
.setPositiveButton("설정으로 이동") { _, _ ->
goToAppSettings(this)
}
.setNegativeButton("취소") { _, _ ->
Toast.makeText(this, "권한이 거부되었습니다. 앱을 종료합니다.", Toast.LENGTH_SHORT).show()
finish()
}
.show()
}
//4. 설정 화면으로 이동
fun goToAppSettings(context: Context) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", context.packageName, null)
}
context.startActivity(intent)
}
override fun onDestroy() {
super.onDestroy()
LocationHelper.stopLocationService(this)
TransferManager.clear_state();
}
}

View File

@ -0,0 +1,218 @@
package com.example.smart119
object MedicalInfo {
private val prtcodeMap = mutableMapOf<String, String>() // 진료과 목록용 맵
private val emogemdvMap = mutableMapOf<String, String>() // 의료기관 목록용 맵
private val addressQZMap = mutableMapOf<String, String>() // QZ 분류용 맵
private val prtGroupMap = mutableMapOf<String, MutableList<String>>() // 진료과 리스트 맵
fun init() {
prtcode_init()
emogemdv_init()
address_qz_init();
prtGroupMap_init();
}
private fun prtcode_init() {
prtcodeMap.put("가정의학과", "D022")
prtcodeMap.put("결핵과", "D015")
prtcodeMap.put("구강내과", "D040")
prtcodeMap.put("구강병리과", "D042")
prtcodeMap.put("구강악안면방사선과", "D041")
prtcodeMap.put("구강악안면외과", "D034")
prtcodeMap.put("구강안면외과", "D054")
prtcodeMap.put("내과", "D001")
prtcodeMap.put("마취통증의학과", "D017")
prtcodeMap.put("방사선종양학과", "D031")
prtcodeMap.put("병리과", "D032")
prtcodeMap.put("비뇨의학과", "D014")
prtcodeMap.put("사상체질과", "D050")
prtcodeMap.put("산부인과", "D011")
prtcodeMap.put("산업의학과", "D025")
prtcodeMap.put("성형외과", "D010")
prtcodeMap.put("소아청소년과", "D002")
prtcodeMap.put("소아치과", "D037")
prtcodeMap.put("신경과", "D003")
prtcodeMap.put("신경외과", "D009")
prtcodeMap.put("심장혈관흉부외과", "D007")
prtcodeMap.put("안과", "D012")
prtcodeMap.put("영상의학과", "D018")
prtcodeMap.put("영상치의학과", "D055")
prtcodeMap.put("예방의학과", "D029")
prtcodeMap.put("예방치과", "D043")
prtcodeMap.put("외과", "D006")
prtcodeMap.put("응급의학과", "D024")
prtcodeMap.put("이비인후과", "D013")
prtcodeMap.put("작업환경의학과", "D053")
prtcodeMap.put("재활의학과", "D016")
prtcodeMap.put("정신건강의학과", "D004")
prtcodeMap.put("정형외과", "D008")
prtcodeMap.put("진단검사의학과", "D033")
prtcodeMap.put("치과", "D026")
prtcodeMap.put("치과교정과", "D036")
prtcodeMap.put("치과보존과", "D039")
prtcodeMap.put("치과보철과", "D035")
prtcodeMap.put("치료방사선과", "D019")
prtcodeMap.put("치주과", "D038")
prtcodeMap.put("침구과", "D051")
prtcodeMap.put("통합치의학과", "D056")
prtcodeMap.put("피부과", "D005")
prtcodeMap.put("한방내과", "D044")
prtcodeMap.put("한방부인과", "D045")
prtcodeMap.put("한방소아과", "D046")
prtcodeMap.put("한방신경정신과", "D048")
prtcodeMap.put("한방안이비인후피부과", "D047")
prtcodeMap.put("한방응급과", "D052")
prtcodeMap.put("한방재활의학과", "D049")
prtcodeMap.put("해부병리과", "D021")
prtcodeMap.put("핵의학과", "D023")
}
private fun emogemdv_init() {
emogemdvMap.put("상급종합병원", "001")
emogemdvMap.put("종합병원", "011")
emogemdvMap.put("병원", "021")
emogemdvMap.put("요양병원", "028")
emogemdvMap.put("의원", "031")
emogemdvMap.put("치과병원", "041")
emogemdvMap.put("치과의원", "051")
emogemdvMap.put("보건소", "071")
emogemdvMap.put("보건지소", "072")
emogemdvMap.put("보건진료소", "073")
emogemdvMap.put("보건의료원", "075")
emogemdvMap.put("한방병원", "092")
emogemdvMap.put("한의원", "093")
}
private fun address_qz_init() { // 주소검색 시 병원 분류 코드
addressQZMap.put("종합병원", "A")
addressQZMap.put("병원", "B")
addressQZMap.put("의원", "C")
addressQZMap.put("요양병원", "D")
addressQZMap.put("한방병원", "E")
addressQZMap.put("한의원", "G")
addressQZMap.put("기타", "I")
addressQZMap.put("치과병원", "M")
addressQZMap.put("치과의원", "N")
addressQZMap.put("보건소", "R")
addressQZMap.put("기타(구급차)", "W")
}
private fun prtGroupMap_init() {
prtGroupMap.put(
"🚨 응급 및 외상 치료",
mutableListOf("응급의학과", "외과", "신경외과", "심장혈관흉부외과")
)
prtGroupMap.put(
"🏥 내과 및 심장 관련",
mutableListOf("내과", "심장내과", "결핵과", "예방의학과", "산업의학과", "작업환경의학과")
)
prtGroupMap.put(
"🦴 골절 및 재활",
mutableListOf("정형외과", "재활의학과", "한방재활의학과")
)
prtGroupMap.put(
"💊 마취 및 통증 관리",
mutableListOf("마취통증의학과")
)
prtGroupMap.put(
"👶 소아 및 여성 건강",
mutableListOf("소아청소년과", "산부인과", "한방소아과", "한방부인과")
)
prtGroupMap.put(
"🦷 치과 계열",
mutableListOf(
"치과", "구강내과", "구강병리과", "구강악안면방사선과", "구강악안면외과",
"소아치과", "치과교정과", "치과보존과", "치과보철과", "치주과",
"영상치의학과", "예방치과", "통합치의학과"
)
)
prtGroupMap.put(
"🧠 신경 및 정신 건강",
mutableListOf("신경과", "정신건강의학과", "한방신경정신과")
)
prtGroupMap.put(
"👂 이비인후과 및 안과",
mutableListOf("이비인후과", "안과", "한방안이비인후피부과")
)
prtGroupMap.put(
"📡 방사선 및 영상의학",
mutableListOf("영상의학과", "치료방사선과", "방사선종양학과", "핵의학과")
)
prtGroupMap.put(
"🩺 병리 및 진단의학",
mutableListOf("병리과", "해부병리과", "진단검사의학과")
)
prtGroupMap.put(
"🩸 비뇨 및 피부과",
mutableListOf("비뇨의학과", "피부과")
)
prtGroupMap.put(
"🌿 한방 계열",
mutableListOf(
"한방내과", "한방부인과", "한방소아과", "한방신경정신과", "한방안이비인후피부과",
"한방응급과", "한방재활의학과", "침구과", "사상체질과"
)
)
}
fun get_prt_code(prtName:String):String? {
return prtcodeMap.get(prtName)
}
fun get_emogemdv_code(emogemdvName:String):String? {
return emogemdvMap.get(emogemdvName)
}
fun get_addressQZ_code(qzName:String):String? {
return addressQZMap.get(qzName)
}
fun get_prt_list():MutableList<String> {
var returnList:MutableList<String> = mutableListOf()
prtcodeMap.forEach { key, value ->
returnList.add(key)
}
return returnList
}
fun get_emogemdv_list():MutableList<String> {
var returnList:MutableList<String> = mutableListOf()
emogemdvMap.forEach { key, value ->
returnList.add(key)
}
return returnList
}
fun get_addressQZ_list():MutableList<String> {
var returnList:MutableList<String> = mutableListOf()
addressQZMap.forEach { key, value ->
returnList.add(key)
}
return returnList
}
fun get_prt_group_map():MutableMap<String, MutableList<String>> {
return prtGroupMap;
}
/* //region 사용 예
val prtCode = MedicalInfo.get_prt_code("신경외과")
val emoCode = MedicalInfo.get_emogemdv_code("병원")
println("신경외과:${prtCode}, 병원:${emoCode}")
val getPRTList = MedicalInfo.get_prt_list()
getPRTList.forEach { prtName ->
println(prtName)
}
println("aaaaaaaaaaaaaaaa")
val getEMOList = MedicalInfo.get_emogemdv_list()
getEMOList.forEach { emoName ->
println(emoName)
}
*/ // endregion
}

View File

@ -0,0 +1,52 @@
package com.example.smart119
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
object MyCookieJar : CookieJar {
private val cookieStore: MutableMap<String, MutableList<Cookie>> = mutableMapOf()
private val cookieCustom: MutableMap<String, String> = mutableMapOf()
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
val host = url.url().host // URL의 호스트 이름 추출
if (cookies.isNotEmpty()) {
cookieStore[host] = cookies.toMutableList()
}
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
val host = url.url().host
return cookieStore[host] ?: mutableListOf()
}
fun setCookie(cookie:String) {
var cookieSplits = cookie.split("=")
// println(cookieSplits[0])
// println(cookieSplits[1])
cookieCustom.put(cookieSplits[0], cookie)
}
fun getCustomCookies():MutableList<String> {
var retList:MutableList<String> = mutableListOf()
cookieCustom.forEach { key, value ->
retList.add(value)
}
return retList;
}
fun getAllCookies():List<Cookie> {
return cookieStore.values.flatten()
}
// 쿠키 상태 확인 (디버깅용)
fun getCookies(): Map<String, List<Cookie>> {
return cookieStore
}
// 모든 쿠키 삭제
fun clearCookies() {
cookieStore.clear()
}
}

View File

@ -0,0 +1,40 @@
package com.example.smart119
import kotlin.random.Random
object RandomNumberUtil {
private var randNumList:List<Int> = mutableListOf()
private var _num_count_min = 0;
private var _num_counter = 0;
private var _num_count_max = 5;
fun num_increase():Boolean {
_num_counter++;
if (_num_counter == _num_count_max) return false
else return true;
}
fun setRandDescending(min: Int, max: Int, count: Int){
_num_count_min = min;
_num_count_max = count;
_num_counter = min;
require(max >= min) { "max should be greater than or equal to min" }
require(count > 0) { "count should be greater than 0" }
// 랜덤 숫자 생성
val randomNumbers = List(count - 1) { Random.nextInt(min, max + 1) } // count-1개의 랜덤 값 생성
// 마지막 값을 0으로 추가
val finalList = randomNumbers + 0
randNumList = finalList.sortedDescending()
// 내림차순 정렬
}
fun getCurrentNumber():Int {
return randNumList[_num_counter]
}
}

View File

@ -0,0 +1,221 @@
package com.example.smart119
import android.graphics.Color
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.JsonObject
class SpreadsheetAdapter(
private var items: List<ListItem>,
private val onButtonClick: (ListItem.Row, Int) -> Unit, // button click 이벤트
private val onRowClick: (ListItem.Row) -> Unit// Row click 이벤트
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val VIEW_TYPE_HEADER = 0
const val VIEW_TYPE_ROW = 1
}
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
is ListItem.Header -> VIEW_TYPE_HEADER
is ListItem.Row -> VIEW_TYPE_ROW
else -> throw IllegalArgumentException("Unknown view type at position $position")
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return if (viewType == VIEW_TYPE_HEADER) {
val view = inflater.inflate(R.layout.header_row, parent, false)
HeaderViewHolder(view)
} else {
val view = inflater.inflate(R.layout.data_row, parent, false)
RowViewHolder(view, onButtonClick, onRowClick)
}
}
private var _columnCount = 0
private var selectedPosition: Int = RecyclerView.NO_POSITION
private var headerList: List<ListItem>? = null
private var _IndexMap:MutableMap<String, Int> = mutableMapOf()
fun setColumnCount(count:Int) {
_columnCount = count
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val item = items[position]) {
is ListItem.Header -> (holder as HeaderViewHolder).bind(item)
is ListItem.Row -> (holder as RowViewHolder).bind(item, position)
else -> throw IllegalArgumentException("Unsupported item type at position $position")
}
/*
holder.itemView.setOnClickListener {
val previousPosition = selectedPosition
selectedPosition = position
// 이전 선택 항목과 현재 선택 항목 갱신
notifyItemChanged(previousPosition)
notifyItemChanged(selectedPosition)
}
*/
}
override fun getItemCount(): Int = items.size
fun setSelectedHospitalPos(selPos:Int) {
selectedPosition = selPos
}
fun getSelectedHospital() : ListItem? {
//return this.items[selectedPosition]
return if(selectedPosition in 0 until items.size) {
this.items[selectedPosition]
} else {
null
}
}
fun getSelectedPosition():Int {
var valid = selectedPosition - _columnCount;
if(valid < 0) valid = 0
return valid
}
fun getSelectedHospital_JSON() : JsonObject? {
//return this.items[selectedPosition]
var returnValue = JsonObject()
if(selectedPosition in 0 until items.size) {
var _row = (this.items[selectedPosition] as ListItem.Row);
returnValue = JsonObject().apply {
addProperty("name", "${_row.values[0]}")
addProperty("address", "${_row.values[1]}")
addProperty("tell","${_row.values[2]}")
addProperty("lidar","${_row.values[3]}")
addProperty("distance","${_row.values[4]}")
}
}
return returnValue
}
private fun default_list_headers() {
this.items = listOf(
ListItem.Header(listOf("병원이름")), // 헤더 열 정의
ListItem.Header(listOf("주소")),
ListItem.Header(listOf("전화번호")),
ListItem.Header(listOf("라이다 혼잡도")),
ListItem.Header(listOf("거리")),
ListItem.Header(listOf("병원별 이송 시작")),
)
}
fun hospitalListUpdate(searchResult:List<List<String>>, idList: List<String>) {
//기본 헤더 설정
default_list_headers()
//추가 검색된 결과 설정
// var loop_count = 0;
// for (addItem in searchResult) {
// this.items = this.items + ListItem.Row(addItem)
// println(addItem[0])
// loop_count++;
// }
searchResult.forEachIndexed { index, addItem ->
this.items = this.items + ListItem.Row(addItem)
if (idList.size > index) {
_IndexMap[idList[index]] = this.items.size - 1 // ID와 리스트 인덱스 매핑
}
}
notifyDataSetChanged()
}
fun getHospitalById(id: String): ListItem.Row? {
val position = _IndexMap[id] ?: return null
return if (position in 0 until items.size) {
items[position] as? ListItem.Row
} else {
null
}
}
fun clearHospitalList() {
if(this.items.size == 6) return;
if (selectedPosition != RecyclerView.NO_POSITION) selectedPosition = RecyclerView.NO_POSITION
if (_IndexMap != null) _IndexMap.clear()
default_list_headers()
notifyDataSetChanged()
}
// fun getSelectedHospitalName(): String {
// return selRowHospName
// }
// 헤더 ViewHolder
class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val columns: List<TextView> = listOf(
itemView.findViewById(R.id.column1) // 추가 열에 따라 확장 가능
)
fun bind(header: ListItem.Header) {
header.columns.forEachIndexed { index, column ->
columns[index].text = column
}
}
}
// 데이터 행 ViewHolder
class RowViewHolder(
itemView: View,
private val onButtonClick: (ListItem.Row, Int) -> Unit,
private val onRowClick: (ListItem.Row) -> Unit
) : RecyclerView.ViewHolder(itemView) {
private val cells: List<TextView> = listOf(
itemView.findViewById(R.id.cell1),
itemView.findViewById(R.id.cell2),
itemView.findViewById(R.id.cell3), // 추가 열에 따라 확장 가능
itemView.findViewById(R.id.cell4),
itemView.findViewById(R.id.cell5)
)
private val lContainer: LinearLayout = itemView.findViewById(R.id.cell6_container)
private val lContButton: Button = itemView.findViewById(R.id.cell6_button)
private val lLidarView : View = itemView.findViewById(R.id.cell4_lidar_color)
fun bind(row: ListItem.Row, pos:Int) {
row.values.take(cells.size).forEachIndexed { index, value ->
cells[index].text = value
if (index == 3) lLidarView.setBackgroundColor(
Color.parseColor(API_Helper.lidar_levelState_Color(value))
)
}
/*
// 선택된 항목 하이라이트 적용
if (isSelected) {
itemView.setBackgroundColor(Color.parseColor("#DCDCDC")) // 선택된 배경색
onRowClick(row)
} else {
itemView.setBackgroundColor(Color.WHITE) // 기본 배경색
}
*/
lContButton.setOnClickListener {
onButtonClick(row, pos) // 버튼 클릭 시 Row의 position 전달
}
}
}
}

View File

@ -0,0 +1,742 @@
package com.example.smart119
import android.graphics.Color
import android.view.View
import android.widget.TextView
import com.example.smart119.datas.HospitalSearch
import com.example.smart119.datas.SaveLogin
import com.example.smart119.interfaces.RetrofitClient
import com.example.smart119.views.FragmentHandler
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.text.SimpleDateFormat
import java.util.Date
import java.util.concurrent.atomic.AtomicBoolean
enum class TransferState {
IDLE, // 유휴상태
STARTED, // 이송시작
CANCELED, // 이송취소
COMPLETED, // 이송완료
END, // 이송 종료
CHANGED // 이송변경
}
enum class ETAState {
CONTINUE, // API 계속 진행 상태
END // API 정지 or Cancel
}
object TransferManager {
@Volatile
private var currentState: TransferState = TransferState.IDLE
public var serverState = AtomicBoolean(false)
@Volatile
private var ETA_STATE: ETAState = ETAState.CONTINUE
private var transferThread: Thread? = null
private val isRunning = AtomicBoolean(false)
private val sleepTime: Long = 5000
private var isKeepFindPath = AtomicBoolean(false)
private var isUpdateETA = AtomicBoolean(false)
private val updateDelayTime: Long = 10000; //5초 update eta 지연 수행 시간
private val runningEvtListener = mutableMapOf<String, (JsonObject?) -> Unit>()
private var errorDefLon: Double = 127.00663305137988;
private var errorDefLat: Double = 37.56777199202061;
private var _selectedHospInfo:JsonObject = JsonObject();
private var lastUpdateData:JsonObject = JsonObject().apply {
addProperty("hpid", "")
addProperty("levelState", "")
addProperty("eta", "")
addProperty("currentTime", "")
addProperty("leftDistance", "")
addProperty("transferState", "")
};
fun set_transfer_data(
hpid:String, levelState:String, eta:String,
currentTime:String, leftDistance:String, transferState:String
) {
if (hpid != "") lastUpdateData.addProperty("hpid", hpid)
if (levelState != "") lastUpdateData.addProperty("levelState", levelState)
if (eta != "") lastUpdateData.addProperty("eta", eta)
if (currentTime != "") lastUpdateData.addProperty("currentTime", currentTime)
if (leftDistance != "") lastUpdateData.addProperty("leftDistance", leftDistance)
if (transferState != "") {
lastUpdateData.addProperty("transferState", transferState)
}
}
fun get_transfer_data():JsonObject {
return lastUpdateData
}
// 이벤트 리스너 등록
fun registerListener(key: String, listener: (JsonObject?) -> Unit) {
if (runningEvtListener.containsKey(key)) {
// println("Listener with key '$key' already registered.")
} else {
runningEvtListener[key] = listener
// println("Listener with key '$key' registered.")
}
}
// 이벤트 발생
private fun dispatchEvent(eventName: String, data: JsonObject? = null) {
val listener = runningEvtListener[eventName]
if (listener != null) {
println("Dispatching event: $eventName")
listener(data)
} else {
println("No listener registered for event: $eventName")
}
}
fun getState(): TransferState {
return currentState
}
// 이송 시작
fun startTransfer():Boolean {
if (currentState == TransferState.STARTED) {
println("Transfer is already started.")
return false
}
currentState = TransferState.STARTED
ETA_STATE = ETAState.CONTINUE;
isRunning.set(true)
isKeepFindPath.set(true)
// "/startEmergency.do" 먼저 실행 후, 결과에 따라 thread 시작
try {
_SET_HOSPINFO(HospitalSearch.getSelectedData())
UpdateTransferInfomation();
}
catch (e:Exception) {
println(e.message)
}
if (API_STARTEMG()) {
transferThread = Thread {
println("Transfer started.")
while (isRunning.get() && currentState == TransferState.STARTED) {
try {
// 이송 중 반복 작업 수행
println("Transfer in progress... SLEEP TIME[${sleepTime}ms]")
// 등록한 이벤트 함수로 update중인 상태 전달
// if (!RandomNumberUtil.num_increase()) {
// println("END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
// API_END_TRANSFER();
// continue
// }
//#region 서버로부터 종료 코드를 받는 경우,
//#endregion
// println("이전 데이터");
// println(HospitalSearch.getSelectedData());
// println("변경 데이터");
// println(_GET_HOSPINFO());
Thread.sleep(sleepTime) // 1초마다 실행
} catch (e: InterruptedException) {
println("Transfer loop interrupted.")
}
}
println("Transfer loop stopped.")
isKeepFindPath.set(false);
}
transferThread?.start()
return true;
} else {
isKeepFindPath.set(false);
SendDialogMsg("응급 이송 시작 오류", "API START시 오류 발생")
return false;
}
}
private fun _SET_HOSPINFO(hospInfo:JsonObject?) {
val copiedJson = JsonParser.parseString(hospInfo.toString()).asJsonObject
_selectedHospInfo = copiedJson
}
private fun _GET_HOSPINFO():JsonObject? {
return _selectedHospInfo;
}
fun SendDialogMsg(title:String, msg:String) {
var sendMsg = JsonObject().apply {
addProperty("title", title)
addProperty("msg", msg)
}
// val transferHandler = FragmentHandler.getView("TransferFragment");
// if (transferHandler != null) {
// val bottomNaviView = transferHandler.findViewById<BottomNavigationView>(R.id.bottom_navigation)
// bottomNaviView.selectedItemId = R.id.hospSearch;
// }
dispatchEvent("transfer_dialogmsg_event", sendMsg)
}
// 이송 취소
fun cancelTransfer() {
if (currentState != TransferState.STARTED &&
serverState == AtomicBoolean(false)) {
println("Cannot cancel. Transfer is not active.")
return
}
currentState = TransferState.CANCELED
println("Transfer set CANCELED")
API_END_TRANSFER()
}
// 이송 종료
fun completeTransfer() {
if (currentState != TransferState.STARTED) {
println("Cannot complete. Transfer is not active.")
return
}
currentState = TransferState.COMPLETED
println("Transfer set COMPLETED")
API_END_TRANSFER()
stopTransferLoop()
}
// 이송 변경
fun changeTransfer() {
if (currentState != TransferState.STARTED) {
println("Cannot change. Transfer is not active.")
return
}
// currentState = TransferState.CHANGED
///updateETDHospId.do
println("Transfer state changed.")
// 이송 변경에 따른 추가 작업 수행
API_CHANGE_HOSPITAL()
}
// 이송 종료 상태로 설정
fun setEnd() {
if (currentState == TransferState.STARTED) {
isRunning.set(false)
isKeepFindPath.set(false)
transferThread?.interrupt()
transferThread = null
}
currentState = TransferState.END;
println("Transfer set to END state.")
SendDialogMsg("이송 종료", "응급 이송이 종료되었습니다.")
}
fun setCancel() {
isRunning.set(false)
isKeepFindPath.set(false)
transferThread?.interrupt()
transferThread = null
currentState = TransferState.END;
SendDialogMsg("이송 종료", "응급 이송이 취소되었습니다.")
}
// IDLE 상태로 설정
fun setIdle() {
if (currentState == TransferState.STARTED) {
stopTransferLoop()
}
currentState = TransferState.IDLE
println("Transfer set to IDLE state.")
}
// 루프 중단
private fun stopTransferLoop() {
isRunning.set(false)
isKeepFindPath.set(false)
transferThread?.interrupt()
transferThread = null
ETA_STATE = ETAState.END
currentState = TransferState.IDLE
SendDialogMsg("응급 이송 종료", "이송이 종료되었습니다.")
}
// 현재 상태 조회
fun getCurrentState(): TransferState {
return currentState
}
//pingpong을 위한 메시지 api 수행
private fun FindPath_ETA() {
isUpdateETA.set(false)
if (!isRunning.get() && currentState != TransferState.STARTED) return;
try {
// val resObj = res.getAsJsonObject();
// val callResult = resObj["isSuccess"]?.asBoolean ?: true;
val lon = LocationHelper.getCurrentLon();
val lat = LocationHelper.getCurrentLat();
// val lon = resObj["lon"]?.asDouble ?: 0.0;
// val lat = resObj["lat"]?.asDouble ?: 0.0;
val src_pos = "${lon}, ${lat}";
val selectedHospital = _GET_HOSPINFO();
var hosp_lon = selectedHospital?.get("lon")?.asString
var hosp_lat = selectedHospital?.get("lat")?.asString
if (hosp_lon == "null" || hosp_lon == "") {
hosp_lon = errorDefLon.toString()
}
if (hosp_lat == "null" || hosp_lat == "") {
hosp_lat = errorDefLat.toString()
}
var dst_pos = "${hosp_lon}, ${hosp_lat}";
if (isKeepFindPath.get()) {
HelpUtility.delay_executor(updateDelayTime) {
RetrofitClient.pathInstance.findPath(
API_Helper.make_findpath_param(src_pos, dst_pos)
).enqueue(findPathResponse())
}
}
else {
UpdateTransferInfomation_TIME()
}
} catch (e: Exception) {
println(e.message)
// SendDialogMsg("오류 발생", "길찾기 중 오류가 발생하였습니다. ${e.message}")
}
}
private fun UpdateTransferInfomation_ETA(loc:JsonObject) {
try {
var tfView = FragmentHandler.getView("TransferFragment")
if (tfView != null) {
var _eta = loc["eta"].asInt;
var _leftDistance = loc["leftDistance"].asString
val currentTime = SimpleDateFormat("HH:mm:ss").format(Date())
val updateTimeTxt = tfView.findViewById<TextView>(R.id.infoUpdateTime)
updateTimeTxt?.text = currentTime;
var _hospInfo = _GET_HOSPINFO()
//#region eta 업데이트
if (_eta != -1 && _eta != null) {
val etaTxt = tfView.findViewById<TextView>(R.id.tvLeftTimeVal)
val _etaStrSec = API_Helper.ETA_SEC_TO_STRING(_eta);
etaTxt?.text = _etaStrSec
if (_hospInfo != null) { _hospInfo.addProperty("eta", _etaStrSec) }
}
//#endregion
//#region etd 업데이트
if (_leftDistance != "" && _leftDistance != null) {
val etdTxt = tfView.findViewById<TextView>(R.id.tvLeftDistVal)
val _etdStrKM = API_Helper.DISTANCE_FORMAT_METER_TO_KM(_leftDistance);
etdTxt?.text = _etdStrKM;
if (_hospInfo != null) { _hospInfo.addProperty("leftDistance", _etdStrKM) }
}
//#endregion
}
} catch (e: Exception) {
println(e.message)
}
}
private fun UpdateTransferInfomation_TIME() {
var tfView = FragmentHandler.getView("TransferFragment")
if (tfView != null) {
val currentTime = SimpleDateFormat("HH:mm:ss").format(Date())
val updateTimeTxt = tfView.findViewById<TextView>(R.id.infoUpdateTime)
updateTimeTxt?.text = currentTime
}
}
private fun UpdateTransferInfomation() {
try {
var tfView = FragmentHandler.getView("TransferFragment")
if (tfView != null) {
//병원 이름, 혼잡도
var hosp_info = _GET_HOSPINFO();
if (hosp_info != null) {
println(hosp_info)
var levelState = hosp_info["levelState"].asString
var hospName = hosp_info["title"].asString
val hospNameTxt = tfView.findViewById<TextView>(R.id.tvTFHospName)
hospNameTxt?.text = hospName
val lidarValTxt = tfView.findViewById<TextView>(R.id.tvTFLidarValue)
lidarValTxt?.text = levelState
val lLidarView : View = tfView.findViewById(R.id.tvTFLidarColor)
lLidarView?.setBackgroundColor(
Color.parseColor(API_Helper.lidar_levelState_Color(levelState))
)
val tfStateTxt = tfView.findViewById<TextView>(R.id.tfState)
when (currentState) {
TransferState.IDLE -> tfStateTxt.text = "유휴 상태"
TransferState.STARTED -> tfStateTxt.text = "이송 시작"
TransferState.CANCELED -> tfStateTxt.text = "이송 취소"
TransferState.COMPLETED -> tfStateTxt.text = "이송 완료"
TransferState.END -> tfStateTxt.text = "이송 종료"
else -> tfStateTxt.text = "-"
}
println()
}
}
} catch (e: Exception) {
println(e.message)
}
}
fun SetLastHospitalInfo() {
try {
var tfView = FragmentHandler.getView("TransferFragment")
if (tfView != null) {
//병원 이름, 혼잡도
var hosp_info = _GET_HOSPINFO();
if (hosp_info != null) {
println(hosp_info)
var levelState = hosp_info["levelState"].asString
var hospName = hosp_info["title"].asString
val hospNameTxt = tfView.findViewById<TextView>(R.id.tvTFHospName)
hospNameTxt?.text = hospName
val lidarValTxt = tfView.findViewById<TextView>(R.id.tvTFLidarValue)
lidarValTxt?.text = levelState
val lLidarView : View = tfView.findViewById(R.id.tvTFLidarColor)
lLidarView?.setBackgroundColor(
Color.parseColor(API_Helper.lidar_levelState_Color(levelState))
)
val tfStateTxt = tfView.findViewById<TextView>(R.id.tfState)
when (currentState) {
TransferState.IDLE -> tfStateTxt.text = "유휴 상태"
TransferState.STARTED -> tfStateTxt.text = "이송 시작"
TransferState.CANCELED -> tfStateTxt.text = "이송 취소"
TransferState.COMPLETED -> tfStateTxt.text = "이송 완료"
TransferState.END -> tfStateTxt.text = "이송 종료"
else -> tfStateTxt.text = "-"
}
var last_eta = "-";
var last_etd = "-";
//#region last eta 업데이트
val etaTxt = tfView.findViewById<TextView>(R.id.tvLeftTimeVal)
if (hosp_info["eta"] != null) {
last_eta = hosp_info["eta"].asString
etaTxt?.text = last_eta
}
//#endregion
//#region last etd 업데이트
val etdTxt = tfView.findViewById<TextView>(R.id.tvLeftDistVal)
if (hosp_info["leftDistance"] != null) {
last_etd = hosp_info["leftDistance"].asString
etdTxt?.text = last_etd;
}
//#endregion
UpdateTransferInfomation_TIME();
}
}
} catch (e: Exception) {
println(e.message)
}
}
private fun API_STARTEMG(): Boolean {
val selectedData = HospitalSearch.getSelectedData()
if (selectedData != null) {
var hpid = selectedData["hpid"].toString().trim('"')
var reqBody = JsonObject().apply {
addProperty("hpid", hpid)
addProperty("hosp_name", selectedData.asJsonObject["title"].asString)
}
RetrofitClient.transferInstance.startEMG(reqBody).enqueue(startTransferResponse())
return true;
} else return false;
}
// 업데이트 ETA API 함수
private fun API_UPDATEETA(reqBody:JsonObject): Boolean {
// API URL : /updateETDDuration
RetrofitClient.transferInstance.updateHospETA(reqBody).enqueue(updateETAResponse())
return false;
}
private fun API_CHANGE_HOSPITAL(): Boolean {
val selectedData = HospitalSearch.getSelectedData()
if (selectedData != null) {
var _sel_hpid = selectedData["hpid"].asString
var _updateETDhospId_body = JsonObject().apply {
addProperty("hpid", _sel_hpid)
addProperty("hosp_name", selectedData.asJsonObject["title"].asString)
}
RetrofitClient.transferInstance.changeHospital(_updateETDhospId_body).enqueue(changeHospitalResponse())
return true;
}
return false;
}
private fun API_END_TRANSFER(): Boolean {
RetrofitClient.transferInstance.endEMG().enqueue(endTransferResponse())
return true;
}
private fun startTransferResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) { //200 ok
val resBody = response.body();
if (resBody != null) {
var _status = resBody.get("status")?.asString;
//response.status = "success" or "duplicate"
if (_status == "success") {
UpdateTransferInfomation()
FindPath_ETA();
println("이송 시작 [onResponse - Success] : ${response.message()}")
}
else {
println("이송 시작 [onResponse - Fail] : ${response.message()}")
//검색창, 다시 환자 정보 전송
SendDialogMsg("동일한 환자정보가 이미 존재합니다",
"같은 차량으로 동일한 환자정보가 이미 전송되어 있습니다.")
// nav_to_hospsearch();
}
}
else {
}
} else { //200 아닌 코드들
println("이송 시작 [onResponse - Fail] : ${response.message()}")
val parseBody = JsonParser.parseString(response.errorBody()?.string())
if (parseBody != null) {
val resStatus = parseBody.asJsonObject["status"].asString
if (resStatus == "duplicate") {
SendDialogMsg("환자정보 전송 실패",
"같은 차량으로 동일한 환자정보가 이미 전송되어 있습니다.")
clear_state();
}
else {
SendDialogMsg("환자정보 전송 실패",
"알수없는 오류로 전송에 실패하였습니다. 담당자에게 문의해주세요.")
clear_state();
}
}
else {
SendDialogMsg("환자정보 전송 실패",
"알수없는 오류로 전송에 실패하였습니다. 담당자에게 문의해주세요.")
clear_state();
}
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
println("이송 시작 [onFailure] ${t.message}")
SendDialogMsg("환자 정보 전송 실패",
"알수없는 오류로 전송에 실패하였습니다. 담당자에게 문의해주세요.")
clear_state();
}
}
}
// 업데이트 ETA API 결과
private fun updateETAResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
var resBody = response.body()
var _status = resBody?.get("status")?.asString;
if (_status == "continue") {
FindPath_ETA();
println("API_ETA_TRANSFER[onResponse - Success] : ${response.message()}")
}
else {
//이송 종료 처리
if (currentState != TransferState.END) setEnd()
}
} else {
println("API_ETA_TRANSFER[onResponse - Fail] : ${response.message()}")
isKeepFindPath.set(false)
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
//대부분 네트워크 문제
println("API_ETA_TRANSFER[OnFailure] : ${t.message}")
isKeepFindPath.set(false)
}
}
}
// 업데이트 ETA API 결과
private fun changeHospitalResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
try {
_SET_HOSPINFO(HospitalSearch.getSelectedData())
UpdateTransferInfomation();
}
catch (e:Exception) {
println(e.message)
}
println("API_CHANGE_HOSPITAL[onResponse - Success] : ${response.message()}")
} else {
println("API_CHANGE_HOSPITAL[onResponse - Fail] : ${response.message()}")
isKeepFindPath.set(false)
SendDialogMsg("병원 정보 변경 실패",
"알수없는 오류로 전송에 실패하였습니다. 담당자에게 문의해주세요.")
clear_state();
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
// ETA_STATE = ETAState.END //?
println("API_CHANGE_HOSPITAL[onFailure] : ${t.message}")
}
}
}
// 업데이트 ETA API 결과
private fun endTransferResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
var resBody = response.body()
var _status = resBody?.get("status")?.asString;
setCancel()
} else {
// ETA_STATE = ETAState.END //?
println("API_END_TRANSFER[onResponse - Fail] : ${response.message()}")
isKeepFindPath.set(false)
clear_state()
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
// ETA_STATE = ETAState.END //?
//대부분 네트워크 문제
// println("API_END_TRANSFER[OnFailure] : ${t.message}")
isKeepFindPath.set(false)
clear_state()
}
}
}
//길찾기 API 결과
private fun findPathResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
try {
var resBody = response.body()
var routes = resBody?.get("routes")?.asJsonArray
if (routes != null && routes.size() > 0) {
var f_route = routes[0].asJsonObject;
var res_code = f_route["result_code"].asString
when(res_code) {
"0" -> {
var f_summary = f_route["summary"].asJsonObject
var _distance = f_summary["distance"].asString;
var _duration = f_summary["duration"].asString;
var _updateETA = JsonObject().apply {
addProperty("leftDistance", _distance)
addProperty("eta", _duration)
}
//duration : 초, distance : 미터
println("distance : ${_distance}km, duration : ${_duration}sec")
UpdateTransferInfomation_ETA(_updateETA)
var _updateETA_body = JsonObject().apply {
addProperty("duration", _duration)
}
API_UPDATEETA(_updateETA_body)
}
"1" -> FindPath_ETA() // 길찾기 결과를 찾을 수 없음, 종료
"101" -> FindPath_ETA() // 경유지 지점 주변의 도로를 탐색할 수 없음, 계속 찾기
"102" -> FindPath_ETA() // 시작 지점 주변의 도로를 탐색할 수 없음, 계속 찾기
"103" -> FindPath_ETA() // 도착 지점 주변의 도로를 탐색할 수 없음, 계속 찾기
"104" -> isKeepFindPath.set(false) // 출발지와 도착지가 5 m 이내로 설정된 경우 경로를 탐색할 수 없음, 이 경우엔 패스
"105" -> FindPath_ETA() // 시작 지점 주변의 도로에 유고 정보(교통 장애)가 있음, 계속 찾기
"106" -> FindPath_ETA() // 도착 지점 주변의 도로에 유고 정보(교통 장애)가 있음, 계속 찾기
"107" -> FindPath_ETA() // 경유지 주변의 도로에 유고 정보(교통 장애)가 있음, 계속 찾기
else -> FindPath_ETA() // 길찾기 시 알수 없는 코드가 나왔을때,
}
}
} catch (e: Exception) {
println(e.message)
isKeepFindPath.set(false)
}
} else {
println("길찾기 결과[Fail] : ${response.message()}")
isKeepFindPath.set(false)
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
//대부분 네트워크 문제
println("길찾기 결과[OnFailure] : ${t.message}")
isKeepFindPath.set(false)
}
}
}
fun nav_to_login() {
val activity = AlertDialogUtil.getActivityContext()
if (activity != null) {
AlertDialogUtil.showAlert_OK(
context = activity,
title = "세션 만료",
message = "기존 세션이 만료되었습니다. 다시 로그인 해주시기 바랍니다.\n",
onPositiveClick = {
SaveLogin.setLogin(false)
LoginManager.setLoggedIn(false);
val bottomNaviView =
activity.findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.emgLogin;
}
)
}
}
fun nav_to_hospsearch() {
val activity = AlertDialogUtil.getActivityContext()
val bottomNav = activity?.findViewById<BottomNavigationView>(R.id.bottom_navigation)
if (bottomNav != null &&
bottomNav.selectedItemId != R.id.hospSearch
){
bottomNav.selectedItemId = R.id.hospSearch
}
}
fun clear_state() {
currentState = TransferState.IDLE
isRunning.set(false)
isKeepFindPath.set(false)
transferThread?.interrupt()
transferThread = null
println("Logout, Clear Transfer State.")
}
}

View File

@ -0,0 +1,20 @@
package com.example.smart119
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.example.smart119.views.AddressSearchFragment
import com.example.smart119.views.LocationSearchFragment
class ViewPagerAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
override fun getItemCount(): Int = 2 // 탭 개수
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> AddressSearchFragment()
1 -> LocationSearchFragment()
else -> AddressSearchFragment()
}
}
}

View File

@ -0,0 +1,484 @@
package com.example.smart119.datas
import android.content.Context
import android.view.LayoutInflater
import android.widget.PopupWindow
import android.widget.Toast
import com.example.smart119.R
import com.example.smart119.interfaces.RetrofitClient
import com.example.smart119.views.FragmentHandler
import com.example.smart119.views.HospitalInfoPopup
import com.example.smart119.views.HospitalSearchPopup
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
object HospitalSearch {
private val savedHospList: MutableList<JsonObject> = mutableListOf()
private var selectedHospitalIdx: Int = -1
private var bUsedFilter: Boolean = false
private var gpsAddress:String = "";
private var hospDetail = mutableMapOf(
"isTransfer" to false,
"info" to mutableMapOf<String, Any>()
)
private val DEF_SEARCH:JsonObject = JsonObject().apply {
addProperty("sidoName", "서울특별시")
addProperty("sidoCode", "11")
}
private var prevLon:Double = 0.0;
private var prevLat:Double = 0.0;
private var prevTime:Long = 0;
private var requestBody:JsonObject = JsonObject().apply {
addProperty("bFilter", false)
add("reqBody", JsonObject())
}
private lateinit var popupSearch: PopupWindow;
private var sidoInfo:JsonObject = JsonObject().apply {
addProperty("isLoaded", false)
}
private var gunGuInfo:JsonObject = JsonObject().apply {
addProperty("isLoaded", false)
}
private var eubMyeonDongInfo:JsonObject = JsonObject().apply {
addProperty("isLoaded", false)
}
private var gunGuNameList:MutableList<String> = mutableListOf()
private var gunGuSelect:String = "군구선택";
private var hospCatSelect:String = "병원분류명선택";
private var prtSelect:String = "진료과목선택";
private var addrFilterSave:JsonObject = JsonObject().apply {
addProperty("si", "")
addProperty("gu", "")
addProperty("relate", "")
}
//true면 주소검색 사용, false면 위경도 검색 사용
fun SET_SearchRequest(isAddress:Boolean, reqBody:JsonObject) {
requestBody.addProperty("bFilter", isAddress)
requestBody.add("reqBody", reqBody)
}
fun GET_SearchRequest():JsonObject {
return requestBody;
}
fun isAddressSearch():Boolean {
return requestBody.get("bFilter")?.asBoolean ?: false
}
fun setPrevGPSUpdate(lon:Double, lat:Double, time:Long) {
prevLon = lon;
prevLat = lat;
prevTime = time;
println("updated!!! ${prevLon}, ${prevLat}, ${prevTime}")
}
fun getPrevGPSInfo():JsonObject {
return JsonObject().apply {
addProperty("prevLon", prevLon)
addProperty("prevLat", prevLat)
addProperty("prevTime", prevTime)
}
}
fun itemStructure(
hpid: String = "", // 병원 아이디
levelState: String = "", // lidar 레벨
distance: String = "", // 거리
city: String = "", // 시
district: String = "", // 구
tel: String = "", // 전화번호
title: String = "", // 병원 이름
lon: String = "", // 경도
lat: String = "" // 위도
// name: String = "",
// address: String = "",
// phoneNumber: String = "",
// lidarState: String = ""
):JsonObject {
return JsonObject().apply({
addProperty("hpid", hpid)
addProperty("levelState", levelState)
addProperty("distance", distance)
addProperty("city", city)
addProperty("district", district)
addProperty("tel", tel)
addProperty("title", title)
addProperty("lon", lon)
addProperty("lat", lat)
})
}
fun init_hospital_req(context:Context) {
var sidoData = sidoInfo.getAsJsonObject("data");
//비어있는 경우에만 요청하여 얻기
if (sidoData == null || sidoData.entrySet().isEmpty()) {
//1. 시 관련 콤보박스 먼저 얻기
RetrofitClient.locInstance.getSidoList().enqueue(sidoResponse())
}
}
fun get_gps_address():String {
return gpsAddress
}
fun set_gps_update(address:String) {
if(gpsAddress.isEmpty()) {
gpsAddress = address
}
}
fun set_selectGunGu(gunGu:String = "군구선택") {
gunGuSelect = gunGu
}
fun get_selectGunGu():String {
return gunGuSelect
}
fun set_selectHospCatAddress(hospCat:String = "병원분류명선택") {
hospCatSelect = hospCat
}
fun get_selectHospCatAddress():String {
return hospCatSelect
}
fun set_selectPrtAddress(prt:String = "진료과목선택") {
prtSelect = prt
}
fun get_selectPrtAddress():String {
return prtSelect
}
fun set_popup_search(searchPopup:PopupWindow) {
popupSearch = searchPopup
}
fun get_popup_search(): PopupWindow {
return popupSearch
}
fun req_gun_list(si:String) {
RetrofitClient.locInstance.getSiGunGuList(JsonObject().apply {
addProperty("code", get_si_code(si) ?: "")
}).enqueue(siGunGuResponse())
}
private fun get_si_code(si:String): String {
return sidoInfo["data"].asJsonObject["${si}"].asString
}
private fun _itemStructure_IDX(
idx: Int = 0,
hpid: String = "", // 병원 아이디
levelState: String = "", // lidar 레벨
distance: String = "", // 거리
city: String = "", // 시
district: String = "", // 구
tel: String = "", // 전화번호
title: String = "", // 병원 이름
lon: String = "", // 경도
lat: String = "" // 위도
):JsonObject {
return JsonObject().apply({
addProperty("idx", idx)
addProperty("hpid", hpid)
addProperty("levelState", levelState)
addProperty("distance", distance)
addProperty("city", city)
addProperty("district", district)
addProperty("tel", tel)
addProperty("title", title)
addProperty("lon", lon)
addProperty("lat", lat)
})
}
// JsonObject 추가
fun addData(jsonObject: JsonObject) {
savedHospList.add(jsonObject)
}
fun setSearchFilter(si:String, gu:String, relate:String) {
addrFilterSave.addProperty("si", when(si) {"시도선택" -> "" else -> si })
addrFilterSave.addProperty("gu", when(gu){"구군선택" -> "" else -> gu })
addrFilterSave.addProperty("relate", relate)
}
fun setFiltered(bFiltered:Boolean) {
bUsedFilter = bFiltered;
}
fun getSearchInfo():JsonObject {
return addrFilterSave;
}
fun getIsFiltered():Boolean {
return bUsedFilter
}
fun addDataHospitalItems(hospList:List<JsonObject>) {
var idx = 0;
hospList.forEach{
hosp ->
var rebuild = _itemStructure_IDX(
idx++,
hosp["hpid"].toString().trim('"'),
hosp["levelState"].toString().trim('"'),
hosp["distance"].toString().trim('"'),
hosp["city"].toString().trim('"'),
hosp["district"].toString().trim('"'),
hosp["tel"].toString().trim('"'),
hosp["title"].toString().trim('"'),
hosp["lon"].toString().trim('"'),
hosp["lat"].toString().trim('"')
)
savedHospList.add(rebuild)
}
// println(savedHospList);
}
// 인덱스로 JsonObject 가져오기
fun getData(index: Int): JsonObject? {
return if (index in savedHospList.indices) savedHospList[index] else null
}
fun setIdx(idx:Int) {
selectedHospitalIdx = idx
}
fun getSelectedData(): JsonObject? {
if (selectedHospitalIdx != -1) {
// var returnData = JsonObject().apply { }
val returnData = savedHospList.getOrNull(selectedHospitalIdx)
return returnData
}
else return null
}
//region hospital detail용 함수
fun getHospitalID():String {
if (selectedHospitalIdx != -1) {
val returnData = savedHospList.getOrNull(selectedHospitalIdx)
return returnData?.get("hpid")?.asString ?: "";
}
else return ""
}
fun setHospitalDetail(info:JsonObject) {
hospDetail["info"] = info;
}
fun getHospitalDetail():Any? {
return hospDetail["info"]
}
//endregion
fun getAllData(withIndex:Boolean = false):List<List<String>> {
val setDataList:MutableList<List<String>> = mutableListOf()
savedHospList.forEach {
json ->
if(withIndex) {
val address =
json["city"].toString().replace("\"", "") + " " +
json["district"].toString().replace("\"", "")
setDataList.apply {
add(
listOf(
json["idx"].toString().replace("\"", ""),
json["title"].toString().replace("\"", ""),
address,
json["tel"].toString().replace("\"", ""),
json["levelState"].toString().replace("\"", ""),
json["distance"].toString().replace("\"", "") + "km"
)
)
}
}
else {
val address =
json["city"].toString().replace("\"", "") + " " +
json["district"].toString().replace("\"", "")
setDataList.apply {
add(
listOf(
json["title"].toString().replace("\"", ""),
address,
json["tel"].toString().replace("\"", ""),
json["levelState"].toString().replace("\"", ""),
json["distance"].toString().replace("\"", "") + "km"
)
)
}
}
}
return setDataList
}
fun clearAllData() {
selectedHospitalIdx = 0;
savedHospList.clear()
}
private fun sidoResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
val jsonObject = response.body();
var dataSet = JsonObject()
// println("sido response success ${jsonObject}");
if (jsonObject != null) {
val resDatas = jsonObject["resData"].asJsonArray;
resDatas.forEach { data ->
val _sicode = data.asJsonObject["sidoCode"].asString
val _siname = data.asJsonObject["sidoName"].asString
dataSet.addProperty(_siname, _sicode)
}
}
//loaded true, data set
sidoInfo.addProperty("isLoaded", true)
sidoInfo.add("data", dataSet)
//#region 현재 서울시를 중점으로 하기 때문에, 기본 코드 API요청
req_gun_list(DEF_SEARCH["sidoName"].asString)
//#endregion
} else {
println("sido response but fail");
sidoDeafult()
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
println("sido response fail ${t.message}");
sidoDeafult()
}
}
}
private fun sidoDeafult() {
var dataSet = JsonObject()
val default_si = DEF_SEARCH["sidoName"].asString;
val default_siCode = DEF_SEARCH["sidoCode"].asString;
dataSet.addProperty(default_si, default_siCode)
sidoInfo.addProperty("isLoaded", false)
sidoInfo.add("data", dataSet)
req_gun_list(default_si)
}
fun GET_SIDO_DEFAULT_NAME():String {
return DEF_SEARCH["sidoName"].asString
}
fun GET_SIDO_DEFAULT_CODE():String {
return DEF_SEARCH["sidoCode"].asString
}
fun getSidoList():List<String> {
var returnList: List<String> = mutableListOf()
if (sidoInfo["isLoaded"].asBoolean) {
var sidoJsonObject = sidoInfo.getAsJsonObject("data")
returnList = sidoJsonObject.entrySet().map { it.key }
}
return returnList;
}
fun getGunGuNameList():MutableList<String> {
return gunGuNameList.toMutableList();
}
private fun siGunGuResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
val jsonObject = response.body();
var dataSet = JsonObject();
// println("sido response success ${jsonObject}");
if (jsonObject != null) {
val resDatas = jsonObject["resData"].asJsonArray;
resDatas.forEach { data ->
val _guguncode = data.asJsonObject["code"].asString
val _gugunName = data.asJsonObject["name"].asString
dataSet.addProperty(_gugunName, _guguncode)
gunGuNameList.add(_gugunName)
}
}
//loaded true, data set
gunGuInfo.addProperty("isLoaded", true)
gunGuInfo.add("data", dataSet)
//세팅 구군 리스트
// var hsfView = FragmentHandler.getView("HospitalSearchFragment")
// if (hsfView != null) {
// nameList.add(0, "구군선택")
// HospitalSearchPopup.set_POPUP_GUGUN_COMBO(popupSearch, hsfView.context, nameList)
// }
} else {
println("gugun response but fail");
SEOUL_DEFAULT_GUNGU()
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
println("gugun response fail ${t.message}");
SEOUL_DEFAULT_GUNGU()
}
}
}
private fun siEubMyeonDongResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
} else {
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
}
}
}
private fun SEOUL_DEFAULT_GUNGU() {
var dataSet = JsonObject();
var nameList:MutableList<String> = mutableListOf()
dataSet.addProperty("11680","강남구")
dataSet.addProperty("11740","강동구")
dataSet.addProperty("11305","강북구")
dataSet.addProperty("11500","강서구")
dataSet.addProperty("11620","관악구")
dataSet.addProperty("11215","광진구")
dataSet.addProperty("11530","구로구")
dataSet.addProperty("11545","금천구")
dataSet.addProperty("11350","노원구")
dataSet.addProperty("11320","도봉구")
dataSet.addProperty("11230","동대문구")
dataSet.addProperty("11590","동작구")
dataSet.addProperty("11440","마포구")
dataSet.addProperty("11410","서대문구")
dataSet.addProperty("11650","서초구")
dataSet.addProperty("11200","성동구")
dataSet.addProperty("11290","성북구")
dataSet.addProperty("11710","송파구")
dataSet.addProperty("11470","양천구")
dataSet.addProperty("11560","영등포구")
dataSet.addProperty("11170","용산구")
dataSet.addProperty("11380","은평구")
dataSet.addProperty("11110","종로구")
dataSet.addProperty("11140","중구")
dataSet.addProperty("11260","중랑구")
for((key, value) in dataSet.entrySet()) {
gunGuNameList.add(value.asString)
}
gunGuInfo.addProperty("isLoaded", false)
gunGuInfo.add("data", dataSet)
println();
}
}

View File

@ -0,0 +1,4 @@
package com.example.smart119.datas
object SampleLocation {
}

View File

@ -0,0 +1,38 @@
package com.example.smart119.datas
import android.content.Context
import android.content.SharedPreferences
object SaveLogin {
private const val PREF_119_NAME = "SMART119_LOGIN"
private const val KEY_IS_LOGGED_IN = "isLoggedIn"
private const val KEY_STR_USER_ID = "strUserId"
private const val KEY_STR_CAR_NO = "strCarNo"
private lateinit var preferences: SharedPreferences
fun init(context: Context) {
preferences = context.getSharedPreferences(PREF_119_NAME, Context.MODE_PRIVATE)
}
fun setLogin(isLoggedIn:Boolean) {
preferences.edit().putBoolean(KEY_IS_LOGGED_IN, isLoggedIn).apply()
}
fun setUserId(strUserId:String) {
preferences.edit().putString(KEY_STR_USER_ID, strUserId).apply()
}
fun setCarNo(strCarNo:String) {
preferences.edit().putString(KEY_STR_CAR_NO, strCarNo).apply()
}
fun getUserId(): String? {
return preferences.getString(KEY_STR_USER_ID, null)
}
fun getCarNo(): String? {
return preferences.getString(KEY_STR_CAR_NO, null)
}
fun isLoggedIn(): Boolean {
return preferences.getBoolean(KEY_IS_LOGGED_IN, false)
}
}

View File

@ -0,0 +1,19 @@
package com.example.smart119.interfaces
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.QueryMap
interface apiFindPath {
@GET("/findPath")
fun findPath(
@QueryMap params: Map<String, String>
) : Call<JsonObject>
@GET("/getAddr")
fun getAddr(
@QueryMap params: Map<String, String>
) : Call<JsonObject>
}

View File

@ -0,0 +1,16 @@
package com.example.smart119.interfaces
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
interface apiHospital {
@POST("hospitalDetail.do")
fun getDetail(
@Body requestBody: JsonObject
) : Call<JsonObject>
}

View File

@ -0,0 +1,58 @@
package com.example.smart119.interfaces
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.POST
import com.google.gson.JsonObject
import retrofit2.http.Body
interface apiLocation {
@POST("/selectSidoList.do")
fun getSidoList() : Call<JsonObject>
@POST("/selectSiGunGuList.do")
fun getSiGunGuList(
@Body requestBody: JsonObject
) : Call<JsonObject>
//body
//SIDO_CODE
//SI_GUN_GU_CODE
@POST("/selectEubMyeonDongList.do")
fun getEubMyeonDongList(
@Body requestBody: JsonObject
) : Call<JsonObject>
@POST("/radiusSearchHospital2.do")
fun getAroundHospitalList(
@Body requestBody: JsonObject
) : Call<JsonObject>
@POST("/searchHospital.do")
fun getAddressHospitalList(
@Body requestBody: JsonObject
) : Call<JsonObject>
/*
//유연하게 사용할 때 아래와 같이 사용
@GET("path/to/endpoint")
//param 날리는 방식 1
fun getSiGunGuList(
@QueryMap options: Map<String, String>
): Call<JsonObject>
//param 날리는 방식 2
fun getSiGunGuList(
@Query("SIDO_CODE") param1: String
) : Call<JsonObject>
val params = mapOf(
"key1" to "value1",
"key2" to "123"
)
RetrofitClient.apiService.getSiGunGuList(params).enqueue(/* Callback */)
*/
}

View File

@ -0,0 +1,13 @@
package com.example.smart119.interfaces
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
interface apiLogin {
@POST("mobileLogin.do")
fun mLogin(
@Body requestBody: JsonObject
) : Call<JsonObject>
}

View File

@ -0,0 +1,33 @@
package com.example.smart119.interfaces
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
interface apiTransfer {
@POST("startEmergency.do")
fun startEMG(
@Body requestBody: JsonObject
) : Call<JsonObject>
@POST("updateETDHospId.do")
fun changeHospital(
@Body requestBody: JsonObject
) : Call<JsonObject>
@POST("updateETDDuration.do")
fun updateHospETA(
@Body requestBody: JsonObject
) : Call<JsonObject>
@GET("endEmergency.do")
fun endEMG() : Call<JsonObject>
@POST("cookie")
fun cookieSend() : Call<JsonObject>
@GET("currentInAction.do")
fun currentAction() : Call<JsonObject>
}

View File

@ -0,0 +1,43 @@
package com.example.smart119.interfaces
import com.example.smart119.MyCookieJar
import okhttp3.Interceptor
import okhttp3.Response
object CookieInterCeptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
// 요청 헤더에서 Cookie 확인
val cookies = request.header("Cookie")
println("Request URL: ${request.url()}")
if (cookies != null) {
println("Cookies in Request: $cookies")
} else {
println("No cookies in Request.")
// val cookies = listOf(
// "sessionId=abc123",
// "csrfToken=xyz456",
// "userId=789"
// )
val cookies = MyCookieJar.getCustomCookies()
if(cookies.size > 0) {
request = request.newBuilder()
// .addHeader("Cookie", "sessionId=abc123")
.addHeader("Cookie", cookies.joinToString("; ")) // 수동으로 쿠키 추가
.build()
}
}
// request = request.newBuilder()
// .addHeader("Cookie", "sessionId=abc123")
//// .addHeader("Cookie", cookies.joinToString("; ")) // 수동으로 쿠키 추가
// .build()
return chain.proceed(request)
}
}

View File

@ -0,0 +1,166 @@
package com.example.smart119.interfaces
import com.example.smart119.MyCookieJar
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import okhttp3.Cookie
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.net.ssl.*
import java.util.concurrent.TimeUnit
object RetrofitClient {
private const val HOST_IP_TEST = "192.168.0.20"
private const val HOST_PORT_TEST = "8080"
private const val BASE_URL_HTTP_TEST = "http://${HOST_IP_TEST}:${HOST_PORT_TEST}"
//#region 테스트용 ip
//#region 일반 백엔드 api 설정
private const val HOST_IP = "112.219.147.186"
private const val HOST_PORT = "8085"
private const val BASE_URL = "http://${HOST_IP}:${HOST_PORT}"
//#endregion
//#region nginx url 설정
private const val NGINX_PORT = "1000"
private const val NGINX_BASE_URL = "http://${HOST_IP}:${NGINX_PORT}"
//#endregion
//#endregion
/*
//#region 소방본부 서버용 ip
//#region 일반 백엔드 api 설정
private const val HOST_IP = "210.104.143.125"
private const val HOST_PORT = "8085"
private const val BASE_URL = "http://${HOST_IP}:${HOST_PORT}"
//#endregion
//#region nginx url 설정
private const val NGINX_IP = "210.104.143.125"
private const val NGINX_PORT = "1000"
private const val NGINX_BASE_URL = "http://${NGINX_IP}:${NGINX_PORT}"
//#endregion
//http://210.104.143.125:8085/
//#endregion
*/
private const val BASE_URL_HTTP = "http://${HOST_IP}:${HOST_PORT}"
private const val HTTP_URL_COOKIE = "http://192.168.0.20:3000"
// 신뢰할 수 없는 인증서를 허용하는 OkHttpClient
private fun getUnsafeOkHttpClient(): OkHttpClient {
return try {
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {}
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> = arrayOf()
})
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
val sslSocketFactory = sslContext.socketFactory
OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true } // 모든 호스트 이름 허용
.addInterceptor(CookieInterCeptor)
.connectTimeout(10, TimeUnit.SECONDS) // 서버 연결 Timeout (기본 10초 → 30초)
.readTimeout(10, TimeUnit.SECONDS) // 서버 응답 Timeout (기본 10초 → 30초)
.writeTimeout(10, TimeUnit.SECONDS) // 서버 요청 Timeout (기본 10초 → 30초)
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
private fun getUnsafeOkHttpClient_http(): OkHttpClient {
return try {
OkHttpClient.Builder()
.addInterceptor(CookieInterCeptor)
.connectTimeout(10, TimeUnit.SECONDS) // 서버 연결 Timeout (기본 10초 → 30초)
.readTimeout(10, TimeUnit.SECONDS) // 서버 응답 Timeout (기본 10초 → 30초)
.writeTimeout(10, TimeUnit.SECONDS) // 서버 요청 Timeout (기본 10초 → 30초)
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
// .client(getUnsafeOkHttpClient()) // 신뢰할 수 없는 인증서 허용 https
.client(getUnsafeOkHttpClient_http())
.addConverterFactory(GsonConverterFactory.create()) // Gson Converter 추가
.build()
}
private val retrofit_NGINX: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(NGINX_BASE_URL)
.client(getUnsafeOkHttpClient()) // 신뢰할 수 없는 인증서 허용
.addConverterFactory(GsonConverterFactory.create()) // Gson Converter 추가
.build()
}
private val retrofit_HTTP: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL_HTTP)
.client(getUnsafeOkHttpClient()) // 신뢰할 수 없는 인증서 허용
.addConverterFactory(GsonConverterFactory.create()) // Gson Converter 추가
.build()
}
private val retrofit_HTTP_TEST: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL_HTTP_TEST)
.client(getUnsafeOkHttpClient()) // 신뢰할 수 없는 인증서 허용
.addConverterFactory(GsonConverterFactory.create()) // Gson Converter 추가
.build()
}
//cookie 테스트용 retrofit
private val retrofit_COOKIE: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(HTTP_URL_COOKIE)
.client(getUnsafeOkHttpClient()) // 신뢰할 수 없는 인증서 허용
.addConverterFactory(GsonConverterFactory.create()) // Gson Converter 추가
.build()
}
val locInstance: apiLocation by lazy {
retrofit.create(apiLocation::class.java)
}
val loginInstance: apiLogin by lazy {
retrofit.create(apiLogin::class.java)
// retrofit_HTTP.create(apiLogin::class.java)
// retrofit_HTTP_TEST.create(apiLogin::class.java)
}
val transferInstance: apiTransfer by lazy {
retrofit.create(apiTransfer::class.java)
// retrofit_HTTP.create(apiTransfer::class.java)
// retrofit_HTTP_TEST.create(apiTransfer::class.java)
}
val pathInstance: apiFindPath by lazy {
retrofit_NGINX.create(apiFindPath::class.java)
}
// val cookieInstance: apiTransfer by lazy {
// retrofit_COOKIE.create(apiTransfer::class.java)
// }
val hospitalInstance:apiHospital by lazy {
retrofit.create(apiHospital::class.java)
}
fun parseJsonFromBuffer(buffer: ByteArray): JsonObject {
val jsonString = buffer.toString(Charsets.UTF_8)
val jsonElement = JsonParser.parseString(jsonString)
return jsonElement.asJsonObject
}
}

View File

@ -0,0 +1,24 @@
package com.example.smart119.viewModel
import android.security.identity.ResultData
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class TransferViewModel: ViewModel() {
private val _someLiveData = MutableLiveData<ResultData>()
val someLiveData:LiveData<ResultData> = _someLiveData
fun requestData() {
viewModelScope.launch {
// try {
// val result = TransferRepository.getData()
// _someLiveData.value = result
// } catch (e: Exception) {
// // 에러 처리
// }
}
}
}

View File

@ -0,0 +1,690 @@
package com.example.smart119.views
import android.graphics.Typeface
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.NavHostFragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.smart119.API_Helper
import com.example.smart119.AlertDialogUtil
import com.example.smart119.ListItem
import com.example.smart119.LoginManager
import com.example.smart119.MedicalInfo
import com.example.smart119.R
import com.example.smart119.SpreadsheetAdapter
import com.example.smart119.TransferManager
import com.example.smart119.TransferState
import com.example.smart119.datas.HospitalSearch
import com.example.smart119.datas.SaveLogin
import com.example.smart119.interfaces.RetrofitClient
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.textfield.TextInputEditText
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.*
class AddressSearchFragment : Fragment() {
private lateinit var asAdapter: SpreadsheetAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.address_search_page, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
FragmentHandler.registerView("AddressSearchFragment", view)
hospitalInit(view);
HospitalSearch.clearAllData()
//#region 광역시/도 콤보박스 설정
val siItems = mutableListOf(HospitalSearch.GET_SIDO_DEFAULT_NAME())
val siSpinner: Spinner = view.findViewById(R.id.siSelectSpinner)
val arrAdapterSi =
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, siItems)
siSpinner.adapter = arrAdapterSi;
//#endregion
// //#region 시/군/구 콤보박스 설정
// val gunGuItems = HospitalSearch.getGunGuNameList()
// val guGunSpinner: Spinner = view.findViewById(R.id.guGunSelectSpinner)
// val arrAdapterGuGun = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, gunGuItems)
// guGunSpinner.adapter = arrAdapterGuGun;
// //#endregion
val searchButtonAS = view.findViewById<Button>(R.id.hospSearchButtonAS)
searchButtonAS.setOnClickListener {
address_hospital_search(view);
}
val gugunSelectorTV = view.findViewById<TextView>(R.id.gunGuSelectText)
gugunSelectorTV.setOnClickListener {
GunGuSelector_Dialog.show(requireContext())
}
val hospCatTV = view.findViewById<TextView>(R.id.hospCatText)
hospCatTV.setOnClickListener {
HospitalCategoryAddress_Dialog.show(requireContext())
}
val prtTV = view.findViewById<TextView>(R.id.prtText)
prtTV.setOnClickListener {
DepartmentAddress_Dialog.show(requireContext())
}
val prevASButton = view.findViewById<Button>(R.id.pagePrevAS)
prevASButton.setOnClickListener {
if (HospitalSearch.getAllData().size > 0) {
var bChanged = PaginationHelper.prevPage(requireView(), { page ->
page_onclick_listener(page)
})
//request page
if (bChanged) {
var currPage = PaginationHelper.currentPage;
// println("prev request number ${PaginationHelper.currentPage}")
page_onclick_listener(currPage)
}
// else println("no request prev")
}
}
val nextASButton = view.findViewById<Button>(R.id.pageNextAS)
nextASButton.setOnClickListener {
if (HospitalSearch.getAllData().size > 0) {
var bChanged = PaginationHelper.nextPage(requireView(), { page ->
page_onclick_listener(page)
})
//request page
if (bChanged) {
var currPage = PaginationHelper.currentPage;
// println("next request number ${PaginationHelper.currentPage}")
page_onclick_listener(currPage)
}
// else println("no request next")
}
}
GunGuSelector_Dialog.registerCallback { evtString ->
println("군구 선택 이벤트!! ${evtString}")
address_hospital_search(view);
}
HospitalCategoryAddress_Dialog.registerCallback { evtString ->
println("병원분류명 선택 이벤트!! ${evtString}")
}
DepartmentAddress_Dialog.registerCallback { evtString ->
println("진료과목 선택 이벤트!! ${evtString}")
}
}
private fun address_hospital_search(view: View) {
asAdapter.clearHospitalList();
HospitalSearch.clearAllData();
var filter_si = "서울특별시";//searchObject["si"].asString;
var filter_gu = "";//searchObject["gu"].asString;
var filter_relate = "";//searchObject["relate"].asString;
var sPage = "1";
var sRow = "4";
var filter_qd = ""; // 병원분류코드
var filter_qz = ""; // 진료과목코드
val siSpin = view.findViewById<Spinner>(R.id.siSelectSpinner);
val gunSpin = view.findViewById<Spinner>(R.id.guGunSelectSpinner);
val editText = view.findViewById<TextInputEditText>(R.id.relateInput)
filter_si = siSpin.selectedItem.toString()
filter_gu =
view.findViewById<TextView>(R.id.gunGuSelectText).text.toString()//HospitalSearch.get_selectGunGu()//gunSpin.selectedItem.toString()
filter_qz =
view.findViewById<TextView>(R.id.hospCatText).text.toString()//HospitalSearch.get_selectHospCatAddress()
filter_qd =
view.findViewById<TextView>(R.id.prtText).text.toString()//HospitalSearch.get_selectPrtAddress()
filter_relate = editText.text.toString()
//#region 항목별 코드 변환
filter_qz = MedicalInfo.get_addressQZ_code(filter_qz).toString()
filter_qd = MedicalInfo.get_prt_code(filter_qd).toString()
//#endregion
if (filter_gu == "null" || filter_gu == "군구선택") {
filter_gu = ""
}
if (filter_qz == "null" || filter_qz == "병원분류명선택") {
filter_qz = ""
}
if (filter_qd == "null" || filter_qd == "진료과목선택") {
filter_qd = ""
}
if (filter_relate == "null") {
filter_relate = ""
}
println("${filter_si}, ${filter_gu}, ${filter_qz}, ${filter_qd}, ${filter_relate}")
var reqBody: JsonObject = API_Helper.make_addressHosp_param(
filter_si, filter_gu, filter_qd, filter_qz, filter_relate,
sPage, sRow
);
try {
LoadingDialog.showDlg(requireContext())
HospitalSearch.SET_SearchRequest(true, reqBody)
RetrofitClient.locInstance.getAddressHospitalList(reqBody)
.enqueue(getAddressResponse())
} catch (e: Exception) {
println(e.message)
LoadingDialog.hideDlg()
}
}
override fun onResume() {
super.onResume()
// Toast.makeText(requireContext(), "위치기반 탭 활성화!", Toast.LENGTH_SHORT).show()
// println("주소기반 탭 활성화!")
//#region 서남병원으로 요청 고정
try {
// RetrofitClient.transferInstance.currentAction().enqueue(currentActionResponse())
LoadingDialog.showDlg(requireContext())
var reqBody: JsonObject = API_Helper.make_addressHosp_param(
"서울특별시", "", "", "", "서남병원",
"1", "4"
);
RetrofitClient.locInstance.getAddressHospitalList(reqBody)
.enqueue(getAddressResponse())
} catch (e: Exception) {
println(e.message)
LoadingDialog.hideDlg()
}
//#endregion
}
override fun onPause() {
super.onPause()
// println("주소기반 탭 비 활성화!")
asAdapter.clearHospitalList()
HospitalSearch.clearAllData()
PaginationHelper.setupPaginationLayout(
requireView(),
0, 0, { page ->
page_onclick_listener(page)
}
)
}
private fun getAddressResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
val resBody = response.body();
if (resBody != null) {
set_hospital_list(resBody)
} else {
nav_to_login();
}
} else {
nav_to_login();
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
println(t.message)
nav_to_login();
}
}
}
private fun hospitalInit(view: View) {
var items = listOf(
ListItem.Header(listOf("병원이름")), // 헤더 열 정의
ListItem.Header(listOf("주소")),
ListItem.Header(listOf("전화번호")),
ListItem.Header(listOf("라이다 혼잡도")),
ListItem.Header(listOf("거리")),
ListItem.Header(listOf("병원별 이송 시작"))
)
val recyclerViewLS: RecyclerView = view.findViewById(R.id.reCyclerViewLS)
val spanCount = 6;
asAdapter = SpreadsheetAdapter(
items,
onButtonClick = { row, pos ->
//row.values[0]
asAdapter.setSelectedHospitalPos(pos)
HospitalSearch.setIdx(asAdapter.getSelectedPosition())
var selectedHospital = asAdapter.getSelectedHospital()
var getSelData = HospitalSearch.getSelectedData()
if (selectedHospital is ListItem.Row) {
var hospitalName = selectedHospital.values[0];
var hospitalAddress = selectedHospital.values[1];
var hospitalCallNumber = selectedHospital.values[2];
var hospitalLiDAR = selectedHospital.values[3];
var hospitalDistance = selectedHospital.values[4];
if (TransferManager.getState() == TransferState.STARTED) {
//이송 변경
//변경된 병원 정보 설정
val spannableMessage = SpannableStringBuilder().apply {
val preText = "병원 이름 : ${hospitalName}" + "\n" +
"주소 : ${hospitalAddress}" + "\n" +
"전화번호 : ${hospitalCallNumber}" + "\n" +
"라이다혼잡도 : ${hospitalLiDAR}" + "\n"
append(preText)
val boldText = "확인"
append(boldText)
setSpan(
StyleSpan(Typeface.BOLD), preText.length,
preText.length + boldText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
append(" 선택시 환자정보가 전송됩니다.(변경 시 기존 이송 병원이 변경됩니다.)\n")
}
AlertDialogUtil.showAlert_CUSTOM(
context = requireActivity(),
title = "이송 병원 정보",
sppStr = spannableMessage,
onPositiveClick = {
AlertDialogUtil.showAlert_OK(
context = requireActivity(),
title = "진행 전 확인사항",
message = "진행하시기 전에, 구급활동일지 임시저장을 하셨는지 확인해 주세요\n",
onPositiveClick = {
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospTransfer;
TransferManager.changeTransfer()
}
)
},
onNegativeClick = {}
)
} else {
val spannableMessage = SpannableStringBuilder().apply {
val preText = "병원 이름 : ${hospitalName}" + "\n" +
"주소 : ${hospitalAddress}" + "\n" +
"전화번호 : ${hospitalCallNumber}" + "\n" +
"라이다혼잡도 : ${hospitalLiDAR}" + "\n"
append(preText)
val boldText = "확인"
append(boldText)
setSpan(
StyleSpan(Typeface.BOLD), preText.length,
preText.length + boldText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
append(" 선택시 환자정보가 전송됩니다.\n")
}
AlertDialogUtil.showAlert_CUSTOM(
context = requireActivity(),
title = "이송 병원 정보",
sppStr = spannableMessage,
onPositiveClick = {
AlertDialogUtil.showAlert_OK(
context = requireActivity(),
title = "진행 전 확인사항",
message = "진행하시기 전에, 구급활동일지 임시저장을 하셨는지 확인해 주세요\n",
onPositiveClick = {
if (TransferManager.startTransfer()) {
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospTransfer;
} else {
// 시작되지 않았습니다.
Toast.makeText(
requireActivity(), "이송이 시작되지 않았습니다. 다시 전송해주세요.",
Toast.LENGTH_SHORT
).show()
}
}
)
},
onNegativeClick = {}
)
}
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "선택된 병원 정보",
message = "병원 검색 후 선택해주세요!"
)
}
//pos, row(ListItem상 선택 위치, 선택한 row 정보) -> hpid는?
},
onRowClick = { row ->
// Toast.makeText(requireActivity(), "${row.values[0]}이 선택되었습니다.", Toast.LENGTH_SHORT)
// .show()
}
)
asAdapter.setColumnCount(spanCount)
recyclerViewLS.adapter = asAdapter
// GridLayoutManager 설정
val layoutManager = GridLayoutManager(requireActivity(), spanCount)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
// return if (adapter.getItemViewType(position) == SpreadsheetAdapter.VIEW_TYPE_HEADER) 3 else 1
return if (asAdapter.getItemViewType(position) == SpreadsheetAdapter.VIEW_TYPE_ROW) {
spanCount // Row가 전체 열을 차지하도록 설정
} else {
1 // 기본적으로 각 항목은 한 열을 차지
}
}
}
recyclerViewLS.layoutManager = layoutManager
}
fun set_hospital_list(resobj: JsonObject?) {
var totPages = resobj?.get("totalPages") // 총 페이지
var totRows = resobj?.get("totalRows") // 총 행 개수
var searchRows = resobj?.get("row") // 검색 페이지 행 개수
var searchPage = resobj?.get("page") // 검색 페이지 번호
var resData = resobj?.getAsJsonArray("resData")
var dataSize = resData?.size() ?: 0;
var hospitalSearchRes: List<List<String>> = mutableListOf()
var hpidList: MutableList<String> = mutableListOf()
if (dataSize > 0) {
// region 검색 결과 리스트에 세팅
try {
resData?.forEach { item ->
val itemObj = item.asJsonObject;
var levelState =
API_Helper.lidar_levelState(itemObj.get("levelState").asInt)
var itemHpid = itemObj.get("hpid").toString().trim('"');
hpidList.add(itemHpid)
var itemDistance = ""
var itemTel = ""
var itemTitle = ""
itemTel = itemObj.get("dutyTel1").toString().trim('"')
itemTitle = itemObj.get("dutyName").toString().trim('"')
var itemLon = itemObj.get("wgs84Lon").toString().trim('"');
var itemLat = itemObj.get("wgs84Lat").toString().trim('"');
HospitalSearch.addDataHospitalItems(
listOf(
HospitalSearch.itemStructure(
itemHpid, levelState, itemDistance,
itemObj.get("city").toString().trim('"'),
itemObj.get("district").toString().trim('"'),
itemTel, itemTitle, itemLon, itemLat
)
)
)
}
var total_Page = totPages?.asInt;
if (total_Page == null) total_Page = 0;
var total_Row = totRows?.asInt;
if (total_Row == null) total_Row = 0;
PaginationHelper.setupPaginationLayout(
requireView(),
total_Page,
total_Row,
{ page ->
page_onclick_listener(page)
})
} catch (e: Exception) {
Toast.makeText(requireActivity(), "${e.message}", Toast.LENGTH_LONG)
.show()
} finally {
}
hospitalSearchRes = HospitalSearch.getAllData();
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "병원이 검색되지 않았습니다. 다시 시도해주세요."
)
if (!PaginationHelper.pageRequest) PaginationHelper.setupPaginationLayout(
requireView(),
0, 0, { page ->
page_onclick_listener(page)
}
)
}
//병원 검색 결과 설정
if (hospitalSearchRes.size > 0) asAdapter.hospitalListUpdate(hospitalSearchRes, hpidList)
// endregion
}
private fun page_onclick_listener(page: Int) {
// println("clicked page ${page}")
var reqBody = HospitalSearch.GET_SearchRequest()
if (reqBody != null) {
try {
// if (HospitalSearch.isAddressSearch()) {
LoadingDialog.showDlg(requireContext())
reqBody = reqBody.getAsJsonObject("reqBody")
reqBody.addProperty("page", "${page}")
RetrofitClient.locInstance.getAddressHospitalList(reqBody).enqueue(
getAddress_Paging_Response()
)
// } else {
// LoadingDialog.showDlg(requireContext())
// reqBody = reqBody.getAsJsonObject("reqBody")
// reqBody.addProperty("page", "${page}")
// RetrofitClient.locInstance.getAroundHospitalList(reqBody)
// .enqueue(arroundHospital_Paging_Response())
// }
} catch (e: Exception) {
println(e.message)
LoadingDialog.hideDlg()
} finally {
// LoadingDialog.hideDlg()
}
}
}
private fun getAddress_Paging_Response(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
val resBody = response.body();
if (resBody != null) {
set_hospital_list_page(resBody)
} else {
nav_to_login();
}
} else {
nav_to_login();
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
println(t.message)
nav_to_login();
}
}
}
fun set_hospital_list_page(resobj: JsonObject?) {
// region 현재 병원 리스트 초기화
asAdapter.clearHospitalList();
HospitalSearch.clearAllData();
// endregion
var totPages = resobj?.get("totalPages") // 총 페이지
var totRows = resobj?.get("totalRows") // 총 행 개수
var searchRows = resobj?.get("row") // 검색 페이지 행 개수
var searchPage = resobj?.get("page") // 검색 페이지 번호
var resData = resobj?.getAsJsonArray("resData")
var dataSize = resData?.size() ?: 0;
var hospitalSearchRes: List<List<String>> = mutableListOf()
var hpidList: MutableList<String> = mutableListOf()
if (dataSize > 0) {
// region 검색 결과 리스트에 세팅
try {
resData?.forEach { item ->
val itemObj = item.asJsonObject;
var levelState =
API_Helper.lidar_levelState(itemObj.get("levelState").asInt)
var itemHpid = itemObj.get("hpid").toString().trim('"');
hpidList.add(itemHpid)
var itemDistance = ""
var itemTel = ""
var itemTitle = ""
///주소 검색 시 사용하는 곳
itemTel = itemObj.get("dutyTel1").toString().trim('"')
itemTitle = itemObj.get("dutyName").toString().trim('"')
var itemLon = itemObj.get("wgs84Lon").toString().trim('"');
var itemLat = itemObj.get("wgs84Lat").toString().trim('"');
HospitalSearch.addDataHospitalItems(
listOf(
HospitalSearch.itemStructure(
itemHpid, levelState, itemDistance,
itemObj.get("city").toString().trim('"'),
itemObj.get("district").toString().trim('"'),
itemTel, itemTitle, itemLon, itemLat
)
)
)
}
var total_Page = totPages?.asInt;
if (total_Page == null) total_Page = 0;
var total_Row = totRows?.asInt;
if (total_Row == null) total_Row = 0;
// PaginationHelper.rePaginationLayout(requireView(), { page ->
// page_onclick_listener(page)
// })
// PaginationHelper.setupPaginationLayout(requireView(), total_Page, total_Row, { page ->
// page_onclick_listener(page)
// })
} catch (e: Exception) {
Toast.makeText(requireActivity(), "${e.message}", Toast.LENGTH_LONG)
.show()
} finally {
}
hospitalSearchRes = HospitalSearch.getAllData();
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "병원이 검색되지 않았습니다. 다시 시도해주세요."
)
if (!PaginationHelper.pageRequest) PaginationHelper.setupPaginationLayout(
requireView(),
0, 0, { page ->
page_onclick_listener(page)
}
)
}
//병원 검색 결과 설정
if (hospitalSearchRes.size > 0) asAdapter.hospitalListUpdate(hospitalSearchRes, hpidList)
// endregion
}
// fun req_gun_list(si:String) {
// RetrofitClient.locInstance.getSiGunGuList(JsonObject().apply {
// addProperty("code", get_si_code(si) ?: "")
// }).enqueue(siGunGuResponse())
// }
fun nav_to_login() {
AlertDialogUtil.showAlert_OK(
context = requireActivity(),
title = "세션 만료",
message = "기존 세션이 만료되었습니다. 다시 로그인 해주시기 바랍니다.\n",
onPositiveClick = {
SaveLogin.setLogin(false)
LoginManager.setLoggedIn(false);
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.emgLogin;
}
)
}
fun get_default_seonam(): JsonObject {
var seonam: JsonObject = JsonObject().apply {
addProperty("userId", "")
addProperty("carNo", "")
addProperty("ver", "")
}
return seonam;
}
override fun onDestroyView() {
super.onDestroyView()
FragmentHandler.unregisterView("AddressSearchFragment")
}
private fun currentActionResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
try {
val resBody = response.body();
val status = resBody?.get("status")?.asString;
if (status == "true") {
TransferManager.serverState.set(true);
AlertDialogUtil.showAlert(
context = requireContext(),
title = "진행 중인 전송 정보",
message = "현재 접속 정보로 진행중인 이송 건이 있습니다. 취소하시겠습니까?",
onPositiveClick = {
TransferManager.cancelTransfer()
},
onNegativeClick = {}
)
//시작된 상황
}
else {
println();
TransferManager.serverState.set(false);
//시작 아닌 상황
}
} catch (e: Exception) {
println(e.message)
}
} else {
println();
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
println();
}
}
}
}

View File

@ -0,0 +1,199 @@
package com.example.smart119.views
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.GridView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import com.example.smart119.MedicalInfo
import com.example.smart119.R
import com.example.smart119.datas.HospitalSearch
object DepartmentAddress_Dialog {
private var dialog: Dialog? = null
private var selectCallback: ((String) -> Unit)? = null
fun show(context: Context) {
dialog?.dismiss() // 기존 다이얼로그가 있으면 닫기 (중복 방지)
dialog = Dialog(context)
val dialogView = LayoutInflater.from(context).inflate(R.layout.department_address, null)
dialog?.setContentView(dialogView)
dialog?.window?.setLayout(1000, 800) // width, height (픽셀 단위)
// val gridView: GridView = dialogView.findViewById(R.id.gridViewOptions)
// val closeButton: Button = dialogView.findViewById(R.id.dialogCloseButton)
// 그리드에 표시할 데이터
// var options = MedicalInfo.get_prt_list()
// options.add(0, "진료과목선택")
val itemClickListener: (String) -> Unit = { item ->
HospitalSearch.set_selectPrtAddress(item)
var addrView = FragmentHandler.getView("AddressSearchFragment")
if (addrView != null) {
val prtTV = addrView.findViewById<TextView>(R.id.prtText)
prtTV.text = item;
}
Toast.makeText(context, "선택된 항목: $item", Toast.LENGTH_SHORT).show()
triggerEvent(item)
}
val getGroups = MedicalInfo.get_prt_group_map();
if (getGroups.size > 0) {
getGroups.forEach { key, value ->
val group_name = key;
val group_list = value;
addGroup(
dialogView, context,
group_name, group_list,
itemClickListener
)
}
}
// val adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, options)
// gridView.adapter = adapter
//
// // 아이템 클릭 이벤트 (선택 시 다이얼로그 닫기)
// gridView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
// try {
// val selectedOption = options[position]
// HospitalSearch.set_selectPrtAddress(selectedOption)
// var addrView = FragmentHandler.getView("AddressSearchFragment")
// if (addrView != null) {
// val prtTV = addrView.findViewById<TextView>(R.id.prtText)
// prtTV.text = selectedOption;
// }
// Toast.makeText(context, "선택된 항목: $selectedOption", Toast.LENGTH_SHORT).show()
//
// triggerEvent(selectedOption)
// } catch (e: Exception) {
// println(e.message)
// }
// dismiss() // 아이템 선택 즉시 다이얼로그 닫기
// }
dialog?.setOnCancelListener {
//진료과 선택
var addrView = FragmentHandler.getView("AddressSearchFragment")
if (addrView != null) {
val prtTV = addrView.findViewById<TextView>(R.id.prtText)
prtTV.text = "진료과목선택";
}
}
dialog?.show()
}
private fun addGroup(
view: View, context: Context, groupName: String, items: List<String>, callback: (String) -> Unit
) {
val container = view.findViewById<LinearLayout>(R.id.departGroupContainer)
// 그룹 레이아웃 생성 (LinearLayout)
val groupLayout = LinearLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
orientation = LinearLayout.VERTICAL
setPadding(0, 4, 0, 4)
}
// 그룹명 (TextView)
val textView = TextView(context).apply {
text = groupName
textSize = 25f
setTypeface(null, Typeface.BOLD)
setPadding(0, 0, 0, 4)
}
// 구분선 (View)
val divider = View(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
2 // 두께
)
setBackgroundColor(Color.GRAY)
}
// GridView 생성
val gridView = GridView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
stretchMode = GridView.STRETCH_COLUMN_WIDTH
gravity = Gravity.CENTER
this.numColumns = GridView.AUTO_FIT
verticalSpacing = 8
horizontalSpacing = 8
}
// GridView 어댑터 설정
// val adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, items)
val adapter = object : ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, items) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent) as TextView
view.textSize = 20f // 텍스트 크기 조정
return view
}
}
gridView.adapter = adapter
gridView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
val selectedItem = parent.getItemAtPosition(position) as String
callback(selectedItem)
dismiss()
}
// GridView 높이 자동 조절 함수
fun setGridViewHeight(gridView: GridView) {
val listAdapter = gridView.adapter ?: return
val totalItems = listAdapter.count
val numColumns = gridView.numColumns
val rows = Math.ceil(totalItems / numColumns.toDouble()).toInt()
val itemHeight = 80 // px 단위 (아이템 하나의 높이)
val params = gridView.layoutParams
params.height = rows * itemHeight
gridView.layoutParams = params
}
// 그룹 레이아웃에 추가
groupLayout.addView(textView)
groupLayout.addView(divider)
groupLayout.addView(gridView)
container.addView(groupLayout)
// GridView 높이 조정
gridView.post { setGridViewHeight(gridView) }
}
fun registerCallback(callback: (String) -> Unit) {
this.selectCallback = callback
}
fun triggerEvent(data: String) {
selectCallback?.invoke(data)
}
fun dismiss() {
dialog?.dismiss()
dialog = null
}
}

View File

@ -0,0 +1,169 @@
package com.example.smart119.views
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.GridView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import com.example.smart119.MedicalInfo
import com.example.smart119.R
import com.example.smart119.datas.HospitalSearch
object DepartmentLocation_Dialog {
private var dialog: Dialog? = null
private var selectCallback: ((String) -> Unit)? = null
fun show(context: Context) {
dialog?.dismiss() // 기존 다이얼로그가 있으면 닫기 (중복 방지)
dialog = Dialog(context)
val dialogView = LayoutInflater.from(context).inflate(R.layout.department_address, null)
dialog?.setContentView(dialogView)
dialog?.window?.setLayout(1000, 800) // width, height (픽셀 단위)
val itemClickListener: (String) -> Unit = { item ->
HospitalSearch.set_selectPrtAddress(item)
var locView = FragmentHandler.getView("LocationSearchFragment")
if (locView != null) {
val prtTV = locView.findViewById<TextView>(R.id.hospPrtLocText)
prtTV.text = item;
}
Toast.makeText(context, "선택된 항목: $item", Toast.LENGTH_SHORT).show()
triggerEvent(item)
dismiss()
}
val getGroups = MedicalInfo.get_prt_group_map();
if (getGroups.size > 0) {
getGroups.forEach { key, value ->
val group_name = key;
val group_list = value;
addGroup(
dialogView, context,
group_name, group_list,
itemClickListener
)
}
}
dialog?.setOnCancelListener {
//진료과 선택
var locView = FragmentHandler.getView("LocationSearchFragment")
if (locView != null) {
val prtTV = locView.findViewById<TextView>(R.id.hospPrtLocText)
prtTV.text = "진료과 선택";
}
}
dialog?.show()
}
private fun addGroup(
view: View, context: Context, groupName: String, items: List<String>, callback: (String) -> Unit
) {
val container = view.findViewById<LinearLayout>(R.id.departGroupContainer)
// 그룹 레이아웃 생성 (LinearLayout)
val groupLayout = LinearLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
orientation = LinearLayout.VERTICAL
setPadding(0, 4, 0, 4)
}
// 그룹명 (TextView)
val textView = TextView(context).apply {
text = groupName
textSize = 25f
setTypeface(null, Typeface.BOLD)
setPadding(0, 0, 0, 4)
}
// 구분선 (View)
val divider = View(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
2 // 두께
)
setBackgroundColor(Color.GRAY)
}
// GridView 생성
val gridView = GridView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
stretchMode = GridView.STRETCH_COLUMN_WIDTH
gravity = Gravity.CENTER
this.numColumns = GridView.AUTO_FIT
verticalSpacing = 8
horizontalSpacing = 8
}
// GridView 어댑터 설정
// val adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, items)
val adapter = object : ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, items) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent) as TextView
view.textSize = 20f // 텍스트 크기 조정
return view
}
}
gridView.adapter = adapter
gridView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
val selectedItem = parent.getItemAtPosition(position) as String
callback(selectedItem)
DepartmentAddress_Dialog.dismiss()
}
// GridView 높이 자동 조절 함수
fun setGridViewHeight(gridView: GridView) {
val listAdapter = gridView.adapter ?: return
val totalItems = listAdapter.count
val numColumns = gridView.numColumns
val rows = Math.ceil(totalItems / numColumns.toDouble()).toInt()
val itemHeight = 80 // px 단위 (아이템 하나의 높이)
val params = gridView.layoutParams
params.height = rows * itemHeight
gridView.layoutParams = params
}
// 그룹 레이아웃에 추가
groupLayout.addView(textView)
groupLayout.addView(divider)
groupLayout.addView(gridView)
container.addView(groupLayout)
// GridView 높이 조정
gridView.post { setGridViewHeight(gridView) }
}
fun registerCallback(callback: (String) -> Unit) {
this.selectCallback = callback
}
fun triggerEvent(data: String) {
selectCallback?.invoke(data)
}
fun dismiss() {
dialog?.dismiss()
dialog = null
}
}

View File

@ -0,0 +1,24 @@
package com.example.smart119.views
import android.view.View
object FragmentHandler {
private val fragmentViews = mutableMapOf<String, View?>()
// View 등록
fun registerView(tag: String, view: View?) {
fragmentViews[tag] = view
}
// View 가져오기
fun getView(tag: String): View? {
return fragmentViews[tag]
}
// View 제거
fun unregisterView(tag: String) {
fragmentViews.remove(tag)
}
}

View File

@ -0,0 +1,94 @@
package com.example.smart119.views
import android.app.Dialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.Button
import android.widget.GridView
import android.widget.TextView
import android.widget.Toast
import com.example.smart119.R
import com.example.smart119.datas.HospitalSearch
object GunGuSelector_Dialog {
private var dialog: Dialog? = null
private var selectCallback: ((String) -> Unit)? = null
fun show(context: Context) {
dialog?.dismiss() // 기존 다이얼로그가 있으면 닫기 (중복 방지)
dialog = Dialog(context)
val dialogView = LayoutInflater.from(context).inflate(R.layout.gungu_selector, null)
dialog?.setContentView(dialogView)
val gridView: GridView = dialogView.findViewById(R.id.gridViewOptions)
// val closeButton: Button = dialogView.findViewById(R.id.dialogCloseButton)
// 그리드에 표시할 데이터
var options = HospitalSearch.getGunGuNameList()
options.add(0, "군구선택")
/*
listOf(
"군구선택", "강남구", "강동구", "강북구", "강서구",
"관악구", "광진구", "구로구", "금천구",
"노원구", "도봉구", "동대문구", "동작구",
"마포구", "서대문구", "서초구", "성동구",
"성북구", "송파구", "양천구", "영등포구",
"용산구", "은평구", "종로구", "중구", "중랑구"
)
*/
val adapter = object : ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, options) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent) as TextView
view.textSize = 20f // 텍스트 크기 조정
return view
}
}
gridView.adapter = adapter
// 아이템 클릭 이벤트 (선택 시 다이얼로그 닫기)
gridView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
try {
val selectedOption = options[position]
HospitalSearch.set_selectGunGu(selectedOption)
var addrView = FragmentHandler.getView("AddressSearchFragment")
if (addrView != null) {
val gugunSelectorTV = addrView.findViewById<TextView>(R.id.gunGuSelectText)
gugunSelectorTV.text = selectedOption;
}
Toast.makeText(context, "선택된 항목: $selectedOption", Toast.LENGTH_SHORT).show()
triggerEvent(selectedOption)
} catch (e: Exception) {
println(e.message)
}
dismiss() // 아이템 선택 즉시 다이얼로그 닫기
}
// 닫기 버튼 클릭 이벤트
// closeButton.setOnClickListener {
// dismiss()
// }
dialog?.show()
}
fun registerCallback(callback: (String) -> Unit) {
this.selectCallback = callback
}
fun triggerEvent(data: String) {
selectCallback?.invoke(data)
}
fun dismiss() {
dialog?.dismiss()
dialog = null
}
}

View File

@ -0,0 +1,82 @@
package com.example.smart119.views
import android.app.Dialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.GridView
import android.widget.TextView
import android.widget.Toast
import com.example.smart119.MedicalInfo
import com.example.smart119.R
import com.example.smart119.datas.HospitalSearch
object HospitalCategoryAddress_Dialog {
private var dialog: Dialog? = null
private var selectCallback: ((String) -> Unit)? = null
fun show(context: Context) {
dialog?.dismiss() // 기존 다이얼로그가 있으면 닫기 (중복 방지)
dialog = Dialog(context)
val dialogView = LayoutInflater.from(context).inflate(R.layout.hospital_category_address, null)
dialog?.setContentView(dialogView)
val gridView: GridView = dialogView.findViewById(R.id.gridViewOptions)
// val closeButton: Button = dialogView.findViewById(R.id.dialogCloseButton)
// 그리드에 표시할 데이터
var options = MedicalInfo.get_addressQZ_list()
options.add(0, "병원분류명선택")
val adapter = object : ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, options) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent) as TextView
view.textSize = 20f // 텍스트 크기 조정
return view
}
}
gridView.adapter = adapter
// 아이템 클릭 이벤트 (선택 시 다이얼로그 닫기)
gridView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
try {
val selectedOption = options[position]
HospitalSearch.set_selectHospCatAddress(selectedOption)
var addrView = FragmentHandler.getView("AddressSearchFragment")
if (addrView != null) {
val hospCatTV = addrView.findViewById<TextView>(R.id.hospCatText)
hospCatTV.text = selectedOption;
}
Toast.makeText(context, "선택된 항목: $selectedOption", Toast.LENGTH_SHORT).show()
triggerEvent(selectedOption)
} catch (e: Exception) {
println(e.message)
}
dismiss() // 아이템 선택 즉시 다이얼로그 닫기
}
// 닫기 버튼 클릭 이벤트
// closeButton.setOnClickListener {
// dismiss()
// }
dialog?.show()
}
fun registerCallback(callback: (String) -> Unit) {
this.selectCallback = callback
}
fun triggerEvent(data: String) {
selectCallback?.invoke(data)
}
fun dismiss() {
dialog?.dismiss()
dialog = null
}
}

View File

@ -0,0 +1,82 @@
package com.example.smart119.views
import android.app.Dialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.GridView
import android.widget.TextView
import android.widget.Toast
import com.example.smart119.MedicalInfo
import com.example.smart119.R
import com.example.smart119.datas.HospitalSearch
object HospitalCategoryLocation_Dialog {
private var dialog: Dialog? = null
private var selectCallback: ((String) -> Unit)? = null
fun show(context: Context) {
dialog?.dismiss() // 기존 다이얼로그가 있으면 닫기 (중복 방지)
dialog = Dialog(context)
val dialogView = LayoutInflater.from(context).inflate(R.layout.hospital_category_location, null)
dialog?.setContentView(dialogView)
val gridView: GridView = dialogView.findViewById(R.id.gridViewOptions)
// val closeButton: Button = dialogView.findViewById(R.id.dialogCloseButton)
// 그리드에 표시할 데이터
var options = MedicalInfo.get_emogemdv_list()
options.add(0, "의료기관 선택")
val adapter = object : ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, options) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent) as TextView
view.textSize = 20f // 텍스트 크기 조정
return view
}
}
gridView.adapter = adapter
// 아이템 클릭 이벤트 (선택 시 다이얼로그 닫기)
gridView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
try {
val selectedOption = options[position]
HospitalSearch.set_selectHospCatAddress(selectedOption)
var addrView = FragmentHandler.getView("LocationSearchFragment")
if (addrView != null) {
val hospCatTV = addrView.findViewById<TextView>(R.id.hospCatLocText)
hospCatTV.text = selectedOption;
}
Toast.makeText(context, "선택된 항목: $selectedOption", Toast.LENGTH_SHORT).show()
triggerEvent(selectedOption)
} catch (e: Exception) {
println(e.message)
}
dismiss() // 아이템 선택 즉시 다이얼로그 닫기
}
// 닫기 버튼 클릭 이벤트
// closeButton.setOnClickListener {
// dismiss()
// }
dialog?.show()
}
fun registerCallback(callback: (String) -> Unit) {
this.selectCallback = callback
}
fun triggerEvent(data: String) {
selectCallback?.invoke(data)
}
fun dismiss() {
dialog?.dismiss()
dialog = null
}
}

View File

@ -0,0 +1,88 @@
package com.example.smart119.views
import android.content.Context
import android.provider.Settings.Global.getString
import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import com.example.smart119.R
import android.view.Gravity
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Toast
import com.example.smart119.datas.HospitalSearch
import com.google.android.material.textfield.TextInputEditText
object HospitalInfoPopup {
private var popupWindow: PopupWindow? = null
fun showPopupTransfer(view: View) {
// 기존 팝업이 존재하면 닫음
dismissPopup()
// PopupView 레이아웃을 인플레이트
val inflater = LayoutInflater.from(view.context)
val popupView = inflater.inflate(R.layout.popup_detail, null)
val displayMetrics = view.resources.displayMetrics
val screenWidth = displayMetrics.widthPixels
val screenHeight = displayMetrics.heightPixels
// PopupWindow 생성
popupWindow = PopupWindow(
popupView,
(screenWidth * 0.4).toInt(), // 너비: 화면의 40%
(screenHeight * 0.6).toInt(), // 높이: 화면의 60%
true // 외부 클릭으로 닫기 가능
).apply {
// 팝업 표시 (뷰 기준 위치)
showAsDropDown(view, 220, -220)
var closeBtn = popupView.findViewById<Button>(R.id.closePopup)
closeBtn.setOnClickListener {
dismissPopup()
}
// 외부 터치 시 닫기
// popupView.setOnTouchListener { _, _ ->
// dismissPopup()
// true
// }
}
}
// 팝업 닫기 함수
fun dismissPopup() {
popupWindow?.dismiss()
popupWindow = null
}
// fun showPopupTransfer(view: View) {
// // PopupView 레이아웃을 인플레이트 (단순 XML 로드)
// val inflater = LayoutInflater.from(view.context)
// val popupView = inflater.inflate(R.layout.popup_detail, null)
//
// val displayMetrics = view.resources.displayMetrics
// val screenWidth = displayMetrics.widthPixels
// val screenHeight = displayMetrics.heightPixels
//
// // PopupWindow 생성
// val popupWindow = PopupWindow(
// popupView,
// (screenWidth * 0.4).toInt(), // 너비: 화면의 40%
// (screenHeight * 0.6).toInt(), // 높이: 화면의 60%
// true // 외부 클릭으로 닫기 가능
// )
//
// // PopupWindow 표시 (뷰 기준 위치)
// popupWindow.showAsDropDown(view, 220, -220) // 기준 뷰 아래에 표시
// }
}

View File

@ -0,0 +1,753 @@
package com.example.smart119.views
import android.content.Context
import android.content.SharedPreferences
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.GridView
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.example.smart119.API_Helper
import com.example.smart119.AlertDialogUtil
import com.example.smart119.ListItem
import com.example.smart119.LocationHelper
import com.example.smart119.LoginManager
import com.example.smart119.MedicalInfo
import com.example.smart119.MyCookieJar
import com.example.smart119.R
import com.example.smart119.SpreadsheetAdapter
import com.example.smart119.ViewPagerAdapter
import com.example.smart119.databinding.FragmentSecondBinding
import com.example.smart119.datas.HospitalSearch
import com.example.smart119.interfaces.RetrofitClient
import com.example.smart119.views.PaginationHelper.pageRequest
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.text.SimpleDateFormat
import java.util.Date
import kotlin.math.log
/**
* A simple [Fragment] subclass as the second destination in the navigation.
*/
class HospitalSearchFragment : Fragment() {
private var _binding: FragmentSecondBinding? = null
private var _listAdapterInit: Boolean? = false
private lateinit var adapter: SpreadsheetAdapter
// private val
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
//창이 보여질때 실행되는 함수
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
FragmentHandler.registerView("HospitalSearchFragment", view)
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// 뒤로 가기 동작 막음
// println("Hospital 뒤로가기클릭!!")
}
})
//#region 탭 레이아웃 설정
val tabLayout: TabLayout = view.findViewById(R.id.tabLayout)
val viewPager: ViewPager2 = view.findViewById(R.id.viewPager)
// ViewPager 어댑터 설정
val vpAdapter = ViewPagerAdapter(requireActivity())
viewPager.adapter = vpAdapter
// TabLayout과 ViewPager 연결
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when (position) {
0 -> "주소 기반 검색"
1 -> "위치 기반 검색"
else -> "주소 기반 검색"
}
}.attach()
//#endregion
//hospitalInit(view);
// HospitalSearch.clearAllData()
if (HospitalSearch.get_gps_address().isEmpty()) {
println("hospital search address updated!")
// Hospital_UIUpdate()
}
else {
// Hospital_UIUpdate_SET()
}
/*
//병원검색 버튼 이벤트
binding.hospSearchButton.setOnClickListener {
var _sel_medical = view.findViewById<Spinner>(R.id.spMedical).selectedItem as String;
var _sel_depart = view.findViewById<Spinner>(R.id.spDepart).selectedItem as String;
var _sel_emg_yn = view.findViewById<Spinner>(R.id.spEmgYN).selectedItem as String;
adapter.clearHospitalList();
HospitalSearch.clearAllData();
if (HospitalSearch.getIsFiltered()) {
var searchObject = HospitalSearch.getSearchInfo()
var filter_si = searchObject["si"].asString;
var filter_gu = searchObject["gu"].asString;
var filter_relate = searchObject["relate"].asString;
var reqBody: JsonObject = API_Helper.make_addressHosp_param(
filter_si, filter_gu, "", filter_relate
);
try {
LoadingDialog.showDlg(requireContext())
HospitalSearch.SET_SearchRequest(true, reqBody)
RetrofitClient.locInstance.getAddressHospitalList(reqBody).enqueue(getAddressResponse())
} catch (e: Exception) {
println(e.message)
} finally {
LoadingDialog.hideDlg()
}
} else {
LocationHelper.getLastKnownLocation({ res ->
//success
val resObj = res.getAsJsonObject();
val callResult = resObj["isSuccess"]?.asBoolean ?: true;
val lon = resObj["lon"]?.asDouble ?: 0.0;
val lat = resObj["lat"]?.asDouble ?: 0.0;
var page = "1"
var row = "4"
var dg_list = emptyList<String>()//listOf("","")
var _code_medical = MedicalInfo.get_emogemdv_code(_sel_medical);
var _code_depart = MedicalInfo.get_prt_code(_sel_depart);
var _emg_yn = _sel_emg_yn;
var _relate = ""
try {
_relate = HospitalSearch.getSearchInfo()["relate"].asString;
} catch (e: Exception) {
println(e.message)
}
if (_code_medical == null) _code_medical = ""
if (_code_depart == null) _code_depart = ""
if (_emg_yn == "응급실 포함여부 선택") _emg_yn = "Y"
// println("의료기관:${_sel_medical}|${_code_medical}, 진료과:${_sel_depart}|${_code_depart}, 응급실:${_emg_yn}")
var reqBody :JsonObject = API_Helper.make_arroundHosp_param2(
lon = lon.toString(),
lat = lat.toString(),
emogemdv = "${_code_medical}",
prtcode = "${_code_depart}",
emergency = "${_emg_yn}",
emogdesc = "${_relate}",
page = "${page}",
row = "${row}"
);
try {
LoadingDialog.showDlg(requireContext())
HospitalSearch.SET_SearchRequest(false, reqBody)
RetrofitClient.locInstance.getAroundHospitalList(reqBody).enqueue(arroundHospitalResponse())
} catch (e: Exception) {
println(e.message)
} finally {
LoadingDialog.hideDlg()
}
}, { res ->
//fail
val resObj = res.getAsJsonObject();
val callResult = resObj["isSuccess"]?.asBoolean ?: false;
val lon = resObj["lon"]?.asDouble ?: 0.0;
val lat = resObj["lat"]?.asDouble ?: 0.0;
println("[FAIL] last location ${lon}, ${lat}");
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "현재 위치를 알수 없어 검색 되지 않았습니다, 다시 시도해주세요."
)
})
}
}
// binding.searchAddressButton.setOnClickListener {
// val inflater = LayoutInflater.from(requireActivity())
// val popupView = inflater.inflate(R.layout.popup_search, null)
//
// var itemSido = HospitalSearch.getSidoList();
// var itemSidoAlpha:MutableList<String> = itemSido.toMutableList()
// itemSidoAlpha.add(0, "시도선택")
//
// var newPopup = HospitalSearchPopup.showPopupHospitalSearch(popupView, requireActivity())
// HospitalSearchPopup.set_POPUP_SIDO_COMBO(newPopup, requireActivity(), itemSidoAlpha)
//
// HospitalSearch.set_popup_search(newPopup)
// }
// binding.cbActiveAddresss.setOnCheckedChangeListener { _, isChecked ->
// if (isChecked) {
// HospitalSearch.setFiltered(true)
// } else {
// HospitalSearch.setFiltered(false)
// }
// }
binding.pagePrev.setOnClickListener {
if (HospitalSearch.getAllData().size > 0) {
var bChanged = PaginationHelper.prevPage(requireView(), { page ->
page_onclick_listener(page)
})
//request page
if (bChanged) {
var currPage = PaginationHelper.currentPage;
// println("prev request number ${PaginationHelper.currentPage}")
page_onclick_listener(currPage)
}
// else println("no request prev")
}
}
binding.pageNext.setOnClickListener {
if (HospitalSearch.getAllData().size > 0) {
var bChanged = PaginationHelper.nextPage(requireView(), { page ->
page_onclick_listener(page)
})
//request page
if (bChanged) {
var currPage = PaginationHelper.currentPage;
// println("next request number ${PaginationHelper.currentPage}")
page_onclick_listener(currPage)
}
// else println("no request next")
}
}
binding.gpsRefresh.setOnClickListener {
Hospital_UIUpdate()
}
*/
//HospitalSearch.init_hospital_req(requireActivity())
}
private fun arroundHospitalResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
var resBody = response.body();
set_hospital_list(resBody)
} else {
Toast.makeText(
requireActivity(),
"Error: ${response.code()}",
Toast.LENGTH_SHORT
).show()
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
Toast.makeText(
requireActivity(),
"Request Failed: ${t.message}",
Toast.LENGTH_SHORT
).show()
/*
var hospitalSearchRes: List<List<String>> = mutableListOf()
var hpidList: MutableList<String> = mutableListOf()
HospitalSearch.addDataHospitalItems(
listOf(
HospitalSearch.itemStructure(
"A1100223", API_Helper.lidar_levelState(3), "1.11",
"서울특별시", "양천구", "1566-6688", "서울특별시서남병원"
),// 126.8371889, 37.5124604
HospitalSearch.itemStructure(
"A1100055", API_Helper.lidar_levelState(-1), "2.22",
"서울특별시", "영등포구", "02-829-5114", "한림대학교 강남성심병원"
),
HospitalSearch.itemStructure(
"A1100004", API_Helper.lidar_levelState(-1), "3.33",
"서울특별시", "용산구", "02-709-9114", "순천향대학교 부속 서울병원"
)
//용산 주변 좌표
//lon 126.9984597, lat 37.5287920
)
)
hospitalSearchRes = HospitalSearch.getAllData();
hpidList.add("A1100223")
hpidList.add("A1100055")
hpidList.add("A1100004")
if (hospitalSearchRes.size > 0)
adapter.hospitalListUpdate(hospitalSearchRes, hpidList)
*/
}
}
}
private fun hospitalInit(view: View) {
/*
var items = listOf(
ListItem.Header(listOf("병원이름")), // 헤더 열 정의
ListItem.Header(listOf("주소")),
ListItem.Header(listOf("전화번호")),
ListItem.Header(listOf("라이다 혼잡도")),
ListItem.Header(listOf("거리")),
ListItem.Header(listOf("병원별 이송 시작"))
)
val recyclerView: RecyclerView = view.findViewById(R.id.reCyclerView)
val spanCount = 6;
adapter = SpreadsheetAdapter(
items,
onButtonClick = { row, pos ->
//row.values[0]
adapter.setSelectedHospitalPos(pos)
HospitalSearch.setIdx(adapter.getSelectedPosition())
var selectedHospital = adapter.getSelectedHospital()
var getSelData = HospitalSearch.getSelectedData()
if (selectedHospital is ListItem.Row) {
var hospitalName = selectedHospital.values[0];
var hospitalAddress = selectedHospital.values[1];
var hospitalCallNumber = selectedHospital.values[2];
var hospitalLiDAR = selectedHospital.values[3];
var hospitalDistance = selectedHospital.values[4];
if (TransferManager.getState() == TransferState.STARTED) {
//이송 변경
//변경된 병원 정보 설정
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "변경 이송 병원 정보",
message = "병원 이름 : ${hospitalName}" + "\n" +
"주소 : ${hospitalAddress}" + "\n" +
"전화번호 : ${hospitalCallNumber}" + "\n" +
"거리 : ${hospitalDistance}" + "\n" +
"라이다혼잡도 : ${hospitalLiDAR}" + "\n\n" +
"확인 선택시 진행(변경 시 기존 이송 정보가 변경됩니다.)",
onPositiveClick = {
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospTransfer;
TransferManager.changeTransfer()
},
onNegativeClick = {}
)
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "이송 병원 정보",
message = "병원 이름 : ${hospitalName}" + "\n" +
"주소 : ${hospitalAddress}" + "\n" +
"전화번호 : ${hospitalCallNumber}" + "\n" +
"라이다혼잡도 : ${hospitalLiDAR}" + "\n\n" +
"확인 선택시 진행",
onPositiveClick = {
if (TransferManager.startTransfer()) {
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospTransfer;
} else {
// 시작되지 않았습니다.
Toast.makeText(
requireActivity(), "이송이 시작되지 않았습니다. 다시 전송해주세요.",
Toast.LENGTH_SHORT
).show()
}
},
onNegativeClick = {}
)
}
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "선택된 병원 정보",
message = "병원 검색 후 선택해주세요!"
)
}
//이송시작
println(row)
//pos, row(ListItem상 선택 위치, 선택한 row 정보) -> hpid는?
},
onRowClick = { row ->
// Toast.makeText(requireActivity(), "${row.values[0]}이 선택되었습니다.", Toast.LENGTH_SHORT)
// .show()
}
)
adapter.setColumnCount(spanCount)
recyclerView.adapter = adapter
// GridLayoutManager 설정
val layoutManager = GridLayoutManager(requireActivity(), spanCount)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
// return if (adapter.getItemViewType(position) == SpreadsheetAdapter.VIEW_TYPE_HEADER) 3 else 1
return if (adapter.getItemViewType(position) == SpreadsheetAdapter.VIEW_TYPE_ROW) {
spanCount // Row가 전체 열을 차지하도록 설정
} else {
1 // 기본적으로 각 항목은 한 열을 차지
}
}
}
recyclerView.layoutManager = layoutManager
*/
}
private fun Hospital_UIUpdate_SET() {
var hsfView = FragmentHandler.getView("HospitalSearchFragment")
if (hsfView != null) {
var addrString = HospitalSearch.get_gps_address()
var geoTv = hsfView.findViewById<TextView>(R.id.tvGeoLocation)
geoTv.text = addrString;
}
}
private fun Hospital_UIUpdate() {
}
//길찾기 API 결과
private fun getPathAddressResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
val resObj = response.body();
val metaObj = resObj?.get("meta");
if (metaObj != null) {
val tot_count = metaObj.asJsonObject["total_count"].asInt
if (tot_count > 0) {
var hsfView = FragmentHandler.getView("HospitalSearchFragment")
if (hsfView != null) {
val docus = resObj.getAsJsonArray("documents")
val addObj = docus[0].asJsonObject["address"];
val add_full = addObj.asJsonObject["address_name"]
val add_si = addObj.asJsonObject["region_1depth_name"]
val add_gu = addObj.asJsonObject["region_2depth_name"]
val add_dong = addObj.asJsonObject["region_3depth_name"]
var geoTv = hsfView.findViewById<TextView>(R.id.tvGeoLocation)
geoTv.text = add_full.asString;
HospitalSearch.set_gps_update(add_full.asString)
}
}
}
//response.body()["meta"].asJsonObject["total_count"]
println(response)
} else {
println("address not found")
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
println(t.message)
}
}
}
private fun getAddressResponse(): Callback<JsonObject>{
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
val resObj = response.body();
set_hospital_list(resObj, true)
} else {
println("hospital not found")
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
println(t.message)
}
}
}
fun set_hospital_list(resobj:JsonObject?, isAddress:Boolean = false) {
var totPages = resobj?.get("totalPages") // 총 페이지
var totRows = resobj?.get("totalRows") // 총 행 개수
var searchRows = resobj?.get("row") // 검색 페이지 행 개수
var searchPage = resobj?.get("page") // 검색 페이지 번호
var resData = resobj?.getAsJsonArray("resData")
var dataSize = resData?.size() ?: 0;
var hospitalSearchRes: List<List<String>> = mutableListOf()
var hpidList: MutableList<String> = mutableListOf()
if (dataSize > 0) {
// region 검색 결과 리스트에 세팅
try {
resData?.forEach { item ->
val itemObj = item.asJsonObject;
var levelState =
API_Helper.lidar_levelState(itemObj.get("levelState").asInt)
var itemHpid = itemObj.get("hpid").toString().trim('"');
hpidList.add(itemHpid)
var itemDistance = ""
var itemTel = ""
var itemTitle = ""
if (!isAddress) {
itemObj.get("distance").toString().trim('"')
itemTel = itemObj.get("tel").toString().trim('"')
itemTitle = itemObj.get("title").toString().trim('"')
}
else {
itemTel = itemObj.get("dutyTel1").toString().trim('"')
itemTitle = itemObj.get("dutyName").toString().trim('"')
}
HospitalSearch.addDataHospitalItems(
listOf(
HospitalSearch.itemStructure(
itemHpid, levelState, itemDistance,
itemObj.get("city").toString().trim('"'),
itemObj.get("district").toString().trim('"'),
itemTel, itemTitle
)
)
)
}
var total_Page = totPages?.asInt;
if (total_Page == null) total_Page = 0;
var total_Row = totRows?.asInt;
if (total_Row == null) total_Row = 0;
PaginationHelper.setupPaginationLayout(requireView(), total_Page, total_Row, { page ->
page_onclick_listener(page)
})
} catch (e: Exception) {
Toast.makeText(requireActivity(), "${e.message}", Toast.LENGTH_LONG)
.show()
} finally {}
hospitalSearchRes = HospitalSearch.getAllData();
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "병원이 검색되지 않았습니다. 다시 시도해주세요."
)
if (!PaginationHelper.pageRequest) PaginationHelper.setupPaginationLayout(
requireView(),
0, 0, { page ->
page_onclick_listener(page)
}
)
}
//병원 검색 결과 설정
if (hospitalSearchRes.size > 0) adapter.hospitalListUpdate(hospitalSearchRes, hpidList)
// endregion
}
private fun page_onclick_listener(page:Int) {
// println("clicked page ${page}")
var reqBody = HospitalSearch.GET_SearchRequest()
if (reqBody != null) {
try {
if (HospitalSearch.isAddressSearch()) {
LoadingDialog.showDlg(requireContext())
reqBody = reqBody.getAsJsonObject("reqBody")
reqBody.addProperty("page", "${page}")
RetrofitClient.locInstance.getAddressHospitalList(reqBody).enqueue(getAddress_Paging_Response())
} else {
LoadingDialog.showDlg(requireContext())
reqBody = reqBody.getAsJsonObject("reqBody")
reqBody.addProperty("page", "${page}")
RetrofitClient.locInstance.getAroundHospitalList(reqBody).enqueue(arroundHospital_Paging_Response())
}
} catch (e: Exception) {
println(e.message)
} finally {
LoadingDialog.hideDlg()
}
}
}
private fun arroundHospital_Paging_Response(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
var resBody = response.body();
set_hospital_list_page(resBody)
} else {
Toast.makeText(
requireActivity(),
"Error: ${response.code()}",
Toast.LENGTH_SHORT
).show()
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
Toast.makeText(
requireActivity(),
"Request Failed: ${t.message}",
Toast.LENGTH_SHORT
).show()
}
}
}
private fun getAddress_Paging_Response(): Callback<JsonObject>{
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
val resObj = response.body();
set_hospital_list_page(resObj, true)
} else {
println("hospital not found")
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
println(t.message)
}
}
}
fun set_hospital_list_page(resobj:JsonObject?, isAddress:Boolean = false) {
// region 현재 병원 리스트 초기화
adapter.clearHospitalList();
HospitalSearch.clearAllData();
// endregion
var totPages = resobj?.get("totalPages") // 총 페이지
var totRows = resobj?.get("totalRows") // 총 행 개수
var searchRows = resobj?.get("row") // 검색 페이지 행 개수
var searchPage = resobj?.get("page") // 검색 페이지 번호
var resData = resobj?.getAsJsonArray("resData")
var dataSize = resData?.size() ?: 0;
var hospitalSearchRes: List<List<String>> = mutableListOf()
var hpidList: MutableList<String> = mutableListOf()
if (dataSize > 0) {
// region 검색 결과 리스트에 세팅
try {
resData?.forEach { item ->
val itemObj = item.asJsonObject;
var levelState =
API_Helper.lidar_levelState(itemObj.get("levelState").asInt)
var itemHpid = itemObj.get("hpid").toString().trim('"');
hpidList.add(itemHpid)
var itemDistance = ""
var itemTel = ""
var itemTitle = ""
if (!isAddress) {
itemObj.get("distance").toString().trim('"')
itemTel = itemObj.get("tel").toString().trim('"')
itemTitle = itemObj.get("title").toString().trim('"')
}
else {
itemTel = itemObj.get("dutyTel1").toString().trim('"')
itemTitle = itemObj.get("dutyName").toString().trim('"')
}
HospitalSearch.addDataHospitalItems(
listOf(
HospitalSearch.itemStructure(
itemHpid, levelState, itemDistance,
itemObj.get("city").toString().trim('"'),
itemObj.get("district").toString().trim('"'),
itemTel, itemTitle
)
)
)
}
var total_Page = totPages?.asInt;
if (total_Page == null) total_Page = 0;
var total_Row = totRows?.asInt;
if (total_Row == null) total_Row = 0;
// PaginationHelper.rePaginationLayout(requireView(), { page ->
// page_onclick_listener(page)
// })
// PaginationHelper.setupPaginationLayout(requireView(), total_Page, total_Row, { page ->
// page_onclick_listener(page)
// })
} catch (e: Exception) {
Toast.makeText(requireActivity(), "${e.message}", Toast.LENGTH_LONG)
.show()
} finally {}
hospitalSearchRes = HospitalSearch.getAllData();
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "병원이 검색되지 않았습니다. 다시 시도해주세요."
)
// if (!PaginationHelper.pageRequest) PaginationHelper.setupPaginationLayout(
// requireView(),
// 0, 0, { page ->
// page_onclick_listener(page)
// }
// )
}
//병원 검색 결과 설정
if (hospitalSearchRes.size > 0) adapter.hospitalListUpdate(hospitalSearchRes, hpidList)
// endregion
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
FragmentHandler.unregisterView("HospitalSearchFragment")
println("hospital search On destroy view")
}
}

View File

@ -0,0 +1,142 @@
package com.example.smart119.views
import android.content.Context
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.PopupWindow
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import com.example.smart119.R
import com.example.smart119.datas.HospitalSearch
import com.google.android.material.textfield.TextInputEditText
object HospitalSearchPopup {
fun showPopupHospitalSearch(view: View, context: Context): PopupWindow {
// PopupView 레이아웃을 인플레이트
val inflater = LayoutInflater.from(view.context)
val popupView = inflater.inflate(R.layout.popup_search, null)
val displayMetrics = view.resources.displayMetrics
val screenWidth = displayMetrics.widthPixels
val screenHeight = displayMetrics.heightPixels
// PopupWindow 생성
val popupWindow = PopupWindow(
popupView,
// LinearLayout.LayoutParams.WRAP_CONTENT,
// LinearLayout.LayoutParams.MATCH_PARENT,
(screenWidth * 0.4).toInt(), // 너비를 화면 너비의 80%로 설정
(screenHeight * 0.4).toInt(), // 높이를 화면 높이의 60%로 설정
true // 외부 클릭으로 닫기 가능
)
// PopupWindow 표시 (뷰 기준 위치)
// popupWindow.showAsDropDown(view, 220, -220) // 기준 뷰 아래에 표시
// 화면의 중앙에 표시
popupWindow.showAtLocation(
view, // 기준이 되는 뷰 (보통은 최상위 뷰)
Gravity.END, // 중앙 정렬
250, // xOffset
-95 // yOffset
)
// PopupView 내의 컴포넌트 제어
val popupText = popupView.findViewById<TextView>(R.id.popupText)
val saveButton = popupView.findViewById<Button>(R.id.savePopup)
// saveButton.position
// 닫기 버튼 클릭 시 PopupWindow 닫기
saveButton.setOnClickListener {
val popupView = popupWindow.contentView
val siSpin = popupView.findViewById<Spinner>(R.id.siSelectSpinner);
val gunSpin = popupView.findViewById<Spinner>(R.id.guGunSelectSpinner);
val editText = popupView.findViewById<TextInputEditText>(R.id.relateInput)
HospitalSearch.setSearchFilter(
siSpin.selectedItem.toString(),
gunSpin.selectedItem.toString(),
editText.text.toString()
)
popupWindow.dismiss()
}
val siSpinner: Spinner = popupView.findViewById(R.id.siSelectSpinner)
val guGunSpinner: Spinner = popupView.findViewById(R.id.guGunSelectSpinner)
// val sidefault = getString(R.string.si_spinner_prompt)
// val siItems = mutableListOf("시도선택")
// val arrAdapterSi = ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, siItems)
// siSpinner.adapter = arrAdapterSi;
siSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
val selectedItem = parent.getItemAtPosition(position).toString()
// Toast.makeText(context, "선택된 항목: $selectedItem", Toast.LENGTH_SHORT).show()
if (selectedItem != "" && selectedItem != "시도선택") {
Toast.makeText(context, "선택된 항목: $selectedItem", Toast.LENGTH_SHORT).show()
HospitalSearch.req_gun_list(selectedItem)
}
else {
}
}
override fun onNothingSelected(parent: AdapterView<*>) {
// 아무것도 선택되지 않았을 때의 동작 (필요 없으면 빈 상태로 둬도 됨)
Toast.makeText(context, "선택되지 않음", Toast.LENGTH_SHORT).show()
}
}
guGunSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
val selectedItem = parent.getItemAtPosition(position).toString()
// Toast.makeText(context, "선택된 항목: $selectedItem", Toast.LENGTH_SHORT).show()
if (selectedItem != "" && selectedItem != "구군선택") {
Toast.makeText(context, "선택된 항목: $selectedItem", Toast.LENGTH_SHORT).show()
}
else {
}
}
override fun onNothingSelected(parent: AdapterView<*>) {
// 아무것도 선택되지 않았을 때의 동작 (필요 없으면 빈 상태로 둬도 됨)
Toast.makeText(context, "선택되지 않음", Toast.LENGTH_SHORT).show()
}
}
val guGunItems = mutableListOf("구군선택")
val arrAdapterguGun = ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, guGunItems)
guGunSpinner.adapter = arrAdapterguGun;
return popupWindow
}
fun set_POPUP_SIDO_COMBO(view: PopupWindow, context: Context, items:List<String>) {
val popupView = view.contentView
val siSpinner: Spinner = popupView.findViewById(R.id.siSelectSpinner)
val arrAdapterSi = ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, items)
siSpinner.adapter = arrAdapterSi;
arrAdapterSi.notifyDataSetChanged()
}
fun set_POPUP_GUGUN_COMBO(view: PopupWindow, context: Context, items:List<String>) {
val popupView = view.contentView
val guGunSpinner: Spinner = popupView.findViewById(R.id.guGunSelectSpinner)
val arrAdapterGuGun = ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, items)
guGunSpinner.adapter = arrAdapterGuGun;
arrAdapterGuGun.notifyDataSetChanged()
}
}

View File

@ -0,0 +1,61 @@
package com.example.smart119.views
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.FragmentNavigator
@Navigator.Name("keep_state_fragment")
class KeepStateFragment(
private val context: Context,
private val manager: FragmentManager,
private val containerId: Int
): FragmentNavigator(context, manager, containerId) {
override fun navigate(
destination: Destination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
val tag = destination.id.toString()
val transaction = manager.beginTransaction()
var initialNavigate = false
val currentFragment = manager.primaryNavigationFragment
if (currentFragment != null) {
transaction.hide(currentFragment)
} else {
initialNavigate = true
}
var fragment = manager.findFragmentByTag(tag)
if (fragment == null) {
// add로 fragment 최초 생성 (add)
val className = destination.className
fragment = manager.fragmentFactory.instantiate(context.classLoader, className)
transaction.add(containerId, fragment, tag)
} else {
transaction.show(fragment)
}
// destination fragment를 primary로 설정
transaction.setPrimaryNavigationFragment(fragment)
// transaction 관련 fragment 상태 변경 최적화
transaction.setReorderingAllowed(true)
transaction.commitNow()
return if (initialNavigate) {
destination
} else {
null
}
}
}

View File

@ -0,0 +1,55 @@
package com.example.smart119.views
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.View
class LidarCustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = 0xFFFF0000.toInt() // 줄무늬 색상
style = Paint.Style.FILL
}
private val lineWidth = 2f // 줄 두께
private val gap = 4f// 줄 사이 간격
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// View 전체 영역
val width = width.toFloat()
val height = height.toFloat()
// 세로 줄무늬 그리기
var x = 0f
while (x < width) {
canvas.drawRect(x, 0f, x + lineWidth, height, paint)
x += lineWidth + gap
}
// 마지막 세로 줄 추가
// if (x - gap < width) {
// canvas.drawRect(x - gap, 0f, width, height, paint)
// }
// 가로 줄무늬 그리기
var y = 0f
while (y < height) {
canvas.drawRect(0f, y, width, y + lineWidth, paint)
y += lineWidth + gap
}
//
// // 마지막 가로 줄 추가
// if (y - gap < height) {
// canvas.drawRect(0f, y - gap, width, height, paint)
// }
}
}

View File

@ -0,0 +1,40 @@
package com.example.smart119.views
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import com.example.smart119.App
import com.example.smart119.R
object LoadingDialog : Dialog(App.instance) {
private var dialog: Dialog? = null
fun showDlg(context: Context) {
// Context가 유효한지 확인
if (dialog?.isShowing == true) return
// Context가 유효하지 않다면 종료
if (context !is android.app.Activity || context.isFinishing || context.isDestroyed) return
dialog = Dialog(context).apply {
setContentView(R.layout.dialog_loading)
setCancelable(false)
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
show()
}
println("show Loading dialog")
}
fun hideDlg() {
dialog?.hide()
}
fun dismissDlg() {
dialog?.dismiss()
dialog = null
}
}

View File

@ -0,0 +1,745 @@
package com.example.smart119.views
import android.graphics.Typeface
import android.location.Location
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.smart119.API_Helper
import com.example.smart119.AlertDialogUtil
import com.example.smart119.HelpUtility
import com.example.smart119.ListItem
import com.example.smart119.LocationHelper
import com.example.smart119.LoginManager
import com.example.smart119.MedicalInfo
import com.example.smart119.R
import com.example.smart119.SpreadsheetAdapter
import com.example.smart119.TransferManager
import com.example.smart119.TransferState
import com.example.smart119.datas.HospitalSearch
import com.example.smart119.datas.SaveLogin
import com.example.smart119.interfaces.RetrofitClient
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class LocationSearchFragment : Fragment() {
private lateinit var lsAdapter: SpreadsheetAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.location_search_page, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
FragmentHandler.registerView("LocationSearchFragment", view)
hospitalInit(view);
HospitalSearch.clearAllData()
val searchButton = view.findViewById<Button>(R.id.hospSearchButton)
searchButton.setOnClickListener {
var _sel_medical = view.findViewById<Spinner>(R.id.spMedical).selectedItem as String;
var _sel_depart = view.findViewById<Spinner>(R.id.spDepart).selectedItem as String;
var _sel_emg_yn = view.findViewById<Spinner>(R.id.spEmgYN).selectedItem as String;
lsAdapter.clearHospitalList();
HospitalSearch.clearAllData();
//success
// val resObj = res.getAsJsonObject();
// val callResult = resObj["isSuccess"]?.asBoolean ?: true;
val lon = LocationHelper.getCurrentLon();
val lat = LocationHelper.getCurrentLat();
var page = "1"
var row = "4"
var dg_list = emptyList<String>()//listOf("","")
val hospCatTV = view.findViewById<TextView>(R.id.hospCatLocText)
val hospPrtTV = view.findViewById<TextView>(R.id.hospPrtLocText)
var _code_medical = MedicalInfo.get_emogemdv_code(hospCatTV.text.toString());
var _code_depart = MedicalInfo.get_prt_code(hospPrtTV.text.toString());
var _emg_yn = _sel_emg_yn;
var _relate = ""
try {
_relate = HospitalSearch.getSearchInfo()["relate"].asString;
} catch (e: Exception) {
println(e.message)
}
if (_code_medical == null) _code_medical = ""
if (_code_depart == null) _code_depart = ""
if (_emg_yn.indexOf("응급실 포함여부") != -1) _emg_yn = "Y"
// println("의료기관:${_sel_medical}|${_code_medical}, 진료과:${_sel_depart}|${_code_depart}, 응급실:${_emg_yn}")
var reqBody: JsonObject = API_Helper.make_arroundHosp_param2(
lon = lon.toString(),
lat = lat.toString(),
emogemdv = "${_code_medical}",
prtcode = "${_code_depart}",
emergency = "${_emg_yn}",
emogdesc = "${_relate}",
page = "${page}",
row = "${row}"
);
try {
LoadingDialog.showDlg(requireContext())
HospitalSearch.SET_SearchRequest(false, reqBody)
RetrofitClient.locInstance.getAroundHospitalList(reqBody)
.enqueue(arroundHospitalResponse())
} catch (e: Exception) {
println(e.message)
LoadingDialog.hideDlg()
}
/*
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "현재 위치를 알수 없어 검색 되지 않았습니다, 다시 시도해주세요."
)
*/
}
val prevLSButton = view.findViewById<Button>(R.id.pagePrevLS)
prevLSButton.setOnClickListener {
if (HospitalSearch.getAllData().size > 0) {
var bChanged = PaginationHelper.prevPage(requireView(), { page ->
page_onclick_listener(page)
})
//request page
if (bChanged) {
var currPage = PaginationHelper.currentPage;
// println("prev request number ${PaginationHelper.currentPage}")
page_onclick_listener(currPage)
}
// else println("no request prev")
}
}
val nextLSButton = view.findViewById<Button>(R.id.pageNextLS)
nextLSButton.setOnClickListener {
if (HospitalSearch.getAllData().size > 0) {
var bChanged = PaginationHelper.nextPage(requireView(), { page ->
page_onclick_listener(page)
})
//request page
if (bChanged) {
var currPage = PaginationHelper.currentPage;
// println("next request number ${PaginationHelper.currentPage}")
page_onclick_listener(currPage)
}
// else println("no request next")
}
}
val gpsRefreshButton = view.findViewById<ImageButton>(R.id.gpsRefresh)
gpsRefreshButton.setOnClickListener {
Hospital_UIUpdate(true)
}
val hospCatLocTV = view.findViewById<TextView>(R.id.hospCatLocText)
hospCatLocTV.setOnClickListener {
HospitalCategoryLocation_Dialog.show(requireContext())
}
HospitalCategoryLocation_Dialog.registerCallback { evtString ->
println("의료기관 선택 이벤트!! ${evtString}")
}
val hospPrtLocTV = view.findViewById<TextView>(R.id.hospPrtLocText)
hospPrtLocTV.setOnClickListener {
DepartmentLocation_Dialog.show(requireContext())
}
DepartmentLocation_Dialog.registerCallback { evtString ->
println("진료과 선택 이벤트!! ${evtString}")
}
try {
if (!HospitalSearch.get_gps_address().isEmpty()) {
Hospital_UIUpdate_SET(view)
}
Hospital_UIUpdate()
} catch (e: Exception) {
println(e.message)
}
}
override fun onResume() {
super.onResume()
// Toast.makeText(requireContext(), "위치기반 탭 활성화!", Toast.LENGTH_SHORT).show()
// println("위치기반 탭 활성화!")
}
override fun onPause() {
super.onPause()
// println("위치기반 탭 비 활성화!")
lsAdapter.clearHospitalList()
HospitalSearch.clearAllData()
PaginationHelper.setupPaginationLayout(
requireView(),
0, 0, { page ->
page_onclick_listener(page)
}
)
}
override fun onDestroyView() {
super.onDestroyView()
FragmentHandler.unregisterView("LocationSearchFragment")
}
override fun onDestroy() {
super.onDestroy()
}
private fun hospitalInit(view: View) {
var items = listOf(
ListItem.Header(listOf("병원이름")), // 헤더 열 정의
ListItem.Header(listOf("주소")),
ListItem.Header(listOf("전화번호")),
ListItem.Header(listOf("라이다 혼잡도")),
ListItem.Header(listOf("거리")),
ListItem.Header(listOf("병원별 이송 시작"))
)
val recyclerViewLS: RecyclerView = view.findViewById(R.id.reCyclerViewLS)
val spanCount = 6;
lsAdapter = SpreadsheetAdapter(
items,
onButtonClick = { row, pos ->
//row.values[0]
lsAdapter.setSelectedHospitalPos(pos)
HospitalSearch.setIdx(lsAdapter.getSelectedPosition())
var selectedHospital = lsAdapter.getSelectedHospital()
var getSelData = HospitalSearch.getSelectedData()
if (selectedHospital is ListItem.Row) {
var hospitalName = selectedHospital.values[0];
var hospitalAddress = selectedHospital.values[1];
var hospitalCallNumber = selectedHospital.values[2];
var hospitalLiDAR = selectedHospital.values[3];
var hospitalDistance = selectedHospital.values[4];
if (TransferManager.getState() == TransferState.STARTED) {
//이송 변경
//변경된 병원 정보 설정
val spannableMessage = SpannableStringBuilder().apply {
val preText = "병원 이름 : ${hospitalName}" + "\n" +
"주소 : ${hospitalAddress}" + "\n" +
"전화번호 : ${hospitalCallNumber}" + "\n" +
"거리 : ${hospitalDistance}" + "\n" +
"라이다혼잡도 : ${hospitalLiDAR}" + "\n"
append(preText)
val boldText = "확인"
append(boldText)
setSpan(
StyleSpan(Typeface.BOLD), preText.length,
preText.length + boldText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
append(" 선택시 환자정보가 전송됩니다.(변경 시 기존 이송 병원이 변경됩니다.)\n")
}
AlertDialogUtil.showAlert_CUSTOM(
context = requireActivity(),
title = "이송 병원 정보",
sppStr = spannableMessage,
onPositiveClick = {
AlertDialogUtil.showAlert_OK(
context = requireActivity(),
title = "진행 전 확인사항",
message = "진행하시기 전에, 구급활동일지 임시저장을 하셨는지 확인해 주세요\n",
onPositiveClick = {
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospTransfer;
TransferManager.changeTransfer()
}
)
},
onNegativeClick = {}
)
} else {
val spannableMessage = SpannableStringBuilder().apply {
val preText = "병원 이름 : ${hospitalName}" + "\n" +
"주소 : ${hospitalAddress}" + "\n" +
"전화번호 : ${hospitalCallNumber}" + "\n" +
"거리 : ${hospitalDistance}" + "\n" +
"라이다혼잡도 : ${hospitalLiDAR}" + "\n"
append(preText)
val boldText = "확인"
append(boldText)
setSpan(
StyleSpan(Typeface.BOLD), preText.length,
preText.length + boldText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
append(" 선택시 환자정보가 전송됩니다.\n")
}
AlertDialogUtil.showAlert_CUSTOM(
context = requireActivity(),
title = "이송 병원 정보",
sppStr = spannableMessage,
onPositiveClick = {
AlertDialogUtil.showAlert_OK(
context = requireActivity(),
title = "진행 전 확인사항",
message = "진행하시기 전에, 구급활동일지 임시저장을 하셨는지 확인해 주세요\n",
onPositiveClick = {
if (TransferManager.startTransfer()) {
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospTransfer;
} else {
// 시작되지 않았습니다.
Toast.makeText(
requireActivity(), "이송이 시작되지 않았습니다. 다시 전송해주세요.",
Toast.LENGTH_SHORT
).show()
}
}
)
},
onNegativeClick = {}
)
}
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "선택된 병원 정보",
message = "병원 검색 후 선택해주세요!"
)
}
//이송시작
println(row)
//pos, row(ListItem상 선택 위치, 선택한 row 정보) -> hpid는?
},
onRowClick = { row ->
// Toast.makeText(requireActivity(), "${row.values[0]}이 선택되었습니다.", Toast.LENGTH_SHORT)
// .show()
}
)
lsAdapter.setColumnCount(spanCount)
recyclerViewLS.adapter = lsAdapter
// GridLayoutManager 설정
val layoutManager = GridLayoutManager(requireActivity(), spanCount)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
// return if (adapter.getItemViewType(position) == SpreadsheetAdapter.VIEW_TYPE_HEADER) 3 else 1
return if (lsAdapter.getItemViewType(position) == SpreadsheetAdapter.VIEW_TYPE_ROW) {
spanCount // Row가 전체 열을 차지하도록 설정
} else {
1 // 기본적으로 각 항목은 한 열을 차지
}
}
}
recyclerViewLS.layoutManager = layoutManager
}
private fun Hospital_UIUpdate(bForceUpdate:Boolean = false) {
val lon = LocationHelper.getCurrentLon();
val lat = LocationHelper.getCurrentLat();
val currentTime = System.currentTimeMillis()
val _prev_gps = HospitalSearch.getPrevGPSInfo()
// println("lon:${lon}, lat:${lat}, prev lon:${_prev_gps["prevLon"].asDouble}, prev lat:${_prev_gps["prevLat"].asDouble}")
//이전 업데이트가 1km 이내에서 한 경우, 업데이트 하지 않음
if (shouldUpdateLocation(
_prev_gps["prevLat"].asDouble,
_prev_gps["prevLon"].asDouble,
_prev_gps["prevTime"].asLong,
lat,
lon,
currentTime
) || bForceUpdate
) {
RetrofitClient.pathInstance.getAddr(
API_Helper.make_getAddr_param(lon.toString(), lat.toString())
).enqueue(getPathAddressResponse())
HospitalSearch.setPrevGPSUpdate(lon, lat, System.currentTimeMillis())
println("카카오 위치 -> 주소 변환 API 요청");
} else {
println("카카오 위치 -> 주소 변환 API 요청 안함");
}
}
private fun Hospital_UIUpdate_SET(view:View) {
var addrString = HospitalSearch.get_gps_address()
var geoTv = view.findViewById<TextView>(R.id.tvGeoLocation)
geoTv.text = addrString;
}
private fun getPathAddressResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
val resObj = response.body();
val metaObj = resObj?.get("meta");
if (metaObj != null) {
val tot_count = metaObj.asJsonObject["total_count"].asInt
if (tot_count > 0) {
val lsfView = requireView()
if (lsfView != null) {
val docus = resObj.getAsJsonArray("documents")
val addObj = docus[0].asJsonObject["address"];
val add_full = addObj.asJsonObject["address_name"]
// val add_si = addObj.asJsonObject["region_1depth_name"]
// val add_gu = addObj.asJsonObject["region_2depth_name"]
// val add_dong = addObj.asJsonObject["region_3depth_name"]
var geoTv = lsfView.findViewById<TextView>(R.id.tvGeoLocation)
geoTv.text = add_full.asString;
HospitalSearch.set_gps_update(add_full.asString)
}
}
}
//response.body()["meta"].asJsonObject["total_count"]
println(response)
} else {
println("address not found")
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
println(t.message)
}
}
}
private fun arroundHospitalResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
val resBody = response.body();
if (resBody != null) {
set_hospital_list(resBody)
}
else {
nav_to_login();
}
} else {
nav_to_login();
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
println(t.message)
nav_to_login();
}
}
}
fun set_hospital_list(resobj:JsonObject?, isAddress:Boolean = false) {
var totPages = resobj?.get("totalPages") // 총 페이지
var totRows = resobj?.get("totalRows") // 총 행 개수
var searchRows = resobj?.get("row") // 검색 페이지 행 개수
var searchPage = resobj?.get("page") // 검색 페이지 번호
var resData = resobj?.getAsJsonArray("resData")
var dataSize = resData?.size() ?: 0;
var hospitalSearchRes: List<List<String>> = mutableListOf()
var hpidList: MutableList<String> = mutableListOf()
if (dataSize > 0) {
// region 검색 결과 리스트에 세팅
try {
resData?.forEach { item ->
val itemObj = item.asJsonObject;
var levelState =
API_Helper.lidar_levelState(itemObj.get("levelState").asInt)
var itemHpid = itemObj.get("hpid").toString().trim('"');
hpidList.add(itemHpid)
var itemDistance = ""
var itemTel = ""
var itemTitle = ""
if (!isAddress) {
itemDistance = itemObj.get("distance").toString().trim('"')
itemTel = itemObj.get("tel").toString().trim('"')
itemTitle = itemObj.get("title").toString().trim('"')
}
else {
itemTel = itemObj.get("dutyTel1").toString().trim('"')
itemTitle = itemObj.get("dutyName").toString().trim('"')
}
HospitalSearch.addDataHospitalItems(
listOf(
HospitalSearch.itemStructure(
itemHpid, levelState, itemDistance,
itemObj.get("city").toString().trim('"'),
itemObj.get("district").toString().trim('"'),
itemTel, itemTitle
)
)
)
}
var total_Page = totPages?.asInt;
if (total_Page == null) total_Page = 0;
var total_Row = totRows?.asInt;
if (total_Row == null) total_Row = 0;
PaginationHelper.setupPaginationLayout(requireView(), total_Page, total_Row, { page ->
page_onclick_listener(page)
})
} catch (e: Exception) {
Toast.makeText(requireActivity(), "${e.message}", Toast.LENGTH_LONG)
.show()
} finally {}
hospitalSearchRes = HospitalSearch.getAllData();
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "병원이 검색되지 않았습니다. 다시 시도해주세요."
)
if (!PaginationHelper.pageRequest) PaginationHelper.setupPaginationLayout(
requireView(),
0, 0, { page ->
page_onclick_listener(page)
}
)
}
//병원 검색 결과 설정
if (hospitalSearchRes.size > 0) lsAdapter.hospitalListUpdate(hospitalSearchRes, hpidList)
// endregion
}
private fun page_onclick_listener(page:Int) {
// println("clicked page ${page}")
var reqBody = HospitalSearch.GET_SearchRequest()
if (reqBody != null) {
try {
// if (HospitalSearch.isAddressSearch()) {
// LoadingDialog.showDlg(requireContext())
// reqBody = reqBody.getAsJsonObject("reqBody")
// reqBody.addProperty("page", "${page}")
// RetrofitClient.locInstance.getAddressHospitalList(reqBody).enqueue(getAddress_Paging_Response())
// } else {
LoadingDialog.showDlg(requireContext())
reqBody = reqBody.getAsJsonObject("reqBody")
reqBody.addProperty("page", "${page}")
RetrofitClient.locInstance.getAroundHospitalList(reqBody)
.enqueue(arroundHospital_Paging_Response())
// }
} catch (e: Exception) {
println(e.message)
LoadingDialog.hideDlg()
}
}
}
/*
private fun getAddress_Paging_Response(): Callback<JsonObject>{
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
val resObj = response.body();
set_hospital_list_page(resObj, true)
} else {
println("hospital not found")
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
println(t.message)
}
}
}
*/
private fun arroundHospital_Paging_Response(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
val resBody = response.body();
if (resBody != null) {
set_hospital_list_page(resBody)
}
else {
nav_to_login();
}
} else {
nav_to_login();
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
println(t.message)
nav_to_login();
}
}
}
fun set_hospital_list_page(resobj:JsonObject?) {
// region 현재 병원 리스트 초기화
lsAdapter.clearHospitalList();
HospitalSearch.clearAllData();
// endregion
var totPages = resobj?.get("totalPages") // 총 페이지
var totRows = resobj?.get("totalRows") // 총 행 개수
var searchRows = resobj?.get("row") // 검색 페이지 행 개수
var searchPage = resobj?.get("page") // 검색 페이지 번호
var resData = resobj?.getAsJsonArray("resData")
var dataSize = resData?.size() ?: 0;
var hospitalSearchRes: List<List<String>> = mutableListOf()
var hpidList: MutableList<String> = mutableListOf()
if (dataSize > 0) {
// region 검색 결과 리스트에 세팅
try {
resData?.forEach { item ->
val itemObj = item.asJsonObject;
var levelState =
API_Helper.lidar_levelState(itemObj.get("levelState").asInt)
var itemHpid = itemObj.get("hpid").toString().trim('"');
hpidList.add(itemHpid)
var itemDistance = ""
var itemTel = ""
var itemTitle = ""
// if (!isAddress) {
//위치기반 검색 시 사용하는 곳
itemDistance = itemObj.get("distance").toString().trim('"')
itemTel = itemObj.get("tel").toString().trim('"')
itemTitle = itemObj.get("title").toString().trim('"')
// }
// else {
///주소 검색 시 사용하는 곳
// itemTel = itemObj.get("dutyTel1").toString().trim('"')
// itemTitle = itemObj.get("dutyName").toString().trim('"')
// }
HospitalSearch.addDataHospitalItems(
listOf(
HospitalSearch.itemStructure(
itemHpid, levelState, itemDistance,
itemObj.get("city").toString().trim('"'),
itemObj.get("district").toString().trim('"'),
itemTel, itemTitle
)
)
)
}
var total_Page = totPages?.asInt;
if (total_Page == null) total_Page = 0;
var total_Row = totRows?.asInt;
if (total_Row == null) total_Row = 0;
// PaginationHelper.rePaginationLayout(requireView(), { page ->
// page_onclick_listener(page)
// })
// PaginationHelper.setupPaginationLayout(requireView(), total_Page, total_Row, { page ->
// page_onclick_listener(page)
// })
} catch (e: Exception) {
Toast.makeText(requireActivity(), "${e.message}", Toast.LENGTH_LONG)
.show()
} finally {}
hospitalSearchRes = HospitalSearch.getAllData();
} else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "병원 검색 결과",
message = "병원이 검색되지 않았습니다. 다시 시도해주세요."
)
if (!PaginationHelper.pageRequest) PaginationHelper.setupPaginationLayout(
requireView(),
0, 0, { page ->
page_onclick_listener(page)
}
)
}
//병원 검색 결과 설정
if (hospitalSearchRes.size > 0) lsAdapter.hospitalListUpdate(hospitalSearchRes, hpidList)
// endregion
}
fun shouldUpdateLocation(
prevLat: Double, prevLng: Double, prevTime: Long, // 이전 위도, 경도, 업데이트 시간 (ms)
newLat: Double, newLng: Double, newTime: Long, // 새로운 위도, 경도, 현재 시간 (ms)
intervalMs: Long = 300000 // 기본 업데이트 주기: 5분 (300,000ms)
): Boolean {
val prevLocation = Location("").apply {
latitude = prevLat
longitude = prevLng
}
val newLocation = Location("").apply {
latitude = newLat
longitude = newLng
}
val distance = prevLocation.distanceTo(newLocation) // 두 지점 간 거리 (단위: m)
val timeDiff = newTime - prevTime // 시간 차이 (ms)
println("${distance}, ${timeDiff}")
val state_dist = (distance >= 1000)
val state_time = (timeDiff >= intervalMs)
val state_lonlat = (prevLat != newLat || prevLng != newLng)
val state_lonlatDist = ((prevLat != newLat || prevLng != newLng) && distance >= 1000)
println("${state_dist}, ${state_time}, ${state_lonlat}, ${state_lonlatDist}")
return ((prevLat != newLat || prevLng != newLng) && distance >= 1000) || timeDiff >= intervalMs
}
fun nav_to_login() {
AlertDialogUtil.showAlert_OK(
context = requireActivity(),
title = "세션 만료",
message = "기존 세션이 만료되었습니다. 다시 로그인 해주시기 바랍니다.\n",
onPositiveClick = {
SaveLogin.setLogin(false)
LoginManager.setLoggedIn(false);
val bottomNaviView =
requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.emgLogin;
}
)
}
}

View File

@ -0,0 +1,355 @@
package com.example.smart119.views
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.navigation.fragment.findNavController
import com.example.smart119.API_Helper
import com.example.smart119.AlertDialogUtil
import com.example.smart119.BuildConfig
import com.example.smart119.LocationHelper
import com.example.smart119.LoginManager
import com.example.smart119.LoginManager.tryLoginBody
import com.example.smart119.MyCookieJar
import com.example.smart119.R
import com.example.smart119.TransferManager
import com.example.smart119.databinding.FragmentFirstBinding
import com.example.smart119.datas.SaveLogin
import com.example.smart119.interfaces.RetrofitClient
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.textfield.TextInputEditText
import okhttp3.Cookie
/**
* A simple [Fragment] subclass as the default destination in the navigation.
*/
class LoginFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private val cookieStore: MutableMap<String, MutableList<Cookie>> = mutableMapOf()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFirstBinding.inflate(inflater, container, false)
return binding.root
}
//현재 창이 보여질 때 호출되는 함수
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
println("login fragment!!")
val button: Button = view.findViewById(R.id.accessButton)
button.setBackgroundColor(Color.parseColor("#0971B8"))
//#region 바로 preference로 로그인 체크
login_check()
//#endregion
binding.accessButton.setOnClickListener {
val userIdTextInput: TextInputEditText = view.findViewById(R.id.loginIdText); //"kwonkhj01"
val carNoTextInput: TextInputEditText = view.findViewById(R.id.carNoText);//"70보2495"
var _inputId = userIdTextInput.text.toString()
var _inputCarno = carNoTextInput.text.toString()
var _isPass:Boolean = false;
// debug용으로, 시작 버튼 눌렀을때 바로 자동 입력
// region
if(_inputId.isEmpty()) {
AlertDialogUtil.showAlert(
context = requireContext(),
title = "로그인 오류",
message = "유저 아이디를 입력해주세요",
onPositiveClick = {
//로그아웃 처리
},
onNegativeClick = {
}
)
_isPass = true;
// _inputId = "keeys1993"
}
else if(_inputCarno.isEmpty()) {
AlertDialogUtil.showAlert(
context = requireContext(),
title = "로그인 오류",
message = "차량 번호를 입력해주세요",
onPositiveClick = {
//로그아웃 처리
},
onNegativeClick = {
}
)
_isPass = true;
// _inputCarno = "998마2352"
}
// endregion
// println("login info ${_inputId}, ${_inputCarno}")
if (!_isPass) {
LoadingDialog.showDlg(requireContext())
LoginManager.setLoggedInfo(_inputId, _inputCarno, BuildConfig.APP_VERSION);
val loginBody = LoginManager.getLoggedInfo();
println("로그인 정보 ${loginBody["userId"].asString}, ${loginBody["carNo"].asString}, ${loginBody["ver"].asString}")
RetrofitClient.loginInstance.mLogin(loginBody)
.enqueue(loginResponse())
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun login_check() {
if (SaveLogin.isLoggedIn()) {
//로그인 상태면 바로 로그인 요청 후 진행
var _inputId = SaveLogin.getUserId()
var _inputCarno = SaveLogin.getCarNo()
if (_inputId == null || _inputId.isEmpty()) return;
if (_inputCarno == null || _inputCarno.isEmpty()) return;
println(BuildConfig.APP_VERSION);
LoadingDialog.showDlg(requireContext())
LoginManager.setLoggedInfo(_inputId, _inputCarno, BuildConfig.APP_VERSION);
val loginBody = LoginManager.getLoggedInfo();
println("로그인 정보 ${loginBody["userId"].asString}, ${loginBody["carNo"].asString}, ${loginBody["ver"].asString}")
RetrofitClient.loginInstance.mLogin(loginBody).enqueue(loginResponse())
}
}
private fun loginResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
// println("Stored cookies: ${MyCookieJar.getCookies()}")
try {
val resBody = response.body();
val status = resBody?.get("status")?.asString;
if (status == "success") {
val cookies = response.headers().values("Set-Cookie")
// println("Set-Cookie Headers: $cookies")
for (cookie in cookies) {
val cookieValue = cookie.split(";")[0] // "sessionId=abc123"만 추출
MyCookieJar.setCookie(cookieValue)
// println("Cookie Value: $cookieValue")
}
LoginManager.setLoggedIn(true);
var loginInfo = LoginManager.getLoggedInfo();
if (loginInfo != null) {
Toast.makeText(
requireActivity(),
"접속 되었습니다. [${loginInfo["userId"].asString}, ${loginInfo["carNo"].asString}]",
Toast.LENGTH_SHORT
).show()
}
LoadingDialog.hideDlg()
//#region 로그인 정보 저장
SaveLogin.setLogin(true);
SaveLogin.setUserId(loginInfo["userId"].asString);
SaveLogin.setCarNo(loginInfo["carNo"].asString);
//#endregion
RetrofitClient.transferInstance.currentAction().enqueue(currentActionResponse())
// moveToHospSearch();
}
else {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "접속 실패",
message = "접속 아이디와 차량번호를 확인해주세요. [${status}]"
)
LoadingDialog.hideDlg()
}
} catch (e: Exception) {
println(e.message)
LoadingDialog.hideDlg()
}
} else {
LoginManager.setLoggedIn(false);
var loginInfo:JsonObject = LoginManager.getLoggedInfo();
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "접속 실패",
message = "접속 아이디와 차량번호를 확인해주세요. [${loginInfo.get("userId").toString()}, ${loginInfo.get("carNo").toString()}]"
)
LoadingDialog.hideDlg()
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "접속 실패",
message = "네트워크 상태를 확인해주세요. \n" + "${t.message}"
)
if (SaveLogin.isLoggedIn()) {
val thisView = requireView();
val userIdTextInput: TextInputEditText =
thisView.findViewById(R.id.loginIdText); //"kwonkhj01"
val carNoTextInput: TextInputEditText =
thisView.findViewById(R.id.carNoText);//"70보2495"
var _inputId = SaveLogin.getUserId()
var _inputCarno = SaveLogin.getCarNo()
if (_inputId == null || _inputId.isEmpty()) return;
if (_inputCarno == null || _inputCarno.isEmpty()) return;
userIdTextInput.setText(_inputId)
carNoTextInput.setText(_inputCarno)
}
}
}
}
private fun cookieSendResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
println("cookie 성공")
} else {
println("cookie 실패!!")
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
//16 println("cookie 실패!!")
println("Request Failed: ${t.message}")
}
}
}
override fun onResume() {
super.onResume()
try {
val thisView = requireView();
val userIdTextInput: TextInputEditText =
thisView.findViewById(R.id.loginIdText); //"kwonkhj01"
val carNoTextInput: TextInputEditText =
thisView.findViewById(R.id.carNoText);//"70보2495"
var _inputId = SaveLogin.getUserId()
var _inputCarno = SaveLogin.getCarNo()
if (_inputId == null || _inputId.isEmpty()) return;
if (_inputCarno == null || _inputCarno.isEmpty()) return;
userIdTextInput.setText(_inputId)
carNoTextInput.setText(_inputCarno)
} catch (e: Exception) {
//TODO("Not yet implemented")
}
}
private fun moveToHospSearch() {
try {
val reqView = requireActivity();
val bottomNaviView = reqView.findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospSearch;
} catch (e: Exception) {
//TODO("Not yet implemented")
}
}
private fun currentActionResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
if (response.isSuccessful) {
try {
val resBody = response.body();
val status = resBody?.get("status")?.asString;
if (status == "true") {
TransferManager.serverState.set(true);
AlertDialogUtil.showAlert_OK(
context = requireContext(),
title = "진행 중인 전송 정보",
message = "현재 접속 정보로 진행중인 이송 건이 있습니다.",
onPositiveClick = {
moveToHospSearch();
}
)
/*
AlertDialogUtil.showAlert(
context = requireContext(),
title = "진행 중인 전송 정보",
message = "현재 접속 정보로 진행중인 이송 건이 있습니다. 취소하시겠습니까?",
onPositiveClick = {
TransferManager.cancelTransfer()
moveToHospSearch();
},
onNegativeClick = {
moveToHospSearch();
}
)
*/
//시작된 상황
}
else {
TransferManager.serverState.set(false);
//시작 아닌 상황
moveToHospSearch();
}
} catch (e: Exception) {
println(e.message)
moveToHospSearch();
}
} else {
moveToHospSearch();
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
moveToHospSearch();
}
}
}
}

View File

@ -0,0 +1,273 @@
package com.example.smart119.views
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import androidx.core.view.setPadding
import com.example.smart119.R
object PaginationHelper {
var totalPages: Int = 1 // 전체 페이지 수
var totalRows: Int = 1 // 전체 행 개수
var currentPage: Int = 1 // 현재 페이지 번호
var minPage: Int = 1 // 현재 표시되는 최소 페이지 번호
var maxPage: Int = 5 // 현재 표시되는 최대 페이지 번호
var buttonMap = mutableMapOf<Int, Button>()
var pageRequest = false;
fun setupPaginationLayout(view: View, totalPage: Int, totalRow: Int, update_page:(Int)-> Unit) {
totalPages = totalPage
totalRows = totalRow
currentPage = 1
// 최소 & 최대 페이지 계산
// minPage = ((currentPage - 1) / 5) * 5 + 1
minPage = currentPage
maxPage = minPage + 4
if (maxPage > totalPages) maxPage = totalPages
val pageButtonsContainer = view.findViewById<LinearLayout>(R.id.pageButtonsContainer)
// dp 값을 px로 변환
val buttonWidthPx = (60 * Resources.getSystem().displayMetrics.density).toInt()
val marginPx = (4 * Resources.getSystem().displayMetrics.density).toInt()
val buttonCount = maxPage - minPage + 1
// 페이지 버튼 컨테이너 너비 설정 (340dp → px 변환)
var totalWidthPx = (340 * Resources.getSystem().displayMetrics.density).toInt()
totalWidthPx = (buttonCount * buttonWidthPx) + ((buttonCount - 1) * (marginPx * 2))
pageButtonsContainer.layoutParams = LinearLayout.LayoutParams(totalWidthPx, ViewGroup.LayoutParams.MATCH_PARENT)
val cornerRadiusPx = (10 * Resources.getSystem().displayMetrics.density).toFloat()
// 기존 버튼 초기화
pageButtonsContainer.removeAllViews()
_clear_button()
if (totalPage == 0) {
pageButtonsContainer.setPadding(10)
return
};
else {
pageButtonsContainer.setPadding(0)
}
// 페이지 개수만큼 버튼 추가
var pageCount = 0;
for (page in minPage..maxPage) {
val button = Button(pageButtonsContainer.context).apply {
id = View.generateViewId()
text = page.toString()
textSize = 14f
setPadding(6, 6, 6, 6)
layoutParams = LinearLayout.LayoutParams(buttonWidthPx, ViewGroup.LayoutParams.MATCH_PARENT).apply {
if (pageCount == 0) {
marginStart = (marginPx)
}
marginEnd = marginPx
}
setOnClickListener {
updatePage(view, page) // 페이지 변경
update_page(page)
}
background = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = cornerRadiusPx
setColor(Color.parseColor("#0971B8"))
}
setTextColor(Color.WHITE)
}
// 현재 페이지 버튼 색상 강조
if (page == currentPage) {
button.background = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = cornerRadiusPx
setColor(Color.BLUE)
}
button.setTextColor(Color.WHITE)
}
pageButtonsContainer.addView(button)
_add_button(page, button)
pageCount++;
}
pageRequest = true;
println(buttonWidthPx)
}
fun _add_button(pageNum:Int, btn:Button) {
buttonMap[pageNum] = btn
}
fun _clear_button() {
buttonMap.clear();
}
fun rePaginationLayout(view: View, update_page:(Int)-> Unit) {
val pageButtonsContainer = view.findViewById<LinearLayout>(R.id.pageButtonsContainer)
// dp 값을 px로 변환
val buttonWidthPx = (60 * Resources.getSystem().displayMetrics.density).toInt()
val marginPx = (4 * Resources.getSystem().displayMetrics.density).toInt()
val buttonCount = maxPage - minPage + 1
// 페이지 버튼 컨테이너 너비 설정 (340dp → px 변환)
var totalWidthPx = (340 * Resources.getSystem().displayMetrics.density).toInt()
totalWidthPx = (buttonCount * buttonWidthPx) + ((buttonCount - 1) * (marginPx * 2))
pageButtonsContainer.layoutParams = LinearLayout.LayoutParams(totalWidthPx, ViewGroup.LayoutParams.MATCH_PARENT)
println("totalWidthPx ${totalWidthPx}")
val cornerRadiusPx = (10 * Resources.getSystem().displayMetrics.density).toFloat()
// 기존 버튼 초기화
pageButtonsContainer.removeAllViews()
_clear_button()
// 페이지 개수만큼 버튼 추가
var pageCount = 0;
for (page in minPage..maxPage) {
val button = Button(pageButtonsContainer.context).apply {
id = View.generateViewId()
text = page.toString()
textSize = 14f
setPadding(6, 6, 6, 6)
layoutParams = LinearLayout.LayoutParams(buttonWidthPx, ViewGroup.LayoutParams.MATCH_PARENT).apply {
if (pageCount == 0) {
marginStart = (marginPx)
}
marginEnd = marginPx
}
setOnClickListener {
updatePage(view, page) // 페이지 변경
update_page(page)
}
background = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = cornerRadiusPx
setColor(Color.parseColor("#0971B8"))
}
setTextColor(Color.WHITE)
}
// 현재 페이지 버튼 색상 강조
if (page == currentPage) {
button.background = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = cornerRadiusPx
setColor(Color.BLUE)
}
button.setTextColor(Color.WHITE)
}
pageButtonsContainer.addView(button)
_add_button(page, button)
pageCount++;
}
}
fun nextPage(view: View, update_page: (Int) -> Unit):Boolean {
if (currentPage == maxPage) {
// 5==5 이고,
if (maxPage < totalPages) {
minPage = maxPage + 1
if ((maxPage + 5) <= totalPages) {
maxPage = maxPage + 5
}
else {
maxPage = totalPages
}
currentPage++;
rePaginationLayout(view, { page ->
update_page(page)
})
return true;
}
else return false
}
else {
var bef_Page = buttonMap.get(currentPage);
var nxt_Page = buttonMap.get(currentPage + 1);
activePageButton(bef_Page, false)
activePageButton(nxt_Page, true)
currentPage++
return true;
}
}
//1,2,3,4,5,6,7,8,9,10
//12345
//678910
//6에서 prev 누른 경우,(min=6, max=10)
fun prevPage(view: View, update_page: (Int) -> Unit):Boolean {
if (currentPage == minPage) {
if (minPage > 1) {
var checkMax = maxPage - 5;
if (checkMax < 5) checkMax = 5;
maxPage = checkMax;
minPage = minPage - 5;
currentPage--;
rePaginationLayout(view, { page ->
update_page(page)
})
return true
}
else
return false
}
else {
if (currentPage > 1) {
var bef_Page = buttonMap.get(currentPage);
var prv_Page = buttonMap.get(currentPage - 1);
activePageButton(bef_Page, false)
activePageButton(prv_Page, true)
currentPage--
return true;
}
else
return false
}
}
fun activePageButton(pageBtn: Button?, isActive: Boolean) {
if (isActive) {
pageBtn?.background = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = (10 * Resources.getSystem().displayMetrics.density).toFloat()
setColor(Color.BLUE)
}
pageBtn?.setTextColor(Color.WHITE)
} else {
pageBtn?.background = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = (10 * Resources.getSystem().displayMetrics.density).toFloat()
setColor(Color.parseColor("#0971B8"))
}
pageBtn?.setTextColor(Color.WHITE)
}
}
fun updatePage(view: View, newPage: Int) {
if (newPage in 1..totalPages) {
var bef_Page = buttonMap.get(currentPage);
var nxt_Page = buttonMap.get(newPage);
activePageButton(bef_Page, false)
activePageButton(nxt_Page, true)
currentPage = newPage
}
}
fun getPageInfo(): String {
return "총 페이지: $totalPages, 현재 페이지: $currentPage, 표시 범위: $minPage ~ $maxPage"
}
}

View File

@ -0,0 +1,184 @@
package com.example.smart119.views
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.viewModels
import com.example.smart119.API_Helper
import com.example.smart119.AlertDialogUtil
import com.example.smart119.R
import com.example.smart119.TransferManager
import com.example.smart119.TransferState
import com.example.smart119.datas.HospitalSearch
import com.example.smart119.interfaces.RetrofitClient
import com.example.smart119.viewModel.TransferViewModel
import com.example.smart119.views.HospitalInfoPopup.showPopupTransfer
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
/**
* A simple [Fragment] subclass.
* Use the [TransferFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class TransferFragment : Fragment() {
// TODO: Rename and change types of parameters
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// TransferManager.registerListener("transfer_update_event", { data ->
// println("Transfer Update Event!!!")
// })
// TransferManager.registerListener("transfer_update_event", ::TransferUpdateEvent)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_transfer, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
FragmentHandler.registerView("TransferFragment", view)
// var tvHospName = view.findViewById<TextView>(R.id.tvTFHospName)
// tvHospName.text = "서울특별시서남병원"
// 카카오 길찾기 API
// val src_pos = "126.705098, 37.412087";
// val dst_pos = "126.696239, 37.395276";
// RetrofitClient.pathInstance.findPath(
// API_Helper.make_findpath_param(src_pos, dst_pos)
// ).enqueue(findPathResponse())
try {
if (TransferManager.getState() == TransferState.STARTED) {
TransferManager.SetLastHospitalInfo()
}
} catch (e: Exception) {
println(e.message)
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// 뒤로 가기 동작 막음
// println("Transfer 뒤로가기클릭!!")
}
})
view.findViewById<Button>(R.id.transferCancel).setOnClickListener {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "이송 취소",
message = "이송을 취소하시겠습니까?",
onPositiveClick = {
// val bottomNaviView = requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
// bottomNaviView.selectedItemId = R.id.hospSearch;
TransferManager.cancelTransfer()
},
onNegativeClick = {
}
)
}
view.findViewById<Button>(R.id.transferComplete).setOnClickListener {
AlertDialogUtil.showAlert(
context = requireActivity(),
title = "이송 완료",
message = "이송을 종료하시겠습니까?",
onPositiveClick = {
val bottomNaviView = requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNaviView.selectedItemId = R.id.hospSearch;
TransferManager.completeTransfer()
},
onNegativeClick = {}
)
}
view.findViewById<Button>(R.id.btnTFDetailInfo).setOnClickListener {
HospitalInfoPopup.showPopupTransfer(it)
var getHPID = HospitalSearch.getHospitalID()
if (!getHPID.isEmpty()) {
try {
LoadingDialog.showDlg(requireContext())
var reqBody = API_Helper.make_detail_hosp_param(getHPID)
RetrofitClient.hospitalInstance.getDetail(reqBody).enqueue(hospDetailResponse())
HospitalInfoPopup.showPopupTransfer(it)
} catch (e: Exception) {
println(e.message)
} finally {
LoadingDialog.hideDlg()
}
}
}
}
private fun hospDetailResponse(): Callback<JsonObject> {
return object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
LoadingDialog.hideDlg()
if (response.isSuccessful) {
val resObj = response.body();
//println(resObj)
/*
val metaObj = resObj?.get("meta");
if (metaObj != null) {
val tot_count = metaObj.asJsonObject["total_count"].asInt
if (tot_count > 0) {
var hsfView = FragmentHandler.getView("HospitalSearchFragment")
if (hsfView != null) {
val docus = resObj.getAsJsonArray("documents")
val addObj = docus[0].asJsonObject["address"];
val add_full = addObj.asJsonObject["address_name"]
val add_si = addObj.asJsonObject["region_1depth_name"]
val add_gu = addObj.asJsonObject["region_2depth_name"]
val add_dong = addObj.asJsonObject["region_3depth_name"]
var geoTv = hsfView.findViewById<TextView>(R.id.tvGeoLocation)
geoTv.text = add_full.asString;
HospitalSearch.set_gps_update(add_full.asString)
}
}
}
*/
//response.body()["meta"].asJsonObject["total_count"]
} else {
println("hospital detail not found")
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
LoadingDialog.hideDlg()
println(t.message)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
// View를 Registry에서 제거
FragmentHandler.unregisterView("TransferFragment")
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 배경색 (optional, 필요 시 설정) -->
<solid android:color="#FFFFFF" /> <!-- 흰색 배경 -->
<!-- 테두리 설정 -->
<stroke
android:width="2dp"
android:color="#000000" /> <!-- 빨간색 테두리 -->
<!-- 모서리 설정 (optional, 필요 시 설정) -->
<corners android:radius="8dp" /> <!-- 둥근 모서리 -->
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#0971B8"/> <!-- 기본 배경색 -->
<corners android:radius="10dp"/> <!-- 둥근 모서리 -->
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- #f5f5f5" 색으로 채움 -->
<solid
android:color="#f5f5f5"/>
<corners
android:bottomLeftRadius="2dp"
android:bottomRightRadius="2dp"
android:topLeftRadius="2dp"
android:topRightRadius="2dp" />
<!-- 1dp 크기의 #000000 색 테두리 -->
<stroke
android:width="0dp"
android:color="#000000"/>
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/MainLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="#0971B8"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
app:navGraph="@navigation/nav_graph"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="63dp"
android:layout_gravity="bottom"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:itemIconSize="18dp"
app:menu="@menu/bottom_navigation_menu">
</com.google.android.material.bottomnavigation.BottomNavigationView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,235 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.5">
<Button
android:id="@+id/hospSearchButtonAS"
android:layout_width="115dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="20dp"
android:text="병원검색"
app:cornerRadius="10dp"
app:layout_constraintStart_toEndOf="@+id/textInputLayout"
app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView
android:id="@+id/cardView9"
android:layout_width="130dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="25dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toEndOf="@+id/cardView8"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/gunGuSelectText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:gravity="start|center_vertical"
android:clickable="true"
android:focusable="true"
android:text="군구선택"
android:textSize="16sp">
</TextView>
<Spinner
android:id="@+id/guGunSelectSpinner"
android:layout_width="match_parent"
android:layout_height="0dp"
android:padding="3dp"
android:prompt="@string/gugun_spinner_prompt"
tools:layout_editor_absoluteX="258dp"
tools:layout_editor_absoluteY="102dp" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cardView10"
android:layout_width="130dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="25dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toEndOf="@+id/cardView9"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/hospCatText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:gravity="start|center_vertical"
android:clickable="true"
android:focusable="true"
android:text="병원분류명선택"
android:textSize="16sp">
</TextView>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cardView11"
android:layout_width="160dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="25dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toEndOf="@+id/cardView10"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/prtText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:gravity="start|center_vertical"
android:clickable="true"
android:focusable="true"
android:text="진료과목선택"
android:textSize="16sp">
</TextView>
</androidx.cardview.widget.CardView>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="15dp"
android:hint="관련검색어를 입력하세요"
android:minWidth="200dp"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@+id/cardView11"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/relateInput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="200dp"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.cardview.widget.CardView
android:id="@+id/cardView8"
android:layout_width="180dp"
android:layout_height="40dp"
android:layout_marginStart="20dp"
android:layout_marginTop="25dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Spinner
android:id="@+id/siSelectSpinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="3dp"
android:prompt="@string/si_spinner_prompt" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="7">
<androidx.cardview.widget.CardView
android:id="@+id/reCyclerCardViewLS"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="70dp"
app:cardCornerRadius="5dp"
app:cardElevation="2dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/reCyclerViewLS"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false" />
</androidx.cardview.widget.CardView>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reCyclerCardViewLS">
<LinearLayout
android:layout_width="500dp"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/pagePrevAS"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@drawable/page_button_background"
android:text="이전"
app:cornerRadius="10dp"></Button>
<LinearLayout
android:id="@+id/pageButtonsContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp"></LinearLayout>
<Button
android:id="@+id/pageNextAS"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@drawable/page_button_background"
android:text="다음"
app:cornerRadius="10dp"></Button>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<fragment
android:id="@+id/nav_host_fragment_content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:id="@+id/cell1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:text="Data 1" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:id="@+id/cell2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:text="Data 2" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:id="@+id/cell3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:text="Data 3" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
<View
android:id="@+id/cell4_lidar_color"
android:layout_width="22dp"
android:layout_height="22dp"
android:background="#FF0000"
android:layout_marginStart="50dp"
/>
<TextView
android:id="@+id/cell4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:paddingStart="10dp"
android:text="Data 4" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center"
android:padding="8dp">
<TextView
android:id="@+id/cell5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:text="Data 5" />
</LinearLayout>
<LinearLayout
android:id="@+id/cell6_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center"
android:padding="8dp">
<Button
android:id="@+id/cell6_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp"
app:cornerRadius="10dp"
android:text="환자 정보 전송" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="진료과목을 선택하세요"
android:textSize="25sp"
android:textStyle="bold"
android:paddingBottom="8dp"
android:layout_weight="1"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_weight="8">
<!-- 그룹 컨테이너 -->
<LinearLayout
android:id="@+id/departGroupContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp"
android:orientation="vertical"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateTint="@color/white"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="데이터 불러오는 중..."
android:textColor="@color/white"
android:textSize="16sp"/>
</LinearLayout>

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:fillViewport="true"
tools:context=".views.LoginFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp">
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="119구급대 응급의료 스마트플랫폼"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="40sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="30dp"
app:cardCornerRadius="13dp"
app:cardElevation="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvTitle">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"
android:gravity="center"
android:text="접속 정보"
android:textColor="#0971B8"
android:textSize="30sp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="5dp"
android:layout_weight="2">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginIdText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="아이디"
android:inputType="text"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="5dp"
android:layout_weight="2">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/carNoText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="차량번호 ( ex: 999허9999 )"
android:inputType="text"
android:maxLength="10"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/accessButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginTop="20dp"
android:layout_marginRight="5dp"
android:layout_weight="0.5"
android:gravity="center"
android:text="시작"
android:textSize="20sp"
app:cornerRadius="10dp"
tools:layout_editor_absoluteX="289dp"
tools:layout_editor_absoluteY="257dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/logo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cardView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context=".views.HospitalSearchFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="응급 병원 검색"
android:textColor="@color/white"
android:textSize="40sp"
android:textStyle="bold"
android:layout_marginTop="20dp"
app:layout_constraintBottom_toTopOf="@+id/cardView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView
android:id="@+id/cardView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="130dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="@color/white"
app:cardCornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:tabGravity="start"
app:tabMode="scrollable"
app:tabIndicatorColor="@android:color/holo_blue_light"
app:tabTextColor="@android:color/black"
app:tabSelectedTextColor="@android:color/holo_blue_dark"
android:layout_weight="8">
</com.google.android.material.tabs.TabLayout>
<!-- ViewPager2 (탭 변경 시 Fragment 변경) -->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_weight="1"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -0,0 +1,494 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/transferScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context=".views.TransferFragment">
<!-- TODO: Update blank fragment layout -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="응급 병원 이송"
android:textColor="@color/white"
android:textSize="40sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/cardView3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView
android:id="@+id/cardView3"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="130dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
app:cardCornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.cardview.widget.CardView
android:id="@+id/cardView6"
android:layout_width="450dp"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@color/black"
app:cardCornerRadius="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#55000000">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="1dp"
app:cardCornerRadius="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView8"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_weight="1"
android:gravity="center"
android:text="이송 병원까지의 정보"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.513"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5">
<TextView
android:id="@+id/tvLeftDistVal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:ellipsize="none"
android:text="0.0km"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@+id/tvLeftDist"
app:layout_constraintTop_toTopOf="@+id/tvLeftDist" />
<TextView
android:id="@+id/tvTFHospName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="16dp"
android:ellipsize="none"
android:text="-"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@+id/tvLeftTime3"
app:layout_constraintTop_toTopOf="parent" />
<!-- <TextView-->
<!-- android:id="@+id/tvTFLidarValue"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="44dp"-->
<!-- android:layout_marginTop="8dp"-->
<!-- android:text="-"-->
<!-- android:textSize="20sp"-->
<!-- android:ellipsize="none"-->
<!-- app:layout_constraintStart_toEndOf="@+id/tvLeftTime4"-->
<!-- app:layout_constraintTop_toBottomOf="@+id/tvTFHospName" />-->
<LinearLayout
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_marginStart="43dp"
android:gravity="start|center_vertical"
android:orientation="horizontal"
app:layout_constraintStart_toEndOf="@+id/tvLeftTime4"
app:layout_constraintTop_toTopOf="@+id/tvLeftTime4">
<View
android:id="@+id/tvTFLidarColor"
android:layout_width="22dp"
android:layout_height="22dp"
android:background="#87CEEB" />
<TextView
android:id="@+id/tvTFLidarValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingStart="8dp"
android:text="정보 없음"
android:textSize="20sp" />
</LinearLayout>
<TextView
android:id="@+id/tvLeftTime3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="병원 이름"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvLeftTime4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="혼잡도"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvLeftTime3" />
<Button
android:id="@+id/btnTFDetailInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="상세정보"
android:visibility="invisible"
app:cornerRadius="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvLeftTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="남은 시간"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvLeftTime4" />
<TextView
android:id="@+id/tvLeftTimeVal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:ellipsize="none"
android:text="0초"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@+id/tvLeftTime"
app:layout_constraintTop_toTopOf="@+id/tvLeftTime" />
<TextView
android:id="@+id/infoUpdateTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:ellipsize="none"
android:text="00:00:00"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@+id/tvLeftTimeVal"
app:layout_constraintTop_toTopOf="@+id/tvLeftTimeVal" />
<TextView
android:id="@+id/tvLeftDist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="남은 거리"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvLeftTime" />
<TextView
android:id="@+id/tfStateLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="이송 상태"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvLeftDist" />
<TextView
android:id="@+id/tfState"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="-"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@+id/tfStateLabel"
app:layout_constraintTop_toTopOf="@+id/tfStateLabel" />
<Button
android:id="@+id/transferCancel"
android:layout_width="137dp"
android:layout_height="77dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:text="이송취소"
android:textSize="20sp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
</androidx.cardview.widget.CardView>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#A0A0A0"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp">
</View>
<LinearLayout
android:layout_width="400dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.cardview.widget.CardView
android:id="@+id/cvGPS_State"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
android:layout_weight="1"
app:cardCornerRadius="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#B04169E1">
<TextView
android:id="@+id/textView27"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:text="00:00:00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.95"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<TextView
android:id="@+id/textView23"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.133"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView18"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GPS 상태"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.133"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cvFindPathAPI_State"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
android:layout_weight="1"
app:cardCornerRadius="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#B04169E1">
<TextView
android:id="@+id/textView26"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:text="00:00:00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.95"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<TextView
android:id="@+id/textView24"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="길찾기 상태"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.133"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<TextView
android:id="@+id/textView20"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.133"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cvServer_State"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
android:layout_weight="1"
app:cardCornerRadius="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#B04169E1">
<TextView
android:id="@+id/textView25"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="서버와의 연결 상태"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.133"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<TextView
android:id="@+id/textView21"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:text="00:00:00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.95"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<TextView
android:id="@+id/textView22"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:text="이송여부"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.09"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
<Button
android:id="@+id/transferComplete"
android:layout_width="244dp"
android:layout_height="129dp"
android:layout_marginStart="168dp"
android:text="이송완료"
android:textSize="20sp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/cardView6"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.061"
android:visibility="invisible"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="군구를 선택하세요"
android:textSize="25sp"
android:textStyle="bold"
android:paddingBottom="8dp" />
<!-- 기존 Spinner 대신 AutoCompleteTextView 사용 -->
<AutoCompleteTextView
android:id="@+id/autoCompleteTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@android:drawable/editbox_background"
android:padding="10dp"
android:dropDownHeight="200dp"
android:visibility="invisible"/>
<GridView
android:id="@+id/gridViewOptions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:numColumns="4"
android:stretchMode="columnWidth"
android:gravity="center">
</GridView>
</LinearLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/column1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:text="Header 1"
android:background="#0971B8"
android:textColor="@color/white"
android:textStyle="bold" />
<!-- <TextView-->
<!-- android:id="@+id/column2"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:gravity="center"-->
<!-- android:padding="8dp"-->
<!-- android:text="Header 2"-->
<!-- android:background="#CCCCCC"-->
<!-- android:textStyle="bold" />-->
<!-- <TextView-->
<!-- android:id="@+id/column3"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:gravity="center"-->
<!-- android:padding="8dp"-->
<!-- android:text="Header 3"-->
<!-- android:background="#CCCCCC"-->
<!-- android:textStyle="bold" />-->
</LinearLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="병원분류명을 선택하세요"
android:textSize="25sp"
android:textStyle="bold"
android:paddingBottom="8dp" />
<GridView
android:id="@+id/gridViewOptions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"
android:gravity="center">
</GridView>
</LinearLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="의료기관을 선택하세요"
android:textSize="25sp"
android:textStyle="bold"
android:paddingBottom="8dp" />
<GridView
android:id="@+id/gridViewOptions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"/>
</LinearLayout>

View File

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.5">
<androidx.cardview.widget.CardView
android:id="@+id/cardView7"
android:layout_width="180dp"
android:layout_height="40dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toEndOf="@+id/gpsRefresh"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/hospCatLocText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:gravity="start|center_vertical"
android:clickable="true"
android:focusable="true"
android:text="의료기관 선택"
android:textSize="16sp">
</TextView>
<Spinner
android:id="@+id/spMedical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:entries="@array/medical_list"
android:padding="3dp"
android:prompt="@string/si_spinner_prompt" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cardView5"
android:layout_width="180dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="20dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toEndOf="@+id/cardView7"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/hospPrtLocText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:gravity="start|center_vertical"
android:clickable="true"
android:focusable="true"
android:text="진료과 선택"
android:textSize="16sp">
</TextView>
<Spinner
android:id="@+id/spDepart"
android:layout_width="match_parent"
android:layout_height="0dp"
android:entries="@array/medical_department_list"
android:padding="3dp"
android:prompt="@string/si_spinner_prompt" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cardView4"
android:layout_width="230dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="20dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toEndOf="@+id/cardView5"
app:layout_constraintTop_toTopOf="parent">
<Spinner
android:id="@+id/spEmgYN"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/emergency_yn_list"
android:padding="3dp"
android:prompt="@string/si_spinner_prompt" />
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/hospSearchButton"
android:layout_width="115dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="18dp"
android:text="병원검색"
app:cornerRadius="10dp"
app:layout_constraintStart_toEndOf="@+id/cardView4"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView5"
android:layout_width="163dp"
android:layout_height="31dp"
android:layout_marginStart="28dp"
android:layout_marginTop="25dp"
android:text="단말기 현재 위치 : "
android:textSize="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvGeoLocation"
android:layout_width="wrap_content"
android:layout_height="31dp"
android:layout_marginStart="10dp"
android:layout_marginTop="25dp"
android:text="서울특별시"
android:textSize="20dp"
app:layout_constraintStart_toEndOf="@+id/textView5"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/gpsRefresh"
android:layout_width="24dp"
android:layout_height="20dp"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:adjustViewBounds="true"
android:background="@null"
android:scaleType="fitXY"
android:src="@drawable/gps"
app:layout_constraintStart_toEndOf="@+id/tvGeoLocation"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="7">
<androidx.cardview.widget.CardView
android:id="@+id/reCyclerCardViewLS"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="70dp"
app:cardCornerRadius="5dp"
app:cardElevation="2dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/reCyclerViewLS"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false" />
</androidx.cardview.widget.CardView>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reCyclerCardViewLS">
<LinearLayout
android:layout_width="500dp"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/pagePrevLS"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@drawable/page_button_background"
android:text="이전"
app:cornerRadius="10dp"></Button>
<LinearLayout
android:id="@+id/pageButtonsContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp"></LinearLayout>
<Button
android:id="@+id/pageNextLS"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@drawable/page_button_background"
android:text="다음"
app:cornerRadius="10dp"></Button>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:padding="16dp"
android:background="@drawable/border_line"
android:elevation="8dp">
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="3dp"
android:layout_marginTop="3dp"
android:layout_marginRight="3dp"
android:layout_marginBottom="3dp"
android:background="@color/white"
app:cardCornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/popupText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="병원 상세 정보"
android:textSize="18sp" />
<Button
android:id="@+id/closePopup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="닫기"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:padding="16dp"
android:background="@drawable/border_line"
android:elevation="8dp">
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="5dp"
android:background="@color/white"
app:cardCornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/savePopup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="36dp"
android:text="적용"
app:cornerRadius="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="75dp"
android:background="@color/black"
android:layout_marginStart="36dp"
android:layout_marginEnd="36dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/popupText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:layout_marginTop="40dp"
android:text="주소 검색 필터 항목"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="130dp"
android:layout_height="30dp"
android:layout_marginStart="230dp"
android:layout_marginTop="100dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Spinner
android:id="@+id/guGunSelectSpinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:prompt="@string/gugun_spinner_prompt"
tools:layout_editor_absoluteX="258dp"
tools:layout_editor_absoluteY="102dp"
android:padding="3dp"/>
</androidx.cardview.widget.CardView>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:layout_marginTop="150dp"
android:hint="관련검색어를 입력하세요"
android:minWidth="240dp"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/relateInput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="200dp"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.cardview.widget.CardView
android:layout_width="180dp"
android:layout_height="30dp"
android:layout_marginStart="36dp"
android:layout_marginTop="100dp"
app:cardBackgroundColor="@color/white"
app:cardElevation="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Spinner
android:id="@+id/siSelectSpinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="3dp"
android:prompt="@string/si_spinner_prompt" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/emgLogin"
android:icon="@drawable/login"
android:title="로그인" />
<item
android:id="@+id/hospSearch"
android:icon="@drawable/search"
android:title="병원찾기" />
<item
android:id="@+id/hospTransfer"
android:icon="@drawable/transfer"
android:title="이송중" />
</menu>

View File

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.smart119.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Some files were not shown because too many files have changed in this diff Show More