목표
1. Android에서 WebView를 활성화하고 @javascriptInterface를 통해서 네이티브 코드를 직접 제어한다.
2. evaluateJavaScript를 이용하여 JAVASCRIPT를 실행시킨다 블루투스 연결을 하고 난 이후 하드웨어와 통신 이후에 WebView에 있는 LocalStroage의 값을 변화시킨다.
목표 1. Android 에서 WebView를 활성화 시키고 @javascriptInterface를 통해 네이티브 코드 제어하기
@JavascriptInterface는 JavaScript에서 Android의 특정 메서드를 호출할 수 있도록 허용하는 데 사용됩니다. 이 기능을 통해 웹 페이지(JavaScript)에서 네이티브 Android 코드를 직접 호출할 수 있습니다. 이 코드에서는 ConnectedKey와 gpsHandler 인스턴스에 @JavascriptInterface를 적용하여, 웹 페이지에서 해당 클래스의 메서드를 사용할 수 있도록 하고 있습니다.
현재 저의 코드는 WebViewActivity에서 진행하고 있습니다. 해당 코드에서는 웹뷰에서 처리해야하는 팝업창, 바텀바, 다이얼로그, 등 다양한 핸들러를 구성하여 쿠키와 로컬스토리지를 다 구성해둔 상태입니다.
그래서 일단 집중적으로 목표에 맞는 코드 구성을 작성해보려고합니다.
주요 구성 요소
- ConnectedKey 클래스
- gpsHandler (GPSHandler 클래스의 인스턴스)
각 부분 설명
1. ConnectedKey 클래스의 @JavascriptInterface 메서드
ConnectedKey는 @JavascriptInterface로 노출된 메서드들을 통해 JavaScript에서 호출할 수 있게 됩니다. 이 클래스는 WebViewActivity의 WebView에 addJavascriptInterface 메서드로 연결됩니다.
- setupWebView 함수의 아래 코드에서 ConnectedKey 객체를 _NativeApp라는 이름으로 WebView에 연결합니다.
-
binding.webView.addJavascriptInterface(ConnectedKey(this@WebViewActivity, binding.webView), "_NativeApp")
- 이제 JavaScript에서 _NativeApp.메서드명()으로 ConnectedKey 클래스의 @JavascriptInterface로 노출된 메서드를 호출할 수 있습니다.
- 예를 들어, ConnectedKey_open(uuid)와 같은 메서드를 호출하여 BLE 장치와의 연결 작업을 JavaScript에서 시작할 수 있습니다.
이 부분은 `ConnectedKey` 클래스의 인스턴스를 WebView에 JavaScript 인터페이스로 추가하여 JavaScript 코드가 Android 네이티브 메서드에 접근할 수 있도록 하는 역할을 합니다. 여기서는 `ConnectedKey` 클래스의 인스턴스를 `"_NativeApp"`이라는 이름으로 WebView에 연결하고 있습니다.
binding.webView.addJavascriptInterface(ConnectedKey(this@WebViewActivity, binding.webView), "_NativeApp")
이 코드는 다음과 같은 의미를 갖습니다:
### 1. `addJavascriptInterface` 메서드
`addJavascriptInterface`는 Android의 WebView에서 JavaScript와 네이티브 Android 코드 간의 상호작용을 가능하게 합니다. 이 메서드는 JavaScript가 Android의 메서드를 호출할 수 있도록 합니다.
형식:
webView.addJavascriptInterface(Object, String)
- **Object**: JavaScript에서 호출하고자 하는 메서드들이 포함된 객체를 지정합니다.
- **String**: JavaScript에서 이 객체를 참조할 이름을 지정합니다.
### 2. `ConnectedKey` 인스턴스를 연결
`ConnectedKey`는 네이티브 메서드를 `@JavascriptInterface`로 노출하는 클래스입니다. 이 클래스의 인스턴스를 WebView와 연결함으로써, `ConnectedKey` 클래스의 메서드들이 JavaScript에서 직접 호출될 수 있습니다.
ConnectedKey(this@WebViewActivity, binding.webView)
- `this@WebViewActivity`: `ConnectedKey` 클래스의 생성자에서 `Context`와 `WebView`가 필요하므로, 이 `Context`로 `WebViewActivity`의 인스턴스를 전달합니다.
- `binding.webView`: 현재 `WebView` 객체를 `ConnectedKey`에 전달합니다. 이는 `ConnectedKey`에서 `evaluateJS` 메서드 등과 같은 WebView 관련 작업을 할 수 있게 합니다.
#### `_NativeApp` 이름
`"_NativeApp"`라는 이름은 JavaScript가 `ConnectedKey` 객체를 참조할 때 사용하는 이름입니다. JavaScript에서 `ConnectedKey` 클래스에 정의된 메서드들을 사용하려면 다음과 같이 호출할 수 있습니다:
// JavaScript에서 _NativeApp 객체에 접근하여 ConnectedKey의 메서드 호출
_NativeApp.메서드명(파라미터);
예를 들어, `ConnectedKey`에 `@JavascriptInterface`로 정의된 `ConnectedKey_open(uuid)` 메서드를 호출하려면:
// JavaScript에서 Android의 ConnectedKey_open 메서드 호출
_NativeApp.ConnectedKey_open("your-uuid-value");
### 전체적인 동작 과정
1. **WebView에서 JavaScript 활성화**: `setupWebView` 메서드 내에서 `webView.settings.javaScriptEnabled = true`로 JavaScript를 활성화합니다.
2. **addJavascriptInterface로 연결**: `ConnectedKey` 클래스 인스턴스를 `"_NativeApp"` 이름으로 WebView에 추가하여 JavaScript가 `_NativeApp` 객체로 Android 메서드를 호출할 수 있게 합니다.
3. **JavaScript에서 Android 메서드 호출**: JavaScript에서 `_NativeApp` 객체를 통해 Android의 `ConnectedKey` 클래스 메서드를 호출하여 BLE 작업, 위치 정보 요청 등 다양한 네이티브 기능을 수행합니다.
이를 통해 Android의 네이티브 코드와 웹(JavaScript) 사이에 상호작용할 수 있습니다.
2. WebView 설정 관련 코드
JavaScript와의 브릿지를 활성화하기 위해 WebView의 설정을 조정하고 JavaScript 실행을 허용해야 합니다.
- JavaScript 실행 허용: webView.settings.javaScriptEnabled = true로 JavaScript를 활성화합니다
binding.webView.settings.javaScriptEnabled = true
- 다중 창 지원: setSupportMultipleWindows(true)를 사용하여 WebView에서 JavaScript로 다중 창을 여는 것을 허용합니다.
settings.setSupportMultipleWindows(true)
3. WebChromeClient와 WebViewClient
이 WebView 설정에서는 JavaScript 알림, 파일 선택기, 위치 권한 등을 처리하기 위해 WebChromeClient와 WebViewClient도 설정합니다. 이 두 객체는 다음과 같은 역할을 합니다.
- WebChromeClient: JavaScript의 alert, confirm, 파일 선택기, 위치 권한 요청을 처리합니다.
- WebViewClient: WebView의 기본 동작을 확장하거나 수정할 수 있습니다.
2. evaluateJavaScript를 이용하여 JAVASCRIPT를 실행시킨다 이후 WebView에 있는 LocalStroage의 값을 변화시킨다.
class ConnectedKey(private val context: Context, private val webView: WebView) {
// JavaScript 코드 실행 함수
fun evaluateJS(script: String, callback: (String?) -> Unit) {
webView.post {
webView.evaluateJavascript(script) { value ->
callback(value)
}
}
}
}
class BluetoothManager(private val context: Context, private val connectedKey: ConnectedKey? = null) {
// 명령 완료 후 처리하는 함수
private fun handleCommandCompletion(uuid: String, rssi: Int) {
if (isOpenCommand) {
// 문 열림 명령이 완료되었음을 저장
isOpen[uuid] = 1
Log.d("Bluetooth", "문 열림 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
saveDoorStatus(uuid, true, rssi)
// localStorage 업데이트
uuidSecretKeyMap[uuid]?.let { secretKey ->
updateLocalStorageStatus(uuid, secretKey, 1)
}
} else {
// 문 닫힘 명령 완료 시 처리
isOpen[uuid] = 0
Log.d("Bluetooth", "문 닫힘 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
saveDoorStatus(uuid, false, rssi)
// localStorage 업데이트
uuidSecretKeyMap[uuid]?.let { secretKey ->
updateLocalStorageStatus(uuid, secretKey, 0)
}
}
Log.d("Bluetooth", "handleCommandCompletion: 최종 carList 상태: $carList")
Log.d("Bluetooth", "handleCommandCompletion: 최종 isOpen 상태: $isOpen")
}
// localStorage의 UUID 상태 업데이트 함수
private fun updateLocalStorageStatus(uuid: String, secretKey: String, status: Int) {
val script = """
var item = localStorage.getItem('ConnectedKey_UUID');
if (item) {
var updatedItem = item.split(',').map(function(pair) {
if (pair.includes('$uuid[$secretKey]')) {
return '$uuid[$secretKey]:$status';
}
return pair;
}).join(',');
localStorage.setItem('ConnectedKey_UUID', updatedItem);
}
"""
connectedKey?.evaluateJS(script) { result ->
Log.d("BluetoothManager", "JavaScript 실행 결과: $result")
}
}
}
ConnectedKey라는 Bridge를 담은 Class에서 evaluateJS 함수를 정의하고 BluetoothManager에서 호출한 뒤 블루투스 연결이 완료된다면 해당 값을 저장하여 ConnectedKey_UUID라는 키를 가진 로컬스토리지 값의 저장한다라는 전체적인 로직으로 진행합니다. 주요 함수들을 정리한다면 아래와 같습니다.
### 주요 흐름 요약
1. `handleCommandCompletion` 메서드: 블루투스 명령(문 열기/닫기) 완료 후 상태를 업데이트합니다.
2. `updateLocalStorageStatus` 메서드: JavaScript를 실행하여 `localStorage`에 값을 설정합니다.
3. `evaluateJS` 메서드: Android 네이티브 코드에서 JavaScript를 실행하기 위한 도구로 활용됩니다.
### 각 부분 설명
#### 1. `handleCommandCompletion` 메서드
`handleCommandCompletion` 메서드는 문이 열리거나 닫힌 후 BLE 명령 처리가 완료되었을 때 호출됩니다. 여기서 UUID와 상태를 업데이트하고, 그 결과를 `localStorage`에 반영합니다.
private fun handleCommandCompletion(uuid: String, rssi: Int) {
if (isOpenCommand) {
// 문 열림 명령이 완료되었음을 저장
isOpen[uuid] = 1
Log.d("Bluetooth", "문 열림 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
saveDoorStatus(uuid, true, rssi)
// localStorage 업데이트
uuidSecretKeyMap[uuid]?.let { secretKey ->
updateLocalStorageStatus(uuid, secretKey, 1)
}
} else {
// 문 닫힘 명령 완료 시 처리
isOpen[uuid] = 0
Log.d("Bluetooth", "문 닫힘 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
saveDoorStatus(uuid, false, rssi)
// localStorage 업데이트
uuidSecretKeyMap[uuid]?.let { secretKey ->
updateLocalStorageStatus(uuid, secretKey, 0)
}
}
Log.d("Bluetooth", "handleCommandCompletion: 최종 carList 상태: $carList")
Log.d("Bluetooth", "handleCommandCompletion: 최종 isOpen 상태: $isOpen")
}
1. `isOpenCommand` 플래그에 따라 명령이 문 열기인지 닫기인지를 판별합니다.
- 문 열기 명령(`isOpenCommand = true`)의 경우, `isOpen` 맵에 `UUID` 키로 값 `1`을 저장하여 열림 상태로 기록합니다.
- 문 닫기 명령(`isOpenCommand = false`)의 경우, `isOpen` 맵에 `UUID` 키로 값 `0`을 저장하여 닫힘 상태로 기록합니다.
2. `saveDoorStatus(uuid, 상태, rssi)`: 블루투스 연결의 결과 상태와 `RSSI`를 저장하여 앱 내의 상태를 업데이트합니다.
3. `localStorage` 업데이트:
- `uuidSecretKeyMap`에서 현재 UUID에 대한 `secretKey`를 찾고, `updateLocalStorageStatus` 메서드를 호출하여 `localStorage`의 상태를 업데이트합니다.
#### 2. `updateLocalStorageStatus` 메서드
이 메서드는 JavaScript 코드 조각을 생성하여 `evaluateJavaScript`를 사용해 WebView의 `localStorage`에 값을 설정합니다.
private fun updateLocalStorageStatus(uuid: String, secretKey: String, status: Int) {
val script = """
var item = localStorage.getItem('ConnectedKey_UUID');
if (item) {
var updatedItem = item.split(',').map(function(pair) {
if (pair.includes('$uuid[$secretKey]')) {
return '$uuid[$secretKey]:$status';
}
return pair;
}).join(',');
localStorage.setItem('ConnectedKey_UUID', updatedItem);
}
"""
connectedKey?.evaluateJS(script) { result ->
Log.d("BluetoothManager", "JavaScript 실행 결과: $result")
}
}
1. **JavaScript 코드 조각 생성**:
- `val script` 변수에 `localStorage` 값을 업데이트하는 JavaScript 코드를 문자열 형태로 저장합니다.
2. **localStorage에서 값 읽기 및 분할**:
- `localStorage.getItem('ConnectedKey_UUID')`를 통해 `ConnectedKey_UUID` 키에 해당하는 값을 가져옵니다. 이 값이 있다면, 다음 작업을 수행합니다.
- `item.split(',')`을 통해 `ConnectedKey_UUID`의 값(예: `"UUID1[SecretKey1]:1,UUID2[SecretKey2]:0"`)을 쉼표로 분할하여 배열로 만듭니다. 각 배열 요소는 `"UUID[SecretKey]:status"` 형식의 문자열입니다.
3. **값 업데이트 및 저장**:
- `.map()`을 사용하여 `uuid`와 `secretKey`가 포함된 항목을 찾고, `status` 값을 변경합니다.
- 업데이트된 배열을 다시 `join(',')`으로 연결하여 `updatedItem` 문자열로 만든 뒤, 이를 `localStorage.setItem('ConnectedKey_UUID', updatedItem)`을 통해 `localStorage`에 저장합니다.
4. **evaluateJavaScript 실행**:
- `evaluateJavaScript`를 통해 `script` 코드를 WebView에서 실행하여 `localStorage`의 `ConnectedKey_UUID` 값을 실시간으로 업데이트합니다.
- 업데이트 후 `result` 콜백을 사용해 `JavaScript 실행 결과`를 로그로 출력하여 성공 여부를 확인할 수 있습니다.
#### 3. `evaluateJavaScript`를 통한 `localStorage` 값 변경
`evaluateJavaScript` 메서드는 Android의 네이티브 코드에서 WebView 내 JavaScript를 실행할 수 있는 방법을 제공합니다. 이를 통해 WebView의 `localStorage`를 실시간으로 수정할 수 있습니다.
- `evaluateJavaScript(script: String, callback: (String?) -> Unit)` 메서드는 `WebView`에서 `JavaScript` 코드(`script`)를 실행하고, 그 결과를 `callback`으로 반환합니다.
- 이 코드는 Bluetooth 통신 후 상태가 변경될 때마다 `localStorage` 값을 즉시 업데이트하여, WebView의 JavaScript 측에서도 새로운 상태 값을 실시간으로 반영할 수 있게 합니다.
### 요약
1. **handleCommandCompletion**: 블루투스 명령 완료 후, 명령 종류에 따라 `localStorage` 업데이트를 위한 UUID, secretKey, 상태값을 결정합니다.
2. **updateLocalStorageStatus**: JavaScript 코드 조각을 생성하여 `evaluateJavaScript`로 WebView의 `localStorage` 값을 실시간으로 업데이트합니다.
3. **evaluateJavaScript 사용**: Android 네이티브 코드에서 JavaScript 코드를 WebView에서 실행하여, 블루투스 작업 완료 후 `localStorage` 상태값을 변경합니다.
'FRONT > AOS' 카테고리의 다른 글
[AOS] Android Studio 에서 WebView 처리 중 <input/> type별 처리 중 QR CODE SCAN (0) | 2024.12.06 |
---|