2025년 3월 27일부터 AOSP를 빌드하고 기여하려면 aosp-main
대신 android-latest-release
를 사용하는 것이 좋습니다. 자세한 내용은 AOSP 변경사항을 참고하세요.
환경설정 자막
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
대부분의 경우 환경설정 요약을 추가하는 작업은 비교적 간단합니다. android:summary
속성을 적절한 문자열 리소스를 사용하여 각각의 환경설정에 추가하기만 하면 되기 때문입니다. 그러나 자막을 동적으로 업데이트해야 하는 경우 맞춤 환경설정 컨트롤러가 필요할 수 있습니다.
정적 자막
환경설정에 정적 자막을 추가하려면 다음 안내를 따르세요.
android:summary
속성을 환경설정에 추가합니다. 예를 들어 L0 디스플레이 설정 환경설정에 요약을 추가하려면 다음 내용을 환경설정 속성에 추가합니다.
android:summary="@string/display_settings_summary"
예를 들어 다음과 같은 전체 환경설정 코드 샘플을 생각해 보세요.
<Preference
android:fragment="com.android.car.settings.display.DisplaySettingsFragment"
android:icon="@drawable/ic_settings_display"
android:key="@string/pk_display_settings_entry"
android:title="@string/display_settings"
android:summary="@string/display_settings_summary"
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
동적 자막
android:summary
속성으로 지정된 자막은 정적이므로 특정 조건에 따라 업데이트할 수 없습니다. 동적 자막의 경우 환경설정의 환경설정 컨트롤러를 수정해야 합니다. 다음 예에서는 위치의 사용 설정 여부를 지정하고 사용 설정된 경우 현재 위치 액세스 권한이 있는 앱의 수를 명시하는 자막을 포함하도록 L0 위치 환경설정을 수정합니다.
- 새 문자열을 정의합니다.
<!-- Summary for Location settings when location is off [CHAR LIMIT=NONE] -->
<string name="location_settings_summary_location_off">Off</string>
<!-- Summary for Location settings when location is on, explaining how many apps have location permission [CHAR LIMIT=NONE]-->
<plurals name="location_settings_summary_location_on">
<item quantity="one">On - <xliff:g id="count">%1$d</xliff:g> app has access to location</item>
<item quantity="other">On - <xliff:g id="count">%1$d</xliff:g> apps have access to location</item>
</plurals>
<!-- Location settings, loading the number of apps which have location permission [CHAR LIMIT=30] -->
<string name="location_settings_loading_app_permission_stats">Loading\u2026</string>
- 위치 환경설정 요약 텍스트를 동적으로 설정하고 변경하는 새 PreferenceController
LocationEntryPreferenceController
를 만듭니다.
public class LocationEntryPreferenceController extends PreferenceController<Preference> {
private static final Logger LOG = new Logger(LocationEntryPreferenceController.class);
private static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED =
new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
private final Context mContext;
private final LocationManager mLocationManager;
/** Total number of apps that have location permissions. */
private int mNumTotal = -1;
private int mNumTotalLoading = 0;
private AtomicInteger mLoadingInProgress = new AtomicInteger(0);
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
refreshUi();
}
};
public LocationEntryPreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
mContext = context;
mLocationManager = (LocationManager) getContext().getSystemService(
Service.LOCATION_SERVICE);
}
@Override
protected Class<Preference> getPreferenceType() {
return Preference.class;
}
@Override
protected void onStartInternal() {
getContext().registerReceiver(mReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED);
}
@Override
protected void onStopInternal() {
getContext().unregisterReceiver(mReceiver);
}
@Override
protected void updateState(Preference preference) {
super.updateState(preference);
updateSummary(preference);
if (!mLocationManager.isLocationEnabled() || mLoadingInProgress.get() != 0) {
return;
}
mNumTotalLoading = 0;
// Retrieve a list of users inside the current user profile group.
List<UserHandle> users = mContext.getSystemService(
UserManager.class).getUserProfiles();
mLoadingInProgress.set(users.size());
for (UserHandle user : users) {
Context userContext = createPackageContextAsUser(mContext, user.getIdentifier());
if (userContext == null) {
if (mLoadingInProgress.decrementAndGet() == 0) {
setLocationAppCount(preference, mNumTotalLoading);
}
continue;
}
PermissionControllerManager permController =
userContext.getSystemService(PermissionControllerManager.class);
permController.countPermissionApps(
Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION),
PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED,
(numApps) -> {
mNumTotalLoading += numApps;
if (mLoadingInProgress.decrementAndGet() == 0) {
setLocationAppCount(preference, mNumTotalLoading);
}
}, null);
}
}
@VisibleForTesting
void setLocationAppCount(Preference preference, int numApps) {
mNumTotal = numApps;
updateSummary(preference);
}
private void updateSummary(Preference preference) {
String summary = "";
if (mLocationManager.isLocationEnabled()) {
if (mNumTotal == -1) {
summary = mContext.getString(R.string.location_settings_loading_app_permission_stats);
} else {
summary = mContext.getResources().getQuantityString(
R.plurals.location_settings_summary_location_on,
mNumTotal, mNumTotal);
}
} else {
summary = mContext.getString(R.string.location_settings_summary_location_off);
}
preference.setSummary(summary);
}
private Context createPackageContextAsUser(Context context, int userId) {
try {
return context.createPackageContextAsUser(
context.getPackageName(), 0 /* flags */, UserHandle.of(userId));
} catch (PackageManager.NameNotFoundException e) {
LOG.e("Failed to create user context", e);
}
return null;
}
}
이 샘플 컨트롤러의 경우
- 위치가 사용 중지되면 요약 텍스트는
location_settings_summary_location_off
문자열로 설정됩니다.
- 위치가 사용 설정되면 위치 정보 액세스 권한이 있는 앱의 수가 추가됩니다. 이를 로드하는 중에
location_settings_loading_app_permission_stats
문자열이 표시됩니다. 데이터가 로드될 때 컨트롤러는 액세스 권한이 지정된 앱 수와 함께 location_settings_summary_location_on
문자열로 요약을 설정합니다.
- 환경설정 컨트롤러가 시작될 때 컨트롤러는 수신기를 등록하고 위치 상태가 변경될 때 환경설정 상태를 새로고침합니다.
- 새 컨트롤러를 관련 환경설정에 연결하도록 프래그먼트 XML 파일을 수정합니다.
<Preference
android:fragment="com.android.car.settings.location.LocationSettingsFragment"
android:icon="@drawable/ic_settings_location"
android:key="@string/pk_location_settings_entry"
android:title="@string/location_settings_title"
settings:controller="com.android.car.settings.location.LocationEntryPreferenceController"/>
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-27(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-07-27(UTC)"],[],[],null,["# Preference subtitles\n\nFor the most part, adding preference summaries is relatively straightforward as it\ninvolves just adding the `android:summary` attribute to the respective preference\nwith the appropriate string resource. However, if the subtitles should update dynamically\nthen a custom preference controller may be necessary.\n\nStatic subtitles\n----------------\n\nTo add a static subtitle to a preference:\n\n1. Add the `android:summary` attribute to the preference. For example, to add a summary to the L0 display settings preference, add something like the following to the preference attributes: \n\n android:summary=\"@string/display_settings_summary\"\n\n For example, consider this full preference code sample: \n\n ```\n \u003cPreference\n android:fragment=\"com.android.car.settings.display.DisplaySettingsFragment\"\n android:icon=\"@drawable/ic_settings_display\"\n android:key=\"@string/pk_display_settings_entry\"\n android:title=\"@string/display_settings\"\n android:summary=\"@string/display_settings_summary\"\n settings:controller=\"com.android.car.settings.common.DefaultRestrictionsPreferenceController\"/\u003e\n ```\n\nDynamic subtitles\n-----------------\n\nSubtitles specified with the `android:summary` attribute are static so cannot\nupdate based on certain conditions. For dynamic subtitles, you need to modify the preference\ncontroller for the preference. The following example modifies the L0 location preference to\nhave a subtitle that specifies if location is on or off and, if on, state how many apps\ncurrently have location access.\n\n1. Define the new strings: \n\n ```\n \u003c!-- Summary for Location settings when location is off [CHAR LIMIT=NONE] --\u003e\n \u003cstring name=\"location_settings_summary_location_off\"\u003eOff\u003c/string\u003e\n \u003c!-- Summary for Location settings when location is on, explaining how many apps have location permission [CHAR LIMIT=NONE]--\u003e\n \u003cplurals name=\"location_settings_summary_location_on\"\u003e\n \u003citem quantity=\"one\"\u003eOn - \u003cxliff:g id=\"count\"\u003e%1$d\u003c/xliff:g\u003e app has access to location\u003c/item\u003e\n \u003citem quantity=\"other\"\u003eOn - \u003cxliff:g id=\"count\"\u003e%1$d\u003c/xliff:g\u003e apps have access to location\u003c/item\u003e\n \u003c/plurals\u003e\n \u003c!-- Location settings, loading the number of apps which have location permission [CHAR LIMIT=30] --\u003e\n \u003cstring name=\"location_settings_loading_app_permission_stats\"\u003eLoading\\u2026\u003c/string\u003e\n ```\n2. Create a new PreferenceController, `LocationEntryPreferenceController`, to dynamically set and change the location preference summary text: \n\n ```\n public class LocationEntryPreferenceController extends PreferenceController\u003cPreference\u003e {\n\n private static final Logger LOG = new Logger(LocationEntryPreferenceController.class);\n private static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED =\n new IntentFilter(LocationManager.MODE_CHANGED_ACTION);\n\n private final Context mContext;\n private final LocationManager mLocationManager;\n /** Total number of apps that have location permissions. */\n private int mNumTotal = -1;\n private int mNumTotalLoading = 0;\n private AtomicInteger mLoadingInProgress = new AtomicInteger(0);\n\n private final BroadcastReceiver mReceiver = new BroadcastReceiver() {\n @Override\n public void onReceive(Context context, Intent intent) {\n refreshUi();\n }\n };\n\n public LocationEntryPreferenceController(Context context, String preferenceKey,\n FragmentController fragmentController, CarUxRestrictions uxRestrictions) {\n super(context, preferenceKey, fragmentController, uxRestrictions);\n mContext = context;\n mLocationManager = (LocationManager) getContext().getSystemService(\n Service.LOCATION_SERVICE);\n }\n\n @Override\n protected Class\u003cPreference\u003e getPreferenceType() {\n return Preference.class;\n }\n\n @Override\n protected void onStartInternal() {\n getContext().registerReceiver(mReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED);\n }\n\n @Override\n protected void onStopInternal() {\n getContext().unregisterReceiver(mReceiver);\n }\n\n @Override\n protected void updateState(Preference preference) {\n super.updateState(preference);\n updateSummary(preference);\n if (!mLocationManager.isLocationEnabled() || mLoadingInProgress.get() != 0) {\n return;\n }\n mNumTotalLoading = 0;\n // Retrieve a list of users inside the current user profile group.\n List\u003cUserHandle\u003e users = mContext.getSystemService(\n UserManager.class).getUserProfiles();\n mLoadingInProgress.set(users.size());\n for (UserHandle user : users) {\n Context userContext = createPackageContextAsUser(mContext, user.getIdentifier());\n if (userContext == null) {\n if (mLoadingInProgress.decrementAndGet() == 0) {\n setLocationAppCount(preference, mNumTotalLoading);\n }\n continue;\n }\n PermissionControllerManager permController =\n userContext.getSystemService(PermissionControllerManager.class);\n permController.countPermissionApps(\n Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION),\n PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED,\n (numApps) -\u003e {\n mNumTotalLoading += numApps;\n if (mLoadingInProgress.decrementAndGet() == 0) {\n setLocationAppCount(preference, mNumTotalLoading);\n }\n }, null);\n }\n }\n\n @VisibleForTesting\n void setLocationAppCount(Preference preference, int numApps) {\n mNumTotal = numApps;\n updateSummary(preference);\n }\n\n private void updateSummary(Preference preference) {\n String summary = \"\";\n if (mLocationManager.isLocationEnabled()) {\n if (mNumTotal == -1) {\n summary = mContext.getString(R.string.location_settings_loading_app_permission_stats);\n } else {\n summary = mContext.getResources().getQuantityString(\n R.plurals.location_settings_summary_location_on,\n mNumTotal, mNumTotal);\n }\n } else {\n summary = mContext.getString(R.string.location_settings_summary_location_off);\n }\n preference.setSummary(summary);\n }\n\n private Context createPackageContextAsUser(Context context, int userId) {\n try {\n return context.createPackageContextAsUser(\n context.getPackageName(), 0 /* flags */, UserHandle.of(userId));\n } catch (PackageManager.NameNotFoundException e) {\n LOG.e(\"Failed to create user context\", e);\n }\n return null;\n }\n }\n ```\n\n For this sample controller:\n - If location is disabled, summary text is set to the `location_settings_summary_location_off` string.\n - If location is enabled, the number of apps that have location permission is added. While this is loading, the `location_settings_loading_app_permission_stats` string is shown. When the data is loaded, the controller sets the summary to the `location_settings_summary_location_on` string with the number of apps with access specified.\n - When the preference controller is started, the controller registers a receiver and refreshes the preference state when the location state changes.\n3. Modify the fragment XML file to attach the new controller to the relevant preference: \n\n ```\n \u003cPreference\n android:fragment=\"com.android.car.settings.location.LocationSettingsFragment\"\n android:icon=\"@drawable/ic_settings_location\"\n android:key=\"@string/pk_location_settings_entry\"\n android:title=\"@string/location_settings_title\"\n settings:controller=\"com.android.car.settings.location.LocationEntryPreferenceController\"/\u003e\n ```"]]