Android 넷퍼넬 대기실 미사용
넷퍼넬 대기실 미사용 가이드는 NetFUNNEL Android 에이전트에서 콘솔에서 설정한 넷퍼넬 대기실을 사용하지 않고, 안드로이드 UI 컴포넌트를 활용한 자체 커스텀 대기실을 적용하기 위한 최소한의 예제를 제공합니다.
1. onContinue
onContinue는 넷퍼넬 대기실이 아닌 자체 커스텀 대기실(다이얼로그 등)로 구현할 경우 사용하는 함수입니다.
| 파라미터 | 타입 | 설명 | 예시 |
|---|---|---|---|
| statusCode | Int | 대기 상태에 대한 응답 코드 | 201 |
| message | String | 대기 상태에 대한 메시지 | Continue |
| aheadWait | Int | 내 앞 대기자 수 | {{aheadWait}} |
| behindWait | Int | 내 뒤 대기자 수 | {{behindWait}} |
| waitTime | String | 예상 대기 시간 | {{waitTime}} |
| progressRate | Int | 진행률 (%) | {{0~100}} |
자체 커스텀 대기실 사용 시, 기본 대기 외 기능 사용에 제한이 있습니다. 따라서 넷퍼넬 대기실을 사용하는 useNetfunnelTemplate=true 설정을 권장합니다.
2. 자체 커스텀 대기실 적용
2.1 초기화 함수 설정
기본 WebView 대기실 대신 자체 구현한 대기실 UI를 사용하려면, useNetfunnelTemplate=false로 설정합니다. 이 설정 시 onContinue 콜백을 통해 실시간 대기 정보를 직접 받아 커스텀 다이얼로그에 반영해야 합니다.
- Kotlin
- Java
import com.nf4.Netfunnel
import android.app.Application
class SampleApplication : Application() {
override fun onCreate() {
super.onCreate()
Netfunnel.initialize(
clientId = "{CLIENT_ID}",
networkTimeout = 3000,
retryCount = 3,
useNetworkRecoveryMode = true,
useNetfunnelTemplate = false // Use custom waiting room
)
}
}
import com.nf4.Netfunnel;
import android.app.Application;
public class SampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Netfunnel.INSTANCE.initialize(
"{CLIENT_ID}",
null, // serverUrl
null, // errorUrl
3000L, // networkTimeout
3, // retryCount
false, // printLog
false, // errorBypass
false, // useNetfunnelTemplate
null, // userId
true, // useNetworkRecoveryMode
null, // healthCheckUrl
null // statusBarStyle
);
}
}
2.2 자체 커스텀 대기실 생성
자체 커스텀 대기실은 예상 대기 시간, 진행률, 앞/뒤 대기자 수, 닫기 버튼 등의 요소를 포함해 XML로 정의할 수 있습니다. 해당 레이아웃은 다이얼로그 등의 커스텀 UI로 inflate하여 사용합니다.
<?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:background="@drawable/bg_dialog_rounded"
android:clipToPadding="false"
android:elevation="4dp"
android:gravity="center"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Estimated Wait Time"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_wait_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="00:00:00"
android:textColor="#112e9a"
android:textSize="28sp"
android:textStyle="bold" />
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="12dp"
android:layout_marginTop="20dp"
android:progress="50"
android:progressDrawable="@drawable/pb_round" />
<TextView
android:id="@+id/txt_front_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Waiting ahead: 0"
android:textColor="#444444"
android:textSize="15sp" />
<TextView
android:id="@+id/txt_behind_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="Waiting behind: 0"
android:textColor="#444444"
android:textSize="15sp" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:background="@drawable/bg_gradient_button"
android:paddingHorizontal="40dp"
android:paddingVertical="10dp"
android:text="Close"
android:textAllCaps="false"
android:textColor="#FFFFFF"
android:textSize="15sp" />
</LinearLayout>
2.3 자체 커스텀 대기실 컨트롤러 생성
다이얼로그(자체 커스텀 대기실)의 생명주기 및 UI 요소를 제어하는 클래스를 생성합니다.
다이얼로그의 닫기 버튼 클릭 시 종료 함수를 반드시 호출해야 합니다.
updateDialog함수는onContinue콜백을 통해 전달된 실시간 대기 정보를 UI에 갱신합니다.
자체 커스텀 대기실의 닫기 버튼은 단순히 다이얼로그만 닫는 것이 아니라, 종료 함수를 호출해 네트워크 요청(대기 요청)을 중단해야 합니다.
- Kotlin
- Java
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.WindowManager
import com.your.package.name.R
import com.your.package.name.databinding.DialogNetfunnelBinding
class NetfunnelDialog(
context: Context,
private val onClose: () -> Unit
) : Dialog(context) {
private lateinit var binding: DialogNetfunnelBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DialogNetfunnelBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnCancel.setOnClickListener {
closeDialog()
}
}
override fun onStart() {
super.onStart()
val width = (context.resources.displayMetrics.widthPixels * 0.9).toInt()
window?.setLayout(width, WindowManager.LayoutParams.WRAP_CONTENT)
window?.setBackgroundDrawableResource(R.color.transparent)
}
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
closeDialog()
}
private fun closeDialog() {
dismiss()
onClose()
}
fun updateDialog(aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int) {
"Waiting ahead: $aheadWait".also { binding.txtFrontQueue.text = it }
"Waiting behind: $behindWait".also { binding.txtBehindQueue.text = it }
binding.txtWaitTime.text = waitTime
binding.progressBar.progress = progressRate
}
}
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import com.your.package.name.R;
import com.your.package.name.databinding.DialogNetfunnelBinding;
public class NetfunnelDialog extends Dialog {
private DialogNetfunnelBinding binding;
private final Runnable onClose;
public NetfunnelDialog(@NonNull Context context, @NonNull Runnable onClose) {
super(context);
this.onClose = onClose;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DialogNetfunnelBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.btnCancel.setOnClickListener(v -> closeDialog());
}
@Override
protected void onStart() {
super.onStart();
int width = (int) (getContext().getResources().getDisplayMetrics().widthPixels * 0.9);
if (getWindow() != null) {
getWindow().setLayout(width, WindowManager.LayoutParams.WRAP_CONTENT);
getWindow().setBackgroundDrawableResource(R.color.transparent);
}
}
@Override
public void onBackPressed() {
closeDialog();
}
private void closeDialog() {
dismiss();
onClose.run();
}
public void updateDialog(int aheadWait, int behindWait, String waitTime, int progressRate) {
binding.txtFrontQueue.setText("Waiting ahead: " + aheadWait);
binding.txtBehindQueue.setText("Waiting behind: " + behindWait);
binding.txtWaitTime.setText(waitTime);
binding.progressBar.setProgress(progressRate);
}
}
2.4 onContinue 사용
onContinue 콜백에서 다이얼로그를 띄우고 실시간 대기 정보를 업데이트합니다.
다이얼로그는 최초 1회만 생성하고 이후
updateDialog로 UI만 갱신합니다.UI 변경은 반드시
runOnUiThread에서 처리합니다.대기 종료 시에는 다이얼로그를 닫고 종료 함수 호출로 진입 키를 반납해야 합니다.
- Kotlin
- Java
import com.nf4.Netfunnel
import com.nf4.NetfunnelCallback
import android.content.Intent
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class StartCallback(private val activity: AppCompatActivity) {
var dialog: NetfunnelDialog? = null
companion object {
private const val TAG = "NetFUNNEL"
}
val callback = object : NetfunnelCallback() {
override fun onSuccess(statusCode: Int, message: String) {
Log.d(TAG, "onSuccess $statusCode $message")
if (dialog != null) {
closeDialog()
stopNetfunnel()
}
navigateToEventActivity(activity)
}
override fun onBlock(statusCode: Int, message: String) {
Log.d(TAG, "onBlock $statusCode $message")
if (dialog != null) {
closeDialog()
stopNetfunnel()
}
}
override fun onClose(statusCode: Int, message: String) {
Log.d(TAG, "onClose $statusCode $message")
}
override fun onContinue(statusCode: Int, message: String, aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int) {
Log.d(TAG, "onContinue $statusCode $message")
activity.runOnUiThread {
if (dialog == null) {
dialog = NetfunnelDialog(activity) { stopNetfunnel() }.apply {
setCanceledOnTouchOutside(false)
show()
}
}
dialog?.updateDialog(aheadWait, behindWait, waitTime, progressRate)
}
}
override fun onError(statusCode: Int, message: String) {
Log.d(TAG, "onError $statusCode $message")
if (dialog != null) {
closeDialog()
stopNetfunnel()
}
navigateToEventActivity(activity)
}
override fun onNetworkError(statusCode: Int, message: String) {
Log.d(TAG, "onNetworkError $statusCode $message")
if (dialog != null) {
closeDialog()
stopNetfunnel()
}
when (statusCode) {
1001 -> {
activity.runOnUiThread {
Toast.makeText(activity, "Network connection failed. Please check your network settings.", Toast.LENGTH_SHORT).show()
}
}
1002 -> {
navigateToEventActivity(activity)
}
}
}
}
private fun navigateToEventActivity(activity: AppCompatActivity) {
val intent = Intent(activity, EventActivity::class.java)
activity.startActivity(intent)
}
private fun closeDialog() {
dialog?.dismiss()
dialog = null
}
private fun stopNetfunnel() {
val callback = StopCallback()
Netfunnel.nfStop("{{PROJECT_KEY}}", "{{SEGMENT_KEY}}", callback.getCallback())
}
}
import com.nf4.Netfunnel;
import com.nf4.NetfunnelCallback;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class StartCallback {
private static final String TAG = "NetFUNNEL";
private final AppCompatActivity activity;
private NetfunnelDialog dialog;
public StartCallback(AppCompatActivity activity) {
this.activity = activity;
}
public final NetfunnelCallback callback = new NetfunnelCallback() {
@Override
public void onSuccess(int statusCode, @NonNull String message) {
Log.d(TAG, "onSuccess " + statusCode + " " + message);
if (dialog != null) {
closeDialog();
stopNetfunnel();
}
navigateToEventActivity();
}
@Override
public void onError(int statusCode, @NonNull String message) {
Log.d(TAG, "onError " + statusCode + " " + message);
if (dialog != null) {
closeDialog();
stopNetfunnel();
}
navigateToEventActivity();
}
@Override
public void onNetworkError(int statusCode, @NonNull String message) {
Log.d(TAG, "onNetworkError " + statusCode + " " + message);
if (dialog != null) {
closeDialog();
stopNetfunnel();
}
switch (statusCode) {
case 1001:
activity.runOnUiThread(() ->
Toast.makeText(activity, "Network connection failed. Please check your network settings.", Toast.LENGTH_SHORT).show()
);
break;
case 1002:
navigateToEventActivity();
break;
}
}
@Override
public void onBlock(int statusCode, @NonNull String message) {
Log.d(TAG, "onBlock " + statusCode + " " + message);
if (dialog != null) {
closeDialog();
stopNetfunnel();
}
}
@Override
public void onClose(int statusCode, @NonNull String message) {
Log.d(TAG, "onClose " + statusCode + " " + message);
}
@Override
public void onContinue(int statusCode, @NonNull String message, int aheadWait, int behindWait, @NonNull String waitTime, int progressRate) {
Log.d(TAG, "onContinue " + statusCode + " " + message);
activity.runOnUiThread(() -> {
if (dialog == null) {
dialog = new NetfunnelDialog(activity, this::stopNetfunnel);
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
dialog.updateDialog(aheadWait, behindWait, waitTime, progressRate);
});
}
};
private void navigateToEventActivity() {
Intent intent = new Intent(activity, EventActivity.class);
activity.startActivity(intent);
}
private void closeDialog() {
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
}
private void stopNetfunnel() {
StopCallback stopCallback = new StopCallback();
Netfunnel.INSTANCE.nfStop("{{PROJECT_KEY}}", "{{SEGMENT_KEY}}", stopCallback.getCallback());
}
}