1bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam/* 2bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * Copyright (C) 2015 The Android Open Source Project 3bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * 4bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * Licensed under the Apache License, Version 2.0 (the "License"); 5bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * you may not use this file except in compliance with the License. 6bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * You may obtain a copy of the License at 7bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * 8bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * http://www.apache.org/licenses/LICENSE-2.0 9bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * 10bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * Unless required by applicable law or agreed to in writing, software 11bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * distributed under the License is distributed on an "AS IS" BASIS, 12bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * See the License for the specific language governing permissions and 14bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * limitations under the License. 15bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 16bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 17bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lampackage com.android.setupwizardlib; 18bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 19bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.annotation.TargetApi; 20bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.content.Context; 21bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.content.res.TypedArray; 22bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.os.Build.VERSION_CODES; 23bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.util.AttributeSet; 24bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.view.LayoutInflater; 25bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.view.View; 26bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.view.ViewGroup; 27bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.view.ViewTreeObserver; 28bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport android.widget.FrameLayout; 29bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 30bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lamimport com.android.setupwizardlib.annotations.Keep; 31bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 32bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam/** 33bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * A generic template class that inflates a template, provided in the constructor or in 34b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * {@code android:layout} through XML, and adds its children to a "container" in the template. When 35bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * inflating this layout from XML, the {@code android:layout} and {@code suwContainer} attributes 36bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * are required. 37bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 38bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lampublic class TemplateLayout extends FrameLayout { 39bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 40bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam /** 41bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * The container of the actual content. This will be a view in the template, which child views 42bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * will be added to when {@link #addView(View)} is called. 43bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 44bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam private ViewGroup mContainer; 45bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 46bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam public TemplateLayout(Context context, int template, int containerId) { 47bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam super(context); 48bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam init(template, containerId, null, R.attr.suwLayoutTheme); 49bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 50bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 51bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam public TemplateLayout(Context context, AttributeSet attrs) { 52bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam super(context, attrs); 53bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam init(0, 0, attrs, R.attr.suwLayoutTheme); 54bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 55bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 56bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @TargetApi(VERSION_CODES.HONEYCOMB) 57bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam public TemplateLayout(Context context, AttributeSet attrs, int defStyleAttr) { 58bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam super(context, attrs, defStyleAttr); 59bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam init(0, 0, attrs, defStyleAttr); 60bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 61bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 62bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam // All the constructors delegate to this init method. The 3-argument constructor is not 63bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam // available in LinearLayout before v11, so call super with the exact same arguments. 64bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam private void init(int template, int containerId, AttributeSet attrs, int defStyleAttr) { 65bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam final TypedArray a = getContext().obtainStyledAttributes(attrs, 66bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam R.styleable.SuwTemplateLayout, defStyleAttr, 0); 67bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam if (template == 0) { 68bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam template = a.getResourceId(R.styleable.SuwTemplateLayout_android_layout, 0); 69bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 70bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam if (containerId == 0) { 71bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam containerId = a.getResourceId(R.styleable.SuwTemplateLayout_suwContainer, 0); 72bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 73bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam inflateTemplate(template, containerId); 74bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 75bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam a.recycle(); 76bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 77bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 78bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @Override 79bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam public void addView(View child, int index, ViewGroup.LayoutParams params) { 80bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam mContainer.addView(child, index, params); 81bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 82bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 83bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam private void addViewInternal(View child) { 84bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam super.addView(child, -1, generateDefaultLayoutParams()); 85bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 86bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 87bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam private void inflateTemplate(int templateResource, int containerId) { 88bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam final LayoutInflater inflater = LayoutInflater.from(getContext()); 89bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam final View templateRoot = onInflateTemplate(inflater, templateResource); 90bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam addViewInternal(templateRoot); 91bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 92bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam mContainer = findContainer(containerId); 93bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam if (mContainer == null) { 94bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam throw new IllegalArgumentException("Container cannot be null in TemplateLayout"); 95bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 96bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam onTemplateInflated(); 97bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 98bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 99bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam /** 100bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * This method inflates the template. Subclasses can override this method to customize the 101bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * template inflation, or change to a different default template. The root of the inflated 102bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * layout should be returned, and not added to the view hierarchy. 103bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * 104bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * @param inflater A LayoutInflater to inflate the template. 105bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * @param template The resource ID of the template to be inflated, or 0 if no template is 106bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * specified. 107bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * @return Root of the inflated layout. 108bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 109bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam protected View onInflateTemplate(LayoutInflater inflater, int template) { 110bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam if (template == 0) { 111bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam throw new IllegalArgumentException("android:layout not specified for TemplateLayout"); 112bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 113bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam return inflater.inflate(template, this, false); 114bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 115bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 116bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam protected ViewGroup findContainer(int containerId) { 117bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam if (containerId == 0) { 118bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam // Maintain compatibility with the deprecated way of specifying container ID. 119bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam containerId = getContainerId(); 120bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 121bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam return (ViewGroup) findViewById(containerId); 122bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 123bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 124bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam /** 125bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * This is called after the template has been inflated and added to the view hierarchy. 126bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * Subclasses can implement this method to modify the template as necessary, such as caching 127bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * views retrieved from findViewById, or other view operations that need to be done in code. 128bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * You can think of this as {@link View#onFinishInflate()} but for inflation of the 129bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * template instead of for child views. 130bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 131bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam protected void onTemplateInflated() { 132bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 133bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 134bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam /** 135bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * @return ID of the default container for this layout. This will be used to find the container 136bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * ViewGroup, which all children views of this layout will be placed in. 137b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * @deprecated Override {@link #findContainer(int)} instead. 138bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 139bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @Deprecated 140bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam protected int getContainerId() { 141bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam return 0; 142bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 143bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 144bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam /* Animator support */ 145bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 146bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam private float mXFraction; 147bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam private ViewTreeObserver.OnPreDrawListener mPreDrawListener; 148bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 149bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam /** 150bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * Set the X translation as a fraction of the width of this view. Make sure this method is not 151b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * stripped out by proguard when using this with {@link android.animation.ObjectAnimator}. You 152b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * may need to add 153b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * <code> 154bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * -keep @com.android.setupwizardlib.annotations.Keep class * 155b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * </code> 156b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * to your proguard configuration if you are seeing mysterious {@link NoSuchMethodError} at 157bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * runtime. 158bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 159bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @Keep 160bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @TargetApi(VERSION_CODES.HONEYCOMB) 161bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam public void setXFraction(float fraction) { 162bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam mXFraction = fraction; 163bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam final int width = getWidth(); 164bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam if (width != 0) { 165bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam setTranslationX(width * fraction); 166bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } else { 167bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam // If we haven't done a layout pass yet, wait for one and then set the fraction before 168bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam // the draw occurs using an OnPreDrawListener. Don't call translationX until we know 169bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam // getWidth() has a reliable, non-zero value or else we will see the fragment flicker on 170bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam // screen. 171bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam if (mPreDrawListener == null) { 172bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { 173bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @Override 174bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam public boolean onPreDraw() { 175bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener); 176bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam setXFraction(mXFraction); 177bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam return true; 178bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 179bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam }; 180bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam getViewTreeObserver().addOnPreDrawListener(mPreDrawListener); 181bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 182bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 183bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 184bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam 185bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam /** 186b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * Return the X translation as a fraction of the width, as previously set in 187b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * {@link #setXFraction(float)}. 188bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * 189bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam * @see #setXFraction(float) 190bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam */ 191bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @Keep 192bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam @TargetApi(VERSION_CODES.HONEYCOMB) 193bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam public float getXFraction() { 194bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam return mXFraction; 195bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam } 196bdfc0132ff90a333de202adfbf204cdc8139e632Maurice Lam} 197