Merge changes from topic "preferences-v14"
* changes:
[automerger] Merge preference-v14 with preference-v7. am: d2965aa084
Merge preference-v14 with preference-v7.
diff --git a/app-toolkit/init.gradle b/app-toolkit/init.gradle
index 393b2f9..654f65c 100644
--- a/app-toolkit/init.gradle
+++ b/app-toolkit/init.gradle
@@ -15,7 +15,6 @@
*/
import android.support.DacOptions
-import org.gradle.internal.os.OperatingSystem
apply from: "${ext.supportRootFolder}/buildSrc/init.gradle"
init.setSdkInLocalPropertiesFile()
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index fe2ba31..4d663af 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -32,7 +32,6 @@
repos.addMavenRepositories(repositories)
-apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'kotlin'
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index c224fdf..30841b6 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -16,7 +16,6 @@
import android.support.DiffAndDocs
-import android.support.SupportConfig
import android.support.gmaven.GMavenVersionChecker
import com.android.build.gradle.internal.coverage.JacocoReportTask
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
@@ -31,7 +30,7 @@
}
def init = new Properties()
ext.init = init
-rootProject.ext.versionChecker = new GMavenVersionChecker(rootProject)
+rootProject.ext.versionChecker = new GMavenVersionChecker(rootProject.logger)
ext.runningInBuildServer = System.env.DIST_DIR != null && System.env.OUT_DIR != null
apply from: "${supportRoot}/buildSrc/dependencies.gradle"
diff --git a/buildSrc/src/main/groovy/android/support/gmaven/GMavenVersionChecker.groovy b/buildSrc/src/main/groovy/android/support/gmaven/GMavenVersionChecker.groovy
deleted file mode 100644
index 0a71fbc..0000000
--- a/buildSrc/src/main/groovy/android/support/gmaven/GMavenVersionChecker.groovy
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 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.gmaven
-
-import android.support.Version
-import com.android.annotations.Nullable
-import groovy.util.slurpersupport.NodeChild
-import org.gradle.api.GradleException
-import org.gradle.api.Project
-import org.gradle.api.logging.Logger
-
-/**
- * Queries maven.google.com to get the version numbers for each artifact.
- * Due to the structure of maven.google.com, a new query is necessary for each group.
- */
-@SuppressWarnings("GroovyUnusedDeclaration")
-class GMavenVersionChecker {
- // wait 2 seconds before retrying if fetch fails
- private static final int RETRY_DELAY = 2000 // ms
- // number of times we'll try to reach maven.google.com before failing
- private static final int DEFAULT_RETRY_LIMIT = 20
- private static final String BASE = "https://dl.google.com/dl/android/maven2/"
- private static final String GROUP_FILE = "group-index.xml"
-
- // cache versions by group to avoid re-querying for each artifact
- private final Map<String, GroupVersionData> versionCache = new HashMap<>()
- // the logger from the project
- private final Logger logger
-
- /**
- * Creates a new instance using the given project's logger
- *
- * @param project This should be the root project. No reason to create multiple instances of
- * this
- */
- GMavenVersionChecker(Project project) {
- this.logger = project.logger
- }
-
- /**
- * Creates the URL which has the XML file that describes the available versions for each
- * artifact in that group
- *
- * @param group Maven group name
- * @return The URL of the XML file
- */
- private static String buildGroupUrl(String group) {
- return BASE + group.replace(".", "/") + "/" + GROUP_FILE
- }
-
- /**
- * Returns the version data for each artifact in a given group.
- * <p>
- * If data is not cached, this will make a web request to get it.
- *
- * @param group The group to query
- * @return A data class which has the versions for each artifact
- */
- private GroupVersionData getVersionData(String group) {
- def versionData = versionCache.get(group)
- if (versionData == null) {
- versionData = fetchGroup(group)
- if (versionData != null) {
- versionCache.put(versionData.name, versionData)
- }
- }
- return versionData
- }
-
- /**
- * Fetches the group version information from maven.google.com
- *
- * @param group The group name to fetch
- * @param retryCount Number of times we'll retry before failing
- * @return GroupVersionData that has the data or null if it is a new item.
- */
- @Nullable
- private GroupVersionData fetchGroup(String group, int retryCount) {
- def url = buildGroupUrl(group)
- for (int run = 0; run < retryCount; run++) {
- logger.info "fetching maven XML from $url"
- try {
- def parsedXml = new XmlSlurper(false, false).parse(url)
- return new GroupVersionData(parsedXml)
- } catch (FileNotFoundException ignored) {
- logger.info "could not find version data for $group, seems like a new file"
- return null
- } catch (IOException ioException) {
- logger.warning "failed to fetch the maven info, retrying in 2 seconds. " +
- "Run $run of $retryCount"
- Thread.sleep(RETRY_DELAY)
- }
- }
- throw new GradleException("Could not access maven.google.com")
- }
-
- /**
- * Fetches the group version information from maven.google.com
- *
- * @param group The group name to fetch
- * @return GroupVersionData that has the data or null if it is a new item.
- */
- @Nullable
- private GroupVersionData fetchGroup(String group) {
- return fetchGroup(group, DEFAULT_RETRY_LIMIT)
- }
-
- /**
- * Return the available versions on maven.google.com for a given artifact
- *
- * @param group The group id of the artifact
- * @param artifactName The name of the artifact
- * @return The set of versions that are available on maven.google.com. Null if artifact is not
- * available.
- */
- @Nullable
- Set<Version> getVersions(String group, String artifactName) {
- def groupData = getVersionData(group)
- return groupData?.artifacts?.get(artifactName)?.versions
- }
-
- /**
- * Checks whether the given artifact is already on maven.google.com.
- *
- * @param group The project group on maven
- * @param artifactName The artifact name on maven
- * @param version The version on maven
- * @return true if the artifact is already on maven.google.com
- */
- boolean isReleased(String group, String artifactName, String version) {
- return getVersions(group, artifactName)?.contains(new Version(version))
- }
-
- /**
- * Data class that holds the artifacts of a single maven group
- */
- private static class GroupVersionData {
- /**
- * The group name
- */
- String name
- /**
- * Map of artifact versions keyed by artifact name
- */
- Map<String, ArtifactVersionData> artifacts = new HashMap<>()
-
- /**
- * Constructs an instance from the given node.
- *
- * @param xml The information node fetched from {@code GROUP_FILE}
- */
- GroupVersionData(NodeChild xml) {
- /**
- * sample input:
- * <android.arch.core>
- * <runtime versions="1.0.0-alpha4,1.0.0-alpha5,1.0.0-alpha6,1.0.0-alpha7"/>
- * <common versions="1.0.0-alpha4,1.0.0-alpha5,1.0.0-alpha6,1.0.0-alpha7"/>
- * </android.arch.core>
- */
- this.name = xml.name()
- xml.childNodes().each {
- def versions = it.attributes['versions'].split(",").collect { version ->
- new Version(version)
- }.toSet()
- artifacts[it.name()] = new ArtifactVersionData(it.name(), versions)
- }
- }
- }
-
- /**
- * Data class that holds the version information about a single artifact
- */
- private static class ArtifactVersionData {
- /**
- * name of the artifact
- */
- final String name
- /**
- * set of version codes that are already on maven.google.com
- */
- final Set<Version> versions
-
- ArtifactVersionData(String name, Set<Version> versions) {
- this.name = name
- this.versions = versions
- }
- }
-}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
index e370801..427f765 100644
--- a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
@@ -19,6 +19,7 @@
const val AUTO_COMMON = "com.google.auto:auto-common:0.6"
const val ANTLR = "org.antlr:antlr4:4.5.3"
const val APACHE_COMMONS_CODEC = "commons-codec:commons-codec:1.10"
+const val CONSTRAINT_LAYOUT = "com.android.support.constraint:constraint-layout:1.0.2"
const val DEXMAKER_MOCKITO = "com.linkedin.dexmaker:dexmaker-mockito:2.2.0"
const val ESPRESSO_CONTRIB = "com.android.support.test.espresso:espresso-contrib:3.0.1"
const val ESPRESSO_CORE = "com.android.support.test.espresso:espresso-core:3.0.1"
diff --git a/buildSrc/src/main/kotlin/android/support/gmaven/GMavenVersionChecker.kt b/buildSrc/src/main/kotlin/android/support/gmaven/GMavenVersionChecker.kt
new file mode 100644
index 0000000..7fc4836
--- /dev/null
+++ b/buildSrc/src/main/kotlin/android/support/gmaven/GMavenVersionChecker.kt
@@ -0,0 +1,180 @@
+/*
+ * Copyright 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.gmaven
+
+import android.support.Version
+import groovy.util.XmlSlurper
+import groovy.util.slurpersupport.Node
+import groovy.util.slurpersupport.NodeChild
+import org.gradle.api.GradleException
+import org.gradle.api.logging.Logger
+import java.io.FileNotFoundException
+import java.io.IOException
+
+/**
+ * Queries maven.google.com to get the version numbers for each artifact.
+ * Due to the structure of maven.google.com, a new query is necessary for each group.
+ *
+ * @param logger Logger of the root project. No reason to create multiple instances of this.
+ */
+class GMavenVersionChecker(private val logger: Logger) {
+ private val versionCache: MutableMap<String, GroupVersionData> = HashMap()
+
+ /**
+ * Checks whether the given artifact is already on maven.google.com.
+ *
+ * @param group The project group on maven
+ * @param artifactName The artifact name on maven
+ * @param version The version on maven
+ * @return true if the artifact is already on maven.google.com
+ */
+ fun isReleased(group: String, artifactName: String, version: String): Boolean {
+ return getVersions(group, artifactName)?.contains(Version(version)) ?: false
+ }
+
+ /**
+ * Return the available versions on maven.google.com for a given artifact
+ *
+ * @param group The group id of the artifact
+ * @param artifactName The name of the artifact
+ * @return The set of versions that are available on maven.google.com. Null if artifact is not
+ * available.
+ */
+ private fun getVersions(group: String, artifactName: String): Set<Version>? {
+ val groupData = getVersionData(group)
+ return groupData?.artifacts?.get(artifactName)?.versions
+ }
+
+ /**
+ * Returns the version data for each artifact in a given group.
+ * <p>
+ * If data is not cached, this will make a web request to get it.
+ *
+ * @param group The group to query
+ * @return A data class which has the versions for each artifact
+ */
+ private fun getVersionData(group: String): GroupVersionData? {
+ return versionCache.getOrMaybePut(group) {
+ fetchGroup(group, DEFAULT_RETRY_LIMIT)
+ }
+ }
+
+ /**
+ * Fetches the group version information from maven.google.com
+ *
+ * @param group The group name to fetch
+ * @param retryCount Number of times we'll retry before failing
+ * @return GroupVersionData that has the data or null if it is a new item.
+ */
+ private fun fetchGroup(group: String, retryCount: Int): GroupVersionData? {
+ val url = buildGroupUrl(group)
+ for (run in 0..retryCount) {
+ logger.info("fetching maven XML from $url")
+ try {
+ val parsedXml = XmlSlurper(false, false).parse(url) as NodeChild
+ return GroupVersionData.from(parsedXml)
+ } catch (ignored: FileNotFoundException) {
+ logger.info("could not find version data for $group, seems like a new file")
+ return null
+ } catch (ioException: IOException) {
+ logger.warn("failed to fetch the maven info, retrying in 2 seconds. " +
+ "Run $run of $retryCount")
+ Thread.sleep(RETRY_DELAY)
+ }
+ }
+ throw GradleException("Could not access maven.google.com")
+ }
+
+ companion object {
+ /**
+ * Creates the URL which has the XML file that describes the available versions for each
+ * artifact in that group
+ *
+ * @param group Maven group name
+ * @return The URL of the XML file
+ */
+ private fun buildGroupUrl(group: String) =
+ "$BASE${group.replace(".","/")}/$GROUP_FILE"
+ }
+}
+
+private fun <K, V> MutableMap<K, V>.getOrMaybePut(key: K, defaultValue: () -> V?): V? {
+ val value = get(key)
+ return if (value == null) {
+ val answer = defaultValue()
+ if (answer != null) put(key, answer)
+ answer
+ } else {
+ value
+ }
+}
+
+/**
+ * Data class that holds the artifacts of a single maven group.
+ *
+ * @param name Maven group name
+ * @param artifacts Map of artifact versions keyed by artifact name
+ */
+private data class GroupVersionData(
+ val name: String,
+ val artifacts: Map<String, ArtifactVersionData>
+) {
+ companion object {
+ /**
+ * Constructs an instance from the given node.
+ *
+ * @param xml The information node fetched from {@code GROUP_FILE}
+ */
+ fun from(xml: NodeChild): GroupVersionData {
+ /*
+ * sample input:
+ * <android.arch.core>
+ * <runtime versions="1.0.0-alpha4,1.0.0-alpha5,1.0.0-alpha6,1.0.0-alpha7"/>
+ * <common versions="1.0.0-alpha4,1.0.0-alpha5,1.0.0-alpha6,1.0.0-alpha7"/>
+ * </android.arch.core>
+ */
+ val name = xml.name()
+ val artifacts: MutableMap<String, ArtifactVersionData> = HashMap()
+
+ xml.childNodes().forEach {
+ val node = it as Node
+ val versions = (node.attributes()["versions"] as String).split(",").map {
+ Version(it)
+ }.toSet()
+ artifacts.put(it.name(), ArtifactVersionData(it.name(), versions))
+ }
+ return GroupVersionData(name, artifacts)
+ }
+ }
+}
+
+/**
+ * Data class that holds the version information about a single artifact
+ *
+ * @param name Name of the maven artifact
+ * @param versions set of version codes that are already on maven.google.com
+ */
+private data class ArtifactVersionData(val name: String, val versions: Set<Version>)
+
+// wait 2 seconds before retrying if fetch fails
+private const val RETRY_DELAY: Long = 2000 // ms
+
+// number of times we'll try to reach maven.google.com before failing
+private const val DEFAULT_RETRY_LIMIT = 20
+
+private const val BASE = "https://dl.google.com/dl/android/maven2/"
+private const val GROUP_FILE = "group-index.xml"
\ No newline at end of file
diff --git a/car/res/layout/car_paged_list_item_content.xml b/car/res/layout/car_paged_list_item_content.xml
index e2e84a0..943c489 100644
--- a/car/res/layout/car_paged_list_item_content.xml
+++ b/car/res/layout/car_paged_list_item_content.xml
@@ -54,10 +54,7 @@
<!-- End icon with divider. -->
<View
android:id="@+id/supplemental_icon_divider"
- android:layout_width="@dimen/car_vertical_line_divider_width"
- android:layout_height="@dimen/car_vertical_line_divider_height"
- android:layout_marginStart="@dimen/car_padding_4"
- android:background="@color/car_list_divider"/>
+ style="@style/CarListVerticalDivider"/>
<ImageView
android:id="@+id/supplemental_icon"
android:layout_width="@dimen/car_primary_icon_size"
@@ -65,13 +62,22 @@
android:layout_marginStart="@dimen/car_padding_4"
android:scaleType="fitCenter"/>
+ <!-- Switch with divider. -->
+ <View
+ android:id="@+id/switch_divider"
+ style="@style/CarListVerticalDivider"/>
+ <Switch
+ android:id="@+id/switch_widget"
+ android:layout_width="@dimen/car_primary_icon_size"
+ android:layout_height="@dimen/car_primary_icon_size"
+ android:layout_marginStart="@dimen/car_padding_4"
+ style="@android:style/Widget.Material.CompoundButton.Switch"
+ />
+
<!-- Up to 2 action buttons with dividers. -->
<View
android:id="@+id/action2_divider"
- android:layout_width="@dimen/car_vertical_line_divider_width"
- android:layout_height="@dimen/car_vertical_line_divider_height"
- android:layout_marginStart="@dimen/car_padding_4"
- android:background="@color/car_list_divider"/>
+ style="@style/CarListVerticalDivider"/>
<Button
android:id="@+id/action2"
android:layout_width="wrap_content"
@@ -85,10 +91,7 @@
style="?android:attr/borderlessButtonStyle"/>
<View
android:id="@+id/action1_divider"
- android:layout_width="@dimen/car_vertical_line_divider_width"
- android:layout_height="@dimen/car_vertical_line_divider_height"
- android:layout_marginStart="@dimen/car_padding_4"
- android:background="@color/car_list_divider"/>
+ style="@style/CarListVerticalDivider"/>
<Button
android:id="@+id/action1"
android:layout_width="wrap_content"
diff --git a/car/res/values/attrs.xml b/car/res/values/attrs.xml
index 317c944..0b9f0ec 100644
--- a/car/res/values/attrs.xml
+++ b/car/res/values/attrs.xml
@@ -44,6 +44,8 @@
</attr>
<!-- Whether to display the scrollbar or not. Defaults to true. -->
<attr name="scrollBarEnabled" format="boolean" />
+ <!-- The top margin before the scroll bar is drawn. -->
+ <attr name="scrollBarTopMargin" format="dimension" />
<!-- Whether or not to show a diving line between each item of the list. -->
<attr name="showPagedListViewDivider" format="boolean" />
<!-- An optional id that specifies a child View whose starting edge will be used to
diff --git a/car/res/values/styles.xml b/car/res/values/styles.xml
index 7ef5823..78d9603 100644
--- a/car/res/values/styles.xml
+++ b/car/res/values/styles.xml
@@ -158,4 +158,12 @@
<!-- Styles for TextInputLayout hints -->
<style name="CarHintTextAppearance" parent="CarBody2">
</style>
+
+ <style name="CarListVerticalDivider">
+ <item name="android:layout_width">@dimen/car_vertical_line_divider_width</item>
+ <item name="android:layout_height">@dimen/car_vertical_line_divider_height</item>
+ <item name="android:layout_marginStart">@dimen/car_padding_4</item>
+ <item name="android:background">@color/car_list_divider</item>
+ </style>
+
</resources>
diff --git a/car/src/main/java/androidx/car/widget/ListItem.java b/car/src/main/java/androidx/car/widget/ListItem.java
index 2e3f065..d345833 100644
--- a/car/src/main/java/androidx/car/widget/ListItem.java
+++ b/car/src/main/java/androidx/car/widget/ListItem.java
@@ -26,6 +26,7 @@
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
+import android.widget.CompoundButton;
import android.widget.RelativeLayout;
import java.lang.annotation.Retention;
@@ -58,6 +59,7 @@
* <li>Supplemental Icon
* <li>One Action Button
* <li>Two Action Buttons
+ * <li>Switch</li>
* </ul>
* </ul>
*
@@ -87,6 +89,7 @@
vh.getPrimaryIcon(),
vh.getTitle(), vh.getBody(),
vh.getSupplementalIcon(), vh.getSupplementalIconDivider(),
+ vh.getSwitch(), vh.getSwitchDivider(),
vh.getAction1(), vh.getAction1Divider(), vh.getAction2(), vh.getAction2Divider()};
for (View v : subviews) {
v.setVisibility(View.GONE);
@@ -141,13 +144,15 @@
@Retention(SOURCE)
@IntDef({SUPPLEMENTAL_ACTION_NO_ACTION, SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON,
- SUPPLEMENTAL_ACTION_ONE_ACTION, SUPPLEMENTAL_ACTION_TWO_ACTIONS})
+ SUPPLEMENTAL_ACTION_ONE_ACTION, SUPPLEMENTAL_ACTION_TWO_ACTIONS,
+ SUPPLEMENTAL_ACTION_SWITCH})
private @interface SupplementalActionType {}
private static final int SUPPLEMENTAL_ACTION_NO_ACTION = 0;
private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON = 1;
private static final int SUPPLEMENTAL_ACTION_ONE_ACTION = 2;
private static final int SUPPLEMENTAL_ACTION_TWO_ACTIONS = 3;
+ private static final int SUPPLEMENTAL_ACTION_SWITCH = 4;
private final Context mContext;
private final List<ViewBinder> mBinders = new ArrayList<>();
@@ -171,6 +176,10 @@
private View.OnClickListener mSupplementalIconOnClickListener;
private boolean mShowSupplementalIconDivider;
+ private boolean mSwitchChecked;
+ private boolean mShowSwitchDivider;
+ private CompoundButton.OnCheckedChangeListener mSwitchOnCheckedChangeListener;
+
private String mAction1Text;
private View.OnClickListener mAction1OnClickListener;
private boolean mShowAction1Divider;
@@ -500,6 +509,16 @@
case SUPPLEMENTAL_ACTION_NO_ACTION:
// Do nothing
break;
+ case SUPPLEMENTAL_ACTION_SWITCH:
+ mBinders.add(vh -> {
+ vh.getSwitch().setVisibility(View.VISIBLE);
+ vh.getSwitch().setChecked(mSwitchChecked);
+ vh.getSwitch().setOnCheckedChangeListener(mSwitchOnCheckedChangeListener);
+ if (mShowSwitchDivider) {
+ vh.getSwitchDivider().setVisibility(View.VISIBLE);
+ }
+ });
+ break;
default:
throw new IllegalArgumentException("Unrecognized supplemental action type.");
}
@@ -681,6 +700,7 @@
*
* @param action1Text button text to display - this button will be closer to item end.
* @param action2Text button text to display.
+ * @return This Builder object to allow for chaining calls to set methods.
*/
public Builder withActions(String action1Text, boolean showAction1Divider,
View.OnClickListener action1OnClickListener,
@@ -704,6 +724,24 @@
}
/**
+ * Sets {@code Supplemental Action} to be represented by a {@link android.widget.Switch}.
+ *
+ * @param checked initial value for switched.
+ * @param showDivider whether to display a vertical bar between switch and text.
+ * @param listener callback to be invoked when the checked state is changed.
+ * @return This Builder object to allow for chaining calls to set methods.
+ */
+ public Builder withSwitch(boolean checked, boolean showDivider,
+ CompoundButton.OnCheckedChangeListener listener) {
+ mSupplementalActionType = SUPPLEMENTAL_ACTION_SWITCH;
+
+ mSwitchChecked = checked;
+ mShowSwitchDivider = showDivider;
+ mSwitchOnCheckedChangeListener = listener;
+ return this;
+ }
+
+ /**
* Adds {@link ViewBinder} to interact with sub-views in
* {@link ListItemAdapter.ViewHolder}. These ViewBinders will always bind after
* other {@link Builder} methods have bond.
diff --git a/car/src/main/java/androidx/car/widget/ListItemAdapter.java b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
index d8a0fe7..a338a51 100644
--- a/car/src/main/java/androidx/car/widget/ListItemAdapter.java
+++ b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
@@ -26,6 +26,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
+import android.widget.Switch;
import android.widget.TextView;
import androidx.car.R;
@@ -181,6 +182,9 @@
private Button mAction2;
private View mAction2Divider;
+ private Switch mSwitch;
+ private View mSwitchDivider;
+
public ViewHolder(View itemView) {
super(itemView);
@@ -194,6 +198,9 @@
mSupplementalIcon = itemView.findViewById(R.id.supplemental_icon);
mSupplementalIconDivider = itemView.findViewById(R.id.supplemental_icon_divider);
+ mSwitch = itemView.findViewById(R.id.switch_widget);
+ mSwitchDivider = itemView.findViewById(R.id.switch_divider);
+
mAction1 = itemView.findViewById(R.id.action1);
mAction1Divider = itemView.findViewById(R.id.action1_divider);
mAction2 = itemView.findViewById(R.id.action2);
@@ -224,6 +231,14 @@
return mSupplementalIconDivider;
}
+ public View getSwitchDivider() {
+ return mSwitchDivider;
+ }
+
+ public Switch getSwitch() {
+ return mSwitch;
+ }
+
public Button getAction1() {
return mAction1;
}
diff --git a/car/src/main/java/androidx/car/widget/PagedListView.java b/car/src/main/java/androidx/car/widget/PagedListView.java
index 63924d8..a6811b1 100644
--- a/car/src/main/java/androidx/car/widget/PagedListView.java
+++ b/car/src/main/java/androidx/car/widget/PagedListView.java
@@ -324,11 +324,13 @@
mScrollBarView.setVisibility(mScrollBarEnabled ? VISIBLE : GONE);
- // Modify the layout the Scroll Bar is not visible.
- if (!mScrollBarEnabled) {
+ if (mScrollBarEnabled) {
+ int topMargin =
+ a.getDimensionPixelSize(R.styleable.PagedListView_scrollBarTopMargin, 0);
+ setScrollBarTopMargin(topMargin);
+ } else {
MarginLayoutParams params = (MarginLayoutParams) mRecyclerView.getLayoutParams();
params.setMarginStart(0);
- mRecyclerView.setLayoutParams(params);
}
setDayNightStyle(DayNightStyle.AUTO);
@@ -380,6 +382,17 @@
mRecyclerView.setClipToPadding(startPadding == 0 && endPadding == 0);
}
+ /**
+ * Sets the top margin above the scroll bar. By default, this margin is 0.
+ *
+ * @param topMargin The top margin.
+ */
+ public void setScrollBarTopMargin(int topMargin) {
+ MarginLayoutParams params = (MarginLayoutParams) mScrollBarView.getLayoutParams();
+ params.topMargin = topMargin;
+ requestLayout();
+ }
+
@NonNull
public RecyclerView getRecyclerView() {
return mRecyclerView;
diff --git a/car/tests/src/androidx/car/widget/ListItemTest.java b/car/tests/src/androidx/car/widget/ListItemTest.java
index 4620580..fa0771b 100644
--- a/car/tests/src/androidx/car/widget/ListItemTest.java
+++ b/car/tests/src/androidx/car/widget/ListItemTest.java
@@ -181,6 +181,28 @@
}
@Test
+ public void testSwitchVisibleAndCheckedState() {
+ List<ListItem> items = Arrays.asList(
+ new ListItem.Builder(mActivity)
+ .withSwitch(true, true, null)
+ .build(),
+ new ListItem.Builder(mActivity)
+ .withSwitch(false, true, null)
+ .build());
+ setupPagedListView(items);
+
+ ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+ assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE)));
+ assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(true)));
+ assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.VISIBLE)));
+
+ viewHolder = getViewHolderAtPosition(1);
+ assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE)));
+ assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(false)));
+ assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.VISIBLE)));
+ }
+
+ @Test
public void testDividersAreOptional() {
List<ListItem> items = Arrays.asList(
new ListItem.Builder(mActivity)
@@ -192,6 +214,9 @@
new ListItem.Builder(mActivity)
.withActions("text", false, v -> { /* Do nothing. */ },
"text", false, v -> { /* Do nothing. */ })
+ .build(),
+ new ListItem.Builder(mActivity)
+ .withSwitch(true, false, null)
.build());
setupPagedListView(items);
@@ -209,6 +234,10 @@
assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.GONE)));
assertThat(viewHolder.getAction2().getVisibility(), is(equalTo(View.VISIBLE)));
assertThat(viewHolder.getAction2Divider().getVisibility(), is(equalTo(View.GONE)));
+
+ viewHolder = getViewHolderAtPosition(3);
+ assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE)));
+ assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.GONE)));
}
@Test
@@ -475,6 +504,35 @@
}
@Test
+ public void testCheckingSwitch() {
+ final boolean[] clicked = {false, false};
+ List<ListItem> items = Arrays.asList(
+ new ListItem.Builder(mActivity)
+ .withSwitch(false, false, (button, isChecked) -> {
+ // Initial value is false.
+ assertTrue(isChecked);
+ clicked[0] = true;
+ })
+ .build(),
+ new ListItem.Builder(mActivity)
+ .withSwitch(true, false, (button, isChecked) -> {
+ // Initial value is true.
+ assertFalse(isChecked);
+ clicked[1] = true;
+ })
+ .build());
+ setupPagedListView(items);
+
+ onView(withId(R.id.recycler_view)).perform(
+ actionOnItemAtPosition(0, clickChildViewWithId(R.id.switch_widget)));
+ assertTrue(clicked[0]);
+
+ onView(withId(R.id.recycler_view)).perform(
+ actionOnItemAtPosition(1, clickChildViewWithId(R.id.switch_widget)));
+ assertTrue(clicked[1]);
+ }
+
+ @Test
public void testClickingSupplementalAction() {
final boolean[] clicked = {false};
List<ListItem> items = Arrays.asList(
diff --git a/car/tests/src/androidx/car/widget/PagedListViewTest.java b/car/tests/src/androidx/car/widget/PagedListViewTest.java
index 67d97e3..67aa638 100644
--- a/car/tests/src/androidx/car/widget/PagedListViewTest.java
+++ b/car/tests/src/androidx/car/widget/PagedListViewTest.java
@@ -38,6 +38,7 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.espresso.Espresso;
@@ -54,6 +55,9 @@
import android.widget.ImageView;
import android.widget.TextView;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -350,6 +354,32 @@
}
}
+ @Test
+ public void testDefaultScrollBarTopMargin() {
+ if (!isAutoDevice()) {
+ return;
+ }
+
+ // Just need enough items to ensure the scroll bar is showing.
+ setUpPagedListView(ITEMS_PER_PAGE * 10);
+ onView(withId(R.id.paged_scroll_view)).check(matches(withTopMargin(0)));
+ }
+
+ @Test
+ public void testSetScrollbarTopMargin() {
+ if (!isAutoDevice()) {
+ return;
+ }
+
+ // Just need enough items to ensure the scroll bar is showing.
+ setUpPagedListView(ITEMS_PER_PAGE * 10);
+
+ int topMargin = 100;
+ mPagedListView.setScrollBarTopMargin(topMargin);
+
+ onView(withId(R.id.paged_scroll_view)).check(matches(withTopMargin(topMargin)));
+ }
+
private static String itemText(int index) {
return "Data " + index;
}
@@ -460,4 +490,26 @@
mResourceCallback = callback;
}
}
+
+ /**
+ * Returns a matcher that matches {@link View}s that have the given top margin.
+ *
+ * @param topMargin The top margin value to match to.
+ */
+ @NonNull
+ public static Matcher<View> withTopMargin(int topMargin) {
+ return new TypeSafeMatcher<View>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("with top margin: " + topMargin);
+ }
+
+ @Override
+ public boolean matchesSafely(View view) {
+ ViewGroup.MarginLayoutParams params =
+ (ViewGroup.MarginLayoutParams) view.getLayoutParams();
+ return topMargin == params.topMargin;
+ }
+ };
+ }
}
diff --git a/compat/res/values/dimens.xml b/compat/res/values/dimens.xml
index 1dcff5e..41abbe6 100644
--- a/compat/res/values/dimens.xml
+++ b/compat/res/values/dimens.xml
@@ -70,4 +70,10 @@
<!-- the paddingtop on the right side of the notification (for time etc.) -->
<dimen name="notification_right_side_padding_top">2dp</dimen>
+
+ <!-- the maximum width of the large icon, above which it will be downscaled -->
+ <dimen name="notification_icon_max_width">320dp</dimen>
+
+ <!-- the maximum height of the large icon, above which it will be downscaled -->
+ <dimen name="notification_icon_max_height">320dp</dimen>
</resources>
diff --git a/compat/src/main/java/android/support/v4/app/NotificationCompat.java b/compat/src/main/java/android/support/v4/app/NotificationCompat.java
index ef41c67..d701478 100644
--- a/compat/src/main/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/src/main/java/android/support/v4/app/NotificationCompat.java
@@ -957,11 +957,37 @@
* Set the large icon that is shown in the ticker and notification.
*/
public Builder setLargeIcon(Bitmap icon) {
- mLargeIcon = icon;
+ mLargeIcon = reduceLargeIconSize(icon);
return this;
}
/**
+ * Reduce the size of a notification icon if it's overly large. The framework does
+ * this automatically starting from API 27.
+ */
+ private Bitmap reduceLargeIconSize(Bitmap icon) {
+ if (icon == null || Build.VERSION.SDK_INT >= 27) {
+ return icon;
+ }
+
+ Resources res = mContext.getResources();
+ int maxWidth = res.getDimensionPixelSize(R.dimen.notification_icon_max_width);
+ int maxHeight = res.getDimensionPixelSize(R.dimen.notification_icon_max_height);
+ if (icon.getWidth() <= maxWidth && icon.getHeight() <= maxHeight) {
+ return icon;
+ }
+
+ double scale = Math.min(
+ maxWidth / (double) Math.max(1, icon.getWidth()),
+ maxHeight / (double) Math.max(1, icon.getHeight()));
+ return Bitmap.createScaledBitmap(
+ icon,
+ (int) Math.ceil(icon.getWidth() * scale),
+ (int) Math.ceil(icon.getHeight() * scale),
+ /* filtered */ true);
+ }
+
+ /**
* Set the sound to play. It will play on the default stream.
*
* <p>
@@ -2115,8 +2141,16 @@
/**
* Sets the title to be displayed on this conversation. May be set to {@code null}.
- * @param conversationTitle Title displayed for this conversation.
- * @return this object for method chaining.
+ *
+ * <p>This API's behavior was changed in SDK version {@link Build.VERSION_CODES#P}. If your
+ * application's target version is less than {@link Build.VERSION_CODES#P}, setting a
+ * conversation title to a non-null value will make {@link #isGroupConversation()} return
+ * {@code true} and passing {@code null} will make it return {@code false}. In
+ * {@link Build.VERSION_CODES#P} and beyond, use {@link #setGroupConversation(boolean)}
+ * to set group conversation status.
+ *
+ * @param conversationTitle Title displayed for this conversation
+ * @return this object for method chaining
*/
public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
mConversationTitle = conversationTitle;
@@ -2186,9 +2220,27 @@
}
/**
- * Returns {@code true} if this notification represents a group conversation.
+ * Returns {@code true} if this notification represents a group conversation, otherwise
+ * {@code false}.
+ *
+ * <p> If the application that generated this {@link MessagingStyle} targets an SDK version
+ * less than {@link Build.VERSION_CODES#P}, this method becomes dependent on whether or
+ * not the conversation title is set; returning {@code true} if the conversation title is
+ * a non-null value, or {@code false} otherwise. From {@link Build.VERSION_CODES#P} forward,
+ * this method returns what's set by {@link #setGroupConversation(boolean)} allowing for
+ * named, non-group conversations.
+ *
+ * @see #setConversationTitle(CharSequence)
*/
public boolean isGroupConversation() {
+ // When target SDK version is < P, a non-null conversation title dictates if this is
+ // as group conversation.
+ if (mBuilder != null
+ && mBuilder.mContext.getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.P) {
+ return mConversationTitle != null;
+ }
+
return mIsGroupConversation;
}
diff --git a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
index 0509af9..28a9813 100644
--- a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
+++ b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
@@ -489,11 +489,13 @@
}
@Test
- public void testMessagingStyle_isGroupConversation() {
+ public void messagingStyle_isGroupConversation() {
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
- .setGroupConversation(true);
- Notification notification = new NotificationCompat.Builder(mContext, "test id")
+ .setGroupConversation(true)
+ .setConversationTitle("test conversation title");
+ new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
@@ -503,6 +505,56 @@
}
@Test
+ public void messagingStyle_isGroupConversation_noConversationTitle() {
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+ NotificationCompat.MessagingStyle messagingStyle =
+ new NotificationCompat.MessagingStyle("self name")
+ .setGroupConversation(true)
+ .setConversationTitle(null);
+ new NotificationCompat.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ assertTrue(messagingStyle.isGroupConversation());
+ }
+
+ @Test
+ public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
+ // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+ NotificationCompat.MessagingStyle messagingStyle =
+ new NotificationCompat.MessagingStyle("self name")
+ .setGroupConversation(false)
+ .setConversationTitle("test conversation title");
+ new NotificationCompat.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ assertTrue(messagingStyle.isGroupConversation());
+ }
+
+ @Test
+ public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+ // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+ NotificationCompat.MessagingStyle messagingStyle =
+ new NotificationCompat.MessagingStyle("self name")
+ .setGroupConversation(true)
+ .setConversationTitle(null);
+ new NotificationCompat.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ assertFalse(messagingStyle.isGroupConversation());
+ }
+
+ @Test
public void testMessagingStyle_extras() {
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("test name")
diff --git a/fragment/src/main/java/android/support/v4/app/FragmentActivity.java b/fragment/src/main/java/android/support/v4/app/FragmentActivity.java
index 78161a8..e3f5684 100644
--- a/fragment/src/main/java/android/support/v4/app/FragmentActivity.java
+++ b/fragment/src/main/java/android/support/v4/app/FragmentActivity.java
@@ -273,6 +273,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mFragments.noteStateNotSaved();
mFragments.dispatchConfigurationChanged(newConfig);
}
diff --git a/jetifier/jetifier/build.gradle b/jetifier/jetifier/build.gradle
index c817220..fbdb617 100644
--- a/jetifier/jetifier/build.gradle
+++ b/jetifier/jetifier/build.gradle
@@ -27,8 +27,11 @@
}
}
+// upload anchor for subprojects to upload their artifacts to the local repo.
+task(mainUpload)
+
subprojects {
- group 'android.support.tools.jetifier'
+ group 'androidx.tools.jetifier'
ext.supportRootFolder = "${project.projectDir}/../../.."
@@ -62,13 +65,20 @@
buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build')
.getCanonicalFile()
project.ext.distDir = new File(System.env.DIST_DIR).getCanonicalFile()
+ // the build server does not pass the build number so we infer it from the last folder of
+ // the dist path.
+ ext.buildNumber = project.ext.distDir.getName()
// the build server should always print out full stack traces for any failures.
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
} else {
buildDir = file("${ext.supportRootFolder}/../../out/host/gradle/frameworks/support/jetifier/build")
project.ext.distDir = new File("${ext.supportRootFolder}/../../out/dist")
+ ext.buildNumber = 0
}
+
+ ext.repoDir = "file://$project.buildDir/repo"
+
subprojects {
// Change buildDir so that all plugins pick up the new value.
project.buildDir = new File("$project.parent.buildDir/../$project.name/build")
@@ -90,7 +100,28 @@
}
}
}
+ buildOnServerTask.dependsOn "createArchive"
}
setupOutDirs()
configureBuildOnServer()
+
+// anchor for prepare repo. This is post unzip.
+task prepareRepo() {
+ description "This task clears the repo folder to ensure that we run a fresh build every" +
+ " time we create arhives. Otherwise, snapshots will accumulate in the builds folder."
+ doFirst {
+ file(rootProject.ext.repoDir).deleteDir()
+ file(rootProject.ext.repoDir).mkdirs()
+ }
+}
+
+task createArchive(type : Zip) {
+ description "Creates a maven repository that includes just the libraries compiled in this" +
+ " project, without any history from prebuilts."
+ from rootProject.ext.repoDir
+ destinationDir rootProject.ext.distDir
+ into 'm2repository'
+ baseName = String.format("top-of-tree-m2repository-%s", project.ext.buildNumber)
+ dependsOn mainUpload
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/build.gradle b/jetifier/jetifier/core/build.gradle
index 1ac3f36..72edcfa 100644
--- a/jetifier/jetifier/core/build.gradle
+++ b/jetifier/jetifier/core/build.gradle
@@ -14,13 +14,26 @@
* limitations under the License
*/
-version '1.0'
+apply plugin: 'maven'
+
+version '0.2'
dependencies {
- compile group: 'org.ow2.asm', name: 'asm', version: '5.2'
- compile group: 'org.ow2.asm', name: 'asm-commons', version: '5.2'
- compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
- compile group: 'org.jdom', name: 'jdom2', version: '2.0.6'
- testCompile group: 'junit', name: 'junit', version: '4.12'
- testCompile group: 'com.google.truth', name: 'truth', version: '0.31'
+ compile("org.ow2.asm:asm:5.2")
+ compile("org.ow2.asm:asm-commons:5.2")
+ compile("com.google.code.gson:gson:2.8.0")
+ compile("org.jdom:jdom2:2.0.6")
+ testCompile("junit:junit:4.12")
+ testCompile("com.google.truth:truth:0.31")
}
+
+uploadArchives {
+ repositories {
+ mavenDeployer {
+ repository(url: rootProject.ext.repoDir)
+ }
+ }
+}
+
+rootProject.mainUpload.dependsOn tasks["uploadArchives"]
+tasks["uploadArchives"].dependsOn rootProject.prepareRepo
\ No newline at end of file
diff --git a/jetifier/jetifier/gradle-plugin/build.gradle b/jetifier/jetifier/gradle-plugin/build.gradle
index ca24b72..3b001f8 100644
--- a/jetifier/jetifier/gradle-plugin/build.gradle
+++ b/jetifier/jetifier/gradle-plugin/build.gradle
@@ -14,9 +14,9 @@
* limitations under the License
*/
-version '0.1'
+apply plugin: 'maven'
-apply plugin: 'maven-publish'
+version '0.2'
dependencies {
compile project(':core')
@@ -31,10 +31,13 @@
with jar
}
-publishing {
- publications {
- mavenJava(MavenPublication) {
- from components.java
+uploadArchives {
+ repositories {
+ mavenDeployer {
+ repository(url: rootProject.ext.repoDir)
}
}
}
+
+rootProject.mainUpload.dependsOn tasks["uploadArchives"]
+tasks["uploadArchives"].dependsOn rootProject.prepareRepo
\ No newline at end of file
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/HolderFragment.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/HolderFragment.java
index 100d10a..ca5e181 100644
--- a/lifecycle/extensions/src/main/java/android/arch/lifecycle/HolderFragment.java
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/HolderFragment.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.support.v4.app.Fragment;
@@ -34,7 +35,7 @@
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class HolderFragment extends Fragment {
+public class HolderFragment extends Fragment implements ViewModelStoreOwner {
private static final String LOG_TAG = "ViewModelStores";
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
@@ -69,6 +70,8 @@
mViewModelStore.clear();
}
+ @NonNull
+ @Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProviders.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProviders.java
index ab6bce6..d9894a8 100644
--- a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProviders.java
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProviders.java
@@ -62,6 +62,7 @@
* @param fragment a fragment, in whose scope ViewModels should be retained
* @return a ViewModelProvider instance
*/
+ @NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
ViewModelProvider.AndroidViewModelFactory factory =
@@ -79,6 +80,7 @@
* @param activity an activity, in whose scope ViewModels should be retained
* @return a ViewModelProvider instance
*/
+ @NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
ViewModelProvider.AndroidViewModelFactory factory =
@@ -97,6 +99,7 @@
* @param factory a {@code Factory} to instantiate new ViewModels
* @return a ViewModelProvider instance
*/
+ @NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
checkApplication(checkActivity(fragment));
@@ -113,6 +116,7 @@
* @param factory a {@code Factory} to instantiate new ViewModels
* @return a ViewModelProvider instance
*/
+ @NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStores.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStores.java
index e79c934..348a06e 100644
--- a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStores.java
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStores.java
@@ -38,6 +38,7 @@
* @param activity an activity whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
+ @NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
@@ -52,6 +53,7 @@
* @param fragment a fragment whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
+ @NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
diff --git a/lifecycle/viewmodel/src/test/java/android/arch/lifecycle/ViewModelProviderTest.java b/lifecycle/viewmodel/src/test/java/android/arch/lifecycle/ViewModelProviderTest.java
index 37d2020..142f19a 100644
--- a/lifecycle/viewmodel/src/test/java/android/arch/lifecycle/ViewModelProviderTest.java
+++ b/lifecycle/viewmodel/src/test/java/android/arch/lifecycle/ViewModelProviderTest.java
@@ -21,6 +21,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import android.arch.lifecycle.ViewModelProvider.NewInstanceFactory;
+import android.support.annotation.NonNull;
import org.junit.Assert;
import org.junit.Before;
@@ -72,6 +73,7 @@
public void testOwnedBy() {
final ViewModelStore store = new ViewModelStore();
ViewModelStoreOwner owner = new ViewModelStoreOwner() {
+ @NonNull
@Override
public ViewModelStore getViewModelStore() {
return store;
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/ListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/ListItemActivity.java
index d270860..6594928 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/ListItemActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/ListItemActivity.java
@@ -227,6 +227,14 @@
.withBody("Only body - no title. " + mContext.getString(R.string.long_text))
.build());
+ mItems.add(new ListItem.Builder(mContext)
+ .withTitle("Switch - initially unchecked")
+ .withSwitch(false, true, (button, isChecked) -> {
+ Toast.makeText(mContext,
+ isChecked ? "checked" : "unchecked", Toast.LENGTH_SHORT).show();
+ })
+ .build());
+
mListProvider = new ListItemProvider.ListProvider(mItems);
}
diff --git a/samples/SupportCarDemos/src/main/res/values/themes.xml b/samples/SupportCarDemos/src/main/res/values/themes.xml
index e2da9a6..068da60 100644
--- a/samples/SupportCarDemos/src/main/res/values/themes.xml
+++ b/samples/SupportCarDemos/src/main/res/values/themes.xml
@@ -18,6 +18,7 @@
<!-- The main theme for all activities within the Car Demo. -->
<style name="CarTheme" parent="android:Theme.Material.Light">
<item name="android:windowBackground">@color/car_grey_50</item>
+ <item name="android:windowLightStatusBar">true</item>
<item name="android:colorAccent">@color/car_yellow_500</item>
<item name="android:colorPrimary">@color/car_highlight</item>
<item name="android:colorPrimaryDark">@color/car_grey_300</item>
@@ -26,6 +27,6 @@
<item name="android:progressBarStyleHorizontal">
@style/Widget.Car.ProgressBar.Horizontal
</item>
- <item name="android:windowLightStatusBar">true</item>
+ <item name="android:colorControlActivated">@color/car_accent</item>
</style>
</resources>
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 facf071..c0a7a61 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
@@ -274,11 +274,14 @@
private PendingIntent getBroadcastIntent(String action, String message) {
Intent intent = new Intent(action);
+ intent.setClass(getContext(), SliceBroadcastReceiver.class);
+ // Ensure a new PendingIntent is created for each message.
+ int requestCode = 0;
if (message != null) {
intent.putExtra(EXTRA_TOAST_MESSAGE, message);
+ requestCode = message.hashCode();
}
- intent.setClass(getContext(), SliceBroadcastReceiver.class);
- return PendingIntent.getBroadcast(getContext(), 0, intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ return PendingIntent.getBroadcast(getContext(), requestCode, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
}
}
diff --git a/transition/src/main/java/android/support/transition/TransitionSet.java b/transition/src/main/java/android/support/transition/TransitionSet.java
index 404245a..12113e7 100644
--- a/transition/src/main/java/android/support/transition/TransitionSet.java
+++ b/transition/src/main/java/android/support/transition/TransitionSet.java
@@ -51,7 +51,7 @@
* transition on the affected view targets:</p>
* <pre>
* <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
- * android:ordering="sequential">
+ * android:transitionOrdering="sequential">
* <fade/>
* <changeBounds/>
* </transitionSet>
diff --git a/wear/build.gradle b/wear/build.gradle
index 4878dab..2eccacb 100644
--- a/wear/build.gradle
+++ b/wear/build.gradle
@@ -10,8 +10,8 @@
api(project(":support-annotations"))
api(project(":support-core-ui"))
api(project(":support-fragment"))
- api(project(":percent"))
api(project(":recyclerview-v7"))
+ api(CONSTRAINT_LAYOUT)
androidTestImplementation(TEST_RUNNER)
androidTestImplementation(ESPRESSO_CORE)
diff --git a/wear/res/layout/ws_single_page_nav_drawer_1_item.xml b/wear/res/layout/ws_single_page_nav_drawer_1_item.xml
index 550d737..fd5a19f 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_1_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_1_item.xml
@@ -13,8 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
@@ -22,12 +23,13 @@
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_0"
android:layout_centerInParent="true"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
- <TextView
- android:id="@+id/ws_nav_drawer_text"
- android:layout_below="@id/ws_nav_drawer_icon_0"
- style="@style/WsSinglePageNavDrawerTextStyle"/>
+ <include layout="@layout/ws_single_page_nav_drawer_text" />
-</android.support.percent.PercentRelativeLayout>
+</android.support.constraint.ConstraintLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_2_item.xml b/wear/res/layout/ws_single_page_nav_drawer_2_item.xml
index 63dbbe6..9868eba 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_2_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_2_item.xml
@@ -13,18 +13,28 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_2_items_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_2_items_end"
+ style="@style/WsVerticalGuideStyle" />
+
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_0"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_2_items"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_start"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
@@ -32,13 +42,12 @@
android:id="@+id/ws_nav_drawer_icon_1"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_2_items"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_end"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
- <TextView
- android:id="@+id/ws_nav_drawer_text"
- android:layout_below="@id/ws_nav_drawer_icon_1"
- style="@style/WsSinglePageNavDrawerTextStyle"/>
+ <include layout="@layout/ws_single_page_nav_drawer_text" />
-</android.support.percent.PercentRelativeLayout>
+</android.support.constraint.ConstraintLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_3_item.xml b/wear/res/layout/ws_single_page_nav_drawer_3_item.xml
index 209ec16..211b741 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_3_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_3_item.xml
@@ -13,38 +13,48 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_3_items_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_3_items_end"
+ style="@style/WsVerticalGuideStyle" />
+
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_0"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_3_items"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_start"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_1"
- android:layout_centerInParent="true"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_2"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_3_items"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_end"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
- <TextView
- android:id="@+id/ws_nav_drawer_text"
- android:layout_below="@id/ws_nav_drawer_icon_2"
- style="@style/WsSinglePageNavDrawerTextStyle"/>
+ <include layout="@layout/ws_single_page_nav_drawer_text" />
-</android.support.percent.PercentRelativeLayout>
+</android.support.constraint.ConstraintLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_4_item.xml b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
index 1b2c163..6969915 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
@@ -13,46 +13,61 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_4_items_top"
+ style="@style/WsHorizontalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_4_items_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_4_items_end"
+ style="@style/WsVerticalGuideStyle" />
+
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_0"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_4_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_1"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_4_items_horizontal"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_2"
- android:layout_centerInParent="true"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_3"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_4_items_horizontal"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
- <TextView
- android:id="@+id/ws_nav_drawer_text"
- android:layout_below="@id/ws_nav_drawer_icon_3"
- style="@style/WsSinglePageNavDrawerTextStyle"/>
+ <include layout="@layout/ws_single_page_nav_drawer_text" />
-</android.support.percent.PercentRelativeLayout>
+</android.support.constraint.ConstraintLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_5_item.xml b/wear/res/layout/ws_single_page_nav_drawer_5_item.xml
index 699533d..047ec52 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_5_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_5_item.xml
@@ -13,56 +13,77 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_5_items_top"
+ style="@style/WsHorizontalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top_row_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_5_items_top_row_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top_row_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_5_items_top_row_end"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_middle_row_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_5_items_middle_row_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_middle_row_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_5_items_middle_row_end"
+ style="@style/WsVerticalGuideStyle" />
+
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_0"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_outer_rows"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_5_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_top_row_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle"/>
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_1"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_outer_rows"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_5_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_top_row_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_2"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_middle_row"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_middle_row_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_3"
- android:layout_centerInParent="true"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_4"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_middle_row"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_middle_row_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
- <TextView
- android:id="@+id/ws_nav_drawer_text"
- android:layout_below="@id/ws_nav_drawer_icon_4"
- style="@style/WsSinglePageNavDrawerTextStyle"/>
+ <include layout="@layout/ws_single_page_nav_drawer_text" />
-</android.support.percent.PercentRelativeLayout>
+</android.support.constraint.ConstraintLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_6_item.xml b/wear/res/layout/ws_single_page_nav_drawer_6_item.xml
index 00625a1..62cb8bf 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_6_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_6_item.xml
@@ -13,64 +13,75 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_6_items_top"
+ style="@style/WsHorizontalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_bottom"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_6_items_bottom"
+ style="@style/WsHorizontalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_6_items_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_6_items_end"
+ style="@style/WsVerticalGuideStyle" />
+
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_0"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_1"
- android:layout_centerHorizontal="true"
- android:layout_alignParentTop="true"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_2"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_3"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
- app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+ app:layout_constraintBottom_toBottomOf="@id/ws_nav_drawer_guide_bottom"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_4"
- android:layout_centerHorizontal="true"
- android:layout_alignParentBottom="true"
- app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+ app:layout_constraintBottom_toBottomOf="@id/ws_nav_drawer_guide_bottom"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
-
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_5"
- android:layout_alignParentEnd="true"
- android:layout_alignParentBottom="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
- app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+ app:layout_constraintBottom_toBottomOf="@id/ws_nav_drawer_guide_bottom"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
-</android.support.percent.PercentRelativeLayout>
+</android.support.constraint.ConstraintLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_7_item.xml b/wear/res/layout/ws_single_page_nav_drawer_7_item.xml
index 5daef22..1e6eb8c 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_7_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_7_item.xml
@@ -13,69 +13,94 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_7_items_top"
+ style="@style/WsHorizontalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_bottom"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_7_items_bottom"
+ style="@style/WsHorizontalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top_bottom_row_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_7_items_top_bottom_row_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_top_bottom_row_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_7_items_top_bottom_row_end"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_middle_row_start"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_7_items_middle_row_start"
+ style="@style/WsVerticalGuideStyle" />
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_guide_middle_row_end"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_margin_7_items_middle_row_end"
+ style="@style/WsVerticalGuideStyle" />
+
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_0"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_top_bottom_row_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_1"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
- app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+ app:layout_constraintTop_toTopOf="@id/ws_nav_drawer_guide_top"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_top_bottom_row_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_2"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_middle_row"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_middle_row_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_3"
- android:layout_centerInParent="true"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_4"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_middle_row"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_middle_row_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_5"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true"
- app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
- app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+ app:layout_constraintBottom_toBottomOf="@id/ws_nav_drawer_guide_bottom"
+ app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_top_bottom_row_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
<android.support.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_6"
- android:layout_alignParentEnd="true"
- android:layout_alignParentBottom="true"
- app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
- app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+ app:layout_constraintBottom_toBottomOf="@id/ws_nav_drawer_guide_bottom"
+ app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_top_bottom_row_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
-</android.support.percent.PercentRelativeLayout>
+</android.support.constraint.ConstraintLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_text.xml b/wear/res/layout/ws_single_page_nav_drawer_text.xml
new file mode 100644
index 0000000..58f2b85
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_text.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2017 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.
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ tools:showIn="@layout/ws_single_page_nav_drawer_5_item">
+
+ <android.support.constraint.Guideline
+ android:id="@+id/ws_nav_drawer_text_guide"
+ app:layout_constraintGuide_percent="@dimen/ws_nav_drawer_text_guide"
+ style="@style/WsHorizontalGuideStyle" />
+
+ <TextView
+ android:id="@+id/ws_nav_drawer_text"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:fontFamily="sans-serif-condensed"
+ android:textSize="@dimen/ws_nav_drawer_text_size"
+ android:gravity="center"
+ android:maxLines="2"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_marginTop="@dimen/ws_nav_drawer_single_page_half_icon_size"
+ app:layout_constraintTop_toBottomOf="@id/ws_nav_drawer_text_guide" />
+</merge>
\ No newline at end of file
diff --git a/wear/res/values-sw210dp/dimens.xml b/wear/res/values-sw210dp/dimens.xml
index 8f69e0b..07cd3b4 100644
--- a/wear/res/values-sw210dp/dimens.xml
+++ b/wear/res/values-sw210dp/dimens.xml
@@ -16,6 +16,7 @@
<resources>
<dimen name="ws_nav_drawer_text_size">14sp</dimen>
+ <dimen name="ws_nav_drawer_single_page_half_icon_size">16dp</dimen>
<dimen name="ws_nav_drawer_single_page_icon_size">32dp</dimen>
<dimen name="ws_nav_drawer_single_page_circle_radius">27dp</dimen>
diff --git a/wear/res/values/dimens.xml b/wear/res/values/dimens.xml
index 26d6c0a..9ae13bb 100644
--- a/wear/res/values/dimens.xml
+++ b/wear/res/values/dimens.xml
@@ -36,23 +36,34 @@
<dimen name="ws_action_drawer_expand_icon_top_margin">-3dp</dimen>
<!-- Dimensions for the single page WearableNavigationDrawerView. -->
+ <dimen name="ws_nav_drawer_single_page_half_icon_size">14dp</dimen>
<dimen name="ws_nav_drawer_single_page_icon_size">28dp</dimen>
<dimen name="ws_nav_drawer_single_page_icon_padding">6dp</dimen>
<dimen name="ws_nav_drawer_single_page_circle_radius">24dp</dimen>
<dimen name="ws_nav_drawer_text_size">12sp</dimen>
- <fraction name="ws_nav_drawer_text_margin">13%</fraction>
- <fraction name="ws_nav_drawer_margin_2_items">20%</fraction>
- <fraction name="ws_nav_drawer_margin_3_items">16%</fraction>
- <fraction name="ws_nav_drawer_margin_4_items_vertical">16.9%</fraction>
- <fraction name="ws_nav_drawer_margin_4_items_horizontal">13%</fraction>
- <fraction name="ws_nav_drawer_margin_5_items_horizontal_outer_rows">27.4%</fraction>
- <fraction name="ws_nav_drawer_margin_5_items_horizontal_middle_row">13%</fraction>
- <fraction name="ws_nav_drawer_margin_5_items_vertical">16.9%</fraction>
- <fraction name="ws_nav_drawer_margin_6_items_horizontal">16%</fraction>
- <fraction name="ws_nav_drawer_margin_6_items_vertical">26.8%</fraction>
- <fraction name="ws_nav_drawer_margin_7_items_horizontal_outer_rows">27.4%</fraction>
- <fraction name="ws_nav_drawer_margin_7_items_horizontal_middle_row">13%</fraction>
- <fraction name="ws_nav_drawer_margin_7_items_vertical">16.9%</fraction>
+ <item name="ws_nav_drawer_text_guide" format="float" type="dimen">0.63</item>
+ <item name="ws_nav_drawer_margin_2_items_start" format="float" type="dimen">.2</item>
+ <item name="ws_nav_drawer_margin_2_items_end" format="float" type="dimen">.8</item>
+ <item name="ws_nav_drawer_margin_3_items_start" format="float" type="dimen">.16</item>
+ <item name="ws_nav_drawer_margin_3_items_end" format="float" type="dimen">.84</item>
+ <item name="ws_nav_drawer_margin_4_items_top" format="float" type="dimen">.169</item>
+ <item name="ws_nav_drawer_margin_4_items_start" format="float" type="dimen">.13</item>
+ <item name="ws_nav_drawer_margin_4_items_end" format="float" type="dimen">.87</item>
+ <item name="ws_nav_drawer_margin_5_items_top" format="float" type="dimen">.169</item>
+ <item name="ws_nav_drawer_margin_5_items_top_row_start" format="float" type="dimen">.274</item>
+ <item name="ws_nav_drawer_margin_5_items_top_row_end" format="float" type="dimen">.726</item>
+ <item name="ws_nav_drawer_margin_5_items_middle_row_start" format="float" type="dimen">.13</item>
+ <item name="ws_nav_drawer_margin_5_items_middle_row_end" format="float" type="dimen">.87</item>
+ <item name="ws_nav_drawer_margin_6_items_top" format="float" type="dimen">.268</item>
+ <item name="ws_nav_drawer_margin_6_items_bottom" format="float" type="dimen">.732</item>
+ <item name="ws_nav_drawer_margin_6_items_start" format="float" type="dimen">.16</item>
+ <item name="ws_nav_drawer_margin_6_items_end" format="float" type="dimen">.84</item>
+ <item name="ws_nav_drawer_margin_7_items_top" format="float" type="dimen">.169</item>
+ <item name="ws_nav_drawer_margin_7_items_bottom" format="float" type="dimen">.831</item>
+ <item name="ws_nav_drawer_margin_7_items_top_bottom_row_start" format="float" type="dimen">.274</item>
+ <item name="ws_nav_drawer_margin_7_items_top_bottom_row_end" format="float" type="dimen">.726</item>
+ <item name="ws_nav_drawer_margin_7_items_middle_row_start" format="float" type="dimen">.13</item>
+ <item name="ws_nav_drawer_margin_7_items_middle_row_end" format="float" type="dimen">.87</item>
<dimen name="ws_peek_view_top_padding">8dp</dimen>
<dimen name="ws_peek_view_bottom_padding">8dp</dimen>
diff --git a/wear/res/values/styles.xml b/wear/res/values/styles.xml
index 5af7e6f..1908946 100644
--- a/wear/res/values/styles.xml
+++ b/wear/res/values/styles.xml
@@ -22,17 +22,15 @@
<item name="background_radius">@dimen/ws_nav_drawer_single_page_circle_radius</item>
<item name="background_color">#33ffffff</item>
</style>
- <style name="WsSinglePageNavDrawerTextStyle">
+ <style name="WsVerticalGuideStyle">
+ <item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
- <item name="android:layout_width">match_parent</item>
- <item name="android:fontFamily">sans-serif-condensed</item>
- <item name="android:textSize">@dimen/ws_nav_drawer_text_size</item>
- <item name="android:gravity">center</item>
- <item name="android:maxLines">2</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="layout_marginTopPercent">@fraction/ws_nav_drawer_text_margin</item>
- <item name="layout_marginStartPercent">@fraction/ws_nav_drawer_text_margin</item>
- <item name="layout_marginEndPercent">@fraction/ws_nav_drawer_text_margin</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+ <style name="WsHorizontalGuideStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">horizontal</item>
</style>
<style name="Widget.Wear.WearableDrawerView" parent="">
<item name="android:elevation">@dimen/ws_wearable_drawer_view_elevation</item>