자동 완성을 위한 앱 최적화

표준 뷰를 사용하는 앱은 특별한 구성 없이도 자동 완성 프레임워크와 호환됩니다. 앱이 프레임워크와 호환되는 방식을 최적화할 수도 있습니다.

자동 완성 환경 설정

이 섹션에서는 앱에 기본 자동 완성 기능을 설정하는 방법을 설명합니다.

자동 완성 서비스 구성

앱에서 자동 완성 프레임워크를 사용하려면 자동 완성 서비스가 기기에 구성되어 있어야 합니다. Android 8.0(API 수준 26) 이상을 실행하는 대다수 스마트폰과 태블릿에는 자동 완성 서비스가 제공되지만 앱을 테스트할 때 Android 자동 완성 프레임워크 샘플의 자동 완성 서비스와 같은 테스트 서비스를 사용하는 것이 좋습니다. 에뮬레이터를 사용하는 경우 자동 완성 서비스를 명시적으로 설정합니다. 에뮬레이터에 기본 서비스가 제공되지 않을 수 있기 때문입니다.

샘플 앱에서 테스트 자동 완성 서비스를 설치한 후 설정 > 시스템 > 언어 및 입력 > 고급 > 입력 지원 > 자동 완성 서비스로 이동하여 자동 완성 서비스를 사용 설정합니다.

자동 완성 테스트를 위한 에뮬레이터 구성에 관한 자세한 내용은 자동 완성으로 앱 테스트를 참고하세요.

자동 완성을 위한 힌트 제공

자동 완성 서비스는 휴리스틱을 사용하여 각 뷰의 유형을 확인합니다. 그러나 앱에서 이러한 휴리스틱에 의존하는 경우 앱을 업데이트할 때 자동 완성 동작이 예기치 않게 변경될 수 있습니다. 자동 완성 서비스에서 앱의 폼 팩터를 올바르게 식별하도록 하려면 자동 완성 힌트를 제공합니다.

android:autofillHints 속성을 사용하여 자동 완성 힌트를 설정할 수 있습니다. 다음 예에서는 EditText"password" 힌트를 설정합니다.

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:autofillHints="password" />

다음 예와 같이 setAutofillHints() 메서드를 사용하여 프로그래매틱 방식으로 힌트를 설정할 수도 있습니다.

Kotlin

val password = findViewById<EditText>(R.id.password)
password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD)

Java

EditText password = findViewById(R.id.password);
password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD);

사전 정의된 힌트 상수 포함

자동 완성 프레임워크는 힌트를 확인하지 않습니다. 변경이나 확인 없이 힌트를 자동 완성 서비스에 전달합니다. 개발자는 모든 값을 사용할 수 있지만 View 클래스와 AndroidX HintConstants 클래스에는 공식적으로 지원되는 힌트 상수의 목록이 포함되어 있습니다.

이러한 상수 조합을 사용하여 일반적인 자동 완성 시나리오의 레이아웃을 빌드할 수 있습니다.

계정 사용자 인증 정보

로그인 양식에 AUTOFILL_HINT_USERNAMEAUTOFILL_HINT_PASSWORD와 같은 계정 사용자 인증 정보 힌트를 포함할 수 있습니다.

새로운 계정을 만들거나 사용자가 사용자 이름과 비밀번호를 변경할 때 개발자는 AUTOFILL_HINT_NEW_USERNAMEAUTOFILL_HINT_NEW_PASSWORD를 사용할 수 있습니다.

신용카드 정보

신용카드 정보를 요청할 때 AUTOFILL_HINT_CREDIT_CARD_NUMBERAUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE와 같은 힌트를 사용할 수 있습니다.

신용카드 만료일의 경우 다음 중 하나를 실행하세요.

실제 주소

실제 주소 양식 필드에는 다음과 같은 힌트를 사용할 수 있습니다.

사람 이름

사람 이름을 요청할 때 다음과 같은 힌트를 사용할 수 있습니다.

전화번호

전화번호의 경우 다음을 사용할 수 있습니다.

일회용 비밀번호(OTP)

단일 뷰에서 일회용 비밀번호를 사용하는 경우 AUTOFILL_HINT_SMS_OTP를 사용할 수 있습니다.

각 뷰가 OTP의 한 자리에 매핑되는 여러 뷰를 사용하는 경우 generateSmsOtpHintForCharacterPosition() 메서드를 사용하여 문자별 힌트를 생성할 수 있습니다.

필드를 자동 완성에 중요한 것으로 표시

자동 완성 목적으로 뷰 구조에 앱의 개별 필드를 포함할 수 있습니다. 기본적으로 뷰는 IMPORTANT_FOR_AUTOFILL_AUTO 모드를 사용하므로 Android에서 휴리스틱을 사용해 뷰가 자동 완성에 중요한지 판단할 수 있습니다.

그러나 뷰, 뷰 구조 또는 전체 활동이 자동 완성에 중요하지 않은 경우가 있습니다.

  • 로그인 활동의 CAPTCHA 필드
  • 사용자가 텍스트나 스프레드시트 편집기와 같은 콘텐츠를 만드는 뷰
  • 게임플레이를 표시하는 것과 같은 게임 내 일부 활동의 뷰

android:importantForAutofill 속성을 사용하여 자동 완성 뷰의 중요도를 설정할 수 있습니다.

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:importantForAutofill="no" />

importantForAutofill의 값은 다음 중 하나일 수 있습니다.

auto
Android 시스템에서 휴리스틱을 사용하여 뷰가 자동 완성에 중요한지 판단할 수 있습니다.
no
이 뷰는 자동 완성에 중요하지 않습니다.
noExcludeDescendants
이 뷰와 하위 요소는 자동 완성에 중요하지 않습니다.
yes
이 뷰는 자동 완성에 중요합니다.
yesExcludeDescendants
이 뷰는 자동 완성에 중요하지만 하위 요소는 자동 완성에 중요하지 않습니다.

setImportantForAutofill() 메서드를 사용할 수도 있습니다.

Kotlin

val captcha = findViewById<TextView>(R.id.captcha)
captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO)

Java

TextView captcha = findViewById(R.id.captcha);
captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);

다음과 같이 자동 완성에 중요하지 않은 이전 사용 사례를 선언할 수 있습니다.

  • 로그인 활동의 CAPTCHA 필드: android:importantForAutofill="no" 또는 IMPORTANT_FOR_AUTOFILL_NO를 사용하여 이 뷰를 중요하지 않은 것으로 표시합니다.
  • 사용자가 콘텐츠를 만드는 뷰: android:importantForAutofill="noExcludeDescendants" 또는 IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS를 사용하여 전체 뷰 구조를 중요하지 않은 것으로 표시합니다.
  • 게임 내 일부 활동의 뷰: android:importantForAutofill="noExcludeDescendants" 또는 IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS를 사용하여 전체 뷰 구조를 중요하지 않은 것으로 표시합니다.

웹사이트 및 모바일 앱 데이터 연결

Google 자동 완성과 같은 자동 완성 서비스는 앱과 웹사이트가 연결되면 브라우저와 Android 기기 간에 사용자 로그인 데이터를 공유할 수 있습니다. 사용자가 두 플랫폼에서 모두 동일한 자동 완성 서비스를 선택하는 경우 웹 앱에 로그인하면 상응하는 Android 앱에 로그인할 때 로그인 사용자 인증 정보를 자동 완성에 사용할 수 있습니다.

Android 앱을 웹사이트와 연결하려면 사이트의 delegate_permission/common.get_login_creds 관계를 사용하여 디지털 애셋 링크를 호스팅합니다. 그런 다음 앱의 AndroidManifest.xml 파일에서 연결을 선언합니다. 웹사이트를 Android 앱과 연결하는 방법에 관한 자세한 내용은 앱과 웹사이트에서 자동 로그인 사용 설정을 참고하세요.

자동 완성 워크플로 완료

이 섹션에서는 앱 사용자를 위한 자동 완성 기능을 단계적으로 개선할 수 있는 특정 시나리오를 설명합니다.

자동 완성 사용 설정 여부 확인

사용자는 설정 > 시스템 > 언어 및 입력 > 고급 > 입력 지원 > 자동 완성 서비스로 이동하여 자동 완성을 사용 설정 또는 사용 중지하고 자동 완성 서비스를 변경할 수도 있습니다. 앱은 사용자의 자동 완성 설정을 재정의할 수 없지만, 사용자가 자동 완성을 사용할 수 있는 경우 앱 또는 앱의 특정 뷰에서 추가 자동 완성 기능을 구현할 수 있습니다.

예를 들어 TextView는 사용자가 자동 완성을 사용할 수 있는 경우 더보기 메뉴에서 자동 완성 항목을 표시합니다. 사용자가 자동 완성을 사용할 수 있는지 확인하려면 AutofillManager 객체의 isEnabled() 메서드를 호출하세요.

자동 완성을 사용할 수 없는 사용자의 가입 및 로그인 환경을 최적화하려면 원탭 로그인을 구현하세요.

자동 완성 요청 강제

사용자 작업에 대한 응답으로 자동 완성 요청이 발생하도록 강제해야 할 때도 있습니다. 예를 들어 TextView는 사용자가 뷰를 길게 터치할 때 자동 완성 메뉴 항목을 제공합니다. 다음 코드 예에서는 자동 완성 요청을 강제하는 방법을 보여줍니다.

Kotlin

fun eventHandler(view: View) {
    val afm = requireContext().getSystemService(AutofillManager::class.java)
    afm?.requestAutofill(view)
}

Java

public void eventHandler(View view) {
    AutofillManager afm = context.getSystemService(AutofillManager.class);
    if (afm != null) {
        afm.requestAutofill(view);
    }
}

cancel() 메서드를 사용하여 현재 자동 완성 컨텍스트를 취소할 수도 있습니다. 로그인 페이지의 필드를 삭제하는 버튼이 있는 경우에 이 방법이 유용할 수 있습니다.

선택 도구 컨트롤의 데이터에 올바른 자동 완성 유형 사용

선택 도구는 날짜 또는 시간 데이터를 저장하는 필드의 값을 사용자가 변경할 수 있는 UI를 제공하므로 자동 완성에서 유용합니다. 예를 들어 신용카드 양식에서 날짜 선택 도구를 사용하면 사용자가 신용카드 만료일을 입력하거나 변경할 수 있습니다. 그러나 선택 도구가 표시되지 않으면 EditText와 같은 다른 뷰를 사용하여 데이터를 표시해야 합니다.

EditText 객체는 기본적으로 AUTOFILL_TYPE_TEXT 유형의 자동 완성 데이터를 예상합니다. 다른 유형의 데이터를 사용한다면 EditText에서 상속받고 상응하는 유형의 데이터를 처리하는 데 필요한 메서드를 구현하는 맞춤 뷰를 만듭니다. 예를 들어 날짜 필드가 있으면 AUTOFILL_TYPE_DATE 유형의 값을 올바르게 처리하는 로직으로 메서드를 구현하세요.

자동 완성 데이터 유형을 지정하면 자동 완성 서비스에서 뷰에 표시되는 데이터의 적절한 표현을 만들 수 있습니다. 자세한 내용은 자동 완성과 함께 선택 도구 사용하기를 참고하세요.

자동 완성 컨텍스트 완료

자동 완성 프레임워크는 자동 완성 컨텍스트가 완료된 후 '자동 완성을 위해 저장하시겠습니까?' 대화상자를 표시하여 향후 사용을 위해 사용자 입력을 저장합니다. 일반적으로 활동이 완료될 때 자동 완성 컨텍스트가 완료됩니다. 그러나 프레임워크에 명시적으로 알려야 하는 상황도 있습니다. 예를 들어 로그인 화면과 콘텐츠 화면에서 동일한 활동이지만 다른 프래그먼트를 사용하는 경우입니다. 이러한 상황에서는 AutofillManager.commit()을 호출하여 컨텍스트를 명시적으로 완료할 수 있습니다.

맞춤 뷰 지원

맞춤 뷰는 자동 완성 API를 사용하여 자동 완성 프레임워크에 노출되는 메타데이터를 지정할 수 있습니다. 일부 뷰는 OpenGL 렌더링 UI가 포함된 뷰와 같은 가상 하위 요소의 컨테이너 역할을 합니다. 이러한 뷰는 자동 완성 프레임워크를 사용하기 전에 API를 사용하여 앱에서 사용된 정보의 구조를 지정해야 합니다.

앱에서 맞춤 뷰를 사용한다면 다음 시나리오를 고려합니다.

  • 맞춤 뷰는 표준 뷰 구조 또는 기본 뷰 구조를 제공합니다.
  • 맞춤 뷰에는 가상 구조 또는 자동 완성 프레임워크에서 사용할 수 없는 뷰 구조가 있습니다.

표준 뷰 구조의 맞춤 뷰

맞춤 뷰는 자동 완성이 작동하는 데 필요한 메타데이터를 정의할 수 있습니다. 맞춤 뷰에서 자동 완성 프레임워크를 사용하기 위해 메타데이터를 적절하게 관리하는지 확인합니다. 맞춤 뷰는 다음 작업을 실행해야 합니다.

  • 프레임워크가 앱으로 전송하는 자동 완성 값을 처리합니다.
  • 자동 완성 유형 및 값을 프레임워크에 제공합니다.

자동 완성이 트리거되면 자동 완성 프레임워크는 뷰의 autofill()을 호출하고 뷰에서 사용해야 하는 값을 전송합니다. autofill()을 구현하여 맞춤 뷰에서 자동 완성 값을 처리하는 방법을 지정합니다.

뷰는 getAutofillType()getAutofillValue() 메서드를 각각 재정의하여 자동 완성 유형과 값을 지정해야 합니다.

마지막으로 사용자가 현재 상태의 뷰 값을 제공할 수 없는 경우(예: 뷰가 사용 중지된 경우) 자동 완성은 뷰를 채워서는 안 됩니다. 이러한 경우 getAutofillType()AUTOFILL_TYPE_NONE을 반환해야 하고 getAutofillValue()null을 반환해야 하며 autofill()은 아무 작업도 하지 않아야 합니다.

다음은 프레임워크 내에서 올바르게 작동하려면 추가 단계가 필요한 예입니다.

  • 맞춤 뷰는 수정할 수 있습니다.
  • 맞춤 뷰에는 민감한 데이터가 포함되어 있습니다.

맞춤 뷰는 수정할 수 있습니다.

뷰를 수정할 수 있다면 AutofillManager 객체에 notifyValueChanged()를 호출하여 자동 완성 프레임워크에 변경사항을 알려야 합니다.

맞춤 뷰에는 민감한 데이터가 포함되어 있습니다.

뷰에 개인 식별 정보(PII)(예: 이메일 주소, 신용카드 번호, 비밀번호)가 포함된 경우 이를 민감한 정보로 표시해야 합니다.

일반적으로 콘텐츠가 정적 리소스에서 비롯되는 뷰에는 민감한 데이터가 포함되지 않지만 콘텐츠가 동적으로 설정된 뷰에는 민감한 데이터가 포함될 수 있습니다. 예를 들어 사용자 이름 입력이 포함된 라벨에는 민감한 데이터가 포함되지 않지만 Hello, John이 포함된 라벨에는 민감한 데이터가 포함됩니다.

자동 완성 프레임워크에서는 모든 데이터가 기본적으로 민감하다고 가정합니다. 데이터가 민감하지 않은 정보임을 표시할 수 있습니다.

뷰에 민감한 정보가 포함되어 있는지 표시하려면 onProvideAutofillStructure()을 구현하고 ViewStructure 객체에 setDataIsSensitive()를 호출합니다.

다음 코드 예에서는 뷰 구조의 데이터를 민감하지 않은 것으로 표시하는 방법을 보여줍니다.

Kotlin

override fun onProvideAutofillStructure(structure: ViewStructure, flags: Int) {
    super.onProvideAutofillStructure(structure, flags)

    structure.setDataIsSensitive(false)
}

Java

@Override
public void onProvideAutofillStructure(ViewStructure structure, int flags) {
    super.onProvideAutofillStructure(structure, flags);

    structure.setDataIsSensitive(false);
}

뷰에서 사전 정의된 값만 허용하는 경우 setAutofillOptions() 메서드를 사용하여 뷰를 자동 완성하는 데 사용할 수 있는 옵션을 설정하면 됩니다. 특히 자동 완성 유형이 AUTOFILL_TYPE_LIST인 뷰는 이 메서드를 사용해야 합니다. 자동 완성 서비스가 뷰를 채우는 데 사용할 수 있는 옵션을 알면 작업을 더 잘할 수 있기 때문입니다.

어댑터를 사용하는 뷰(예: Spinner)가 유사한 경우입니다. 예를 들어 신용카드 만료 필드에 사용할 동적으로 만들어진 연도(현재 연도에 기반)를 제공하는 스피너는 Adapter 인터페이스의 getAutofillOptions() 메서드를 구현하여 연도 목록을 제공할 수 있습니다.

ArrayAdapter를 사용하는 뷰는 값 목록도 제공할 수 있습니다. ArrayAdapter는 정적 리소스의 자동 완성 옵션을 자동으로 설정합니다. 값을 동적으로 제공하는 경우 getAutofillOptions()를 재정의합니다.

가상 구조의 맞춤 뷰

자동 완성 프레임워크에는 앱의 UI에서 정보를 수정하고 저장할 수 있는 뷰 구조가 필요합니다. 다음과 같은 상황에서는 프레임워크에 뷰 구조를 사용할 수 없습니다.

  • 앱이 OpenGL과 같은 낮은 수준의 렌더링 엔진을 사용하여 UI를 렌더링합니다.
  • 앱이 Canvas 인스턴스를 사용하여 UI를 그립니다.

이러한 경우에는 onProvideAutofillVirtualStructure()를 구현하고 다음 단계를 따라 뷰 구조를 지정할 수 있습니다.

  1. addChildCount()를 호출하여 뷰 구조의 하위 요소 수를 늘립니다.
  2. newChild()를 호출하여 하위 요소를 추가합니다.
  3. setAutofillId()를 호출하여 하위 요소의 자동 완성 ID를 설정합니다.
  4. 자동 완성 값 및 유형과 같은 관련 속성을 설정합니다.
  5. 가상 하위 요소의 데이터가 민감한 경우 truesetDataIsSensitive()에 전달합니다. 그 외의 경우에는 false를 전달합니다.

다음 코드 스니펫은 가상 구조에서 새로운 하위 요소를 만드는 방법을 보여줍니다.

Kotlin

override fun onProvideAutofillVirtualStructure(structure: ViewStructure, flags: Int) {

    super.onProvideAutofillVirtualStructure(structure, flags)

    // Create a new child in the virtual structure.
    structure.addChildCount(1)
    val child = structure.newChild(childIndex)

    // Set the autofill ID for the child.
    child.setAutofillId(structure.autofillId!!, childVirtualId)

    // Populate the child by providing properties such as value and type.
    child.setAutofillValue(childAutofillValue)
    child.setAutofillType(childAutofillType)

    // Some children can provide a list of values, such as when the child is
    // a spinner.
    val childAutofillOptions = arrayOf<CharSequence>("option1", "option2")
    child.setAutofillOptions(childAutofillOptions)

    // Just like other types of views, mark the data as sensitive when
    // appropriate.
    val sensitive = !contentIsSetFromResources()
    child.setDataIsSensitive(sensitive)
}

자바

@Override
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {

    super.onProvideAutofillVirtualStructure(structure, flags);

    // Create a new child in the virtual structure.
    structure.addChildCount(1);
    ViewStructure child =
            structure.newChild(childIndex);

    // Set the autofill ID for the child.
    child.setAutofillId(structure.getAutofillId(), childVirtualId);

    // Populate the child by providing properties such as value and type.
    child.setAutofillValue(childAutofillValue);
    child.setAutofillType(childAutofillType);

    // Some children can provide a list of values, such as when the child is
    // a spinner.
    CharSequence childAutofillOptions[] = { "option1", "option2" };
    child.setAutofillOptions(childAutofillOptions);

    // Just like other types of views, mark the data as sensitive when
    // appropriate.
    boolean sensitive = !contentIsSetFromResources();
    child.setDataIsSensitive(sensitive);
}

가상 구조의 요소가 변경되면 다음 작업을 실행하여 프레임워크에 알립니다.

  • 하위 요소 내부의 포커스가 변경되면 AutofillManager 객체의 notifyViewEntered()notifyViewExited()를 호출합니다.
  • 하위 요소의 값이 변경되면 AutofillManager 객체의 notifyValueChanged()를 호출합니다.
  • 사용자가 워크플로의 단계를 완료하여 뷰 계층 구조를 더 이상 사용할 수 없다면(예: 사용자가 로그인 형식을 사용하여 로그인한 경우) AutofillManager 객체에 commit()을 호출합니다.
  • 사용자가 워크플로의 단계를 취소하여 뷰 계층 구조가 더 이상 유효하지 않다면(예: 사용자가 로그인 형식을 삭제하는 버튼을 클릭하는 경우) AutofillManager 객체에 cancel()을 호출합니다.

자동 완성 이벤트에서 콜백 사용

앱에서 자체 자동 완성 뷰를 제공한다면 UI 자동 완성 어포던스의 변경에 응답하여 뷰를 사용 설정 또는 중지하라고 앱에 알리는 메커니즘이 필요합니다. 자동 완성 프레임워크는 AutofillCallback 형식으로 이 메커니즘을 제공합니다.

이 클래스는 onAutofillEvent(View, int) 메서드를 제공하고 뷰와 연결된 자동 완성 상태의 변경 후에 앱에서 이 메서드를 호출합니다. 이 메서드의 오버로드된 버전도 있습니다. 이 버전에는 앱에서 가상 뷰와 함께 사용할 수 있는 childId 매개변수가 포함됩니다. 사용 가능한 상태는 콜백에서 상수로 정의됩니다.

AutofillManager 클래스의 registerCallback() 메서드를 사용하여 콜백을 등록할 수 있습니다. 다음 코드 예는 자동 완성 이벤트의 콜백을 선언하는 방법을 보여줍니다.

Kotlin

val afm = context.getSystemService(AutofillManager::class.java)

afm?.registerCallback(object : AutofillManager.AutofillCallback() {
    // For virtual structures, override
    // onAutofillEvent(View view, int childId, int event) instead.
    override fun onAutofillEvent(view: View, event: Int) {
        super.onAutofillEvent(view, event)
        when (event) {
            EVENT_INPUT_HIDDEN -> {
                // The autofill affordance associated with the view was hidden.
            }
            EVENT_INPUT_SHOWN -> {
                // The autofill affordance associated with the view was shown.
            }
            EVENT_INPUT_UNAVAILABLE -> {
                // Autofill isn't available.
            }
        }

    }
})

Java

AutofillManager afm = getContext().getSystemService(AutofillManager.class);

afm.registerCallback(new AutofillManager.AutofillCallback() {
    // For virtual structures, override
    // onAutofillEvent(View view, int childId, int event) instead.
    @Override
    public void onAutofillEvent(@NonNull View view, int event) {
        super.onAutofillEvent(view, event);
        switch (event) {
            case EVENT_INPUT_HIDDEN:
                // The autofill affordance associated with the view was hidden.
                break;
            case EVENT_INPUT_SHOWN:
                // The autofill affordance associated with the view was shown.
                break;
            case EVENT_INPUT_UNAVAILABLE:
                // Autofill isn't available.
                break;
        }
    }
});

콜백을 삭제할 때가 되면 unregisterCallback() 메서드를 사용하세요.

자동 완성 강조표시 드로어블 맞춤설정

뷰가 자동 완성되면 플랫폼은 뷰 위에 Drawable을 렌더링하여 뷰 콘텐츠가 자동 완성된다고 표시합니다. 기본적으로 이 드로어블은 배경을 그리는 데 사용되는 테마 색상보다 약간 어두운 반투명 색상의 단색 직사각형입니다. 드로어블을 변경할 필요는 없지만 이 예와 같이 애플리케이션 또는 활동에서 사용하는 테마android:autofilledHighlight 항목을 재정의하여 맞춤설정할 수 있습니다.

res/values/styles.xml

<resources>
    <style name="MyAutofilledHighlight" parent="...">
        <item name="android:autofilledHighlight">@drawable/my_drawable</item>
    </style>
</resources>

res/drawable/my_drawable.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#4DFF0000" />
</shape>

AndroidManifest.xml

<application ...
    android:theme="@style/MyAutofilledHighlight">
<!-- or -->
<activity ...
    android:theme="@style/MyAutofilledHighlight">

자동 완성 인증

자동 완성 서비스는 서비스에서 앱의 필드를 완료하기 전에 사용자의 인증을 요구할 수 있습니다. 이 경우 Android 시스템은 서비스의 인증 활동을 활동 스택의 일부로 실행합니다.

앱을 업데이트하여 인증을 지원할 필요는 없습니다. 인증이 서비스 내에서 실행되기 때문입니다. 그러나 활동이 다시 시작될 때 활동의 뷰 구조가 유지되는지 확인해야 합니다(예: onStart() 또는 onResume()이 아닌 onCreate()에서 뷰 구조 만들기).

자동 완성 서비스에 인증이 필요할 때 AutofillFramework 샘플의 HeuristicsService를 사용하고 완성 응답 인증을 요구하도록 구성하여 앱이 어떻게 동작하는지 확인할 수 있습니다. BadViewStructureCreationSignInActivity 샘플을 사용하여 이 문제를 에뮬레이션할 수도 있습니다.

자동 완성 ID를 재활용된 뷰에 할당

RecyclerView 클래스와 같이 뷰를 재활용하는 컨테이너는 큰 데이터 세트에 기반한 요소의 스크롤 목록을 표시해야 하는 앱에 유용합니다. 컨테이너가 스크롤되면 시스템은 레이아웃의 뷰를 재사용하지만 뷰에는 새로운 콘텐츠가 포함됩니다.

재활용된 뷰의 초기 콘텐츠가 채워지면 자동 완성 서비스는 자동 완성 ID를 사용하여 뷰의 논리적 의미를 유지합니다. 시스템이 레이아웃의 뷰를 재사용하므로 뷰의 논리적 ID가 동일하게 유지되어 잘못된 자동 완성 사용자 데이터가 자동 완성 ID와 연결될 때 문제가 발생합니다.

Android 9(API 수준 28) 이상을 실행하는 기기에서 이 문제를 해결하려면 RecyclerView에서 사용하는 뷰의 자동 완성 ID를 아래의 메서드를 사용하여 명시적으로 관리합니다.

  • getNextAutofillId() 메서드는 활동에 고유한 새로운 자동 완성 ID를 가져옵니다.
  • setAutofillId() 메서드는 활동에서 이 뷰의 고유한 논리적 자동 완성 ID를 설정합니다.

알려진 문제 해결

이 섹션에서는 자동 완성 프레임워크 내의 알려진 문제를 해결하는 방법을 보여줍니다.

자동 완성으로 인해 Android 8.0, 8.1에서 앱이 비정상 종료됩니다.

Android 8.0(API 수준 26) 및 8.1(API 수준 27)에서는 자동 완성으로 인해 앱이 특정 시나리오에서 비정상 종료될 수 있습니다. 발생할 수 있는 문제를 해결하려면 자동 완성되지 않은 모든 뷰에 importantForAutofill=no로 태그를 지정합니다. importantForAutofill=noExcludeDescendants로 전체 활동에 태그를 지정할 수도 있습니다.

크기가 조절된 대화상자는 자동 완성에 고려되지 않음

Android 8.1(API 수준 27) 이하에서는 대화상자의 뷰가 이미 표시된 후에 크기가 조절되면 뷰를 자동 완성에 고려하지 않습니다. 이러한 뷰는 Android 시스템이 자동 완성 서비스에 전송하는 AssistStructure 객체에 포함되지 않습니다. 결과적으로 서비스는 뷰를 채울 수 없습니다.

이 문제를 해결하려면 대화상자 창 매개변수의 token 속성을 대화상자를 만드는 활동의 token 속성으로 교체합니다. 자동 완성이 사용 설정된 것을 확인한 후 Dialog에서 상속받은 클래스의 onWindowAttributesChanged() 메서드에 창 매개변수를 저장합니다. 그런 다음 저장된 매개변수의 token 속성을 onAttachedToWindow() 메서드에 있는 상위 활동의 token 속성으로 교체합니다.

다음 코드 스니펫은 이 해결 방법을 구현하는 클래스를 보여줍니다.

Kotlin

class MyDialog(context: Context) : Dialog(context) {

    // Used to store the dialog window parameters.
    private var token: IBinder? = null

    private val isDialogResizedWorkaroundRequired: Boolean
        get() {
            if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
                return false
            }
            val autofillManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                context.getSystemService(AutofillManager::class.java)
            } else {
                null
            }
            return autofillManager?.isEnabled ?: false
        }

    override fun onWindowAttributesChanged(params: WindowManager.LayoutParams) {
        if (params.token == null && token != null) {
            params.token = token
        }

        super.onWindowAttributesChanged(params)
    }

    override fun onAttachedToWindow() {
        if (isDialogResizedWorkaroundRequired) {
            token = ownerActivity!!.window.attributes.token
        }

        super.onAttachedToWindow()
    }

}

Java

public class MyDialog extends Dialog {

    public MyDialog(Context context) {
        super(context);
    }

    // Used to store the dialog window parameters.
    private IBinder token;

    @Override
    public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
        if (params.token == null && token != null) {
            params.token = token;
        }

        super.onWindowAttributesChanged(params);
    }

    @Override
    public void onAttachedToWindow() {
        if (isDialogResizedWorkaroundRequired()) {
            token = getOwnerActivity().getWindow().getAttributes().token;
        }

        super.onAttachedToWindow();
    }

    private boolean isDialogResizedWorkaroundRequired() {
        if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O
                || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
            return false;
        }
        AutofillManager autofillManager =
                null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            autofillManager = getContext().getSystemService(AutofillManager.class);
        }
        return autofillManager != null && autofillManager.isEnabled();
    }

}

불필요한 작업을 피하기 위해 다음 코드 스니펫은 자동 완성을 기기에서 지원하고 현재 사용자가 사용할 수 있는지, 이 해결 방법이 필요한지 확인하는 방법을 보여줍니다.

Kotlin

// AutofillExtensions.kt

fun Context.isDialogResizedWorkaroundRequired(): Boolean {
    // After the issue is resolved on Android, check whether the
    // workaround is still required for the current device.
    return isAutofillAvailable()
}

fun Context.isAutofillAvailable(): Boolean {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        // The autofill framework is available on Android 8.0
        // or higher.
        return false
    }

    val afm = getSystemService(AutofillManager::class.java)
    // Return true if autofill is supported by the device and enabled
    // for the current user.
    return afm != null && afm.isEnabled
}

Java

public class AutofillHelper {

    public static boolean isDialogResizedWorkaroundRequired(Context context) {
        // After the issue is resolved on Android, check whether the
        // workaround is still required for the current device.
        return isAutofillAvailable(context);
    }

    public static boolean isAutofillAvailable(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            // The autofill framework is available on Android 8.0
            // or higher.
            return false;
        }

        AutofillManager afm = context.getSystemService(AutofillManager.class);
        // Return true if autofill is supported by the device and enabled
        // for the current user.
        return afm != null && afm.isEnabled();
    }
}

자동 완성으로 앱 테스트

자동 완성 서비스와 호환되도록 앱을 최적화한 후 의도대로 자동 완성 서비스와 호환되는지 테스트합니다.

에뮬레이터 또는 Android 8.0(API 수준 26) 이상을 실행하는 실제 기기를 사용하여 앱을 테스트합니다. 에뮬레이터를 만드는 방법에 관한 자세한 내용은 가상 기기 만들기 및 관리를 참고하세요.

자동 완성 서비스 설치

자동 완성으로 앱을 테스트하려면 자동 완성 서비스를 제공하는 다른 앱을 설치해야 합니다. 이러한 목적으로 서드 파티 앱을 사용할 수도 있지만 간단하게 샘플 자동 완성 서비스를 사용하면 서드 파티 서비스에 가입할 필요가 없습니다.

자바에서 Android 자동 완성 프레임워크 샘플을 사용하여 자동 완성 서비스로 앱을 테스트할 수 있습니다. 샘플 앱은 앱에서 사용하기 전에 워크플로를 테스트하는 데 사용할 수 있는 자동 완성 서비스와 클라이언트 Activity 클래스를 제공합니다. 이 페이지에서는 android-AutofillFramework 샘플 앱을 참조합니다.

앱을 설치한 후 설정 > 시스템 > 언어 및 입력 > 고급 > 입력 지원 > 자동 완성 서비스로 이동하여 에뮬레이터의 시스템 설정에서 자동 완성 서비스를 사용 설정합니다.

데이터 요구사항 분석

자동 완성 서비스로 앱을 테스트하려면 서비스에 앱을 채우는 데 사용할 수 있는 데이터가 있어야 합니다. 서비스는 앱의 뷰에서 어떤 데이터 유형을 예상하는지도 알아야 합니다. 예를 들어 앱에 사용자 이름을 예상하는 뷰가 있다면 서비스에는 사용자 이름이 포함된 데이터 세트와 뷰에서 이러한 데이터를 예상하는 것을 아는 메커니즘이 있어야 합니다.

android:autofillHints 속성을 설정하여 뷰에 어떤 데이터 유형이 필요한지 서비스가 알 수 있도록 합니다. 일부 서비스는 정교한 휴리스틱을 사용하여 데이터 유형을 파악하지만 샘플 앱과 같은 다른 서비스는 개발자에게 의존하여 이 정보를 제공합니다. 자동 완성과 관련된 뷰에서 android:autofillHints 속성을 설정하면 앱에서 자동 완성 서비스가 더 잘 작동합니다.

테스트 실행

데이터 요구사항을 분석하면 테스트를 실행할 수 있습니다. 여기에는 자동 완성 서비스에 테스트 데이터 저장과 앱에서 자동 완성 트리거가 포함됩니다.

서비스에 데이터 저장

현재 활성화된 자동 완성 서비스에 데이터를 저장하려면 다음 단계를 따르세요.

  1. 테스트 중에 사용하려는 데이터 유형을 예상하는 뷰가 포함된 앱을 엽니다. android-AutofillFramework 샘플 앱은 신용카드 번호와 사용자 이름과 같은 여러 유형의 데이터를 예상하는 뷰를 UI에 제공합니다.
  2. 필요한 데이터 유형을 보유한 뷰를 탭합니다.
  3. 뷰에 값을 입력합니다.
  4. 로그인 또는 제출과 같은 확인 버튼을 탭합니다. 일반적으로 서비스에서 데이터를 저장하기 전에 양식을 제출해야 합니다.
  5. 시스템 대화상자에서 권한 요청을 확인합니다. 시스템 대화상자에는 현재 활성화된 서비스의 이름이 표시되고, 그것이 테스트에 사용하려는 서비스인지 묻는 메시지가 표시됩니다. 서비스를 사용하려면 저장을 탭합니다.

Android가 권한 대화상자를 표시하지 않거나 서비스가 테스트에 사용하려는 것이 아니라면 시스템 설정에서 서비스가 현재 활성화되어 있는지 확인합니다.

앱에서 자동 완성 트리거

앱에서 자동 완성을 트리거하려면 다음을 따르세요.

  1. 앱을 열고 테스트하려는 뷰가 있는 활동으로 이동합니다.
  2. 채워야 하는 뷰를 탭합니다.
  3. 그림 1과 같이 뷰를 채울 수 있는 데이터 세트가 포함된 자동 완성 UI가 시스템에 표시됩니다.
  4. 사용하려는 데이터가 포함된 데이터 세트를 탭합니다. 이전에 서비스에 저장된 데이터가 뷰에 표시됩니다.
'데이터 세트-2'를 사용 가능한 데이터 세트로 표시하는 자동 완성 UI
그림 1. 사용 가능한 데이터 세트를 표시하는 자동 완성 UI

Android에서 자동 완성 UI를 표시하지 않는다면 다음 문제 해결 옵션을 시도해 보세요.

  • 앱의 뷰가 android:autofillHints 속성의 올바른 값을 사용하는지 확인합니다. 속성에 가능한 값의 목록은 View 클래스에서 AUTOFILL_HINT가 앞에 붙은 상수를 참고하세요.
  • android:importantForAutofill 속성이 채워져야 하는 뷰에서 no 이외의 값으로 설정되어 있는지 또는 뷰나 상위 요소 중 하나에서 noExcludeDescendants 이외의 값으로 설정되어 있는지 확인합니다.