웹뷰에서 약관 체크 박스가 있고

전체체크 박스도 있다.

전체 체크 박스 클릭하면 모든 체크 박스가 체크/언체크 된다.

체크 박스가 모두 checked 되면 전체 체크 박스가 checked 된다.

체크 박스가 모두 checked 되지 않으면 전체 체크 박스가 checked 되지 않는다.

 

안드로이드앱에서 javascript 인터페이스로 연동 하는 샘플도 있다.

 

 

약관 샘플 (html + javascript)

 

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>보안서약서 동의</title>
    <style>
        .main {
            font-size: 14px;
            font-weight: normal;
            line-height: 1.43;
            color: #000;
            margin-top: 20px;
            margin-left: 20px;
            margin-right: 20px;
            margin-bottom: 20px;
            text-align: left; /* 나머지 내용 왼쪽 정렬 */
        }

        h1 {
            text-align: center;
            font-size: 20px; /* 제목 폰트 크기 20px로 설정 */
        }

        .pl {
            margin-left: 20px;
        }

        .checkbox-container {
            text-align: left; /* 체크 박스 왼쪽 정렬 */
            margin-left: 20px;
        }
    </style>
</head>
<body>
<div class="main">
    <h1>보안서약서</h1>
    <p>나의회사명을 방문하는 방문자 OOO 은(는) 다음의 정보보호에 관한 내용에 동의하며, 준수할 것을 서약합니다.</p>
    <br>

    <p class="pl">1. 안내실(보안실)의 검문검색 및 신분 확인에 협조하여 주십시오.</p>
    <p class="pl">2. 방문 승인 외 장소 출입은 관련팀의 동의를 받아야 합니다.</p>
    <p class="pl">3. 제공한 업무와 관련된 정보에 대해 타인에게 누설하지 않겠습니다.</p>
    <p class="pl">4. 업무 관련정보를 승인 없이 출력 하지 않을 것이며 배포, 공유하지 않겠습니다.</p>
    <p class="pl">5. 비인가 촬영 장비 등을 임의로 반입하여 회사 내 시설을 촬영하지 않겠습니다.</p>
    <p class="pl">6. 위 보안사항을 위반을 한 경우 즉시 신고하도록 하겠습니다.</p>
    <p class="pl">7. 상기 준수항목 외에도 보안유지를 위해 요구하는 조치사항에 협조하겠습니다.</p>
    <br>
    <p>본인은 상기 사항에 동의하며 위배 시 관련 법령 및 회사 규정에 따른 책임과 제재에 이의를 제기하지 않습니다. 발생된 손해에 대해 즉시 변상 및 복구할 것을 서약합니다.</p>

    <div class="checkbox-container">
        <input type="checkbox" id="agreeCheckbox" name="agreeCheckbox" onclick="checkAgreeAll()">
        <label for="agreeCheckbox">위 약관에 동의 합니다.</label>
    </div>
    <br><br>

    <h1>개인정보 수집 및 이용 동의</h1>
    <div class="main">
        <p>나의회사명은 “개인정보 보호법”, “정보통신망 이용촉진 및 정보보호 등에 관한 법률” 등 관련 법령상의 개인정보 수집 및 이용에 대한 동의를 거부할 수 있는 권리가 있습니다.</p>
        <br>
        <div class="mb">
            <strong>1. 개인정보 수집 및 이용목적</strong><br>
            <div class="detail">가. 방문 등록 시 본인확인 및 방문 업체 확인, 해당 사업장 출입 예약 및 제공</div>
            <div class="detail">나. 출입통제 및 방문 이력 관리</div>
            <div class="detail">다. 분실·도난 등 사고 발생 시 사후 추적</div>
        </div>
        <div class="mb">
            <strong>2. 개인정보의 수집 항목 및 수집 방법</strong><br>
            <div class="detail">회사는 방문 예약 시스템의 운영을 위해 필요한 최소한의 범위 내에서 아래와 같은 개인정보를 수집하고 있습니다.</div>
            <div class="detail">가. 필수 수집 항목</div>
            <div class="detail" style="padding-left: 14px;">- 이름(방문자명), 회사명, 연락처(휴대폰번호)</div>
            <div class="detail">나. 수집 방법</div>
            <div class="detail" style="padding-left: 14px;">- 웹 방문 신청 페이지</div>
        </div>
        <div class="mb">
            <strong>3. 개인정보 처리 및 보유 기간</strong><br>
            <div class="detail">회사는 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의 받은 개인정보 보유·이용기간 내에서 개인정보를 처리·보유합니다.</div>
            <div class="detail">가. 수집 동의에 의한 보유기간</div>
            <div class="detail" style="padding-left: 14px;">- 3개월간 보유 후 파기</div>
        </div>
        <div class="mb">
            <strong>4. 개인정보 파기절차 및 파기방법</strong><br>
            <div class="detail">회사는 개인정보 보유기간의 경과, 처리 목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체 없이 해당 개인정보를 파기합니다.</div>
            <div class="detail">가. 파기절차</div>
            <div class="detail" style="padding-left: 14px;">- 이용자가 입력한 정보는 목적 달성 후 별도의 DB에 옮겨져 내부 방침 및 기타 관련 법령에 따라 일정 기간
                저장된 후 혹은 즉시 파기됩니다. 이때, DB로 옮겨진 개인정보는 법률에 의한 경우가 아니고서는 다른 목적으로 이용되지 않습니다.
            </div>
            <div class="detail">가. 파기방법</div>
            <div class="detail" style="padding-left: 14px;">- 종이에 출력된 개인정보는 분쇄기로 분쇄하거나 소각을 통하여 파기합니다.</div>
            <div class="detail" style="padding-left: 14px;">- 전자적 파일 형태로 저장된 개인정보는 기록을 재생할 수 없는 기술적 방법을 사용하여 삭제합니다.</div>
        </div>
        <div class="mb">
            <strong>5. 동의 거부권 및 동의 거부에 따른 불이익 내용</strong><br>
            <div class="detail">개인정보 수집 및 이용 동의를 거부할 수 있으며, 거부 시 제공되는 서비스가 일부 제한될 수 있습니다.</div>
        </div>
        <div class="mb">
            <strong>6. 고객의 권익침해에 대한 구제방법</strong><br>
            <div class="detail">이용자는 회사의 서비스를 이용하시며 발생하는 모든 개인정보보호 관련 민원을 개인정보 관리 책임자 혹은 담당 부서로 신고하실 수 있습니다.</div>
            <div class="detail">기타 개인정보침해에 대한 신고나 상담이 필요하신 경우에는 아래 기관에 문의하시기 바랍니다.</div>
            <div class="detail" style="padding-left: 14px;">- 개인정보침해신고센터: www.privacy.kisa.co.kr(국번 없이 118)</div>
            <div class="detail" style="padding-left: 14px;">- 대검찰청 사이버 수사과: www.spo.go.kr (국번 없이 1301)</div>
            <div class="detail" style="padding-left: 14px;">- 경찰청 사이버 안전국: cyberbureau.police.go.kr (국번없이 182)</div>
        </div>

    </div>
    <div class="checkbox-container">
        <input type="checkbox" id="agreeCheckbox2" name="agreeCheckbox2" onclick="checkAgreeAll()">
        <label for="agreeCheckbox2">위 약관에 동의 합니다.</label>
    </div>
    <br><br>

    <h1>민감정보 수집 및 이용 동의</h1>
    <div class="main">
        <p>나의회사명의 안면인식 기반 출입 시스템를 이용하기 위해 본인은 아래 동의 내용을 숙지하였으며, 이에 따라 본인의 민감정보를 나의회사명이 수집 및 이용하는 것에 동의합니다.</p>
        <br>
        <div class="mb">
            <strong>1. 민감정보 수집 및 이용목적</strong><br>
            <div class="detail">가. 안면인식 기반 출입 시스템에서 이용자 식별 및 본인 인증</div>
            <div class="detail">나. 출입통제 및 방문 이력 관리</div>
        </div>
        <div class="mb">
            <strong>2. 민감정보의 수집 항목 및 수집 방법</strong><br>
            <div class="detail">회사는 방문 예약 시스템의 운영을 위해 필요한 최소한의 범위 내에서 아래와 같은 민감정보를 수집하고 있습니다.</div>
            <div class="detail">가. 필수 수집 항목</div>
            <div class="detail" style="padding-left: 14px;">- 안면정보</div>
            <div class="detail">나. 수집 방법</div>
            <div class="detail" style="padding-left: 14px;">- 등록, 출입 시 카메라에서 인식된 사진 및 얼굴정보 수집</div>
        </div>
        <div class="mb">
            <strong>3. 민감정보 처리 및 보유 기간</strong><br>
            <div class="detail">회사는 법령에 따른 민감정보 보유·이용기간 또는 정보주체로부터 민감정보를 수집 시에 동의 받은 민감정보 보유·이용기간 내에서 민감정보를 처리·보유합니다.</div>
            <div class="detail">가. 수집 동의에 의한 보유기간</div>
            <div class="detail" style="padding-left: 14px;">- 3개월간 보유 후 파기</div>
        </div>
        <div class="mb">
            <strong>4. 동의 거부권 및 동의 거부에 따른 불이익 내용</strong><br>
            <div class="detail">민감정보 수집 및 이용 동의를 거부할 수 있으며, 거부 시 제공되는 서비스가 일부 제한될 수 있습니다.</div>
        </div>
    </div>
    <div class="checkbox-container">
        <input type="checkbox" id="agreeCheckbox3" name="agreeCheckbox3" onclick="checkAgreeAll()">
        <label for="agreeCheckbox3">위 약관에 동의 합니다.</label>
    </div>
    <br><br>

    <div class="checkbox-container">
        <input type="checkbox" id="agreeCheckboxAll" name="agreeCheckboxAll" onclick="checkAll()">
        <label for="agreeCheckboxAll">위 약관에 모두 동의 합니다.</label>
    </div>
    <br><br>

    <script>
        function checkAgreeAll() {
            var agreeCheckbox = document.getElementById("agreeCheckbox");
            var agreeCheckbox2 = document.getElementById("agreeCheckbox2");
            var agreeCheckbox3 = document.getElementById("agreeCheckbox3");
            var agreeCheckboxAll = document.getElementById("agreeCheckboxAll");

            if (agreeCheckbox.checked && agreeCheckbox2.checked && agreeCheckbox3.checked) {
                // 모든 체크 박스 클릭
                agreeCheckboxAll.checked = true;
                goAllCheckedStatus('Y');
            } else {
                // 모든 체크 박스 언클릭
                agreeCheckboxAll.checked = false;
                goAllCheckedStatus('N');
            }
        }

        function checkAll() {
            var agreeCheckboxAll = document.getElementById("agreeCheckboxAll");
            var checkboxes = document.querySelectorAll('input[type="checkbox"][name^="agreeCheckbox"]');

            checkboxes.forEach(function(checkbox) {
                checkbox.checked = agreeCheckboxAll.checked;
            });
            if(agreeCheckboxAll.checked){
                // 모든 체크 박스 클릭
                goAllCheckedStatus('Y');
            }else{
                // 모든 체크 박스 언클릭
                goAllCheckedStatus('N');
            }

        }
        function goAllCheckedStatus(allChecked){
            //  안드로이드 APP 인 경우
            window.HybridApp.getAllCheckedStatus(allChecked); // (Front --> 앱)
        }
        //Native App에서 호출, 전체 약관 동의값을 전달  (앱 --> Front)
        function callAllCheckedStatus(isAllChecked){
            var agreeCheckboxAll = document.getElementById("agreeCheckboxAll");
            var checkboxes = document.querySelectorAll('input[type="checkbox"][name^="agreeCheckbox"]');

            if(isAllChecked=='Y'){  // 모두 체크
               checkboxes.forEach(function(checkbox) {
                    checkbox.checked = true;
               });
               agreeCheckboxAll.checked = true;
            }else{  // 모두 언체크
               checkboxes.forEach(function(checkbox) {
                    checkbox.checked = false;
               });
               agreeCheckboxAll.checked = false;
            }
        }
    </script>
</div>
</body>
</html>

 

 

안드로이드앱에서 javascript 연동 샘플

TermsDialog.java  (약관 다이알로그)

 


import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.widget.CheckBox;
import android.widget.ImageView;

import androidx.annotation.NonNull;


public class TermsDialog extends Dialog {

    private View termsDialogView;
    private VisitorViewModel mViewModel;

    private final Handler handler = new Handler();

    private Context mContext;

    CheckBox ckTermsAgree = null;

    public TermsDialog(@NonNull Context context, String termsPath) {
        super(context, android.R.style.Theme_Material_Light_NoActionBar_Fullscreen);
        mContext = context;
        termsDialogView = getLayoutInflater().inflate(R.layout.dialog_terms, null);
        setContentView(termsDialogView);
        ImageView backButton = (ImageView) termsDialogView.findViewById(R.id.termsBackButton);
        ImageView closedButton = (ImageView) termsDialogView.findViewById(R.id.termsClosedButton);
        ckTermsAgree = (CheckBox) termsDialogView.findViewById(R.id.ckTermsAgree);


        backButton.setOnClickListener(v->{
            this.dismiss();
        });
        closedButton.setOnClickListener(v->{
            this.dismiss();
        });


        WebView webView = (WebView) termsDialogView.findViewById(R.id.termsWebView);

        webView.getSettings().setLoadWithOverviewMode(true);  // WebView 화면크기에 맞추도록 설정 - setUseWideViewPort 와 같이 써야함
        webView.getSettings().setUseWideViewPort(true);  // wide viewport 설정 - setLoadWithOverviewMode 와 같이 써야함

        webView.getSettings().setSupportZoom(false);  // 줌 설정 여부
        webView.getSettings().setBuiltInZoomControls(false);  // 줌 확대/축소 버튼 여부

        webView.getSettings().setJavaScriptEnabled(true); // 자바스크립트 사용여부
        webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false); // javascript가 window.open()을 사용할 수 있도록 설정
        webView.getSettings().setSupportMultipleWindows(false); // 멀티 윈도우 사용 여부
        webView.getSettings().setDomStorageEnabled(false);  // 로컬 스토리지 (localStorage) 사용여부

        webView.addJavascriptInterface(new AndroidBridge(), "HybridApp");

        //약관 웹페이지 호출
        webView.loadUrl(termsPath);

        // 약관 전체 동의 체크시 Front로 이벤트 전달
        ckTermsAgree.setOnClickListener(v->{
            String isAllChecked = "N";
            if(this.ckTermsAgree.isChecked()){
                isAllChecked = "Y";
            }else{
                isAllChecked = "N";
            }
            webView.loadUrl("javascript:callAllCheckedStatus('"+isAllChecked+"');");
        });

    }

    private class AndroidBridge {

        @JavascriptInterface
        public void getAllCheckedStatus(final String allChecked) { // must be final,   전체동의 여부 상태
            handler.post(new Runnable() {
                public void run() {
//                    Toast.makeText(mContext,"전체 동의 여부:"+allChecked,Toast.LENGTH_LONG).show();
                    if(allChecked != null && allChecked.equals("Y")){
                        ckTermsAgree.setChecked(true);
                    }else{
                        ckTermsAgree.setChecked(false);
                    }


                }
            });
        }

    }
}

 

 

약관 동의 다이알로그 xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:weightSum="1"
    >
    <!-- 약관. -->
    <FrameLayout
        android:id="@+id/terms_toolbar"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="@color/white"
        tools:ignore="MissingConstraints">
        <ImageView
            android:id="@+id/termsBackButton"
            android:padding="22dp"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_gravity="center_vertical"
            android:visibility="gone"
            android:src="@drawable/back_button" />
        <TextView
            android:id="@+id/termsTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/termsTitle"
            android:textStyle="bold"
            android:textColor="@color/black"
            android:layout_gravity="center"
            android:textSize="24sp" />
        <ImageView
            android:id="@+id/termsClosedButton"
            android:padding="22dp"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_gravity="center_vertical|right"
            android:src="@drawable/x_icon_40" />
        <View
            android:layout_marginTop="79dp"
            android:background="@color/list_divider_gray"
            android:layout_width="match_parent"
            android:layout_height="1dp" />
    </FrameLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="1"
        >
        <WebView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/termsWebView"
            tools:ignore="WebViewLayout" />

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <androidx.appcompat.widget.AppCompatCheckBox
            android:id="@+id/ckTermsAgree"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@drawable/selector_checked"
            android:text="위 약관에 모두 동의 합니다."
            android:textSize="15sp"/>

        <android.widget.Button
            android:id="@+id/btnTermsNext"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_weight="1"
            android:background="@drawable/blue_btn_pressed"
            android:gravity="center"
            android:padding="5dp"
            android:text="다음"
            android:textColor="@drawable/btn_text_selector"
            android:textSize="20sp" />
    </LinearLayout>


</LinearLayout>

 

+ Recent posts