Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ConstraintLayout 2.0 sneak peek

ConstraintLayout 2.0 sneak peek

A presentation at ChicagoRoboto 2018 of what's coming up soon with ConstraintLayout 2.0

Nicolas Roard

April 12, 2018
Tweet

More Decks by Nicolas Roard

Other Decks in Programming

Transcript

  1. CONSTRAINT LAYOUT 2.0
    Nicolas Roard & John Hoford
    @camaelon @johnhoford
    (Sneak peek)

    View full-size slide

  2. BUT FIRST…

    View full-size slide

  3. ConstraintLayout 2.0 preview
    GOOGLE MAVEN REPOSITORY
    dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.1.0'
    }

    View full-size slide

  4. ConstraintLayout 2.0 preview
    CONSTRAINT LAYOUT 1.1
    ➤ Constraints
    ➤ Helper objects
    ➤ Optimizer
    ➤ ton of bug fixes!

    View full-size slide

  5. ConstraintLayout 2.0 preview
    CONSTRAINTS
    ➤ Constrained Dimensions : percent, min/max, wrap
    ➤ Circular constraints
    ➤ Complex chains

    View full-size slide

  6. ConstraintLayout 2.0 preview
    CIRCULAR CONSTRAINTS

    app:layout_constraintCircle=“@+id/buttonA"
    app:layout_constraintCircleRadius=“100dp"
    app:layout_constraintCircleAngle="45" />

    View full-size slide

  7. ConstraintLayout 2.0 preview
    EXAMPLE
    Andrew Kelly
    https://medium.com/devnibbles/
    constraintlayout-circular-
    positioning-9489b11cb0e5

    View full-size slide

  8. ConstraintLayout 2.0 preview
    EXAMPLE
    Andrew Kelly
    https://medium.com/devnibbles/
    constraintlayout-circular-
    positioning-9489b11cb0e5

    View full-size slide

  9. ConstraintLayout 2.0 preview
    HELPER OBJECTS
    ➤ Barriers
    ➤ Groups
    ➤ Placeholders

    View full-size slide

  10. ConstraintLayout 2.0 preview
    BARRIERS
    ➤ A barrier takes the minimum or maximum of a set of widget
    ➤ Other widgets can then be constrained to it
    ➤ Allows to build more flexible layouts
    Helper object

    View full-size slide

  11. Barriers
    1
    2
    3 4

    View full-size slide

  12. ConstraintLayout 2.0 preview
    GROUPS
    ➤ Define a set of widgets
    ➤ Apply visibility to them
    Helper object

    View full-size slide

  13. Groups
    textview2
    textview3

    View full-size slide

  14. Groups
    Invisible Gone

    View full-size slide

  15. class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    }
    fun select(v: View) {
    TransitionManager.beginDelayedTransition(main_layout)
    placeholder.setContentId(v.id)
    main_title.text= v.tag as CharSequence?;"";
    }
    }
    Placeholder

    View full-size slide

  16. class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    }
    fun select(v: View) {
    TransitionManager.beginDelayedTransition(main_layout)
    placeholder.setContentId(v.id)
    main_title.text= v.tag as CharSequence?;"";
    }
    }
    Placeholder

    View full-size slide

  17. ConstraintLayout 2.0 preview
    OPTIMIZER
    ➤ New optimizer in 1.1
    ➤ A lot more powerful :)
    ➤ Multiple optimizations passes available
    ➤ Direct & Center connections
    ➤ Barriers
    ➤ Chains (experimental)
    ➤ Dimensions (experimental)

    View full-size slide

  18. ConstraintLayout 2.0 preview
    OPTIMIZER ATTRIBUTE
    app:layout_optimizationLevel=“standard”
    app:layout_optimizationLevel=“direct|barriers|chains|dimensions”

    View full-size slide

  19. ConstraintLayout 2.0 preview
    OPTIMIZER
    CONSTRAINTS
    OPTIMIZER
    SOLVER
    MEASURES

    View full-size slide

  20. ConstraintLayout 2.0 preview
    OPTIMIZER
    Widget A

    View full-size slide

  21. ConstraintLayout 2.0 preview
    OPTIMIZER
    Widget A

    View full-size slide

  22. ConstraintLayout 2.0 preview
    OPTIMIZER
    Widget A

    View full-size slide

  23. ConstraintLayout 2.0 preview
    OPTIMIZER
    Widget A

    View full-size slide

  24. ConstraintLayout 2.0 preview
    OPTIMIZER
    Widget A
    Widget B

    View full-size slide

  25. ConstraintLayout 2.0 preview
    OPTIMIZER
    Widget A
    Widget B

    View full-size slide

  26. ConstraintLayout 2.0 preview
    OPTIMIZER
    Widget A
    Widget B

    View full-size slide

  27. ConstraintLayout 2.0 preview
    OPTIMIZER

    View full-size slide

  28. ConstraintLayout 2.0 preview
    OPTIMIZER

    View full-size slide

  29. CONSTRAINT LAYOUT 2.0Preview

    View full-size slide

  30. CL
    2.0
    UI : RAISING THE BAR

    View full-size slide

  31. CL
    2.0
    VOCABULARY

    View full-size slide

  32. ConstraintLayout 2.0 preview
    TOOLING
    ➤ Integrated manipulation in the component tree (like for barriers)
    ➤ Exposed in the Palette
    ➤ Support 3rd party libraries
    ➤ exposing their custom views / helpers

    View full-size slide

  33. CL
    2.0
    HELPERS

    View full-size slide

  34. ConstraintLayout 2.0 preview
    THREE MAIN CATEGORIES OF HELPERS
    Layout Manipulation
    Rendering or Decorating
    Post-Layout Manipulation

    View full-size slide

  35. ConstraintLayout 2.0 preview
    CONSTRAINT HELPERS
    ➤ Keep a reference to existing views
    ➤ Flat hierarchy
    ➤ Views can be referenced by multiple helpers
    ➤ Essentially, it allows you to tag views, setting specific behaviors

    View full-size slide

  36. ConstraintLayout 2.0 preview
    API
    ➤ Access a list of views
    ➤ pre/post layouts callbacks
    ➤ normal views
    ➤ already used
    ➤ barriers
    ➤ groups

    View full-size slide

  37. ConstraintLayout 2.0 preview
    COLLECTION
    ➤ Trampoline object
    ➤ forward function calls to referenced views
    ➤ setVisibility
    ➤ setAlpha
    ➤ setRotation, etc.
    ➤ no getter!

    View full-size slide

  38. CL
    2.0
    DECORATORS

    View full-size slide

  39. ConstraintLayout 2.0 preview
    DECORATORS
    ➤ Can reference views
    ➤ Easily build decorators that depends on views location / visibility / etc

    View full-size slide

  40. ConstraintLayout 2.0 preview
    METABALLS
    public class MetaballsDecorator extends ConstraintHelper {
    public void updatePostLayout(ConstraintLayout container) {
    int[] ids = getReferencedIds();
    final int count = ids.length;
    for (int i = 0; i < count; i++) {
    View view = container.getViewById(ids[i]);
    // do something
    }
    }
    @Override
    public void onDraw(Canvas canvas) {
    // do something
    }
    }

    View full-size slide

  41. Decorator
    Canvas
    ImageViews

    View full-size slide

  42. Decorator
    Canvas
    ImageViews

    View full-size slide

  43. Decorator
    Canvas
    ImageViews

    View full-size slide

  44. Metaballs
    Decorator

    View full-size slide

  45. Metaballs
    Decorator

    View full-size slide

  46. Metaballs
    Decorator

    View full-size slide

  47. Metaballs
    Decorator

    View full-size slide

  48. ConstraintLayout 2.0 preview
    LAYER DECORATOR
    ➤ Group ++
    ➤ Manipulate graphically a collection of views
    ➤ Supports transforms, etc.
    ➤ Can be set as the bounding box of the referenced views

    View full-size slide

  49. ConstraintLayout 2.0 preview
    LET’S CREATE A DRAWABLE…
    android:shape="rectangle">



    View full-size slide

  50. ConstraintLayout 2.0 preview
    LAYER DECORATOR
    android:id=“@+id/layer”
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/frame"
    android:padding="32dp"
    app:constraint_referenced_ids=“button1,button2,button3,
    button4,button5”
    />

    View full-size slide

  51. ConstraintLayout 2.0 preview
    LAYER DECORATOR
    android:id=“@+id/layer”
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/frame"
    android:padding="32dp"
    app:constraint_referenced_ids=“button1,button2,button3,
    button4,button5”
    />

    View full-size slide

  52. ConstraintLayout 2.0 preview
    LAYER DECORATOR

    View full-size slide

  53. ConstraintLayout 2.0 preview
    LAYER DECORATOR

    View full-size slide

  54. ConstraintLayout 2.0 preview
    LAYERS : PROGRAMMATIC ACCESS
    Layer layer = findViewById(R.id.layer);
    layer.setRotation(angle);
    layer.setScaleX(1+(180 - Math.abs(angle-180))/20f);
    layer.setScaleY(1+(180 - Math.abs(angle-180))/20f);
    float shift_x = 500 * (float) Math.sin(Math.toRadians(angle*5));
    float shift_y = 500 * (float) Math.sin(Math.toRadians(angle*7));
    layer.setTranslationX(shift_x);
    layer.setTranslationY(shift_y);

    View full-size slide

  55. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  56. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  57. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  58. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  59. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  60. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  61. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  62. ConstraintLayout 2.0 preview
    FLY-IN DECORATOR

    View full-size slide

  63. ConstraintLayout 2.0 preview
    FLY IN DECORATOR
    @Override
    public void updatePostLayout(ConstraintLayout container) {
    update();
    }
    void update(){
    if (mContainer == null) {
    return;
    }
    mComputedCenterX = Float.NaN;
    mComputedCenterY = Float.NaN;
    View[] views = getViews(mContainer);
    calcCenters();
    float shift = myFlyValue-1;
    for (int i = 0; i < mCount; i++) {
    View view = views[i];
    int x = (view.getLeft() + view.getRight()) / 2;
    int y = (view.getTop() + view.getBottom()) / 2;
    view.setTranslationX((x - mComputedCenterX)*shift);
    view.setTranslationY((y - mComputedCenterY)*shift);
    }
    }

    View full-size slide

  64. ConstraintLayout 2.0 preview
    FLY IN DECORATOR
    @Override
    public void updatePreLayout(ConstraintLayout container) {
    if (mContainer!=container) {
    setFlyIn(10);
    ObjectAnimator.ofFloat(this, "FlyIn", 1f)
    .setDuration(1000).start();
    }
    mContainer = container;
    }
    public void setFlyIn(float flyIn) {
    myFlyValue = flyIn;
    update();
    }

    View full-size slide

  65. ConstraintLayout 2.0 preview
    FLY IN DECORATOR: XML
    android:id="@+id/flyinhelper"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F00"
    app:constraint_referenced_ids=“button1,button2,button3,
    imageView,button4" />

    View full-size slide

  66. ConstraintLayout 2.0 preview
    (BONUS CODE)
    public void calcCenters() {
    if (!(Float.isNaN(mComputedCenterX) || Float.isNaN(mComputedCenterY))) {
    return;
    }
    if (Float.isNaN(mCenterX) || Float.isNaN(mCenterY)) {
    int minx = Integer.MAX_VALUE, miny= Integer.MAX_VALUE;
    int maxx= Integer.MIN_VALUE,maxy= Integer.MIN_VALUE;
    View []views = getViews(mContainer);
    for (int i = 0; i < mCount; i++) {
    View view = views[i];
    minx = Math.min(minx, view.getLeft());
    miny = Math.min(miny, view.getTop());
    maxx = Math.max(maxx, view.getRight());
    maxy = Math.max(maxy, view.getBottom());
    }
    mComputedCenterX = (Float.isNaN(mCenterX))?(minx + maxx) / 2:mCenterX;
    mComputedCenterY = (Float.isNaN(mCenterY))?(miny + maxy) / 2:mCenterY;
    } else {
    mComputedCenterY = mCenterY;
    mComputedCenterX = mCenterX;
    }
    }

    View full-size slide

  67. ConstraintLayout 2.0 preview
    CIRCULAR REVEAL
    android:id="@+id/helper"
    android:layout_width=“wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/lake"
    app:constraint_referenced_ids="imageView"
    />

    View full-size slide

  68. ConstraintLayout 2.0 preview
    CIRCULAR REVEAL
    android:id="@+id/helper"
    android:layout_width=“wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/lake"
    app:constraint_referenced_ids="imageView"
    />

    View full-size slide

  69. ConstraintLayout 2.0 preview
    CIRCULAR REVEAL
    @Override
    public void updatePostLayout(ConstraintLayout container) {
    super.updatePostLayout(container);
    if (mContainer != container) {
    int rad =(int) Math.hypot(mComputedMaxY-
    mComputedMinY,mComputedMaxX-mComputedMinX);
    Animator anim = ViewAnimationUtils.createCircularReveal(this,
    (int)mComputedCenterX-getLeft(),
    (int)mComputedCenterY-getTop(), 0, rad);
    anim.setDuration(2000);
    anim.start();
    }
    mContainer = container;
    }

    View full-size slide

  70. ConstraintLayout 2.0 preview
    DECORATOR HELPERS
    ➤ Tag your views with behavior
    ➤ Encapsulate behavior
    ➤ Declaratively use them

    View full-size slide

  71. CL
    2.0
    VIRTUAL LAYOUTS

    View full-size slide

  72. ConstraintLayout 2.0 preview
    VIRTUAL LAYOUTS
    ➤ Layout the referenced views
    ➤ Still keep flat hierarchy
    ➤ Linear
    ➤ Flow

    View full-size slide

  73. ConstraintLayout 2.0 preview
    LINEAR
    ➤ Create horizontal or vertical chains
    ➤ Somewhat similar to linear layout, but as a helper

    View full-size slide

  74. ConstraintLayout 2.0 preview
    LINEAR
    android:id=“@+id/linear"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:constraint_referenced_ids="button1,button2,button3"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    View full-size slide

  75. ConstraintLayout 2.0 preview
    LINEAR

    View full-size slide

  76. ConstraintLayout 2.0 preview
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="20dp"
    android:layout_marginTop="20dp"
    android:text="TextView"
    app:layout_constraintStart_toEndOf="@+id/linear"
    app:layout_constraintTop_toBottomOf="@+id/linear" />

    View full-size slide

  77. ConstraintLayout 2.0 preview
    LINEAR

    View full-size slide

  78. ConstraintLayout 2.0 preview
    FLOW
    ➤ Implements FlexboxLayout-like semantics
    ➤ Overflow elements will be pushed to the next row
    ➤ Still flat layout!
    ➤ Able to position outside elements relative to the ones in Flow

    View full-size slide

  79. ConstraintLayout 2.0 preview
    FLOW
    android:id="@+id/flow"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_marginTop="8dp"
    app:constraint_referenced_ids=“button1,button2,button3,button4,
    button5,button6,button7,button8"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

    View full-size slide

  80. ConstraintLayout 2.0 preview
    REFERENCE BUTTONS
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:src="@drawable/background"
    android:scaleType="centerCrop"
    app:layout_constraintLeft_toLeftOf="@+id/button6"
    app:layout_constraintTop_toBottomOf="@id/button8"/>

    View full-size slide

  81. ConstraintLayout 2.0 preview
    FLOW

    View full-size slide

  82. ConstraintLayout 2.0 preview
    FLOW

    View full-size slide

  83. ConstraintLayout 2.0 preview
    FLOW

    View full-size slide

  84. CL
    2.0
    LAYOUT MANAGEMENT

    View full-size slide

  85. ConstraintLayout 2.0 preview
    MANAGING STATES

    View full-size slide

  86. ConstraintLayout 2.0 preview
    MANAGING STATES

    View full-size slide

  87. ConstraintLayout 2.0 preview
    STATE XML

    android:id="@+id/small"
    app:constraints=“@layout/layout_small” />
    android:id="@+id/large"
    app:constraints=“@layout/layout_large” />

    View full-size slide

  88. ConstraintLayout 2.0 preview
    JAVA-SIDE
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout);
    cl = findViewById(R.id.root);
    cl.setLayoutDescription(R.xml.layout_states);
    }

    View full-size slide

  89. ConstraintLayout 2.0 preview
    JAVA-SIDE
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout);
    cl = findViewById(R.id.root);
    cl.setLayoutDescription(R.xml.layout_states);
    }
    public void change(View v) {
    cl.setState(closed ? R.id.large : R.id.small);
    closed = !closed;
    }

    View full-size slide

  90. ConstraintLayout 2.0 preview
    STATE XML : SIZE QUALIFIERS

    app:constraints=“@layout/layout_small">
    app:constraints="@layout/layout_small"
    app:region_widthLessThan="550dp" />
    app:constraints="@layout/layout_large"
    app:region_widthMoreThan="450dp"/>


    View full-size slide

  91. ConstraintLayout 2.0 preview
    ON CONFIGURATION CHANGE
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    cl.setState(newConfig.screenWidthDp, newConfig.screenHeightDp);
    }

    View full-size slide

  92. ConstraintLayout 2.0 preview
    LIVE RESIZE

    View full-size slide

  93. ConstraintLayout 2.0 preview
    LIVE RESIZE

    View full-size slide

  94. ConstraintLayout 2.0 preview
    LIVE RESIZE

    View full-size slide

  95. ConstraintLayout 2.0 preview
    LIVE RESIZE

    View full-size slide

  96. ConstraintLayout 2.0 preview
    LIVE RESIZE

    View full-size slide

  97. ConstraintLayout 2.0 preview
    LIVE RESIZE

    View full-size slide

  98. ConstraintLayout 2.0 preview
    CALLBACK
    cl.setOnConstraintsChanged(new ConstraintsChangedListener() {
    @Override
    public void preLayoutChange(int state,int layoutId) {
    TransitionManager.beginDelayedTransition(cl);
    }
    });

    View full-size slide

  99. ConstraintLayout 2.0 preview
    ANIMATED LIVE RESIZE (TRANSITION MANAGER)

    View full-size slide

  100. ConstraintLayout 2.0 preview
    ANIMATED LIVE RESIZE (TRANSITION MANAGER)

    View full-size slide

  101. ConstraintLayout 2.0 preview
    LAYOUT MANAGEMENT
    ➤ Centralize multiple layouts in a single XML
    ➤ Allows to easily initialize and deal with different representations for your layout
    ➤ Size qualifiers & live resize

    View full-size slide

  102. CL
    2.0
    FLUENT API

    View full-size slide

  103. ConstraintLayout 2.0 preview
    FLUENT API
    ➤ ConstraintSet: great to capture full state and manage it
    ➤ …But not that ideal for direct manipulation
    ➤ Fluent API to change layout params directly

    View full-size slide

  104. ConstraintLayout 2.0 preview
    FLUENT API
    new Constraints(view).margin(ConstraintProperties.TOP,23).apply();
    Constraints.on(view)
    .margin(TOP, 16)
    .margin(BOTTOM, 16)
    .margin(LEFT, 16)
    .margin(RIGHT, 16)
    .apply();
    Constraints.on(view)
    .center(R.id.button1, LEFT, 23, R.id.button2, RIGHT, 23,.05f)
    .connect(TOP,PARENT_ID, TOP, 32)
    .apply();

    View full-size slide

  105. ConstraintLayout 2.0 preview
    EXAMPLE : CENTER
    Constraints.on(myView)
    .center(R.id.button1, LEFT, R.id.button2, RIGHT)
    .horizontalBias(.5f)
    .top(PARENT_ID,TOP,32)
    .apply();

    View full-size slide

  106. ConstraintLayout 2.0 preview
    center(int firstID, int firstSide, int firstMargin, int secondId, int secondSide, int secondMargin, f
    centerHorizontally(int leftId, int leftSide, int leftMargin, int rightId, int rightSide, int rightMar
    centerHorizontallyRtl(int startId, int startSide, int startMargin, int endId, int endSide, int endMar
    centerVertically(int topId, int topSide, int topMargin, int bottomId, int bottomSide, int bottomMargi
    centerHorizontally(int toView)
    centerHorizontallyRtl(int toView)
    centerVertically(int toView)
    removeConstraints(int anchor)
    margin(int anchor, int value)
    goneMargin(int anchor, int value)
    horizontalBias(float bias)
    verticalBias(float bias)
    dimensionRatio(String ratio)
    visibility(int visibility)
    alpha(float alpha)
    elevation(float elevation)
    rotation(float rotation)
    rotationX(float rotationX)
    rotationY(float rotationY)
    scaleX(float scaleX)
    scaleY(float scaleY)
    CONSTRAINTS API…
    translationX(float translationX)
    translationY(float translationY)
    translation(float translationX, float translationY)
    translationZ(float translationZ)
    constrainHeight(int height)
    constrainWidth(int width)
    constrainMaxHeight(int height)
    constrainMaxWidth(int width)
    constrainMinHeight(int height)
    constrainMinWidth(int width)
    constrainDefaultHeight(int height)
    constrainDefaultWidth(int width)
    horizontalWeight(float weight)
    verticalWeight(float weight)
    horizontalChainStyle(int chainStyle)
    verticalChainStyle(int chainStyle)
    addToHorizontalChain(int leftId, int rightId)
    addToHorizontalChainRTL(int leftId, int rightId)
    addToVerticalChain(int topId, int bottomId)
    removeFromVerticalChain()
    removeFromHorizontalChain()
    connect(int startSide, int endID, int endSide, int margin

    View full-size slide

  107. ConstraintLayout 2.0 preview
    CONSTRAINT LAYOUT 2.0 - SUMMARY
    ➤ Virtual Layouts
    ➤ Layers & Transitions
    ➤ Decorators
    ➤ State management & Live Resize
    ➤ Fluent API for LayoutParams
    ➤ …more to come!

    View full-size slide

  108. THANKS!
    Nicolas Roard & John Hoford
    @camaelon @johnhoford

    View full-size slide

  109. CL
    2.0
    TO BE CONTINUED…
    … at Google IO 2018!

    View full-size slide

  110. ConstraintLayout 2.0 preview
    DOCUMENTATION
    ➤ http://www.constraintlayout.com
    ➤ https://developer.android.com/reference/android/support/constraint/package-
    summary.html
    ➤ https://developer.android.com/training/constraint-layout/index.html
    ➤ https://codelabs.developers.google.com/codelabs/constraint-layout
    ➤ https://medium.com/google-developers/building-interfaces-with-
    constraintlayout-3958fa38a9f7

    View full-size slide