# Android

<figure><img src="https://169964493-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwMb5WkedgvZ9Yk3Yxqep%2Fuploads%2FoCyaDTPB6oJRvlgbwXqa%2FFlutter.png?alt=media&#x26;token=d20e6582-bf40-40cc-96a5-06c601be77b4" alt=""><figcaption></figcaption></figure>

***

Android 가이드 문서는 아래 조건을 기준으로 작성되었습니다.

* 사용언어 : <mark style="color:blue;">`Kotlin`</mark>&#x20;
  * 해당 가이드는 코틀린 언어를 기반으로 작성되었습니다.
* 아키텍쳐 : <mark style="color:blue;">`ViewBinding`</mark>
  * 해당 가이드는 [<mark style="color:orange;">ViewBinding(뷰결합)</mark>](https://developer.android.com/develop/ui/views/layout/webapps/webview?hl=ko#EnablingJavaScript) 아키텍처로 작성되었습니다.
  * findViewByld 형식의 아키텍처를 사용하시는 경우, 자체적인 샘플 코드의 수정이 필요할 수 있습니다.

```kotlin
// build.gradle (:app)
android {
  // ...
  buildFeatures {
      viewBinding true
  }
  // ...
}
```

***

## 1단계: Activity 레이아웃 파일 생성

<mark style="color:blue;">`res/layout`</mark> 경로에 런처를 실행할 Activity 레이아웃 파일을 생성합니다.

{% hint style="warning" %}
파일명, 컴포넌트명, 속성값은 샘플로 작성된 명칭으로 수정하셔도 무방합니다.
{% endhint %}

```xml
<!-- res/layout/activity_launcher.xml -->

<?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:background="@color/white"
    tools:context=".LauncherActivity">

    <WebView
        android:id="@+id/webViewLauncher"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

<mark style="color:blue;">`AndroidManifest.xml`</mark> 파일의 <mark style="color:blue;">`<application></application>`</mark> 태그 내 런처 WebView Activity를 추가합니다.

* 게임은 세로모드 해상도에 최적화 되어 <mark style="color:blue;">`screenOrientation`</mark> 값을 <mark style="color:blue;">`portrait`</mark> 으로 고정해주세요.

```xml
<!-- AndroidManifest.xml -->
<application ...>
    <!-- 추가 -->
    <activity
        android:name=".LauncherActivity"
        android:screenOrientation="portrait"
        android:theme="@style/Theme.{projectName}.NoActionBar"
        tools:ignore="LockedOrientationActivity" /> 
</application>
```

***

## 2단계: Activity 내 WebView 설정 추가

### 1. Javascript Interface 선언

런처 ↔ 클라이언트 앱 간의 통신을 위해 우리는 <mark style="color:blue;">`Javascript Interface`</mark> 선언이 필요합니다.&#x20;

이와 관련된 내용은 [<mark style="color:orange;">\[안드로이드 개발자 사이트\]</mark>](https://developer.android.com/guide/webapps/webview?hl=ko#EnablingJavaScript)를 참고 할 수 있습니다.

#### Javascript Interface 추가 코드

* <mark style="color:blue;">`addJavascriptInterface`</mark> 선언 시 name 값을 <mark style="color:blue;">`BlpLauncher`</mark> 로 설정해야 합니다.

```xml
binding.webViewLauncher.apply {
    // JavascriptInterface 추가
    addJavascriptInterface(
        ExampleWebAppInterface(this@LauncherActivity),
        "BlpLauncher"
    )
}
```

<mark style="color:blue;">`ExampleWebAppInterface`</mark> 클래스 내부에 아래 케이스 function들을 선언합니다.

<table><thead><tr><th width="274">Interface function</th><th>Description</th></tr></thead><tbody><tr><td><mark style="color:orange;"><code>closeLauncher()</code></mark></td><td>런처의 Back 버튼 UI 클릭 시 (뒤로가기 처리)</td></tr><tr><td><mark style="color:orange;"><code>launcherLoaded()</code></mark></td><td>런처의 로드가 완료된 후 추가적인 처리 필요 시 사용</td></tr><tr><td><mark style="color:orange;"><code>timerMissionComplete()</code></mark></td><td>타이머 미션 완료 후 추가적인 처리 필요 시 사용</td></tr><tr><td><mark style="color:orange;"><code>giftReceived()</code></mark></td><td>육성완료 후 추가적인 처리 필요 시 사용</td></tr></tbody></table>

```kotlin
// 블리피 Launcher Web <-> App 인터페이스 규격
class ExampleWebAppInterface(private val mContext: Context) {
    @JavascriptInterface
    fun closeLauncher() {
        // 런처 뒤로가기 > 런처 종료
        (mContext as Activity).finish()
    }
    
    @JavascriptInterface
    fun launcherLoaded() {
        // 런처의 로드가 완료된 후 추가적인 처리 필요 시 사용
    }
    
    @JavascriptInterface
    fun timerMissionComplete() {
        // 타이머 미션 완료 후 추가적인 처리 필요 시 사용
    }
    
    @JavascriptInterface
    fun giftReceived() {
        // 육성완료 후 추가적인 처리 필요 시 사용
    }
}
```

### 2. WebView 기본 설정 추가

런처가 WebView 내에서 정상적인 동작을 하려면 몇가지 기본 설정이 필요합니다.

* WebView에 로드한 런처 자바스크립트 코드가 정상적으로 허용되도록 <mark style="color:blue;">`javaScriptEnabled`</mark> 값을 <mark style="color:blue;">`true`</mark> 로 설정해주세요.
* 런처에서는 <mark style="color:blue;">`Storage`</mark> 영역을 사용하기에 <mark style="color:blue;">`domStorageEnabled`</mark> 값을 <mark style="color:blue;">`true`</mark> 로 설정해주세요.

```kotlin
binding.webViewLauncher.settings.apply {
    javaScriptEnabled = true    // 자바 스크립트 허용
    domStorageEnabled = true    // storage enabled (스토리지 사용하여 dom 가져올 수 있도록 함)
}
```

### 3. WebView URL 로드

Activity가 실행되었을 때 <mark style="color:blue;">`onCreate()`</mark> 안에서 <mark style="color:blue;">`런처 호출 URL`</mark> 을 WebView에 로드합니다.

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...

    binding.webViewLauncher.apply {
        // WebView에 런처 호출 URL을 로드
        loadUrl("런처 호출 URL")
    }

    // ...
}
```

***

## 3단계: onDestroy() 설정

Activity가 종료될 때 <mark style="color:blue;">`onDestroy()`</mark> 라이프 사이클이 수행됩니다. \
이 때 WebView 인스턴스를 초기화 하고 Destroy 하여 메모리 누수를 방지합니다.&#x20;

런처 WebView가 포함된 Activity 뿐만 아니라 이 후 추가적으로 WebView Activity를 생성할 경우 다음과 같이 <mark style="color:blue;">`onDestroy()`</mark> 처리를 넣어주는 편이 좋습니다.

```kotlin
// WebView 초기화 및 destroy - 메모리 누수 방지
override fun onDestroy() {
    super.onDestroy()
		
    binding.webViewLauncher.apply {
        clearCache(true)
        clearHistory()
        loadUrl("about:blank")
        removeAllViews()
        destroy()
    }
}
```

{% hint style="info" %}
개발에 대한 추가 설명이 더 필요하신가요?

"[<mark style="color:orange;">\[Client Admin\]</mark>](https://client-admin.bleepy.io/login) 로그인 → 오른쪽 하단 채널톡 위젯" 클릭 후 개발 카테고리에 문의 남겨주시면 기술 개발팀에서 확인 후 연락드리겠습니다.
{% endhint %}

***

## 4단계: 화면 전환 설정

### 1. AndroidManifest 설정

Activity별 <mark style="color:blue;">`<intent-filter></intent-filter>`</mark> 내부 data-element에 <mark style="color:blue;">`scheme`</mark> 와 <mark style="color:blue;">`host`</mark> 설정을 추가해주세요.

```xml
<!-- AndroidManifest.xml -->
...
<activity
    android:name=".{ActivityName}"
    android:screenOrientation="portrait"
    android:theme="@style/Theme.Example.NoActionBar"
    android:exported="true"
    tools:ignore="LockedOrientationActivity">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="{host}"
            android:scheme="{scheme}" />
    </intent-filter>
</activity>
...
```

자세한 설명은 [<mark style="color:orange;">\[Manifest data-element\]</mark>](https://developer.android.com/guide/topics/manifest/data-element?hl=ko) 를 참고합니다.

### 2. queryString을 통한 데이터 얻기

<mark style="color:blue;">`scheme`</mark> , <mark style="color:blue;">`host`</mark> 값 외 queryString을 사용하여 Activity에서 원하는 데이터를 사용할 수 있습니다.

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding = ActivityWebviewBinding.inflate(layoutInflater)
    setContentView(binding.root)

    // URI query string 데이터 값 추출
    val intent = intent
    if (Intent.ACTION_VIEW == intent.action) {
        val uri: Uri? = intent.data

        // 딥링크 내 queryString형태로 정의한 parameter key로 value를 얻습니다.
        val paramValue: String? = uri?.getQueryParameter("parameter key")
    }

    ...
}
```

{% hint style="info" %}
개발에 대한 추가 설명이 더 필요하신가요?

"[<mark style="color:orange;">\[Client Admin\]</mark>](https://client-admin.bleepy.io/login) 로그인 → 오른쪽 하단 채널톡 위젯" 클릭 후 개발 카테고리에 문의 남겨주시면 기술 개발팀에서 확인 후 연락드리겠습니다.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bleepy.gitbook.io/bleepy-developers/game-promotion/android.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
