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_KEY; 21 22 import android.annotation.NonNull; 23 import android.annotation.XmlRes; 24 import android.content.Context; 25 import android.os.Bundle; 26 import android.text.TextUtils; 27 import android.util.Log; 28 29 import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; 30 import com.android.settingslib.core.AbstractPreferenceController; 31 32 import org.xmlpull.v1.XmlPullParserException; 33 34 import java.io.IOException; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Set; 38 import java.util.TreeSet; 39 40 /** 41 * Helper to load {@link BasePreferenceController} lists from Xml. 42 */ 43 public class PreferenceControllerListHelper { 44 45 private static final String TAG = "PrefCtrlListHelper"; 46 47 /** 48 * Instantiates a list of controller based on xml definition. 49 */ 50 @NonNull 51 public static List<BasePreferenceController> getPreferenceControllersFromXml(Context context, 52 @XmlRes int xmlResId) { 53 final List<BasePreferenceController> controllers = new ArrayList<>(); 54 List<Bundle> preferenceMetadata; 55 try { 56 preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, 57 MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER); 58 } catch (IOException | XmlPullParserException e) { 59 Log.e(TAG, "Failed to parse preference xml for getting controllers", e); 60 return controllers; 61 } 62 63 for (Bundle metadata : preferenceMetadata) { 64 final String controllerName = metadata.getString(METADATA_CONTROLLER); 65 if (TextUtils.isEmpty(controllerName)) { 66 continue; 67 } 68 BasePreferenceController controller; 69 try { 70 controller = BasePreferenceController.createInstance(context, controllerName); 71 } catch (IllegalStateException e) { 72 Log.d(TAG, "Could not find Context-only controller for pref: " + controllerName); 73 final String key = metadata.getString(METADATA_KEY); 74 if (TextUtils.isEmpty(key)) { 75 Log.w(TAG, "Controller requires key but it's not defined in xml: " 76 + controllerName); 77 continue; 78 } 79 try { 80 controller = BasePreferenceController.createInstance(context, controllerName, 81 key); 82 } catch (IllegalStateException e2) { 83 Log.w(TAG, "Cannot instantiate controller from reflection: " + controllerName); 84 continue; 85 } 86 } 87 controllers.add(controller); 88 } 89 return controllers; 90 } 91 92 /** 93 * Return a sub list of {@link AbstractPreferenceController} to only contain controller that 94 * doesn't exist in filter. 95 * 96 * @param filter The filter. This list will be unchanged. 97 * @param input This list will be filtered into a sublist and element is kept 98 * IFF the controller key is not used by anything from {@param filter}. 99 */ 100 @NonNull 101 public static List<BasePreferenceController> filterControllers( 102 @NonNull List<BasePreferenceController> input, 103 List<AbstractPreferenceController> filter) { 104 if (input == null || filter == null) { 105 return input; 106 } 107 final Set<String> keys = new TreeSet<>(); 108 final List<BasePreferenceController> filteredList = new ArrayList<>(); 109 for (AbstractPreferenceController controller : filter) { 110 final String key = controller.getPreferenceKey(); 111 if (key != null) { 112 keys.add(key); 113 } 114 } 115 for (BasePreferenceController controller : input) { 116 if (keys.contains(controller.getPreferenceKey())) { 117 Log.w(TAG, controller.getPreferenceKey() + " already has a controller"); 118 continue; 119 } 120 filteredList.add(controller); 121 } 122 return filteredList; 123 } 124 125 } 126