blob: f42092130af1e7010ba61a5e0456d84c84bb9d0a [file] [log] [blame]
/*
* Copyright (C) 2016 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 com.android.systemui.util;
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) {
assertIfAlreadyBuilt();
if (mReplaceMap == null) {
mReplaceMap = new ArrayMap<String, String>();
}
mReplaceMap.put(from.getName(), 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);
}
});
}
}