1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.core; 18 19 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_CONTROLLER; 20 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_FOR_WORK; 21 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; 22 23 import android.annotation.NonNull; 24 import android.annotation.XmlRes; 25 import android.content.Context; 26 import android.os.Bundle; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; 31 import com.android.settingslib.core.AbstractPreferenceController; 32 33 import org.xmlpull.v1.XmlPullParserException; 34 35 import java.io.IOException; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Set; 39 import java.util.TreeSet; 40 41 /** 42 * Helper to load {@link BasePreferenceController} lists from Xml. 43 */ 44 public class PreferenceControllerListHelper { 45 46 private static final String TAG = "PrefCtrlListHelper"; 47 48 /** 49 * Instantiates a list of controller based on xml definition. 50 */ 51 @NonNull getPreferenceControllersFromXml(Context context, @XmlRes int xmlResId)52 public static List<BasePreferenceController> getPreferenceControllersFromXml(Context context, 53 @XmlRes int xmlResId) { 54 final List<BasePreferenceController> controllers = new ArrayList<>(); 55 List<Bundle> preferenceMetadata; 56 try { 57 preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, 58 MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER 59 | MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_FOR_WORK); 60 } catch (IOException | XmlPullParserException e) { 61 Log.e(TAG, "Failed to parse preference xml for getting controllers", e); 62 return controllers; 63 } 64 65 for (Bundle metadata : preferenceMetadata) { 66 final String controllerName = metadata.getString(METADATA_CONTROLLER); 67 if (TextUtils.isEmpty(controllerName)) { 68 continue; 69 } 70 BasePreferenceController controller; 71 try { 72 controller = BasePreferenceController.createInstance(context, controllerName); 73 } catch (IllegalStateException e) { 74 Log.d(TAG, "Could not find Context-only controller for pref: " + controllerName); 75 final String key = metadata.getString(METADATA_KEY); 76 final boolean isWorkProfile = metadata.getBoolean(METADATA_FOR_WORK, false); 77 if (TextUtils.isEmpty(key)) { 78 Log.w(TAG, "Controller requires key but it's not defined in xml: " 79 + controllerName); 80 continue; 81 } 82 try { 83 controller = BasePreferenceController.createInstance(context, controllerName, 84 key, isWorkProfile); 85 } catch (IllegalStateException e2) { 86 Log.w(TAG, "Cannot instantiate controller from reflection: " + controllerName); 87 continue; 88 } 89 } 90 controllers.add(controller); 91 } 92 return controllers; 93 } 94 95 /** 96 * Return a sub list of {@link AbstractPreferenceController} to only contain controller that 97 * doesn't exist in filter. 98 * 99 * @param filter The filter. This list will be unchanged. 100 * @param input This list will be filtered into a sublist and element is kept 101 * IFF the controller key is not used by anything from {@param filter}. 102 */ 103 @NonNull filterControllers( @onNull List<BasePreferenceController> input, List<AbstractPreferenceController> filter)104 public static List<BasePreferenceController> filterControllers( 105 @NonNull List<BasePreferenceController> input, 106 List<AbstractPreferenceController> filter) { 107 if (input == null || filter == null) { 108 return input; 109 } 110 final Set<String> keys = new TreeSet<>(); 111 final List<BasePreferenceController> filteredList = new ArrayList<>(); 112 for (AbstractPreferenceController controller : filter) { 113 final String key = controller.getPreferenceKey(); 114 if (key != null) { 115 keys.add(key); 116 } 117 } 118 for (BasePreferenceController controller : input) { 119 if (keys.contains(controller.getPreferenceKey())) { 120 Log.w(TAG, controller.getPreferenceKey() + " already has a controller"); 121 continue; 122 } 123 filteredList.add(controller); 124 } 125 return filteredList; 126 } 127 128 } 129