設定のサブタイトル

ほとんどの場合、設定の概要は比較的簡単に追加できます。必要なのは、適切な文字列リソースを使用して、それぞれの設定に android:summary 属性を追加することだけです。ただし、サブタイトルを動的に更新する必要がある場合は、カスタムの設定コントローラが必要になることがあります。

静的なサブタイトル

設定に静的なサブタイトルを追加するには、次のようにします。

  1. 設定に 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 の位置情報の設定を変更して、位置情報のオン / オフを指定するサブタイトルを追加し、オンの場合、位置情報へのアクセスが許可されているアプリの数が表示されるようにしています。

  1. 新しい文字列を定義します。
    <!-- 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>
    
  2. LocationEntryPreferenceController を作成します。この新しい PreferenceController により、位置情報設定の概要テキストを動的に設定および変更します。
    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 文字列に設定するとともに、アクセス権限を持つアプリの数を指定します。
    • 設定コントローラが起動されると、コントローラはレシーバを登録し、位置情報の状態が変化したときに設定の状態を更新します。
  3. フラグメントの 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"/>