[SetupWizardLib] Themes and attrs support

Support for specifying themes and attributes for SetupWizardLayout.
Also consolidated SetupWizardLayout and SetupWizardLayoutHelper,
backtracking from the delegate model to a simpler (but less flexible)
implementation.

SetupWizardLayoutHelper will be removed once references to it in
SetupWizard are removed.

Bug: 18685749
Change-Id: I2d5a261ce4965fb41a24f1ea4775b52639b1875b
diff --git a/library/res/values/attrs.xml b/library/res/values/attrs.xml
index c780d37..c51c956 100644
--- a/library/res/values/attrs.xml
+++ b/library/res/values/attrs.xml
@@ -17,6 +17,8 @@
 
 <resources>
 
+    <attr name="suwLayoutTheme" format="reference" />
+
     <attr name="suwNavBarBackgroundColor" format="color" />
     <attr name="suwNavBarTextColor" format="color" />
     <attr name="suwNavBarTheme" format="reference" />
@@ -31,6 +33,12 @@
 
     <declare-styleable name="SuwSetupWizardLayout">
         <attr name="android:layout" />
+        <attr name="suwBackground" format="color|reference" />
+        <attr name="suwBackgroundTile" format="color|reference" />
+        <attr name="suwHeaderText" format="string" localization="suggested" />
+        <attr name="suwIllustration" format="color|reference" />
+        <attr name="suwIllustrationHorizontalTile" format="color|reference" />
+        <attr name="suwIllustrationImage" format="color|reference" />
     </declare-styleable>
 
 </resources>
diff --git a/library/src/com/android/setupwizardlib/SetupWizardLayout.java b/library/src/com/android/setupwizardlib/SetupWizardLayout.java
index e329855..ce1e9c4 100644
--- a/library/src/com/android/setupwizardlib/SetupWizardLayout.java
+++ b/library/src/com/android/setupwizardlib/SetupWizardLayout.java
@@ -16,14 +16,28 @@
 
 package com.android.setupwizardlib;
 
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
 import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.TextView;
 
+import com.android.setupwizardlib.view.Illustration;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class SetupWizardLayout extends FrameLayout {
 
     private static final String TAG = "SetupWizardLayout";
@@ -52,11 +66,52 @@
     public SetupWizardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         if (attrs != null) {
-            final TypedArray a = context.obtainStyledAttributes(attrs,
+            final TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(R.attr.suwLayoutTheme, outValue, true);
+            final Theme layoutTheme = getResources().newTheme();
+            layoutTheme.applyStyle(outValue.resourceId, true);
+
+            final TypedArray a = layoutTheme.obtainStyledAttributes(attrs,
                     R.styleable.SuwSetupWizardLayout, defStyleAttr, 0);
             final int template = a.getResourceId(
                     R.styleable.SuwSetupWizardLayout_android_layout, 0);
             inflateTemplate(template);
+
+            // Set the background from XML, either directly or built from a bitmap tile
+            final Drawable background =
+                    a.getDrawable(R.styleable.SuwSetupWizardLayout_suwBackground);
+            if (background != null) {
+                setBackground(background);
+            } else {
+                final Drawable backgroundTile =
+                        a.getDrawable(R.styleable.SuwSetupWizardLayout_suwBackgroundTile);
+                if (backgroundTile != null) {
+                    setBackgroundTile(backgroundTile);
+                }
+            }
+
+            // Set the illustration from XML, either directly or built from image + horizontal tile
+            final Drawable illustration =
+                    a.getDrawable(R.styleable.SuwSetupWizardLayout_suwIllustration);
+            if (illustration != null) {
+                setIllustration(illustration);
+            } else {
+                final Drawable illustrationImage =
+                        a.getDrawable(R.styleable.SuwSetupWizardLayout_suwIllustrationImage);
+                final Drawable horizontalTile = a.getDrawable(
+                        R.styleable.SuwSetupWizardLayout_suwIllustrationHorizontalTile);
+                if (illustrationImage != null && horizontalTile != null) {
+                    setIllustration(illustrationImage, horizontalTile);
+                }
+            }
+
+            // Set the header text
+            final CharSequence headerText =
+                    a.getText(R.styleable.SuwSetupWizardLayout_suwHeaderText);
+            if (headerText != null) {
+                setHeaderText(headerText);
+            }
+
             a.recycle();
         } else {
             inflateTemplate(0);
@@ -111,4 +166,119 @@
     protected int getContainerId() {
         return R.id.suw_layout_content;
     }
+
+    public void setHeaderText(int title) {
+        final TextView titleView = (TextView) findViewById(R.id.suw_layout_title);
+        if (titleView != null) {
+            titleView.setText(title);
+        }
+    }
+
+    public void setHeaderText(CharSequence title) {
+        final TextView titleView = (TextView) findViewById(R.id.suw_layout_title);
+        if (titleView != null) {
+            titleView.setText(title);
+        }
+    }
+
+    /**
+     * Set the illustration of the layout. The drawable will be applied as is, and the bounds will
+     * be set as implemented in {@link com.android.setupwizardlib.view.Illustration}. To create
+     * a suitable drawable from an asset and a horizontal repeating tile, use
+     * {@link #setIllustration(int, int)} instead.
+     *
+     * @param drawable The drawable specifying the illustration.
+     */
+    public void setIllustration(Drawable drawable) {
+        final View view = findViewById(R.id.suw_layout_decor);
+        if (view instanceof Illustration) {
+            final Illustration illustration = (Illustration) view;
+            illustration.setIllustration(drawable);
+        }
+    }
+
+    /**
+     * Set the illustration of the layout, which will be created asset and the horizontal tile as
+     * suitable. On phone layouts (not sw600dp), the asset will be scaled, maintaining aspect ratio.
+     * On tablets (sw600dp), the assets will always have 256dp height and the rest of the
+     * illustration area that the asset doesn't fill will be covered by the horizontalTile.
+     *
+     * @param asset Resource ID of the illustration asset.
+     * @param horizontalTile Resource ID of the horizontally repeating tile for tablet layout.
+     */
+    public void setIllustration(int asset, int horizontalTile) {
+        final View view = findViewById(R.id.suw_layout_decor);
+        if (view instanceof Illustration) {
+            final Illustration illustration = (Illustration) view;
+            final Drawable illustrationDrawable = getIllustration(asset, horizontalTile);
+            illustration.setIllustration(illustrationDrawable);
+        }
+    }
+
+    private void setIllustration(Drawable asset, Drawable horizontalTile) {
+        final View view = findViewById(R.id.suw_layout_decor);
+        if (view instanceof Illustration) {
+            final Illustration illustration = (Illustration) view;
+            final Drawable illustrationDrawable = getIllustration(asset, horizontalTile);
+            illustration.setIllustration(illustrationDrawable);
+        }
+    }
+
+    /**
+     * Set the background of the layout, which is expected to be able to extend infinitely. If it is
+     * a bitmap tile and you want it to repeat, use {@link #setBackgroundTile(int)} instead.
+     */
+    public void setBackground(Drawable background) {
+        final View view = findViewById(R.id.suw_layout_decor);
+        if (view != null) {
+            view.setBackground(background);
+        }
+    }
+
+    /**
+     * Set the background of the layout to a repeating bitmap tile. To use a different kind of
+     * drawable, use {@link #setBackground(android.graphics.drawable.Drawable)} instead.
+     */
+    public void setBackgroundTile(int backgroundTile) {
+        final Drawable backgroundTileDrawable = getContext().getDrawable(backgroundTile);
+        setBackgroundTile(backgroundTileDrawable);
+    }
+
+    private void setBackgroundTile(Drawable backgroundTile) {
+        if (backgroundTile instanceof BitmapDrawable) {
+            ((BitmapDrawable) backgroundTile).setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
+        }
+        setBackground(backgroundTile);
+    }
+
+    private Drawable getIllustration(int asset, int horizontalTile) {
+        final Context context = getContext();
+        final Drawable assetDrawable = context.getResources().getDrawable(asset);
+        final Drawable tile = context.getResources().getDrawable(horizontalTile);
+        return getIllustration(assetDrawable, tile);
+    }
+
+    @SuppressLint("RtlHardcoded")
+    private Drawable getIllustration(Drawable asset, Drawable horizontalTile) {
+        final Context context = getContext();
+        if (context.getResources().getBoolean(R.bool.suwUseTabletLayout)) {
+            // If it is a "tablet" (sw600dp), create a LayerDrawable with the horizontal tile.
+            if (horizontalTile instanceof BitmapDrawable) {
+                ((BitmapDrawable) horizontalTile).setTileModeX(TileMode.REPEAT);
+                ((BitmapDrawable) horizontalTile).setGravity(Gravity.TOP);
+            }
+            if (asset instanceof BitmapDrawable) {
+                // Always specify TOP | LEFT, Illustration will flip the entire LayerDrawable.
+                ((BitmapDrawable) asset).setGravity(Gravity.TOP | Gravity.LEFT);
+            }
+            final LayerDrawable layers =
+                    new LayerDrawable(new Drawable[] { horizontalTile, asset });
+            layers.setAutoMirrored(true);
+            return layers;
+        } else {
+            // If it is a "phone" (not sw600dp), simply return the illustration
+            asset.setAutoMirrored(true);
+            return asset;
+        }
+    }
 }