| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the |
| * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| */ |
| |
| package android.testing; |
| |
| import android.annotation.NonNull; |
| import android.content.Context; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Builder class to create a {@link LayoutInflater} with various properties. |
| * |
| * Call any desired configuration methods on the Builder and then use |
| * {@link Builder#build} to create the LayoutInflater. This is an alternative to directly using |
| * {@link LayoutInflater#setFilter} and {@link LayoutInflater#setFactory}. |
| * @hide for use by framework |
| */ |
| public class LayoutInflaterBuilder { |
| private static final String TAG = "LayoutInflaterBuilder"; |
| |
| private Context mFromContext; |
| private Context mTargetContext; |
| private Map<String, String> mReplaceMap; |
| private Set<Class> mDisallowedClasses; |
| private LayoutInflater mBuiltInflater; |
| |
| /** |
| * Creates a new Builder which will construct a LayoutInflater. |
| * |
| * @param fromContext This context's LayoutInflater will be cloned by the Builder using |
| * {@link LayoutInflater#cloneInContext}. By default, the new LayoutInflater will point at |
| * this same Context. |
| */ |
| public LayoutInflaterBuilder(@NonNull Context fromContext) { |
| mFromContext = fromContext; |
| mTargetContext = fromContext; |
| mReplaceMap = null; |
| mDisallowedClasses = null; |
| mBuiltInflater = null; |
| } |
| |
| /** |
| * Instructs the Builder to point the LayoutInflater at a different Context. |
| * |
| * @param targetContext Context to be provided to |
| * {@link LayoutInflater#cloneInContext(Context)}. |
| * @return Builder object post-modification. |
| */ |
| public LayoutInflaterBuilder target(@NonNull Context targetContext) { |
| assertIfAlreadyBuilt(); |
| mTargetContext = targetContext; |
| return this; |
| } |
| |
| /** |
| * Instructs the Builder to configure the LayoutInflater such that all instances |
| * of one {@link View} will be replaced with instances of another during inflation. |
| * |
| * @param from Instances of this class will be replaced during inflation. |
| * @param to Instances of this class will be inflated as replacements. |
| * @return Builder object post-modification. |
| */ |
| public LayoutInflaterBuilder replace(@NonNull Class from, @NonNull Class to) { |
| return replace(from.getName(), to); |
| } |
| |
| /** |
| * Instructs the Builder to configure the LayoutInflater such that all instances |
| * of one {@link View} will be replaced with instances of another during inflation. |
| * |
| * @param tag Instances of this tag will be replaced during inflation. |
| * @param to Instances of this class will be inflated as replacements. |
| * @return Builder object post-modification. |
| */ |
| public LayoutInflaterBuilder replace(@NonNull String tag, @NonNull Class to) { |
| assertIfAlreadyBuilt(); |
| if (mReplaceMap == null) { |
| mReplaceMap = new ArrayMap<String, String>(); |
| } |
| mReplaceMap.put(tag, to.getName()); |
| return this; |
| } |
| |
| /** |
| * Instructs the Builder to configure the LayoutInflater such that any attempt to inflate |
| * a {@link View} of a given type will throw a {@link InflateException}. |
| * |
| * @param disallowedClass The Class type that will be disallowed. |
| * @return Builder object post-modification. |
| */ |
| public LayoutInflaterBuilder disallow(@NonNull Class disallowedClass) { |
| assertIfAlreadyBuilt(); |
| if (mDisallowedClasses == null) { |
| mDisallowedClasses = new ArraySet<Class>(); |
| } |
| mDisallowedClasses.add(disallowedClass); |
| return this; |
| } |
| |
| /** |
| * Builds and returns the LayoutInflater. Afterwards, this Builder can no longer can be |
| * used, all future calls on the Builder will throw {@link AssertionError}. |
| */ |
| public LayoutInflater build() { |
| assertIfAlreadyBuilt(); |
| mBuiltInflater = |
| LayoutInflater.from(mFromContext).cloneInContext(mTargetContext); |
| setFactoryIfNeeded(mBuiltInflater); |
| setFilterIfNeeded(mBuiltInflater); |
| return mBuiltInflater; |
| } |
| |
| private void assertIfAlreadyBuilt() { |
| if (mBuiltInflater != null) { |
| throw new AssertionError("Cannot use this Builder after build() has been called."); |
| } |
| } |
| |
| private void setFactoryIfNeeded(LayoutInflater inflater) { |
| if (mReplaceMap == null) { |
| return; |
| } |
| inflater.setFactory( |
| new LayoutInflater.Factory() { |
| @Override |
| public View onCreateView(String name, Context context, AttributeSet attrs) { |
| String replacingClassName = mReplaceMap.get(name); |
| if (replacingClassName != null) { |
| try { |
| return inflater.createView(replacingClassName, null, attrs); |
| } catch (ClassNotFoundException e) { |
| Log.e(TAG, "Could not replace " + name |
| + " with " + replacingClassName |
| + ", Exception: ", e); |
| } |
| } |
| return null; |
| } |
| }); |
| } |
| |
| private void setFilterIfNeeded(LayoutInflater inflater) { |
| if (mDisallowedClasses == null) { |
| return; |
| } |
| inflater.setFilter( |
| new LayoutInflater.Filter() { |
| @Override |
| public boolean onLoadClass(Class clazz) { |
| return !mDisallowedClasses.contains(clazz); |
| } |
| }); |
| } |
| } |