/* * Copyright (C) 2015 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.google.android.setupcompat.internal; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.os.Build.VERSION_CODES; import androidx.annotation.Keep; import androidx.annotation.LayoutRes; import androidx.annotation.StyleRes; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import com.google.android.setupcompat.R; import com.google.android.setupcompat.template.Mixin; import java.util.HashMap; import java.util.Map; /** * A generic template class that inflates a template, provided in the constructor or in {@code * android:layout} through XML, and adds its children to a "container" in the template. When * inflating this layout from XML, the {@code android:layout} and {@code suwContainer} attributes * are required. * *
This class is designed to use inside the library; it is not suitable for external use.
*/
public class TemplateLayout extends FrameLayout {
/**
* The container of the actual content. This will be a view in the template, which child views
* will be added to when {@link #addView(View)} is called.
*/
private ViewGroup container;
private final Map In general, clients should still set the activity theme to the corresponding theme in setup
* wizard lib, so that the content area gets the correct styles as well.
*
* @param inflater A LayoutInflater to inflate the template.
* @param fallbackTheme A fallback theme to apply to the template. If the values defined in the
* fallback theme is already defined in the original theme, the value in the original theme
* takes precedence.
* @param template The layout template to be inflated.
* @return Root of the inflated layout.
* @see FallbackThemeWrapper
*/
protected final View inflateTemplate(
LayoutInflater inflater, @StyleRes int fallbackTheme, @LayoutRes int template) {
if (template == 0) {
throw new IllegalArgumentException("android:layout not specified for TemplateLayout");
}
if (fallbackTheme != 0) {
inflater =
LayoutInflater.from(new FallbackThemeWrapper(inflater.getContext(), fallbackTheme));
}
return inflater.inflate(template, this, false);
}
/**
* This method inflates the template. Subclasses can override this method to customize the
* template inflation, or change to a different default template. The root of the inflated layout
* should be returned, and not added to the view hierarchy.
*
* @param inflater A LayoutInflater to inflate the template.
* @param template The resource ID of the template to be inflated, or 0 if no template is
* specified.
* @return Root of the inflated layout.
*/
protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) {
return inflateTemplate(inflater, 0, template);
}
protected ViewGroup findContainer(int containerId) {
return (ViewGroup) findViewById(containerId);
}
/**
* This is called after the template has been inflated and added to the view hierarchy. Subclasses
* can implement this method to modify the template as necessary, such as caching views retrieved
* from findViewById, or other view operations that need to be done in code. You can think of this
* as {@link View#onFinishInflate()} but for inflation of the template instead of for child views.
*/
protected void onTemplateInflated() {}
/**
* This is called before the template has been inflated and added to the view hierarchy.
* Subclasses can implement this method to modify the template as necessary, such as something
* need to be done before onTemplateInflated which is called while still in the constructor.
*/
protected void onBeforeTemplateInflated(AttributeSet attrs, int defStyleAttr) {}
/* Animator support */
private float xFraction;
private ViewTreeObserver.OnPreDrawListener preDrawListener;
/**
* Set the X translation as a fraction of the width of this view. Make sure this method is not
* stripped out by proguard when using this with {@link android.animation.ObjectAnimator}. You may
* need to add
* -keep @androidx.annotation.Keep class *
*
to your proguard configuration if you are seeing mysterious {@link NoSuchMethodError}
* at runtime.
*/
@Keep
@TargetApi(VERSION_CODES.HONEYCOMB)
public void setXFraction(float fraction) {
xFraction = fraction;
final int width = getWidth();
if (width != 0) {
setTranslationX(width * fraction);
} else {
// If we haven't done a layout pass yet, wait for one and then set the fraction before
// the draw occurs using an OnPreDrawListener. Don't call translationX until we know
// getWidth() has a reliable, non-zero value or else we will see the fragment flicker on
// screen.
if (preDrawListener == null) {
preDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
setXFraction(xFraction);
return true;
}
};
getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
}
}
/**
* Return the X translation as a fraction of the width, as previously set in {@link
* #setXFraction(float)}.
*
* @see #setXFraction(float)
*/
@Keep
@TargetApi(VERSION_CODES.HONEYCOMB)
public float getXFraction() {
return xFraction;
}
}