Merge "MediaSessionCompat: Fix hasCallback"
diff --git a/buildSrc/build_dependencies.gradle b/buildSrc/build_dependencies.gradle
index f693884..73cdf37 100644
--- a/buildSrc/build_dependencies.gradle
+++ b/buildSrc/build_dependencies.gradle
@@ -16,7 +16,7 @@
 
 def build_versions = [:]
 
-build_versions.kotlin = '1.2.0'
+build_versions.kotlin = '1.2.20'
 
 rootProject.ext['build_versions'] = build_versions
 
diff --git a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
index ea9edf9..76a1e7f 100644
--- a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
@@ -30,7 +30,8 @@
 const val JAVAPOET = "com.squareup:javapoet:1.8.0"
 const val JSR250 = "javax.annotation:javax.annotation-api:1.2"
 const val JUNIT = "junit:junit:4.12"
-const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib:1.2.0"
+const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib:1.2.20"
+const val KOTLIN_METADATA = "me.eugeniomarletti:kotlin-metadata:1.2.1"
 const val LINT = "com.android.tools.lint:lint:26.0.0"
 const val MOCKITO_CORE = "org.mockito:mockito-core:2.7.6"
 const val MULTIDEX = "com.android.support:multidex:1.0.1"
diff --git a/car/res/layout/car_alert_dialog.xml b/car/res/layout/car_alert_dialog.xml
new file mode 100644
index 0000000..63dc70c
--- /dev/null
+++ b/car/res/layout/car_alert_dialog.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<!-- Note: the width is 0dp because ColumnCardView will automatically set a width based
+     on the number of columns it should take up. See ColumnCardView for more details. -->
+<androidx.car.widget.ColumnCardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_gravity="center"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:elevation="@dimen/car_dialog_elevation"
+    app:cardBackgroundColor="@color/car_card"
+    app:cardCornerRadius="@dimen/car_radius_3">
+
+    <LinearLayout
+        android:id="@+id/content_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/car_keyline_1"
+        android:layout_marginEnd="@dimen/car_keyline_1"
+        android:paddingTop="@dimen/car_padding_4"
+        android:paddingBottom="@dimen/car_padding_4"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/car_dialog_header_height"
+            android:textAppearance="@style/CarTitle2"
+            android:gravity="center_vertical|start"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/body"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/CarBody2"
+            android:visibility="gone" />
+
+        <LinearLayout
+            android:id="@+id/button_panel"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/car_dialog_action_bar_height"
+            android:layout_marginTop="@dimen/car_padding_2"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:visibility="gone">
+
+            <Button
+                android:id="@+id/positive_button"
+                android:layout_marginEnd="@dimen/car_padding_4"
+                android:layout_width="wrap_content"
+                android:minWidth="0dp"
+                android:padding="0dp"
+                android:textColor="@color/car_accent"
+                android:visibility="gone"
+                style="@style/Widget.Car.Button.Borderless.Colored" />
+
+            <Button
+                android:id="@+id/negative_button"
+                android:layout_width="wrap_content"
+                android:minWidth="0dp"
+                android:padding="0dp"
+                android:textColor="@color/car_accent"
+                android:visibility="gone"
+                style="@style/Widget.Car.Button.Borderless.Colored" />
+        </LinearLayout>
+    </LinearLayout>
+</androidx.car.widget.ColumnCardView>
diff --git a/car/res/values/dimens.xml b/car/res/values/dimens.xml
index d397df0..389bed9 100644
--- a/car/res/values/dimens.xml
+++ b/car/res/values/dimens.xml
@@ -103,6 +103,7 @@
     <!-- Dialogs -->
     <dimen name="car_dialog_header_height">@dimen/car_card_header_height</dimen>
     <dimen name="car_dialog_action_bar_height">@dimen/car_card_action_bar_height</dimen>
+    <dimen name="car_dialog_elevation">16dp</dimen>
 
     <!-- Slide Up Menu -->
     <dimen name="car_slide_up_menu_initial_height">76dp</dimen>
diff --git a/car/src/main/java/androidx/car/app/CarAlertDialog.java b/car/src/main/java/androidx/car/app/CarAlertDialog.java
new file mode 100644
index 0000000..6a85546
--- /dev/null
+++ b/car/src/main/java/androidx/car/app/CarAlertDialog.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2018 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 androidx.car.app;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.text.TextUtils;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.car.R;
+
+/**
+ * A subclass of {@link Dialog} that is tailored for the car environment. This dialog can display a
+ * title text, body text, and up to two buttons -- a positive and negative button. There is no
+ * affordance for displaying a custom view or list of content, differentiating it from a regular
+ * {@code AlertDialog}.
+ */
+public class CarAlertDialog extends Dialog {
+    private final DialogData mData;
+
+    private final int mTopPadding;
+    private final int mBottomPadding;
+    private final int mButtonMinWidth;
+    private final int mButtonSpacing;
+
+    private View mContentView;
+    private TextView mTitleView;
+    private TextView mBodyView;
+
+    private View mButtonPanel;
+    private Button mPositiveButton;
+    private Button mNegativeButton;
+    private ButtonPanelTouchDelegate mButtonPanelTouchDelegate;
+
+    private CarAlertDialog(Context context, DialogData data) {
+        super(context);
+        mData = data;
+
+        Resources res = context.getResources();
+        mTopPadding = res.getDimensionPixelSize(R.dimen.car_padding_4);
+        mBottomPadding = res.getDimensionPixelSize(R.dimen.car_padding_4);
+        mButtonMinWidth = res.getDimensionPixelSize(R.dimen.car_button_min_width);
+        mButtonSpacing = res.getDimensionPixelSize(R.dimen.car_padding_4);
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        // Ideally this method should be private; the dialog should only be modifiable through the
+        // Builder. Unfortunately, this method is defined with the Dialog itself and is public.
+        // So, throw an error if this method is ever called. setTitleInternal() should be used
+        // to set the title within this class.
+        throw new UnsupportedOperationException("Title should only be set from the Builder");
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Window window = getWindow();
+        window.setContentView(R.layout.car_alert_dialog);
+
+        // By default, the decor background is white. Set this to be transparent so that
+        // the dialog can have rounded corners and will show the background.
+        window.getDecorView().setBackgroundColor(Color.TRANSPARENT);
+
+        initializeViews();
+        initializeDialogWithData();
+    }
+
+    private void setTitleInternal(CharSequence title) {
+        boolean hasTitle = !TextUtils.isEmpty(title);
+
+        mTitleView.setText(title);
+        mTitleView.setVisibility(hasTitle ? View.VISIBLE : View.GONE);
+
+        // If there's a title, then remove the padding at the top of the content view.
+        int topPadding = hasTitle ? 0 : mTopPadding;
+        mContentView.setPaddingRelative(
+                mContentView.getPaddingStart(),
+                topPadding,
+                mContentView.getPaddingEnd(),
+                mContentView.getPaddingBottom());
+    }
+
+    private void setBody(CharSequence body) {
+        mBodyView.setText(body);
+        mBodyView.setVisibility(TextUtils.isEmpty(body) ? View.GONE : View.VISIBLE);
+    }
+
+    private void setPositiveButton(CharSequence text) {
+        boolean showButton = !TextUtils.isEmpty(text);
+
+        mPositiveButton.setText(text);
+        mPositiveButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
+
+        updateTargetTargetForButton(mPositiveButton);
+        updateButtonPanelVisibility();
+        updateButtonSpacing();
+    }
+
+    private void setNegativeButton(CharSequence text) {
+        mNegativeButton.setText(text);
+        mNegativeButton.setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);
+
+        updateTargetTargetForButton(mNegativeButton);
+        updateButtonPanelVisibility();
+        updateButtonSpacing();
+    }
+
+    /**
+     * Checks if the given view that represents a positive or negative button currently meets the
+     * minimum touch target size that is dictated by {@link #mButtonMinWidth}. If it does not, then
+     * this method will utilize a {@link TouchDelegate} to expand the touch target size of that
+     * button.
+     *
+     * @param button One of {@link #mPositiveButton} or {@link #mNegativeButton}.
+     */
+    private void updateTargetTargetForButton(View button) {
+        if (button != mPositiveButton && button != mNegativeButton) {
+            throw new IllegalArgumentException("Method must be passed one of mPositiveButton or "
+                    + "mNegativeButton");
+        }
+
+        if (button.getVisibility() != View.VISIBLE) {
+            return;
+        }
+
+        // The TouchDelegate needs to be set after the panel has been laid out in order to get the
+        // hit Rect.
+        mButtonPanel.post(() -> {
+            Rect rect = new Rect();
+            button.getHitRect(rect);
+
+            int hitWidth = Math.abs(rect.right - rect.left);
+
+            TouchDelegate touchDelegate = null;
+
+            // If the button does not meet the minimum requirements for touch target size, then
+            // expand its hit area with a TouchDelegate.
+            if (hitWidth < mButtonMinWidth) {
+                int amountToIncrease = (mButtonMinWidth - hitWidth) / 2;
+                rect.left -= amountToIncrease;
+                rect.right += amountToIncrease;
+
+                touchDelegate = new TouchDelegate(rect, button);
+            }
+
+            if (button == mPositiveButton) {
+                mButtonPanelTouchDelegate.setPositiveButtonDelegate(touchDelegate);
+            } else {
+                mButtonPanelTouchDelegate.setNegativeButtonDelegate(touchDelegate);
+            }
+        });
+    }
+
+    /**
+     * Checks if spacing should be added between the positive and negative button. The spacing is
+     * only needed if both buttons are visible.
+     */
+    private void updateButtonSpacing() {
+        int marginEnd;
+
+        // If both buttons are visible, then there needs to be spacing between them.
+        if ((mPositiveButton.getVisibility() == View.VISIBLE
+                && mNegativeButton.getVisibility() == View.VISIBLE)) {
+            marginEnd = mButtonSpacing;
+        } else {
+            marginEnd = 0;
+        }
+
+        ViewGroup.MarginLayoutParams layoutParams =
+                (ViewGroup.MarginLayoutParams) mPositiveButton.getLayoutParams();
+        layoutParams.setMarginEnd(marginEnd);
+        mPositiveButton.requestLayout();
+    }
+
+    /**
+     * Toggles whether or not the panel containing the action buttons are visible depending on if
+     * a button should be shown.
+     */
+    private void updateButtonPanelVisibility() {
+        boolean hasButtons = mPositiveButton.getVisibility() == View.VISIBLE
+                || mNegativeButton.getVisibility() == View.VISIBLE;
+
+        int visibility = hasButtons ? View.VISIBLE : View.GONE;
+
+        // Visibility is already correct, so nothing further needs to be done.
+        if (mButtonPanel.getVisibility() == visibility) {
+            return;
+        }
+
+        mButtonPanel.setVisibility(visibility);
+
+        // If there are buttons, then remove the padding at the bottom of the content view.
+        int buttonPadding = hasButtons ? 0 : mBottomPadding;
+        mContentView.setPaddingRelative(
+                mContentView.getPaddingStart(),
+                mContentView.getPaddingTop(),
+                mContentView.getPaddingEnd(),
+                buttonPadding);
+    }
+
+    /**
+     * Looks through the {@link DialogData} that was passed to this dialog and initialize its
+     * contents based on what data is present.
+     */
+    private void initializeDialogWithData() {
+        setTitleInternal(mData.mTitle);
+        setBody(mData.mBody);
+        setPositiveButton(mData.mPositiveButtonText);
+        setNegativeButton(mData.mNegativeButtonText);
+    }
+
+    /**
+     * Initializes the views within the dialog that are modifiable based on the data that has been
+     * set on it. Also responsible for hooking up listeners for button clicks.
+     */
+    private void initializeViews() {
+        Window window = getWindow();
+
+        mContentView = window.findViewById(R.id.content_view);
+        mTitleView = window.findViewById(R.id.title);
+        mBodyView = window.findViewById(R.id.body);
+
+        mButtonPanel = window.findViewById(R.id.button_panel);
+        mButtonPanelTouchDelegate = new ButtonPanelTouchDelegate(mButtonPanel);
+        mButtonPanel.setTouchDelegate(mButtonPanelTouchDelegate);
+
+        mPositiveButton = window.findViewById(R.id.positive_button);
+        mNegativeButton = window.findViewById(R.id.negative_button);
+
+        mPositiveButton.setOnClickListener(v -> onPositiveButtonClick());
+        mNegativeButton.setOnClickListener(v -> onNegativeButtonClick());
+    }
+
+    /** Delegates to a listener on the positive button if it exists or dismisses the dialog. */
+    private void onPositiveButtonClick() {
+        if (mData.mPositiveButtonListener != null) {
+            mData.mPositiveButtonListener.onClick(this /* dialog */, BUTTON_POSITIVE);
+        } else {
+            dismiss();
+        }
+    }
+
+    /** Delegates to a listener on the negative button if it exists or dismisses the dialog. */
+    private void onNegativeButtonClick() {
+        if (mData.mNegativeButtonListener != null) {
+            mData.mNegativeButtonListener.onClick(this /* dialog */, BUTTON_NEGATIVE);
+        } else {
+            dismiss();
+        }
+    }
+
+    /**
+     * A composite {@link TouchDelegate} for a button panel that has two buttons. It can hold
+     * multiple {@code TouchDelegate}s and will delegate out touch events to each.
+     */
+    private static final class ButtonPanelTouchDelegate extends TouchDelegate {
+        @Nullable private TouchDelegate mPositiveButtonDelegate;
+        @Nullable private TouchDelegate mNegativeButtonDelegate;
+
+        ButtonPanelTouchDelegate(View view) {
+            super(new Rect(), view);
+        }
+
+        public void setPositiveButtonDelegate(@Nullable TouchDelegate delegate) {
+            mPositiveButtonDelegate = delegate;
+        }
+
+        public void setNegativeButtonDelegate(@Nullable TouchDelegate delegate) {
+            mNegativeButtonDelegate = delegate;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            boolean result = false;
+            float x = event.getX();
+            float y = event.getY();
+            event.setLocation(x, y);
+
+            if (mPositiveButtonDelegate != null) {
+                result = mPositiveButtonDelegate.onTouchEvent(event);
+            }
+
+            if (mNegativeButtonDelegate != null) {
+                result |= mNegativeButtonDelegate.onTouchEvent(event);
+            }
+
+            return result;
+        }
+    }
+
+    /**
+     * A class that holds the data that is settable by the {@link Builder} and should be displayed
+     * in the {@link CarAlertDialog}.
+     */
+    private static class DialogData {
+        private CharSequence mTitle;
+        private CharSequence mBody;
+        private CharSequence mPositiveButtonText;
+        private OnClickListener mPositiveButtonListener;
+        private CharSequence mNegativeButtonText;
+        private OnClickListener mNegativeButtonListener;
+    }
+
+    /**
+     * Builder class that can be used to create a {@link CarAlertDialog} by configuring the options
+     * for what shows up in the resulting dialog.
+     */
+    public static class Builder {
+        private final Context mContext;
+        private final DialogData mDialogData;
+
+        private boolean mCancelable = true;
+        private OnCancelListener mOnCancelListener;
+        private OnDismissListener mOnDismissListener;
+
+        /**
+         * Creates a new instance of the {@code Builder}.
+         *
+         * @param context The {@code Context} that the dialog is to be created in.
+         */
+        public Builder(Context context) {
+            mContext = context;
+            mDialogData = new DialogData();
+        }
+
+        /**
+         * Sets the main title of the dialog to be the given string resource.
+         *
+         * @param titleId The resource id of the string to be used as the title.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setTitle(@StringRes int titleId) {
+            mDialogData.mTitle = mContext.getString(titleId);
+            return this;
+        }
+
+        /**
+         * Sets the main title of the dialog for be the given string.
+         *
+         * @param title The string to be used as the title.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setTitle(CharSequence title) {
+            mDialogData.mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets the body text of the dialog to be the given string resource.
+         *
+         * @param bodyId The resource id of the string to be used as the body text.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setBody(@StringRes int bodyId) {
+            mDialogData.mBody = mContext.getString(bodyId);
+            return this;
+        }
+
+        /**
+         * Sets the body text of the dialog to be the given string.
+         *
+         * @param body The string to be used as the body text.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setBody(CharSequence body) {
+            mDialogData.mBody = body;
+            return this;
+        }
+
+        /**
+         * Sets the text of the positive button and the listener that will be invoked when the
+         * button is pressed. If a listener is not provided, then the dialog will dismiss itself
+         * when the positive button is clicked.
+         *
+         * <p>The positive button should be used to accept and continue with the action (e.g.
+         * an "OK" action).
+         *
+         * @param textId The resource id of the string to be used for the positive button text.
+         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
+         *                 be {@code null} to represent no listener.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setPositiveButton(@StringRes int textId,
+                @Nullable OnClickListener listener) {
+            mDialogData.mPositiveButtonText = mContext.getString(textId);
+            mDialogData.mPositiveButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Sets the text of the positive button and the listener that will be invoked when the
+         * button is pressed. If a listener is not provided, then the dialog will dismiss itself
+         * when the positive button is clicked.
+         *
+         * <p>The positive button should be used to accept and continue with the action (e.g.
+         * an "OK" action).
+         *
+         * @param text The string to be used for the positive button text.
+         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
+         *                 be {@code null} to represent no listener.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setPositiveButton(CharSequence text, @Nullable OnClickListener listener) {
+            mDialogData.mPositiveButtonText = text;
+            mDialogData.mPositiveButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Sets the text of the negative button and the listener that will be invoked when the
+         * button is pressed. If a listener is not provided, then the dialog will dismiss itself
+         * when the negative button is clicked.
+         *
+         * <p>The negative button should be used to cancel any actions the dialog represents.
+         *
+         * @param textId The resource id of the string to be used for the negative button text.
+         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
+         *                 be {@code null} to represent no listener.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setNegativeButton(@StringRes int textId,
+                @Nullable OnClickListener listener) {
+            mDialogData.mNegativeButtonText = mContext.getString(textId);
+            mDialogData.mNegativeButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Sets the text of the negative button and the listener that will be invoked when the
+         * button is pressed. If a listener is not provided, then the dialog will dismiss itself
+         * when the negative button is clicked.
+         *
+         * <p>The negative button should be used to cancel any actions the dialog represents.
+         *
+         * @param text The string to be used for the negative button text.
+         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
+         *                 be {@code null} to represent no listener.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setNegativeButton(CharSequence text, @Nullable OnClickListener listener) {
+            mDialogData.mNegativeButtonText = text;
+            mDialogData.mNegativeButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Sets whether the dialog is cancelable or not. Default is {@code true}.
+         *
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setCancelable(boolean cancelable) {
+            mCancelable = cancelable;
+            return this;
+        }
+
+        /**
+         * Sets the callback that will be called if the dialog is canceled.
+         *
+         * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
+         * being canceled or one of the supplied choices being selected.
+         * If you are interested in listening for all cases where the dialog is dismissed
+         * and not just when it is canceled, see {@link #setOnDismissListener(OnDismissListener)}.
+         *
+         * @param onCancelListener The listener to be invoked when this dialog is canceled.
+         * @return This {@code Builder} object to allow for chaining of calls.
+         *
+         * @see #setCancelable(boolean)
+         * @see #setOnDismissListener(OnDismissListener)
+         */
+        public Builder setOnCancelListener(OnCancelListener onCancelListener) {
+            mOnCancelListener = onCancelListener;
+            return this;
+        }
+
+        /**
+         * Sets the callback that will be called when the dialog is dismissed for any reason.
+         *
+         * @return This {@code Builder} object to allow for chaining of calls.
+         */
+        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
+            mOnDismissListener = onDismissListener;
+            return this;
+        }
+
+        /**
+         * Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}.
+         *
+         * <p>Calling this method does not display the dialog. If no additional processing is
+         * needed, {@link #show()} may be called instead to both create and display the dialog.
+         */
+        public CarAlertDialog create() {
+            CarAlertDialog dialog = new CarAlertDialog(mContext, mDialogData);
+
+            dialog.setCancelable(mCancelable);
+            dialog.setCanceledOnTouchOutside(mCancelable);
+            dialog.setOnCancelListener(mOnCancelListener);
+            dialog.setOnDismissListener(mOnDismissListener);
+
+            return dialog;
+        }
+
+        /**
+         * Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}
+         * and immediately displays the dialog.
+         *
+         * <p>Calling this method is functionally identical to:
+         * <pre>
+         *     CarAlertDialog dialog = new CarAlertDialog.Builder().create();
+         *     dialog.show();
+         * </pre>
+         */
+        public CarAlertDialog show() {
+            CarAlertDialog dialog = create();
+            dialog.show();
+            return dialog;
+        }
+    }
+}
diff --git a/fragment/src/androidTest/java/android/support/v4/app/LoaderViewModelTest.java b/fragment/src/androidTest/java/android/support/v4/app/LoaderViewModelTest.java
index b53f7dd..911e169 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/LoaderViewModelTest.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/LoaderViewModelTest.java
@@ -17,6 +17,7 @@
 package android.support.v4.app;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
@@ -63,6 +64,8 @@
         assertFalse("LoaderInfo shouldn't be destroyed before onCleared", info.mDestroyed);
         loaderViewModel.onCleared();
         assertTrue("LoaderInfo should be destroyed after onCleared", info.mDestroyed);
+        assertNull("LoaderInfo should be removed from LoaderViewModel after onCleared",
+                loaderViewModel.getLoader(0));
     }
 
     private class AlwaysRunningLoaderInfo extends LoaderManagerImpl.LoaderInfo<Boolean> {
diff --git a/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java b/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java
index 951d908..fdde79f 100644
--- a/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java
+++ b/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java
@@ -294,6 +294,7 @@
                 LoaderInfo info = mLoaders.valueAt(index);
                 info.destroy();
             }
+            mLoaders.clear();
         }
 
         public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
index caebb50..aa2e1e3 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
@@ -23,6 +23,7 @@
 import android.support.tools.jetifier.core.transform.TransformationContext
 import android.support.tools.jetifier.core.transform.Transformer
 import android.support.tools.jetifier.core.transform.bytecode.ByteCodeTransformer
+import android.support.tools.jetifier.core.transform.metainf.MetaInfTransformer
 import android.support.tools.jetifier.core.transform.pom.PomDocument
 import android.support.tools.jetifier.core.transform.pom.PomScanner
 import android.support.tools.jetifier.core.transform.proguard.ProGuardTransformer
@@ -37,8 +38,10 @@
  * the registered [Transformer]s over the set and creates new archives that will contain the
  * transformed files.
  */
-class Processor private constructor (private val context: TransformationContext)
-    : ArchiveItemVisitor {
+class Processor private constructor (
+    private val context: TransformationContext,
+    private val transformers: List<Transformer>
+) : ArchiveItemVisitor {
 
     companion object {
         private const val TAG = "Processor"
@@ -49,6 +52,27 @@
         private const val REVERSE_RESTRICT_TO_PACKAGE = "androidx"
 
         /**
+         * Transformers to be used when refactoring general libraries.
+         */
+        private fun createTransformers(context: TransformationContext) = listOf(
+            // Register your transformers here
+            ByteCodeTransformer(context),
+            XmlResourcesTransformer(context),
+            ProGuardTransformer(context)
+        )
+
+        /**
+         * Transformers to be used when refactoring the support library itself.
+         */
+        private fun createSLTransformers(context: TransformationContext) = listOf(
+            // Register your transformers here
+            ByteCodeTransformer(context),
+            XmlResourcesTransformer(context),
+            ProGuardTransformer(context),
+            MetaInfTransformer(context)
+        )
+
+        /**
          * Creates a new instance of the [Processor].
          * [config] Transformation configuration
          * [reversedMode] Whether the processor should run in reversed mode
@@ -73,18 +97,17 @@
                 )
             }
 
-            val context = TransformationContext(newConfig, rewritingSupportLib)
-            return Processor(context)
+            val context = TransformationContext(newConfig, rewritingSupportLib, reversedMode)
+            val transformers = if (rewritingSupportLib) {
+                createSLTransformers(context)
+            } else {
+                createTransformers(context)
+            }
+
+            return Processor(context, transformers)
         }
     }
 
-    private val transformers = listOf(
-        // Register your transformers here
-        ByteCodeTransformer(context),
-        XmlResourcesTransformer(context),
-        ProGuardTransformer(context)
-    )
-
     /**
      * Transforms the input libraries given in [inputLibraries] using all the registered
      * [Transformer]s and returns new libraries stored in [outputPath].
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt
index b5b4064..08a22a4 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt
@@ -25,7 +25,11 @@
 /**
  * Context to share the transformation state between individual [Transformer]s.
  */
-class TransformationContext(val config: Config, val rewritingSupportLib: Boolean) {
+class TransformationContext(
+    val config: Config,
+    val rewritingSupportLib: Boolean = false,
+    val isInReversedMode: Boolean = false
+) {
 
     // Merges all packages prefixes into one regEx pattern
     private val packagePrefixPattern = Pattern.compile(
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt
new file mode 100644
index 0000000..3220dae
--- /dev/null
+++ b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 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 android.support.tools.jetifier.core.transform.metainf
+
+import android.support.tools.jetifier.core.archive.ArchiveFile
+import android.support.tools.jetifier.core.transform.TransformationContext
+import android.support.tools.jetifier.core.transform.Transformer
+import java.nio.charset.StandardCharsets
+
+/**
+ * Transformer for META-INF/(.*).version files.
+ *
+ * Replaces version files from the META-INF directory. This should be used only for processing
+ * of the support library itself.
+ */
+class MetaInfTransformer internal constructor(
+    private val context: TransformationContext
+) : Transformer {
+
+    companion object {
+        const val FROM_VERSION = "28.0.0-SNAPSHOT"
+
+        const val TO_VERSION = "1.0.0-SNAPSHOT"
+
+        const val META_INF_DIR = "meta-inf"
+
+        const val VERSION_FILE_SUFFIX = ".version"
+    }
+
+    override fun canTransform(file: ArchiveFile): Boolean {
+        return context.rewritingSupportLib
+            && file.relativePath.toString().contains(META_INF_DIR, ignoreCase = true)
+            && file.fileName.endsWith(VERSION_FILE_SUFFIX, ignoreCase = true)
+    }
+
+    override fun runTransform(file: ArchiveFile) {
+        val sb = StringBuilder(file.data.toString(StandardCharsets.UTF_8))
+
+        var from = FROM_VERSION
+        var to = TO_VERSION
+        if (context.isInReversedMode) {
+            from = TO_VERSION
+            to = FROM_VERSION
+        }
+
+        if (sb.toString() != from) {
+            return
+        }
+
+        file.data = to.toByteArray()
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt
new file mode 100644
index 0000000..db6cd21
--- /dev/null
+++ b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 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 android.support.tools.jetifier.core.transform.metainf
+
+import android.support.tools.jetifier.core.archive.ArchiveFile
+import android.support.tools.jetifier.core.config.Config
+import android.support.tools.jetifier.core.map.TypesMap
+import android.support.tools.jetifier.core.transform.PackageMap
+import android.support.tools.jetifier.core.transform.TransformationContext
+import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.google.common.truth.Truth
+import org.junit.Test
+import java.nio.charset.Charset
+import java.nio.file.Path
+import java.nio.file.Paths
+
+class MetaInfTransformerTest {
+
+    @Test
+    fun rewriteVersion_forward() {
+        testRewrite(
+            given = "28.0.0-SNAPSHOT",
+            expected = "1.0.0-SNAPSHOT",
+            filePath = Paths.get("something/META-INF", "support_preference-v7.version"),
+            reverseMode = false
+        )
+    }
+
+    @Test
+    fun rewriteVersion_reversed() {
+        testRewrite(
+            given = "1.0.0-SNAPSHOT",
+            expected = "28.0.0-SNAPSHOT",
+            filePath = Paths.get("something/META-INF", "support_preference-v7.version"),
+            reverseMode = true
+        )
+    }
+
+    @Test
+    fun rewriteVersion_notSLRewrite_shouldSkip() {
+        testRewrite(
+            given = "28.0.0-SNAPSHOT",
+            expected = "28.0.0-SNAPSHOT",
+            filePath = Paths.get("something/META-INF", "support_preference-v7.version"),
+            reverseMode = false,
+            rewritingSupportLib = false,
+            expectedCanTransform = false
+        )
+    }
+
+    @Test
+    fun rewriteVersion_notMatchingVersion_shouldNoOp() {
+        testRewrite(
+            given = "test",
+            expected = "test",
+            filePath = Paths.get("something/META-INF", "support_preference-v7.version")
+        )
+    }
+
+    @Test
+    fun rewriteVersion_notValidSuffix_shouldSkip() {
+        testRewrite(
+            given = "28.0.0-SNAPSHOT",
+            expected = "28.0.0-SNAPSHOT",
+            filePath = Paths.get("something/META-INF", "support_preference-v7.none"),
+            expectedCanTransform = false
+        )
+    }
+
+    @Test
+    fun rewriteVersion_notInMetaInfDir_shouldSkip() {
+        testRewrite(
+            given = "28.0.0-SNAPSHOT",
+            expected = "28.0.0-SNAPSHOT",
+            filePath = Paths.get("something/else", "support_preference-v7.version"),
+            expectedCanTransform = false
+        )
+    }
+
+    private fun testRewrite(
+        given: String,
+        expected: String,
+        filePath: Path,
+        reverseMode: Boolean = false,
+        expectedCanTransform: Boolean = true,
+        rewritingSupportLib: Boolean = true
+    ) {
+        val config = Config(
+            restrictToPackagePrefixes = emptyList(),
+            rewriteRules = emptyList(),
+            slRules = emptyList(),
+            pomRewriteRules = emptyList(),
+            packageMap = PackageMap.EMPTY,
+            typesMap = TypesMap.EMPTY,
+            proGuardMap = ProGuardTypesMap.EMPTY
+        )
+        val context = TransformationContext(config,
+            rewritingSupportLib = rewritingSupportLib,
+            isInReversedMode = reverseMode)
+        val transformer = MetaInfTransformer(context)
+
+        val file = ArchiveFile(filePath, given.toByteArray())
+
+        val canTransform = transformer.canTransform(file)
+        if (canTransform) {
+            transformer.runTransform(file)
+        }
+
+        val strResult = file.data.toString(Charset.defaultCharset())
+
+        Truth.assertThat(canTransform).isEqualTo(expectedCanTransform)
+        Truth.assertThat(strResult).isEqualTo(expected)
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt
index cb43c5f..37075d3 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt
@@ -85,7 +85,7 @@
     class ProGuardTesterForFile(private val config: Config, private val given: String) {
 
         fun rewritesTo(expected: String) {
-            val context = TransformationContext(config, rewritingSupportLib = false)
+            val context = TransformationContext(config)
             val transformer = ProGuardTransformer(context)
             val file = ArchiveFile(Paths.get("proguard.txt"), given.toByteArray())
             transformer.runTransform(file)
@@ -100,7 +100,7 @@
     class ProGuardTesterForType(private val config: Config, private val given: String) {
 
         fun getsRewrittenTo(expectedType: String) {
-            val context = TransformationContext(config, rewritingSupportLib = false)
+            val context = TransformationContext(config)
             val mapper = ProGuardTypesMapper(context)
             val result = mapper.replaceType(given)
 
@@ -112,7 +112,7 @@
     class ProGuardTesterForArgs(private val config: Config, private val given: String) {
 
         fun getRewrittenTo(expectedArguments: String) {
-            val context = TransformationContext(config, rewritingSupportLib = false)
+            val context = TransformationContext(config)
             val mapper = ProGuardTypesMapper(context)
             val result = mapper.replaceMethodArgs(given)
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt
index c020814..4aaaae0 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt
@@ -330,7 +330,7 @@
             proGuardMap = ProGuardTypesMap.EMPTY,
             packageMap = packageMap
         )
-        val context = TransformationContext(config, rewritingSupportLib)
+        val context = TransformationContext(config, rewritingSupportLib = rewritingSupportLib)
         context.libraryName = libraryName
         val processor = XmlResourcesTransformer(context)
         val fileName = if (isManifestFile) {
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index 0d62cf8..c3319ac 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -49,6 +49,7 @@
     compile(JAVAPOET)
     compile(ANTLR)
     compile(XERIAL)
+    compile(KOTLIN_METADATA)
     compile(APACHE_COMMONS_CODEC)
     testCompile(GOOGLE_COMPILE_TESTING)
     testCompile project(":paging:common")
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/PojoProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/PojoProcessor.kt
index 0ba311e..3d73948 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/PojoProcessor.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/PojoProcessor.kt
@@ -47,6 +47,13 @@
 import com.google.auto.common.AnnotationMirrors
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
+import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
+import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
+import me.eugeniomarletti.kotlin.metadata.jvm.getJvmConstructorSignature
+import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
+import org.jetbrains.kotlin.serialization.ProtoBuf
+import org.jetbrains.kotlin.serialization.deserialization.NameResolver
+import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.element.ExecutableElement
 import javax.lang.model.element.Modifier.ABSTRACT
 import javax.lang.model.element.Modifier.PRIVATE
@@ -69,12 +76,28 @@
                     val element: TypeElement,
                     val bindingScope: FieldProcessor.BindingScope,
                     val parent: EmbeddedField?,
-                    val referenceStack: LinkedHashSet<Name> = LinkedHashSet<Name>()) {
+                    val referenceStack: LinkedHashSet<Name> = LinkedHashSet())
+    : KotlinMetadataUtils {
     val context = baseContext.fork(element)
+
+    // for KotlinMetadataUtils
+    override val processingEnv: ProcessingEnvironment
+        get() = context.processingEnv
+
+    // opportunistic kotlin metadata
+    private val kotlinMetadata by lazy {
+        try {
+            element.kotlinMetadata
+        } catch (throwable: Throwable) {
+            context.logger.d(element, "failed to read get kotlin metadata from %s", element)
+        }
+    }
+
     companion object {
         val PROCESSED_ANNOTATIONS = listOf(ColumnInfo::class, Embedded::class,
-                    Relation::class)
+                Relation::class)
     }
+
     fun process(): Pojo {
         return context.cache.pojos.get(Cache.PojoKey(element, bindingScope, parent), {
             referenceStack.add(element.qualifiedName)
@@ -94,9 +117,9 @@
                     !it.hasAnnotation(Ignore::class)
                             && !it.hasAnyOf(STATIC)
                             && (!it.hasAnyOf(TRANSIENT)
-                                    || it.hasAnnotation(ColumnInfo::class)
-                                    || it.hasAnnotation(Embedded::class)
-                                    || it.hasAnnotation(Relation::class))
+                            || it.hasAnnotation(ColumnInfo::class)
+                            || it.hasAnnotation(Embedded::class)
+                            || it.hasAnnotation(Relation::class))
                 }
                 .groupBy { field ->
                     context.checker.check(
@@ -199,6 +222,40 @@
                 constructor = constructor)
     }
 
+    /**
+     * Retrieves the parameter names of a method. If the method is inherited from a dependency
+     * module, the parameter name is not available (not in java spec). For kotlin, since parameter
+     * names are part of the API, we can read them via the kotlin metadata annotation.
+     * <p>
+     * Since we are using an unofficial library to read the metadata, all access to that code
+     * is safe guarded to avoid unexpected failures. In other words, it is a best effort but
+     * better than not supporting these until JB provides a proper API.
+     */
+    private fun getParamNames(method: ExecutableElement): List<String> {
+        val paramNames = method.parameters.map { it.simpleName.toString() }
+        if (paramNames.isEmpty()) {
+            return emptyList()
+        }
+        (kotlinMetadata as? KotlinClassMetadata)?.let {
+            try {
+                val kotlinParams = it
+                        .findConstructor(method)
+                        ?.tryGetParameterNames(it.data.nameResolver)
+                if (kotlinParams != null) {
+                    return kotlinParams
+                }
+            } catch (throwable: Throwable) {
+                context.logger.d(
+                        method,
+                        "Cannot read kotlin metadata, falling back to jvm signature. %s",
+                        throwable.message as Any)
+            }
+        }
+        // either it is java or something went wrong w/ kotlin metadata. default to whatever
+        // we can read.
+        return paramNames
+    }
+
     private fun chooseConstructor(
             myFields: List<Field>,
             embedded: List<EmbeddedField>,
@@ -208,10 +265,12 @@
         val fieldMap = myFields.associateBy { it.name }
         val embeddedMap = embedded.associateBy { it.field.name }
         val typeUtils = context.processingEnv.typeUtils
-        val failedConstructors = mutableMapOf<ExecutableElement, List<Constructor.Param?>>()
+        // list of param names -> matched params pairs for each failed constructor
+        val failedConstructors = arrayListOf<FailedConstructor>()
         val goodConstructors = constructors.map { constructor ->
-            val params = constructor.parameters.map param@ { param ->
-                val paramName = param.simpleName.toString()
+            val parameterNames = getParamNames(constructor)
+            val params = constructor.parameters.mapIndexed param@ { index, param ->
+                val paramName = parameterNames[index]
                 val paramType = param.asType()
 
                 val matches = fun(field: Field?): Boolean {
@@ -261,7 +320,7 @@
                 } else {
                     context.logger.e(param, ProcessorErrors.ambigiousConstructor(
                             pojo = element.qualifiedName.toString(),
-                            paramName = param.simpleName.toString(),
+                            paramName = paramName,
                             matchingFields = matchingFields.map { it.getPath() }
                                     + embedded.map { it.field.getPath() }
                     ))
@@ -269,7 +328,7 @@
                 }
             }
             if (params.any { it == null }) {
-                failedConstructors.put(constructor, params)
+                failedConstructors.add(FailedConstructor(constructor, parameterNames, params))
                 null
             } else {
                 @Suppress("UNCHECKED_CAST")
@@ -278,12 +337,8 @@
         }.filterNotNull()
         if (goodConstructors.isEmpty()) {
             if (failedConstructors.isNotEmpty()) {
-                val failureMsg = failedConstructors.entries.joinToString("\n") { entry ->
-                    val paramsMatching = entry.key.parameters.withIndex().joinToString(", ") {
-                        "param:${it.value.simpleName} -> matched field:" +
-                                (entry.value[it.index]?.log() ?: "unmatched")
-                    }
-                    "${entry.key} -> [$paramsMatching]"
+                val failureMsg = failedConstructors.joinToString("\n") { entry ->
+                    entry.log()
                 }
                 context.logger.e(element, ProcessorErrors.MISSING_POJO_CONSTRUCTOR +
                         "\nTried the following constructors but they failed to match:\n$failureMsg")
@@ -512,7 +567,7 @@
         val recursiveTailTypeName = typeElement.qualifiedName
 
         val referenceRecursionList = mutableListOf<Name>()
-        with (referenceRecursionList) {
+        with(referenceRecursionList) {
             add(recursiveTailTypeName)
             addAll(referenceStack.toList().takeLastWhile { it != recursiveTailTypeName })
             add(recursiveTailTypeName)
@@ -650,4 +705,51 @@
         }
         return candidates.first()
     }
+
+    /**
+     * Finds the kotlin meteadata for a constructor.
+     */
+    private fun KotlinClassMetadata.findConstructor(
+            executableElement: ExecutableElement
+    ): ProtoBuf.Constructor? {
+        val (nameResolver, classProto) = data
+        val jvmSignature = executableElement.jvmMethodSignature
+        // find constructor
+        return classProto.constructorList.singleOrNull {
+            it.getJvmConstructorSignature(nameResolver, classProto.typeTable) == jvmSignature
+        }
+    }
+
+    /**
+     * Tries to get the parameter names of a kotlin method
+     */
+    private fun ProtoBuf.Constructor.tryGetParameterNames(
+            nameResolver: NameResolver
+    ): List<String>? {
+        return valueParameterList.map {
+            if (it.hasName()) {
+                nameResolver.getName(it.name)
+                        .asString()
+                        .replace("`", "")
+                        .removeSuffix("?")
+                        .trim()
+            } else {
+                // early return bad parameter
+                return null
+            }
+        }
+    }
+
+    private data class FailedConstructor(
+            val method: ExecutableElement,
+            val params: List<String>,
+            val matches: List<Constructor.Param?>
+    ) {
+        fun log(): String {
+            val logPerParam = params.withIndex().joinToString(", ") {
+                "param:${it.value} -> matched field:" + (matches[it.index]?.log() ?: "unmatched")
+            }
+            return "$method -> [$logPerParam]"
+        }
+    }
 }
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 64db19c..421e192 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -43,7 +43,6 @@
     implementation(project(":arch:runtime"))
 
     implementation(SUPPORT_APPCOMPAT, libs.support_exclude_config)
-    kapt project(":room:compiler")
     kaptAndroidTest project(":room:compiler")
 
     androidTestImplementation(TEST_RUNNER) {
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.TestDatabase/1.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.TestDatabase/1.json
index 6362ae8..014d98c 100644
--- a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.TestDatabase/1.json
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.TestDatabase/1.json
@@ -2,7 +2,7 @@
   "formatVersion": 1,
   "database": {
     "version": 1,
-    "identityHash": "0729a81dd89abcbdd49f45ab0807043d",
+    "identityHash": "f78c9a055453bd6db377a3fcc3007e54",
     "entities": [
       {
         "tableName": "Book",
@@ -199,11 +199,37 @@
         },
         "indices": [],
         "foreignKeys": []
+      },
+      {
+        "tableName": "DataClassFromDependency",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
       }
     ],
     "setupQueries": [
       "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
-      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"0729a81dd89abcbdd49f45ab0807043d\")"
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"f78c9a055453bd6db377a3fcc3007e54\")"
     ]
   }
 }
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/TestDatabase.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/TestDatabase.kt
index 74d8853..33162ee 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/TestDatabase.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/TestDatabase.kt
@@ -19,19 +19,22 @@
 import android.arch.persistence.room.Database
 import android.arch.persistence.room.RoomDatabase
 import android.arch.persistence.room.integration.kotlintestapp.dao.BooksDao
+import android.arch.persistence.room.integration.kotlintestapp.dao.DependencyDao
 import android.arch.persistence.room.integration.kotlintestapp.dao.DerivedDao
 import android.arch.persistence.room.integration.kotlintestapp.vo.Author
 import android.arch.persistence.room.integration.kotlintestapp.vo.Book
 import android.arch.persistence.room.integration.kotlintestapp.vo.BookAuthor
+import android.arch.persistence.room.integration.kotlintestapp.vo.DataClassFromDependency
 import android.arch.persistence.room.integration.kotlintestapp.vo.NoArgClass
 import android.arch.persistence.room.integration.kotlintestapp.vo.Publisher
 
-@Database(entities = arrayOf(Book::class, Author::class, Publisher::class, BookAuthor::class,
-        NoArgClass::class),
-        version = 1)
+@Database(entities = [Book::class, Author::class, Publisher::class, BookAuthor::class,
+    NoArgClass::class, DataClassFromDependency::class], version = 1)
 abstract class TestDatabase : RoomDatabase() {
 
     abstract fun booksDao(): BooksDao
 
     abstract fun derivedDao(): DerivedDao
+
+    abstract fun dependencyDao(): DependencyDao
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/DependencyDaoTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/DependencyDaoTest.kt
new file mode 100644
index 0000000..2008fca
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/DependencyDaoTest.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 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 android.arch.persistence.room.integration.kotlintestapp.test
+
+import android.arch.persistence.room.integration.kotlintestapp.vo.DataClassFromDependency
+import android.support.test.runner.AndroidJUnit4
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DependencyDaoTest : TestDatabaseTest() {
+    @Test
+    fun insertAndGet() {
+        val dao = database.dependencyDao()
+        val data = DataClassFromDependency(
+                id = 3,
+                name = "foo"
+        )
+        dao.insert(data)
+        assertThat(dao.selectAll(), `is`(listOf(data)))
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/main/java/android/arch/persistence/room/integration/kotlintestapp/dao/DependencyDao.kt b/room/integration-tests/kotlintestapp/src/main/java/android/arch/persistence/room/integration/kotlintestapp/dao/DependencyDao.kt
new file mode 100644
index 0000000..efe3c5b
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/main/java/android/arch/persistence/room/integration/kotlintestapp/dao/DependencyDao.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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 android.arch.persistence.room.integration.kotlintestapp.dao
+
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.Insert
+import android.arch.persistence.room.OnConflictStrategy
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.integration.kotlintestapp.vo.DataClassFromDependency
+
+@Dao
+interface DependencyDao {
+    @Query("select * from DataClassFromDependency")
+    fun selectAll(): List<DataClassFromDependency>
+
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insert(vararg input: DataClassFromDependency)
+}
diff --git a/room/integration-tests/kotlintestapp/src/main/java/android/arch/persistence/room/integration/kotlintestapp/vo/DataClassFromDependency.kt b/room/integration-tests/kotlintestapp/src/main/java/android/arch/persistence/room/integration/kotlintestapp/vo/DataClassFromDependency.kt
new file mode 100644
index 0000000..258ada6
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/main/java/android/arch/persistence/room/integration/kotlintestapp/vo/DataClassFromDependency.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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 android.arch.persistence.room.integration.kotlintestapp.vo
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.PrimaryKey
+
+/**
+ * used to test the case where kotlin classes from dependencies cannot be read properly.
+ * Since the main db in this app is in the test module, the original classes serve as a dependency.
+ */
+@Entity
+data class DataClassFromDependency(
+        @PrimaryKey(autoGenerate = true)
+        val id: Int,
+        val name: String)
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
index 49818e6..85af90f 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
@@ -85,6 +85,7 @@
     public Slice onBindSlice(Uri sliceUri) {
         String path = sliceUri.getPath();
         switch (path) {
+            // TODO: add list / grid slices with 'see more' options
             case "/message":
                 return createMessagingSlice(sliceUri);
             case "/wifi":
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index 6b7f915..8a953cb 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -5,6 +5,9 @@
     ctor public GridBuilder(androidx.app.slice.builders.ListBuilder);
     method public androidx.app.slice.builders.GridBuilder addCell(androidx.app.slice.builders.GridBuilder.CellBuilder);
     method public androidx.app.slice.builders.GridBuilder addCell(java.util.function.Consumer<androidx.app.slice.builders.GridBuilder.CellBuilder>);
+    method public androidx.app.slice.builders.GridBuilder addSeeMoreAction(android.app.PendingIntent);
+    method public androidx.app.slice.builders.GridBuilder addSeeMoreCell(androidx.app.slice.builders.GridBuilder.CellBuilder);
+    method public androidx.app.slice.builders.GridBuilder addSeeMoreCell(java.util.function.Consumer<androidx.app.slice.builders.GridBuilder.CellBuilder>);
   }
 
   public static final class GridBuilder.CellBuilder extends androidx.app.slice.builders.TemplateSliceBuilder {
@@ -31,6 +34,9 @@
     method public androidx.app.slice.builders.ListBuilder addRange(java.util.function.Consumer<androidx.app.slice.builders.ListBuilder.RangeBuilder>);
     method public androidx.app.slice.builders.ListBuilder addRow(androidx.app.slice.builders.ListBuilder.RowBuilder);
     method public androidx.app.slice.builders.ListBuilder addRow(java.util.function.Consumer<androidx.app.slice.builders.ListBuilder.RowBuilder>);
+    method public androidx.app.slice.builders.ListBuilder addSeeMoreAction(android.app.PendingIntent);
+    method public androidx.app.slice.builders.ListBuilder addSeeMoreRow(androidx.app.slice.builders.ListBuilder.RowBuilder);
+    method public androidx.app.slice.builders.ListBuilder addSeeMoreRow(java.util.function.Consumer<androidx.app.slice.builders.ListBuilder.RowBuilder>);
     method public androidx.app.slice.builders.ListBuilder setActions(androidx.app.slice.builders.ListBuilder.ActionBuilder);
     method public androidx.app.slice.builders.ListBuilder setActions(java.util.function.Consumer<androidx.app.slice.builders.ListBuilder.ActionBuilder>);
     method public androidx.app.slice.builders.ListBuilder setHeader(androidx.app.slice.builders.ListBuilder.HeaderBuilder);
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
index 6f967cb..0dff405 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
@@ -36,6 +36,7 @@
 import androidx.app.slice.builders.impl.GridBuilderListV1Impl;
 import androidx.app.slice.builders.impl.TemplateBuilderImpl;
 
+
 /**
  * Builder to construct a row of slice content in a grid format.
  * <p>
@@ -46,6 +47,7 @@
 public class GridBuilder extends TemplateSliceBuilder {
 
     private androidx.app.slice.builders.impl.GridBuilder mImpl;
+    private boolean mHasSeeMore;
 
     /**
      * Create a builder which will construct a slice displayed in a grid format.
@@ -109,6 +111,76 @@
     }
 
     /**
+     * If all content in a slice cannot be shown, the cell added here may be displayed where the
+     * content is cut off.
+     * <p>
+     * This method should only be used if you want to display a custom cell to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom cell, the cell should have
+     * {@link CellBuilder#setContentIntent(PendingIntent)} specified to take the user to an
+     * activity to see all of the content.
+     * </p>
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public GridBuilder addSeeMoreCell(@NonNull CellBuilder builder) {
+        if (mHasSeeMore) {
+            throw new IllegalStateException("Trying to add see more cell when one has "
+                    + "already been added");
+        }
+        mImpl.addSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
+        mHasSeeMore = true;
+        return this;
+    }
+
+    /**
+     * If all content in a slice cannot be shown, the cell added here may be displayed where the
+     * content is cut off.
+     * <p>
+     * This method should only be used if you want to display a custom cell to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom cell, the cell should have
+     * {@link CellBuilder#setContentIntent(PendingIntent)} specified to take the user to an
+     * activity to see all of the content.
+     * </p>
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @RequiresApi(Build.VERSION_CODES.N)
+    @NonNull
+    public GridBuilder addSeeMoreCell(@NonNull Consumer<CellBuilder> c) {
+        CellBuilder b = new CellBuilder(this);
+        c.accept(b);
+        return addSeeMoreCell(b);
+    }
+
+    /**
+     * If all content in a slice cannot be shown, a "see more" affordance may be displayed where
+     * the content is cut off. The action added here should take the user to an activity to see
+     * all of the content, and will be invoked when the "see more" affordance is tapped.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public GridBuilder addSeeMoreAction(@NonNull PendingIntent intent) {
+        if (mHasSeeMore) {
+            throw new IllegalStateException("Trying to add see more action when one has "
+                    + "already been added");
+        }
+        mImpl.addSeeMoreAction(intent);
+        mHasSeeMore = true;
+        return this;
+    }
+
+
+    /**
      * @hide
      */
     @RestrictTo(LIBRARY)
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
index d27a41a..dbd3a98 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
@@ -57,6 +57,7 @@
  */
 public class ListBuilder extends TemplateSliceBuilder {
 
+    private boolean mHasSeeMore;
     private androidx.app.slice.builders.impl.ListBuilder mImpl;
 
     /**
@@ -180,6 +181,74 @@
     }
 
     /**
+     * If all content in a slice cannot be shown, the row added here may be displayed where the
+     * content is cut off. This row should have an affordance to take the user to an activity to
+     * see all of the content.
+     * <p>
+     * This method should only be used if you want to display a custom row to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom row, the row should have a content intent or action end item
+     * specified to take the user to an activity to see all of the content.
+     * </p>
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public ListBuilder addSeeMoreRow(@NonNull RowBuilder builder) {
+        if (mHasSeeMore) {
+            throw new IllegalArgumentException("Trying to add see more row when one has "
+                    + "already been added");
+        }
+        mImpl.addSeeMoreRow((TemplateBuilderImpl) builder.mImpl);
+        mHasSeeMore = true;
+        return this;
+    }
+
+    /**
+     * If all content in a slice cannot be shown, the row added here may be displayed where the
+     * content is cut off. This row should have an affordance to take the user to an activity to
+     * see all of the content.
+     * <p>
+     * This method should only be used if you want to display a custom row to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom row, the row should have a content intent or action end item
+     * specified to take the user to an activity to see all of the content.
+     * </p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    @NonNull
+    public ListBuilder addSeeMoreRow(@NonNull Consumer<RowBuilder> c) {
+        RowBuilder b = new RowBuilder(this);
+        c.accept(b);
+        return addSeeMoreRow(b);
+    }
+
+    /**
+     * If all content in a slice cannot be shown, a "see more" affordance may be displayed where
+     * the content is cut off. The action added here should take the user to an activity to see
+     * all of the content, and will be invoked when the "see more" affordance is tapped.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public ListBuilder addSeeMoreAction(@NonNull PendingIntent intent) {
+        if (mHasSeeMore) {
+            throw new IllegalArgumentException("Trying to add see more action when one has "
+                    + "already been added");
+        }
+        mImpl.addSeeMoreAction(intent);
+        mHasSeeMore = true;
+        return this;
+    }
+
+    /**
      * @hide
      */
     @RestrictTo(LIBRARY)
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
index 91266a4..302e1b3 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
@@ -43,11 +43,33 @@
     TemplateBuilderImpl createGridBuilder(Uri uri);
 
     /**
-     * Add a cell to this builder. Expected to be a builder from {@link #createGridBuilder};
+     * Add a cell to this builder. Expected to be a builder from {@link #createGridBuilder}.
      */
     void addCell(TemplateBuilderImpl impl);
 
     /**
+     * If all content in a slice cannot be shown, the cell added here will be displayed where the
+     * content is cut off. This cell should have an affordance to take the user to an activity to
+     * see all of the content. Expected to be a builder from {@link #createGridBuilder}.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    void addSeeMoreCell(TemplateBuilderImpl impl);
+
+    /**
+     * If all content in a slice cannot be shown, a "see more" affordance will be displayed where
+     * the content is cut off. The action added here should take the user to an activity to see
+     * all of the content, and will be invoked when the "see more" affordance is tapped.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    void addSeeMoreAction(PendingIntent intent);
+
+    /**
      * Builds a standalone slice of this grid builder (i.e. not contained within a List).
      */
     Slice buildIndividual();
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
index 63f7506..67bbfa1 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
@@ -65,6 +65,18 @@
     /**
      */
     @Override
+    public void addSeeMoreCell(TemplateBuilderImpl impl) {
+    }
+
+    /**
+     */
+    @Override
+    public void addSeeMoreAction(PendingIntent intent) {
+    }
+
+    /**
+     */
+    @Override
     public Slice buildIndividual() {
         // Empty slice, nothing useful from a grid to basic.
         return getBuilder().build();
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
index 140bd9b..fe755bd 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
@@ -20,6 +20,7 @@
 import static android.app.slice.Slice.HINT_LARGE;
 import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.Slice.HINT_PARTIAL;
+import static android.app.slice.Slice.HINT_SEE_MORE;
 import static android.support.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.app.PendingIntent;
@@ -72,6 +73,26 @@
         getBuilder().addSubSlice(builder.build());
     }
 
+
+    /**
+     */
+    @Override
+    public void addSeeMoreCell(@NonNull TemplateBuilderImpl builder) {
+        builder.getBuilder().addHints(HINT_SEE_MORE);
+        getBuilder().addSubSlice(builder.build());
+    }
+
+    /**
+     */
+    @Override
+    public void addSeeMoreAction(PendingIntent intent) {
+        getBuilder().addSubSlice(
+                new Slice.Builder(getBuilder())
+                        .addHints(HINT_SEE_MORE)
+                        .addAction(intent, new Slice.Builder(getBuilder()).build(), null)
+                        .build());
+    }
+
     /**
      */
     @Override
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java
index b3fa934..d92083e 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java
@@ -64,6 +64,27 @@
     void addRange(TemplateBuilderImpl builder);
 
     /**
+     * If all content in a slice cannot be shown, the row added here will be displayed where the
+     * content is cut off. This row should have an affordance to take the user to an activity to
+     * see all of the content.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    void addSeeMoreRow(TemplateBuilderImpl builder);
+    /**
+     * If all content in a slice cannot be shown, a "see more" affordance will be displayed where
+     * the content is cut off. The action added here should take the user to an activity to see
+     * all of the content, and will be invoked when the "see more" affordance is tapped.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    void addSeeMoreAction(PendingIntent intent);
+
+    /**
      * Sets the color to tint items displayed by this template (e.g. icons).
      */
     void setColor(int color);
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java
index 689f0fc..8657c90 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java
@@ -80,8 +80,19 @@
     /**
      */
     @Override
-    public void setColor(int color) {
+    public void addSeeMoreRow(TemplateBuilderImpl builder) {
+    }
 
+    /**
+     */
+    @Override
+    public void addSeeMoreAction(PendingIntent intent) {
+    }
+
+    /**
+     */
+    @Override
+    public void setColor(int color) {
     }
 
     /**
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
index 1e8b4e8..4ec4ac0 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
@@ -21,6 +21,7 @@
 import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.Slice.HINT_NO_TINT;
 import static android.app.slice.Slice.HINT_PARTIAL;
+import static android.app.slice.Slice.HINT_SEE_MORE;
 import static android.app.slice.Slice.HINT_SELECTED;
 import static android.app.slice.Slice.HINT_SUMMARY;
 import static android.app.slice.Slice.HINT_TITLE;
@@ -119,6 +120,26 @@
     }
 
     /**
+     */
+    @Override
+    public void addSeeMoreRow(TemplateBuilderImpl builder) {
+        builder.getBuilder().addHints(HINT_SEE_MORE);
+        getBuilder().addSubSlice(builder.build());
+    }
+
+    /**
+     */
+    @Override
+    public void addSeeMoreAction(PendingIntent intent) {
+        getBuilder().addSubSlice(
+                new Slice.Builder(getBuilder())
+                        .addHints(HINT_SEE_MORE)
+                        .addAction(intent, new Slice.Builder(getBuilder()).build(), null)
+                        .build());
+    }
+
+
+    /**
      * Builder to construct an input row.
      */
     public static class RangeBuilderImpl extends TemplateBuilderImpl implements RangeBuilder {
diff --git a/slices/core/src/main/java/androidx/app/slice/Slice.java b/slices/core/src/main/java/androidx/app/slice/Slice.java
index b528dfb..779460e 100644
--- a/slices/core/src/main/java/androidx/app/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/app/slice/Slice.java
@@ -23,6 +23,7 @@
 import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.Slice.HINT_NO_TINT;
 import static android.app.slice.Slice.HINT_PARTIAL;
+import static android.app.slice.Slice.HINT_SEE_MORE;
 import static android.app.slice.Slice.HINT_SELECTED;
 import static android.app.slice.Slice.HINT_SUMMARY;
 import static android.app.slice.Slice.HINT_TITLE;
@@ -33,7 +34,6 @@
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
-
 import static androidx.app.slice.SliceConvert.unwrap;
 
 import android.annotation.TargetApi;
@@ -80,7 +80,7 @@
      */
     @RestrictTo(Scope.LIBRARY)
     @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
-            HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY})
+            HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY, HINT_SEE_MORE})
     public @interface SliceHint{ }
 
     private final SliceItem[] mItems;
diff --git a/v7/appcompat/src/main/java/android/support/v7/view/menu/MenuPopupHelper.java b/v7/appcompat/src/main/java/android/support/v7/view/menu/MenuPopupHelper.java
index 35721b8..a6ee759 100644
--- a/v7/appcompat/src/main/java/android/support/v7/view/menu/MenuPopupHelper.java
+++ b/v7/appcompat/src/main/java/android/support/v7/view/menu/MenuPopupHelper.java
@@ -269,7 +269,7 @@
             final int hgrav = GravityCompat.getAbsoluteGravity(mDropDownGravity,
                     ViewCompat.getLayoutDirection(mAnchorView)) & Gravity.HORIZONTAL_GRAVITY_MASK;
             if (hgrav == Gravity.RIGHT) {
-                xOffset += mAnchorView.getWidth();
+                xOffset -= mAnchorView.getWidth();
             }
 
             popup.setHorizontalOffset(xOffset);
diff --git a/v7/appcompat/src/main/java/android/support/v7/view/menu/StandardMenuPopup.java b/v7/appcompat/src/main/java/android/support/v7/view/menu/StandardMenuPopup.java
index d94ff72..7026959 100644
--- a/v7/appcompat/src/main/java/android/support/v7/view/menu/StandardMenuPopup.java
+++ b/v7/appcompat/src/main/java/android/support/v7/view/menu/StandardMenuPopup.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Parcelable;
+import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
 import android.support.v7.widget.MenuPopupWindow;
 import android.view.Gravity;
@@ -260,7 +261,6 @@
                     mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
             subPopup.setPresenterCallback(mPresenterCallback);
             subPopup.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(subMenu));
-            subPopup.setGravity(mDropDownGravity);
 
             // Pass responsibility for handling onDismiss to the submenu.
             subPopup.setOnDismissListener(mOnDismissListener);
@@ -270,8 +270,17 @@
             mMenu.close(false /* closeAllMenus */);
 
             // Show the new sub-menu popup at the same location as this popup.
-            final int horizontalOffset = mPopup.getHorizontalOffset();
+            int horizontalOffset = mPopup.getHorizontalOffset();
             final int verticalOffset = mPopup.getVerticalOffset();
+
+            // As xOffset of parent menu popup is subtracted with Anchor width for Gravity.RIGHT,
+            // So, again to display sub-menu popup in same xOffset, add the Anchor width.
+            final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
+                    ViewCompat.getLayoutDirection(mAnchorView)) & Gravity.HORIZONTAL_GRAVITY_MASK;
+            if (hgrav == Gravity.RIGHT) {
+                horizontalOffset += mAnchorView.getWidth();
+            }
+
             if (subPopup.tryShow(horizontalOffset, verticalOffset)) {
                 if (mPresenterCallback != null) {
                     mPresenterCallback.onOpenSubMenu(subMenu);
diff --git a/wear/tests/src/android/support/wear/ambient/AmbientModeSupportResumeTest.java b/wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportResumeTest.java
similarity index 100%
rename from wear/tests/src/android/support/wear/ambient/AmbientModeSupportResumeTest.java
rename to wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportResumeTest.java
diff --git a/wear/tests/src/android/support/wear/ambient/AmbientModeSupportResumeTestActivity.java b/wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportResumeTestActivity.java
similarity index 100%
rename from wear/tests/src/android/support/wear/ambient/AmbientModeSupportResumeTestActivity.java
rename to wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportResumeTestActivity.java
diff --git a/wear/tests/src/android/support/wear/ambient/AmbientModeSupportTest.java b/wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportTest.java
similarity index 100%
rename from wear/tests/src/android/support/wear/ambient/AmbientModeSupportTest.java
rename to wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportTest.java
diff --git a/wear/tests/src/android/support/wear/ambient/AmbientModeSupportTestActivity.java b/wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportTestActivity.java
similarity index 100%
rename from wear/tests/src/android/support/wear/ambient/AmbientModeSupportTestActivity.java
rename to wear/src/androidTest/java/android/support/wear/ambient/AmbientModeSupportTestActivity.java