# Android SDK 매뉴얼

{% hint style="info" %}
본 가이드는 Android Studio 21.1.1 기준으로 작성되었습니다.

SDK가 지원하는 최소 버전은 Android API 21 이상입니다.
{% endhint %}

### SDK 다운로드

1\) SDK를 다운로드 합니다.

{% content-ref url="/pages/teXz81azxFOb0wIYwH4F" %}
[SDK 다운로드](/app-push/sdk-manual/sdk-down.md)
{% endcontent-ref %}

### FCM APP 생성

{% hint style="danger" %}
기존 GCM 프로젝트가 있는 경우, 반드시 기존 GCM 프로젝트를 추가해서 사용해야 합니다.
{% endhint %}

1\) 프로젝트를 추가합니다.

![](/files/jW9rw0HUSPswSIW5fJE9)

2\) 앱 추가 후, 다운로드한 'google-services.json' 파일을 프로젝트 앱 모듈 루트 디렉토리로 이동 시킵니다.

![](/files/5QDkRsa47TCiP9x7IG57) ![](/files/GkExtKOPTwi7DiTtsJ5h)

3\) '프로젝트 설정 > 서비스 계정' 메뉴로 이동하여 '새 비공개 키 생성' 버튼을 클릭하여 비공개 키(\*.json)를 생성 및 다운로드 받습니다.

<figure><img src="/files/LifGyEKBJLoAaQpv4EP2" alt=""><figcaption></figcaption></figure>

{% hint style="danger" %}
비공개 키는 복수로 생성할 수 있으나, 생성된 키는 1회만 다운로드 가능하니 안전한 위치에 저장합니다.
{% endhint %}

<div data-full-width="true"><figure><img src="/files/KvhkLzB2DKIJiuU5Duq4" alt="" width="375"><figcaption></figcaption></figure></div>

4\) 비공개 키를 생성하고 '클라우드 메시징' 메뉴에서 FCM API(V1)이 '사용 중지됨' 된 경우 'Google Cloud Console > APIS' 로 이동하여 사용 설정해야 합니다.

<figure><img src="/files/K0UT8YBaWmldEvMhCWYE" alt=""><figcaption></figcaption></figure>

{% embed url="<https://console.cloud.google.com/apis>" %}

5\) API 항목에서 Firebase Cloud Messaging API 를 선택한 뒤, 사용을 클릭하여 '사용 설정됨' 으로 변경되어 있는지 확인합니다.

<figure><img src="/files/OhecOm2emF7EZRIrjoS6" alt=""><figcaption></figcaption></figure>

### 핑거푸시 사용자 콘솔 APP 생성

1\) 핑거푸시 사용자 콘솔에서 '앱 등록' 버튼을 누른 뒤 앱을 생성합니다.

<figure><img src="/files/2tjl5Yj4dxJ27bmkvpF1" alt=""><figcaption></figcaption></figure>

2\) '앱 설정' 메뉴로 이동하면 App Key, App Secret을 확인할 수 있습니다.

<figure><img src="/files/rvN4XjAmm8xiMTrr7d4p" alt=""><figcaption></figcaption></figure>

3\) 플랫폼 설정 내 FCM > 설정을 클릭하여 다운로드 받은 비공개 키(\*.json) 및 프로젝트 패키지명을 입력하고 등록합니다.

<figure><img src="/files/GH43dGTns1vMTYmlYItd" alt=""><figcaption></figcaption></figure>

<div align="left"><figure><img src="/files/MEbJ70BB50yu5su1VWGN" alt=""><figcaption></figcaption></figure> <figure><img src="/files/ZsF5Lcm6IsevJlE1xSHr" alt=""><figcaption></figcaption></figure></div>

### SDK 적용하기

#### Gradle 설정

a. 프로젝트 레벨의 build.gradle 에 'com.google.gms:google-services' 라이브러리와 kotlin 라이브러리를 추가합니다.

{% code title="<project>/build.gradle" %}

```
buildscript {
    ...
    dependencies {
        ...
        classpath 'com.google.gms:google-services:4.4.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21"
    }
}
```

{% endcode %}

b. 앱 레벨의 build.gradle 하단에 google-services 플러그인을 추가합니다.

{% code title="<project>/<app-module>/build.gradle" %}

```
apply plugin: 'com.google.gms.google-services'
```

{% endcode %}

c. 다운받은 SDK 를 libs 폴더로 옮긴 뒤, 앱 레벨의 build.gradle 에 핑거푸시 SDK 및 firebase 모듈을 추가합니다.

{% code title="<project>/<app-module>/build.gradle" %}

```
dependencies {
    ...
    implementation files('libs/fingerpush_3.8.2.aar')
    implementation 'com.google.firebase:firebase-core:21.1.1'
    implementation 'com.google.firebase:firebase-messaging:24.1.2'
}
```

{% endcode %}

d. gradle.properties 파일에서 두 개의 플래그를 사용합니다.

{% code title="gradle.properties" %}

```
android.useAndroidX=true
android.enableJetifier=true
```

{% endcode %}

#### 핑거푸시 키 설정

a. Application class에서 핑거푸시 키를 설정합니다.

{% hint style="danger" %} <mark style="color:red;">앱키, 앱시크릿 변경시 기존 설정값들이 적용되지 않을 수 있기 때문에 앱 삭제 후 재설치를 권장드립니다.</mark>
{% endhint %}

```java
FingerPushManager.setAppKey(String appKey);
FingerPushManager.setAppSecret(String secretKey);
```

#### 알림 권한 추가 및 요청

Android 13(API Level33) 이상에서 알림 수신을 위해 권한 요청 및 허용되어야 합니다.

{% code title="AndroidManifest.xml" %}

```xml
<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>
```

{% endcode %}

{% code title="MainActivity.java" %}

```java
// 핑거푸시 단말기 등록 (앱 실행 시 매번 호출되어야 합니다.)
private void setDevice() {
    FingerPushManager.getInstance(this).setDevice(new NetworkUtility.ObjectListener() {
        @Override
        public void onComplete(String code, String message, JSONObject jsonObject) {
            // code 200, 201 단말기 등록.
        }

        @Override
        public void onError(String code, String message) {
            // code 504 단말기가 이미 등록됨.
        }
    });
}

// 알림 권한 요청 결과
private final ActivityResultLauncher<String> requestPermissionLauncher =
        registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
            setDevice()
        });

// 알림 권한 요청
private void askNotificationPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
            // 이미 알림 권한이 허용된 상태
            setDevice();
        } else {
            // 사용자에게 알림 권한 요청
            requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
        }
    } else {
        setDevice();
    }
}
```

{% endcode %}

#### 채널 생성

{% hint style="danger" %}
Android 8(API Level 26) 이상은 Channel 을 생성해야 알림이 노출됩니다.

채널 생성은 앱을 시작할 때 생성하는게 안전합니다.

Channel 에 대한 상세 내용은 아래 주소를 참고바랍니다.
{% endhint %}

{% embed url="<https://developer.android.com/training/notify-user/channels>" %}

{% code title="MainActivity.java" %}

```java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    String channelId  = getString(R.string.default_notification_channel_id);
    String channelName = getString(R.string.default_notification_channel_name);
    fingerNotification.createChannel(channelId, channelName);
}   
```

{% endcode %}

#### 알림 수신부

a. IntentService 클래스를 생성한 뒤, 'FingerPushFcmListener' 를 상속받습니다.

{% code title="IntentService.java" %}

```java
public class IntentService extends FingerPushFcmListener {
    @Override
    public void onMessage(Context context, Bundle data) {
        createNotification(data);
    }
}
```

{% endcode %}

b. onMessage(Context context, Bundle data) 에서 PayLoad 데이터를 확인할 수 있으며, 푸시 알림을 구현할 수 있습니다.

#### Payload 데이터

```
data.msgTag : 메세지 번호
data.code : CD:1;IM:0;PT:DEFT    <!-- (CD:커스텀데이터 여부(0없음, 1있음), IM:이미지여부(0없음, 1있음) ,PT:메세지타입 (DEFT:일반, LNGT:롱푸시, STOS:타겟푸시)  -->
data.time : 보낸시간
data.appTitle : 핑거푸시 앱이름
data.badge : 뱃지
data.sound : 사운드
data.title : 메세지 제목
data.message : 메세지내용
data.weblink : 웹링크 url
data.labelCode : 라벨코드
data.img : 이미지 여부            <!-- (0:없음;1:있음) -->
data.imgUrl : 이미지url
data.cd1 : 커스텀 데이터 
data.cd2 : 커스텀 데이터
data.cd3 : 커스텀 데이터
```

#### 알림 생성

기본 Notification 생성방법입니다.

{% code title="IntentService.java" %}

```java
private void createNotification(Bundle data) {
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        String channelId  = getString(R.string.default_notification_channel_id);
        String channelName = getString(R.string.default_notification_channel_name);
        
        NotificationChannel mChannel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
        mChannel.setDescription(null);
        mNotificationManager.createNotificationChannel(mChannel);
    }
 
    Intent intent = new Intent(IntentService.this, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
 
    PendingIntent pendingIntent;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        pendingIntent = PendingIntent.getActivity(IntentService.this, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
    } else {
        pendingIntent = PendingIntent.getActivity(IntentService.this, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_CANCEL_CURRENT);
    }
 
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
        .setSmallIcon(R.mipmap.ic_launcher)
        .setContentTitle(data.getString("data.title"))
        .setContentText(data.getString("data.message"));
    mBuilder.setContentIntent(pendingIntent);
 
    mNotificationManager.notify((int) System.currentTimeMillis(), mBuilder.build());
}
```

{% endcode %}

핑거푸시에서 제공하는 Notification 생성 방법입니다.

{% code title="IntentService.java" %}

```java
private void createNotification(Bundle data) {
    Intent intent = new Intent(IntentService.this, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
 
    PendingIntent pendingIntent;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        pendingIntent = PendingIntent.getActivity(IntentService.this, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
    } else {
        pendingIntent = PendingIntent.getActivity(IntentService.this, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_CANCEL_CURRENT);
    }
 
    FingerNotification fingerNotification = new FingerNotification(this);
    fingerNotification.setNotificationIdentifier((int) System.currentTimeMillis());
    fingerNotification.setIcon(R.drawable.m_ic_launcher);
    fingerNotification.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
    fingerNotification.setVibrate(new long[]{0, 500, 600, 1000});
    fingerNotification.setLights(Color.parseColor("#ffff00ff"), 500, 500);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        fingerNotification.setColor(Color.rgb(0, 114, 162));
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        String channelId  = getString(R.string.default_notification_channel_id);
        String channelName = getString(R.string.default_notification_channel_name);
        fingerNotification.createChannel(channelId, channelName);
    }                  
    fingerNotification.showNotification(data, pendingIntent);
}
```

{% endcode %}

c. FingerPush MessageID, MessageLabel, PushMode 값은 아래 함수를 통해 확인 가능합니다.

```java
String messageId = FingerPushManager.getMessageId(Bundle bundle);
String messageLabel = FingerPushManager.getMessageLabel(Bundle bundle);
String pushMode = FingerPushManager.getPushMode(Bundle bundle);
```

#### allowBackup 설정

a. allowBackup 값을 false 로 설정합니다.

{% code title="AndroidManifest.xml" %}

```
<application
    ...
    android:allowBackup="false"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name">
    ...
</application>
```

{% endcode %}

b. 푸시 수신을 서비스를 추가합니다.

{% code title="AndroidManifest.xml" %}

```xml
<manifest ...>
    ...
    <application ...>
        <service
            android:name=".IntentService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
    </application>
</manifest>
```

{% endcode %}

{% hint style="info" %}
ProGuard를 사용하는 경우 룰을 추가합니다.
{% endhint %}

```
# For FingerPush SDK
-dontwarn com.fingerpush.**
```

### Android API Reference

Android API 는 아래 링크에서 확인 할 수 있습니다.

{% content-ref url="/pages/fx2bsLtt8TXjECxe6mfO" %}
[Android API Reference](/app-push/api-reference/android.md)
{% endcontent-ref %}

## API 연동 결과 코드

<table><thead><tr><th width="150">코드</th><th width="326">내용</th><th>비고</th></tr></thead><tbody><tr><td>200</td><td>정상 처리</td><td></td></tr><tr><td>403</td><td>App_key, secret오류, 권한 없음</td><td></td></tr><tr><td>404</td><td>조회 대상 없음, 조회 결과 없음</td><td></td></tr><tr><td>500</td><td>처리 중 에러</td><td></td></tr><tr><td>503</td><td>필수 값 없음</td><td></td></tr><tr><td>504</td><td>이미 등록된 토큰</td><td>디바이스 등록에서만 사용</td></tr></tbody></table>


---

# 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://developers.fingerpush.com/app-push/sdk-manual/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.
